嵌套前缀与Python中的记录器相对应

时间:2014-11-10 21:52:48

标签: python logging

我目前正在开发一个使用单根记录器的项目。我从阅读中了解到日志记录是一件很糟糕的事情,但我很难找到一个很好的解决方案来为我们带来好处。

我们所做的事情(部分是为了避免使用不同的记录器,但部分给我们提供了一个很好的功能)就是拥有一个log_prefix装饰器。

e.g。

@log_prefix("Download")
def download_file():
    logging.info("Downloading file..")
    connection = get_connection("127.0.0.1")
    //Do other stuff
    return file

@log_prefix("GetConnection")
def get_connection(url):
    logging.info("Making connection")
    //Do other stuff
    logging.info("Finished making connection")
    return connection

这为我们提供了一些很好的格式化日志,可能看起来像:

Download:Downloading file..
Download:GetConnection:Making Connection
Download:GetConnection:Other stuff
Download:GetConnection:Finished making connection
Download:Other stuff

这也意味着如果我们有

@log_prefix("StartTelnetSession")
logging.info("Starting telnet session..")
connection = get_connection("127.0.0.1")

我们在最后得到相同的前缀:

StartTelnetSession:Starting telnet session..
StartTelnetSession:GetConnection:Making Connection
StartTelnetSession:GetConnection:Other stuff
StartTelnetSession:GetConnection:Finished making connection

事实证明,这对于开发和支持非常有用。

我可以看到很多情况,实际上只是使用一个单独的记录器来解决我们的问题,但我也可以看到丢弃我们的嵌套会使事情变得更糟的情况。

是否有任何模式或常用用于嵌套记录器?即

logging.getLogger("Download").getLogger("MakingConnection")

或者我在这里遗漏了什么?

3 个答案:

答案 0 :(得分:3)

日志名称是分层的。

logger = logging.getLogger("Download.MakingConnection")

此记录器将继承logging.getLogger("Download")。'

中的任何配置

Python 2.7还添加了一个便捷函数,用于访问任意记录器的后代。

logger = logging.getLogger("Download.MakingConnection")

parent_logger = logging.getLogger("Download")
child_logger = parent_logger.getChild("MakingConnection")

assert logger is child_logger

答案 1 :(得分:3)

您可以使用LoggerAdapter添加额外的上下文信息:

utils_logging.py:

import functools

def log_prefix(logger, label, prefix=list()):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            prefix.append(label)
            logger.extra['prefix'] = ':'.join(prefix)
            result = func(*args, **kwargs)
            prefix.pop()
            logger.extra['prefix'] = ':'.join(prefix)
            return result
        return wrapper
    return decorator

foo.py:

import logging
import utils_logging as UL
import bar

logger = logging.LoggerAdapter(logging.getLogger(__name__), {'prefix':''})
@UL.log_prefix(logger, "Download")
def download_file():
    logger.info("Downloading file..")
    connection = bar.get_connection("127.0.0.1")

if __name__ == '__main__':
    logging.basicConfig(
        level=logging.INFO,
        format='%(prefix)s %(name)s %(levelname)s %(message)s')

    download_file()
    bar.get_connection('foo')

bar.py:

import logging
import utils_logging as UL
logger = logging.LoggerAdapter(logging.getLogger(__name__), {'prefix':''})

@UL.log_prefix(logger, "GetConnection")
def get_connection(url):
    logger.info("Making connection")
    logger.info("Finished making connection")

产量

Download __main__ INFO Downloading file..
Download:GetConnection bar INFO Making connection
Download:GetConnection bar INFO Finished making connection
GetConnection bar INFO Making connection
GetConnection bar INFO Finished making connection

注意:我认为为每个实例添加一个新的Logger实例并不是一个好主意 前缀因为these instances are not garbage collected。所有 你需要的是某个prefix变量取决于具有不同的值 上下文。你不需要一个新的Logger实例 - 一个LoggerAdapter 做。

答案 2 :(得分:0)

以下是使用logging.Filter修改record.msg的替代方法。通过修改消息而不是添加%(prefix)s字段, 格式不需要改变。

这样可以更轻松地混合使用log_prefix和不使用{j}的记录器。

要获取前缀,应通过调用add_prefix_filter来初始化记录器:

logger = UL.add_prefix_filter(logging.getLogger(__name__))

要将标签附加到前缀,这些函数应该像以前一样用@log_prefix(label)进行修饰。


utils_logging.py:

import functools
import logging

prefix = list()

def log_prefix(label):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            prefix.append(label)
            try:
                result = func(*args, **kwargs)
            finally:
                prefix.pop()
            return result
        return wrapper
    return decorator

class PrefixFilter(logging.Filter):
    def filter(self, record):
        if prefix:
            record.msg = '{}:{}'.format(':'.join(prefix), record.msg)
        return True

def add_prefix_filter(logger):
    logger.addFilter(PrefixFilter())
    return logger

main.py:

import logging
import bar
import utils_logging as UL

logger = UL.add_prefix_filter(logging.getLogger(__name__))

@UL.log_prefix("Download")
def download_file():
    logger.info("Downloading file..")
    connection = bar.get_connection("127.0.0.1")

if __name__ == '__main__':
    logging.basicConfig(
        level=logging.INFO,
        format='%(message)s')

    logger.info('Starting...')
    download_file()
    bar.get_connection('foo')

bar.py:

import logging
import utils_logging as UL

logger = UL.add_prefix_filter(logging.getLogger(__name__))

@UL.log_prefix("GetConnection")
def get_connection(url):
    logger.info("Making connection")
    logger.info("Finished making connection")

产量

Starting...
Download:Downloading file..
Download:GetConnection:Making connection
Download:GetConnection:Finished making connection
GetConnection:Making connection
GetConnection:Finished making connection