我目前正在开发一个使用单根记录器的项目。我从阅读中了解到日志记录是一件很糟糕的事情,但我很难找到一个很好的解决方案来为我们带来好处。
我们所做的事情(部分是为了避免使用不同的记录器,但部分给我们提供了一个很好的功能)就是拥有一个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")
或者我在这里遗漏了什么?
答案 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