【发布时间】:2019-02-25 18:21:45
【问题描述】:
使用 python 标准日志记录模块,可以使用以下命令添加原始日志调用的行号:%(lineno)s.
如何使用 structlog 来完成?
【问题讨论】:
标签: structlog
使用 python 标准日志记录模块,可以使用以下命令添加原始日志调用的行号:%(lineno)s.
如何使用 structlog 来完成?
【问题讨论】:
标签: structlog
我也有类似的需求,最后我创建了一个custom processor
我查看了what structlog does 以输出模块和行号,当它被告知“假装” 以与logging 库兼容的模式进行格式化时(意思是:当它使用普通的stdlib.LoggerFactory),我从中找到了灵感。关键是以下几个词...
通过使用 structlog 的 structlog.stdlib.LoggerFactory,还可以确保函数名称和行号等变量在您的日志格式中正确展开。
代码似乎一直在寻找执行帧,直到找到与日志无关的模块。
我在一个名为my_libs.util.logger 的模块中设置了structlog 的所有设置,因此我想获取不在该模块中的第一帧。为了做到这一点,我告诉它将与日志记录相关的my_libs.util.logger 添加到这些排除项中。这就是下面代码中的additional_ignores 的作用。
在示例中,为了清楚起见,我在排除列表中硬编码了模块的名称 ('my_libs.util.logger'),但如果您有类似的设置,则最好使用 __name__。这样做是忽略由于日志机制到位而存在的执行帧。您可以将其视为一种忽略在实际记录消息过程中可能发生的调用的方式。或者,换句话说,调用发生在您确实想要输出的实际模块/行中发生的logging.info("Foo") 之后。
一旦找到正确的框架,提取任何类型的信息(模块名称、函数名称、行号...)都非常容易,尤其是使用inspect module。我选择输出模块名称和行号,但可以添加更多字段。
# file my_libs/util/logger.py
import inspect
from structlog._frames import _find_first_app_frame_and_name
def show_module_info_processor(logger, _, event_dict):
# If by any chance the record already contains a `modline` key,
# (very rare) move that into a 'modline_original' key
if 'modline' in event_dict:
event_dict['modline_original'] = event_dict['modline']
f, name = _find_first_app_frame_and_name(additional_ignores=[
"logging",
'my_libs.util.logger', # could just be __name__
])
if not f:
return event_dict
frameinfo = inspect.getframeinfo(f)
if not frameinfo:
return event_dict
module = inspect.getmodule(f)
if not module:
return event_dict
if frameinfo and module:
# The `if` above is probably redundant, since we already
# checked for frameinfo and module but... eh... paranoia.
event_dict['modline'] = '{}:{}'.format(
module.__name__,
frameinfo.lineno,
)
return event_dict
def setup_structlog(env=None):
# . . .
ch.setFormatter(logging.Formatter('%(message)s'))
logging.getLogger().handlers = [ch]
processors = [
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
# . . . more . . .
show_module_info_processor, # THIS!!!
structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S"),
structlog.processors.format_exc_info,
structlog.processors.StackInfoRenderer(),
# . . . more . . .
]
# . . . more . . .
structlog.configure_once(
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
context_class=structlog.threadlocal.wrap_dict(dict),
processors=processors,
)
这会产生如下输出:
server_1
| INFO [my_libs.hdfs] 2019-07-01 01:01:01 [info ] Initialized HDFS
[my_libs.hdfs] modline=my_libs.hdfs:31
【讨论】:
import inspect 添加到代码中
frameinfo.function。值得注意的是,_find_first_app_frame_and_name 可能会在未来版本的 structlog 中中断;建议在您的答案中包含您自己的副本。或者更好的是,提交 PR 并将其放入图书馆 - 非常有用!
TypeError: 'str' object does not support item assignment:您已将自定义处理器置于 JSONRenderer 之下。不要那样做。 处理器的顺序很重要!!!
看看这个关于如何获取行号的更一般问题的答案。 https://stackoverflow.com/a/3056270/5909155 这不能使用 log.bind(...) 绑定到记录器,因为每次登录时都必须对其进行评估。因此,您应该像这样添加一个键值对
logger.log(..., lineno=inspect.getframeinfo(inspect.currentframe()).lineno)
每次。不过,也许可以将其包装在一个函数中,如下所示:https://stackoverflow.com/a/20372465/5909155 别忘了
import inspect
【讨论】: