Как зарегистрировать два logging.Logger?
Есть вот такая функция регистрации logger
import logging
def register_logger(
base_path: PathLike[str],
file_name: str,
register_name: str | None,
log_level: Any,
) -> logging.Logger:
path = Path(base_path, f"{file_name}.log")
logger = logging.getLogger(register_name)
logger.setLevel(level=log_level)
handler = logging.FileHandler(path, encoding="utf8")
formatter = logging.Formatter("\n%(asctime)s - %(levelname)s\n%(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
Нужно зарегистрировать два обработчика: один безымянный, второй с именем. Условно это выглядит так:
logger = register_logger(path, "server", None, 1)
logger_web = register_logger(path, "web", "web", 1)
# И допустим в другом файле
logger_web = logging.getLogger("web")
Но, при попытке использования logger_web.error(...)
, последний дублирует записи в оба файла. При этом безымянный logger
пишет только в свой файл .../path/server.log
.
Что здесь может быть не так, и как избежать дублирования записей?
Ответы (1 шт):
Потому что когда создается логгер без имени, то он называется root
логгер:
logger = register_logger(path, "server", None, 1)
Он есть по умолчанию и это корневой логгер, к которому все другие логгеры привязаны.
Поэтому, если хочешь абсолютно независимые логгеры, то дай им обоим названия:
logger = register_logger(path, "server", "server", 1)
logger_web = register_logger(path, "web", "web", 1)
UPD:
Если это решение не подходит, то можно использовать фильтр для хэндлера.
Для этого нужно будет сначала удалить все хэндлеры по умолчанию от root логгера. Затем добавить свой хэндлер, в котором есть фильтр.
А фильтре мы будем принимать только логи, которые пришли от root логгера, а все остальные будут игнорироваться.
class OnlyRootFilter(logging.Filter):
def filter(self, record):
return record.name == "root"
logger = logging.getLogger()
for handler in logger.handlers:
logger.removeHandler(handler)
handler = logging.StreamHandler()
handler.addFilter(OnlyRootFilter())
logger.addHandler(handler)
Надеюсь идея понятна, это можно внедрить в твою функцию, например с проверкой if register_name == None:
или подобной.
UPDUPD Вот такой простой и короткий вариант:
# Добавим в конце функции register_logger
if register_name is None or register_name == "root":
handler.addFilter(logging.Filter(name="root"))