如何让日志显示调用Logger的文件行号而非Logger自身文件的行号?
问题:自定义Logger日志的filename和lineno始终指向logger.py而非调用文件
我自定义了Logger类,日志格式如下:
log_format = "%(asctime)s.%(msecs)d %(levelname)-8s [%(processName)s] [%(threadName)s] %(filename)s:%(lineno)d --- %(message)s"
项目目录结构:
. ├── exceptions.py ├── logger.py ├── settings.py └── main.py
在main.py中通过Logger.info("Transcribed audio successfully.")调用日志,但日志输出里的filename和lineno始终指向logger.py:
2023-02-15 10:48:06,241.241 INFO [MainProcess] [MainThread] logger.py:38 --- Transcribed audio successfully.
期望日志显示调用方的文件和行号:
2023-02-15 10:48:06,241.241 INFO [MainProcess] [MainThread] main.py:98 --- Transcribed audio successfully.
当前logger.py代码:
import logging from logging.handlers import RotatingFileHandler class Logger: log_format = "%(asctime)s.%(msecs)d %(levelname)-8s [%(processName)s] [%(threadName)s] %(filename)s:%(lineno)d --- %(message)s" @staticmethod def setup_single_logger(name, logfile, level): handler = RotatingFileHandler(logfile, mode='a', maxBytes=1024 * 1024, backupCount=10) handler.setFormatter(logging.Formatter(Logger.log_format)) logger = logging.getLogger(name) logger.setLevel(level) logger.addHandler(handler) return logger @staticmethod def setup_logging(): Logger.info_logger = Logger.setup_single_logger('INFO', '/path/to/your/logfile.log', logging.INFO) @staticmethod def info(msg, *args, **kwargs): Logger.info_logger.info(msg, *args, **kwargs) Logger.setup_logging()
main.py示例代码:
from logger import Logger Logger.info("Transcribed audio successfully.")
解决方案
问题根源是:Logger.info作为静态方法,调用info_logger.info时,日志模块会捕获当前栈帧(即logger.py中info方法的行号),而非实际调用Logger.info的位置。需要让日志模块跳过当前栈层级,定位到真正的调用方。
方法1:使用Python 3.8+的stacklevel参数(推荐)
Python 3.8及以上版本的日志模块支持stacklevel参数,指定要跳过的栈层级数。修改Logger.info方法如下:
@staticmethod def info(msg, *args, **kwargs): # stacklevel=2 表示跳过当前方法的栈帧,直接获取调用者的信息 Logger.info_logger.info(msg, *args, stacklevel=2, **kwargs)
方法2:兼容旧版本Python(手动获取调用者信息)
如果项目运行在Python 3.8以下版本,可通过inspect模块手动提取调用者的栈帧信息,再通过extra参数传递给日志记录器:
import logging from logging.handlers import RotatingFileHandler import inspect # 新增导入 class Logger: log_format = "%(asctime)s.%(msecs)d %(levelname)-8s [%(processName)s] [%(threadName)s] %(filename)s:%(lineno)d --- %(message)s" @staticmethod def setup_single_logger(name, logfile, level): handler = RotatingFileHandler(logfile, mode='a', maxBytes=1024 * 1024, backupCount=10) handler.setFormatter(logging.Formatter(Logger.log_format)) logger = logging.getLogger(name) logger.setLevel(level) logger.addHandler(handler) return logger @staticmethod def setup_logging(): Logger.info_logger = Logger.setup_single_logger('INFO', '/path/to/your/logfile.log', logging.INFO) @staticmethod def info(msg, *args, **kwargs): # 获取调用者的栈帧 frame = inspect.currentframe().f_back try: # 提取文件名(仅保留最后部分)和行号 filename = frame.f_code.co_filename.split('/')[-1] lineno = frame.f_lineno # 传递自定义的文件名和行号 extra = {'filename': filename, 'lineno': lineno} Logger.info_logger.info(msg, *args, extra=extra, **kwargs) finally: # 手动删除帧引用,避免循环引用导致内存泄漏 del frame Logger.setup_logging()
修改后,日志输出将正确显示实际调用日志的文件和行号。
内容的提问来源于stack exchange,提问作者waykiki
相关产品推荐
相关产品推荐

