如何在整个程序中为python定义一次记录器?

时间:2018-06-15 14:15:15

标签: python python-2.7 logging

我想在我的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 个答案:

答案 0 :(得分: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()