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

如何在Python中实现支持标准行为的自定义日志级别?

解决自定义日志级别显示正确调用文件的问题

这个问题我之前也碰到过!核心原因是你用的那个addLoggingLevel方法没有处理日志调用的栈帧层级,导致日志系统错误地把定义mylog函数的main.py当成了调用源。标准日志级别(比如debug、info)之所以能正确显示调用文件,是因为它们内部做了栈帧跳过处理,咱们只需要把这个逻辑加到自定义级别里就行。

正确的自定义日志级别实现方法

针对Python 3.8及以上版本(现在主流环境都是这个版本了),我们可以利用logging.log新增的stacklevel参数来完美解决这个问题。下面是改进后的addLoggingLevel函数,完全兼容标准日志级别的所有行为:

import logging

def addLoggingLevel(levelName, levelNum, methodName=None):
    """
    新增自定义日志级别,兼容标准日志行为,正确显示调用文件和行号
    :param levelName: 级别名称(大写,比如'MYLOG')
    :param levelNum: 级别数值(比如介于INFO和WARNING之间可以设为25)
    :param methodName: 调用方法名(小写,比如'mylog',默认是levelName小写)
    """
    if not methodName:
        methodName = levelName.lower()

    # 先把级别名称注册到logging模块
    logging.addLevelName(levelNum, levelName)
    
    # 给Logger类添加对应级别实例方法
    def logForLevel(self, message, *args, **kwargs):
        if self.isEnabledFor(levelNum):
            # stacklevel=2 跳过当前函数,捕捉实际调用mylog的位置
            self._log(levelNum, message, args, **kwargs, stacklevel=2)

    # 给logging模块添加全局便捷方法(比如logging.mylog)
    def logToRoot(message, *args, **kwargs):
        logging.log(levelNum, message, *args, **kwargs, stacklevel=2)

    # 绑定方法到对应对象
    setattr(logging.Logger, methodName, logForLevel)
    setattr(logging, methodName, logToRoot)

使用示例

1. 在main.py中配置和初始化

# main.py
import logging
# 可以把上面的addLoggingLevel函数直接写在这里,或者从其他文件导入

# 添加自定义级别MYLOG,数值设为25(介于INFO(20)和WARNING(30)之间)
addLoggingLevel('MYLOG', 25, 'mylog')

# 配置日志格式,和你原来的一致
logging.basicConfig(
    format=u'[%(asctime)s] # %(levelname)-4s\t%(filename)s[LINE:%(lineno)d]\t%(message)s',
    level=logging.MYLOG
)

# 本地调用测试
logging.mylog("这是main.py里输出的日志")

# 导入其他模块并调用
import other_module
other_module.test_custom_log()

2. 在other_module.py中调用自定义日志

# other_module.py
import logging

def test_custom_log():
    logging.mylog("这是other_module.py里输出的日志")

运行main.py后,你会看到日志输出里的filename分别对应main.py和other_module.py,完全符合预期!

为什么这个方法有效?

  • 我们给Logger类添加了和标准级别逻辑一致的实例方法,和logger.info等方法的实现对齐;
  • stacklevel=2参数告诉日志系统跳过当前的包装函数,直接捕捉到调用mylog的那一行代码,这样filename和lineno就会显示实际调用的位置;
  • 同时保留了标准日志级别的所有特性:比如可以通过logger.setLevel控制输出级别、支持格式化参数、兼容日志处理器配置等。

内容的提问来源于stack exchange,提问作者Kroll

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 19:27:43