You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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. 第1层:logging.Logger.log 方法本身
  2. 第2层:Logger.write/Logger.flush 方法(调用log的地方)
  3. 第3层:内置print函数(调用write的地方)
  4. 第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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 16:07:36