代码之家  ›  专栏  ›  技术社区  ›  Remotec

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

  •  0
  • Remotec  · 技术社区  · 7 年前

    我想在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 回复  |  直到 7 年前
        1
  •  1
  •   Martijn Pieters    7 年前

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

    下面我将详细介绍如何处理这个问题,但是还有一个替代方案,它完全不涉及适配器,而是使用附加到您创建的处理程序的筛选器,这样您就可以避免完全处理适配器。再往下看。

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

    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
    

    这将直接在record对象上添加额外的字段,并 总是 收益率 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() 功能齐全,使用方便 日志记录.getLogger() 到处都是。