Python Logging无法获取原始调用类及行号的问题解决
问题解决:让日志显示
print的原始调用位置 问题分析
当前日志输出显示logger.py->write():39,是因为logging模块默认记录调用log()方法的栈帧位置(也就是Logger.write方法的位置)。要显示触发print的原始代码位置,需要让logging回溯到print的上层调用栈。
解决方案(Python 3.8+ 推荐)
Python 3.8及以上版本的logging模块提供了stacklevel参数,可直接指定日志记录要跳过的栈层级,从而定位到原始调用位置。修改Logger类的write和flush方法,在调用logger.log时添加stacklevel=3参数即可:
修改后的完整代码:
import logging import sys from datetime import datetime class Logger(object): """ Fake file-like stream object that redirects writes to a logger instance. """ def __init__(self, log_level=logging.INFO): month = datetime.today().strftime('%Y-%m') self.logFile = f'./logs/{month}.log' self.terminal = sys.stdout logger = logging.getLogger(__name__) logger.handlers = [] fh = logging.FileHandler(filename=self.logFile, mode='a') formatter = logging.Formatter('%(asctime)s:[%(filename)s->%(funcName)s():%(lineno)s] %(levelname)s>> %(message)s') fh.setFormatter(formatter) logger.addHandler(fh) logger.setLevel("DEBUG") self.logger = logger self.log_level = log_level self.linebuf = '' sys.stdout = self def write(self, buf): self.terminal.write(buf) temp_linebuf = self.linebuf + buf self.linebuf = '' for line in temp_linebuf.splitlines(True): if line[-1] == '\n': # 跳过log、write、print三层栈,定位到原始调用位置 self.logger.log(self.log_level, line.rstrip(), stacklevel=3) else: self.linebuf += line def flush(self): if self.linebuf != '': self.logger.log(self.log_level, self.linebuf.rstrip(), stacklevel=3) self.linebuf = ''
为什么用stacklevel=3?
栈层级的逻辑链:
- 第1层:
logging.Logger.log方法本身 - 第2层:
Logger.write/Logger.flush方法(调用log的地方) - 第3层:内置
print函数(调用write的地方) - 第4层:原始调用位置(比如
Foo.bar)
设置stacklevel=3会让logging跳过前3层,直接记录第4层的栈帧信息,也就是你期望的原始代码位置。
兼容Python 3.7及以下版本
如果使用Python 3.7或更早版本(无stacklevel参数),可以手动通过inspect模块获取栈帧信息,再通过extra参数传递给日志记录:
修改write方法中的日志调用部分:
import inspect # 在write方法内的日志调用处替换为: if line[-1] == '\n': # 回溯到原始调用的栈帧 frame = inspect.currentframe().f_back.f_back.f_back extra = { 'filename': frame.f_code.co_filename.split('/')[-1], 'funcName': frame.f_code.co_name, 'lineno': frame.f_lineno } self.logger.log(self.log_level, line.rstrip(), extra=extra) # 释放栈帧引用,避免内存泄漏 del frame
同理,flush方法中的日志调用也需要做相同修改。
内容的提问来源于stack exchange,提问作者Edgar O
相关产品推荐
相关产品推荐

