【问题标题】:How to write own logging methods for own logging levels如何为自己的日志记录级别编写自己的日志记录方法
【发布时间】:2011-02-10 13:48:07
【问题描述】:


我想用我自己的方法扩展我的记录器(由 logging.getLogger("rrcheck") 获取),例如: def warnpfx(...):

如何做到最好?

我最初的愿望是让根记录器将所有内容写入文件并另外命名记录器(“rrcheck”)写入标准输出,但后者还应该有一些其他方法和级别。我需要它来添加一些带有“!PFXWRN”前缀的消息(但只有那些进入标准输出的消息)并保持其他消息不变。我还想为 root 和命名记录器单独设置日志记录级别。

这是我的代码:

class CheloExtendedLogger(logging.Logger):
    """
    Custom logger class with additional levels and methods
    """
    WARNPFX = logging.WARNING+1

    def __init__(self, name):
        logging.Logger.__init__(self, name, logging.DEBUG)                

        logging.addLevelName(self.WARNPFX, 'WARNING')

        console = logging.StreamHandler()
        console.setLevel(logging.DEBUG)
        # create formatter and add it to the handlers
        formatter = logging.Formatter("%(asctime)s [%(funcName)s: %(filename)s,%(lineno)d] %(message)s")
        console.setFormatter(formatter)

        # add the handlers to logger
        self.addHandler(console)

        return

    def warnpfx(self, msg, *args, **kw):
        self.log(self.WARNPFX, "! PFXWRN %s" % msg, *args, **kw)


logging.setLoggerClass(CheloExtendedLogger)    
rrclogger = logging.getLogger("rrcheck")
rrclogger.setLevel(logging.INFO)

def test():
    rrclogger.debug("DEBUG message")
    rrclogger.info("INFO message")
    rrclogger.warnpfx("warning with prefix")

test()

这是一个输出 - 函数和 lilne 编号是错误的:warnpfx 而不是 test

2011-02-10 14:36:51,482 [test: log4.py,35] INFO message
2011-02-10 14:36:51,497 [warnpfx: log4.py,26] ! PFXWRN warning with prefix

也许我自己的记录器方法不是最好的?
您建议走哪个方向(自己的记录器、自己的处理程序、自己的格式化程序等)?

如果我想要另一个记录器,如何进行?
不幸的是,记录无法注册自己的记录器,因此 getLogger(name) 将需要一个...

问候,
兹比格纽

【问题讨论】:

    标签: python logging


    【解决方案1】:

    如果您检查Python sources,您会发现罪魁祸首是Logger.findCaller 方法,它遍历调用堆栈并搜索不在logging.py 文件中的第一行。因此,您在 CheloExtendedLogger.warnpfx 中对 self.log 的自定义调用会注册错误的行。

    不幸的是,logging.py 中的代码不是很模块化,因此修复相当难看:您必须自己在子类中重新定义 findCaller 方法,以便它考虑到 logging.py 文件和记录器所在的文件(请注意,文件中除了记录器之外不应有任何代码,否则结果将不准确)。这需要在方法体中进行一行更改:

    class CheloExtendedLogger(logging.Logger):
    
        [...]
    
        def findCaller(self):
            """
            Find the stack frame of the caller so that we can note the source
            file name, line number and function name.
            """
            f = logging.currentframe().f_back
            rv = "(unknown file)", 0, "(unknown function)"
            while hasattr(f, "f_code"):
                co = f.f_code
                filename = os.path.normcase(co.co_filename)
                if filename in (_srcfile, logging._srcfile): # This line is modified.
                    f = f.f_back
                    continue
                rv = (filename, f.f_lineno, co.co_name)
                break
            return rv
    

    为此,您需要在文件中定义自己的_srcfile 变量。同样,logging.py 不使用函数,而是将所有代码放在模块级别,因此您必须再次复制粘贴:

    if hasattr(sys, 'frozen'): #support for py2exe
        _srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:])
    elif string.lower(__file__[-4:]) in ['.pyc', '.pyo']:
        _srcfile = __file__[:-4] + '.py'
    else:
        _srcfile = __file__
    _srcfile = os.path.normcase(_srcfile)
    

    好吧,也许如果你不关心编译版本,最后两行就足够了。

    现在,您的代码按预期工作:

    2011-02-10 16:41:48,108 [test: lg.py,16] INFO message
    2011-02-10 16:41:48,171 [test: lg.py,17] ! PFXWRN warning with prefix
    

    对于多个记录器类,如果您不介意记录器名称和记录器类之间的依赖关系,您可以创建logging.Logger 的子类,它将其调用委托给适当的记录器类,基于它的内容名字是。可能还有其他更优雅的可能性,但我现在想不出。

    【讨论】:

    • 感谢 DzinX。我做了更多尝试,看起来只是调用 self._log 而不是 self.log 让它运行。
    • 啊啊,那是因为 currentframe() 返回调用堆栈上的第四个顶部函数 :) 它们有多丑 :)
    猜你喜欢
    • 2016-12-15
    • 1970-01-01
    • 1970-01-01
    • 2020-11-19
    • 1970-01-01
    • 2023-03-23
    • 2011-05-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多