日志写入逻辑封装为函数后,如何让日志正确记录调用方的funcName字段
解决日志
funcName字段识别错误的问题 你的问题根源在于Python的logging模块默认会记录直接调用日志方法(比如logger.info)的函数名,也就是你的LoggerWriter,而不是上层的调用者DoSomething。下面提供两种简单有效的解决方案:
方法一:通过inspect模块手动指定调用者函数名
这种方法直接在日志封装函数里获取上层调用者的函数名,通过extra参数传给日志记录方法,覆盖默认的funcName:
import logging import inspect logging.config.fileConfig('logging.ini', disable_existing_loggers=False) logger = logging.getLogger(__name__) def LoggerWriter(df, text): # 获取调用当前函数的上层栈帧,提取函数名 caller_frame = inspect.currentframe().f_back caller_func_name = caller_frame.f_code.co_name # 传入extra参数覆盖默认的funcName logger.info( 'Test: {}; Method: {}; Description: {}'.format(df['Test'], df['Method'], text), extra={'funcName': caller_func_name} ) class DoStuff: # 注意:实例方法需要添加self参数,否则会报错 def DoSomething(self, df): LoggerWriter(df, 'Start Function') # 此处执行相关操作 LoggerWriter(df, 'Finish Function')
说明:
inspect.currentframe().f_back获取到调用LoggerWriter的栈帧对象f_code.co_name从栈帧中提取对应的函数名extra参数会将自定义的funcName注入到日志记录中,覆盖默认值
如果你的日志封装函数可能被多层嵌套调用(比如A调用LoggerWriter,B调用A),可以用inspect.stack()获取更上层的调用者:
# 替换LoggerWriter中的函数名获取逻辑 caller_func_name = inspect.stack()[1].function
不过inspect.stack()的性能开销比currentframe()略高,高频调用场景下建议优先用第一种方式。
方法二:自定义JSON格式化器,统一修改funcName
如果不想在每个日志调用处手动处理,可以自定义JsonFormatter,在格式化阶段自动调整funcName:
from pythonjsonlogger.jsonlogger import JsonFormatter import logging class CustomJsonFormatter(JsonFormatter): def format(self, record): # 从栈信息中解析上层调用者的函数名 if hasattr(record, 'stack_info') and record.stack_info: # 栈信息的第二行通常包含调用者的函数名 caller_line = record.stack_info.split('\n')[1].strip() if 'in ' in caller_line: record.funcName = caller_line.split('in ')[-1] return super().format(record)
然后修改logging.ini中的formatter配置,指定自定义的格式化器:
[formatter_json] class=your_module_name.CustomJsonFormatter # 替换成你存放CustomJsonFormatter的模块名 format=%(asctime)s %(name)s %(funcName)s %(levelname)s %(message)s
同时需要确保日志记录时开启栈信息,比如在调用logger.info时添加stack_info=True,或者在配置中设置全局栈信息参数。
验证效果
使用第一种方法后,日志输出的funcName会变成DoSomething,完全符合你的期望:
{"asctime": "2021-05-30 14:58:31,796", "name": "__main__", "funcName": "DoSomething", "levelname": "INFO", "message": "Test: X; Method: dev; Description: Start Function."}
内容的提问来源于stack exchange,提问作者Keity
相关产品推荐
相关产品推荐

