首页 > 解决方案 > 如何在 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".

有没有一种方法可以定义我的日志配置一次并在整个应用程序中重复使用它?

标签: pythonpython-2.7logging

解决方案


ALoggingAdapter是一个单独的对象,它不会替换logging.getLogger()调用的结果。您需要将它存储在可以在不同模块之间共享的地方。例如,您可以使用一个单独的模块,您的项目中的所有其他内容都可以从中导入。

我将在下面详细说明如何处理这个问题,但还有一个根本不涉及适配器的替代方案,而是使用附加到您创建的处理程序的过滤器,让您不必完全处理适配器。再往下看。

我也将配置和日志对象处理分开;让主模块调用“设置”函数来配置处理程序和日志级别,并在模块中设置适配器以供其他人导入:

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()任何地方使用。


推荐阅读