如何为自己的日志级别编写自己的日志记录方法

时间:2011-02-10 13:48:08

标签: python logging

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

如何做到最好?

我最初希望有一个根记录器将所有内容写入文件,并另外命名为logger(“rrcheck”)写入stdout,但后者还应该有其他一些方法和级别。我需要它用“!PFXWRN”前缀(但只有那些去stdout)前缀一些消息并保留其他消息不变。我还想为root和named logger设置日志级别单独

这是我的代码:

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(名称)将需要一个... ...

的问候,
布里

1 个答案:

答案 0 :(得分:5)

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

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

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 的子类,该子类将其调用委托给适当的记录器类,基于它的名字是什么。可能还有其他更优雅的可能性,但我现在想不到。