Как определить логгер в Python один раз для всей программы?

Я хочу настроить свой логгер один раз в моем проекте Python и использовать его на протяжении всего проекта.

main.py:

import logging
import test    

hostname = {"hostname": socket.gethostname()}
logger = logging.getLogger()
syslog = logging.StreamHandler()
formatter = logging.Formatter("{\"label\":\"%(name)s\", \"level\":\"%(levelname)s\", \"hostname\":\"%(hostname)s\", \"logEntry\": %(message)s, \"timestamp\", \"%(asctime)s\"}")
syslog.setFormatter(formatter)
logger.setLevel(logging.DEBUG)
logger.addHandler(syslog)
logger = logging.LoggerAdapter(logger, hostname)

def entry_point():
    logger.debug("entry_point")
    test.test_function()

entry_point()

test.py:

import logging

logger = logging.getLogger()
logger.setLevel(logging.DEBUG)

def test_function():
    logger.debug("test_function")

Это должно дать мне:

entry_point
test_function

... оба отформатированы с помощью поставщика форматов.

Однако я на самом деле получаю ошибку KeyError: 'hostname' потому что, казалось бы, второй регистратор не знает о hostname формат провайдера. Я также попытался инициализировать оба регистратора с __name__ но потом я получаю No handlers could be found for logger "test",

Есть ли способ, которым я могу определить свою конфигурацию регистрации один раз и повторно использовать ее в моем приложении?

1 ответ

Решение

LoggingAdapter это отдельный объект, он не заменяет результат logging.getLogger() звонки. Вам нужно будет хранить его где-нибудь, чтобы его можно было разделить между разными модулями. Например, вы можете использовать отдельный модуль, из которого импортируется все остальное в вашем проекте.

Ниже я подробно расскажу, как с этим справиться, но есть и альтернатива, которая вообще не использует адаптеры, а вместо этого использует фильтр, который присоединен к созданному вами обработчику, что позволяет вам вообще не иметь дело с адаптерами. Смотрите дальше вниз.

Я бы также выделил конфигурацию и обработку объектов журнала; Пусть основной модуль вызовет функцию "setup" для настройки обработчиков и уровней журнала, а также настроит адаптер в модуле, чтобы другие могли импортировать:

log.py:

import logging
import socket
def setup_logging():
    """Adds a configured stream handler to the root logger"""
    syslog = logging.StreamHandler()
    formatter = logging.Formatter(
        '{"label":"%(name)s", "level":"%(levelname)s",'
        ' "hostname":"%(hostname)s", "logEntry": %(message)s,'
        ' "timestamp", "%(asctime)s"}')
    syslog.setFormatter(formatter)

    logger = logging.getLogger()
    logger.addHandler(syslog)
    logger.setLevel(logging.DEBUG)


def host_log_adapter(logger):
    hostname = {"hostname": socket.gethostname()}
    return logging.LoggerAdapter(logger, hostname)


logger = host_log_adapter(logging.getLogger())

Затем в main делать:

import log
import test

log.setup_logging()

def entry_point():
    log.logger.debug("entry_point")
    test.test_function()

entry_point()

И в test:

from log import logger

def test_function():
    logger.debug("test_function")

В качестве альтернативы, вместо использования адаптера, вы можете добавить информацию в записи журнала с помощью (ab), используя фильтр:

class HostnameInjectingFilter(logging.Filter):
    def __init__(self):
        self.hostname = socket.gethostname()}
    def filter(self, record):
        record.hostname = self.hostname
        return True

Это добавляет дополнительное поле непосредственно к объекту записи и всегда возвращает True, Затем просто добавьте этот фильтр в обработчик, который должен иметь доступное имя хоста:

syslog = logging.StreamHandler()
formatter = logging.Formatter(
    '{"label":"%(name)s", "level":"%(levelname)s",'
    ' "hostname":"%(hostname)s", "logEntry": %(message)s,'
    ' "timestamp", "%(asctime)s"}')
syslog.setFormatter(formatter)
syslog.setFilter(HostnameInjectingFilter())

Это избавляет от необходимости использовать адаптер полностью, так что вы можете удалить host_log_adapter() функционировать целиком и просто использовать logging.getLogger() везде.

Другие вопросы по тегам