在自定义类中包装记录器功能时,显示正确的funcName [英] Showing the right funcName when wrapping logger functionality in a custom class

查看:53
本文介绍了在自定义类中包装记录器功能时,显示正确的funcName的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我用于记录的格式化字符串:

This is the formatting string that I am using for logging:

'%(asctime)s - %(levelname)-10s - %(funcName)s - %(message)s'

但是为了显示日志消息,我有一个包装器做更多的事情(我设置了不同的日志级别,配置了不同的日志后端,提供了访问自定义级别的便捷功能等):

But to show the logging messages I have a wrapper doing a bit more (I setup different log levels, configure different logging backends, provide convenience functions to access the custom levels, etc):

class MyLogger(logging.Logger):

    def split_line(self, level, message):
        ....
        self.log.(level, line)

    def progress(self, message):
        self.split_line(PROGRESS, message)

通过此设置,每当我记录一些内容时:

With this setup, whenever I log something:

def myfunc():
    log.progress('Hello')

我得到:

013-10-27 08:47:30,130 - PROGRESS   - split_line - Hello

这不是我想要的,即:

013-10-27 08:47:30,130 - PROGRESS   - myfunc     - Hello

如何告诉记录器对函数名称使用正确的上下文?我认为这实际上在堆栈框架上要高出两个级别.

How can I tell the logger to use the right context for the function name? I think this would be actually two levels higher on the stackframe.

这是显示问题的测试程序:

This is a test program showing the problem:

import sys
import logging

PROGRESS = 1000

class MyLogger(logging.Logger):

    PROGRESS = PROGRESS
    LOG_FORMATTER = '%(asctime)s - %(levelname)-10s - %(funcName)s - %(message)s'
    DEF_LOGGING_LEVEL = logging.WARNING

    def __init__(self, log_name, level=None):
        logging.Logger.__init__(self, log_name)
        self.formatter = logging.Formatter(self.LOG_FORMATTER)
        self.initLogger(level)

    def initLogger(self, level=None):
        self.setLevel(level or self.DEF_LOGGING_LEVEL)
        self.propagate = False

    def add_handler(self, log_file, use_syslog):
        if use_syslog : hdlr = logging.handlers.SysLogHandler(address='/dev/log')
        elif log_file : hdlr = logging.FileHandler(log_file)
        else          : hdlr = logging.StreamHandler(sys.stderr)
        hdlr.setFormatter(self.formatter)
        self.addHandler(hdlr)
        return hdlr

    def addHandlers(self, log_file=None, progress_file=None, use_syslog=False):
        self.logger_hdlr = self.add_handler(log_file, use_syslog)
        if progress_file:
            self.progress_hdlr = self.add_handler(progress_file, use_syslog)
            self.progress_hdlr.setLevel(self.PROGRESS)
        else:
            self.progress_hdlr = None

    def split_line(self, level, txt, *args):
        txt = txt % (args)
        for line in txt.split('\n'):
            self.log(level, line)

    def progress(self, txt, *args):
        self.split_line(self.PROGRESS, txt, *args)

logging.setLoggerClass(MyLogger)
logging.addLevelName(PROGRESS, 'PROGRESS')
logger = logging.getLogger(__name__)
logger.addHandlers()

name = 'John'
logger.progress('Hello %s\nHow are you doing?', name)

产生:

2013-10-27 09:47:39,577 - PROGRESS   - split_line - Hello John
2013-10-27 09:47:39,577 - PROGRESS   - split_line - How are you doing?

推荐答案

本质上,应归咎于 Logger 类的代码:

Essentially, the code to blame lies in the Logger class:

此方法

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 = currentframe()
    #On some versions of IronPython, currentframe() returns None if
    #IronPython isn't run with -X:Frames.
    if f is not None:
        f = f.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 == _srcfile:
            f = f.f_back
            continue
        rv = (co.co_filename, f.f_lineno, co.co_name)
        break
    return rv

返回调用者链中不属于当前模块的第一个函数.

returns the first function in the chain of callers which doesn't belong to the current module.

您可以继承 Logger 的子类,并通过添加稍微复杂的逻辑来覆盖此方法.跳过其他层次的通话深度或添加其他条件.

You could subclass Logger and override this method by adding a slightly more complex logic. skipping another level of calling depth or adding another condition.

在非常特殊的情况下,避免自动行拆分和执行操作可能会更简单

In your very special case, it would probably be simpler to refrain from the automatic line splitting and to do

logger.progress('Hello %s', name)
logger.progress('How are you doing?')

或要做

def splitter(txt, *args)
    txt = txt % (args)
    for line in txt.split('\n'):
        yield line

for line in splitter('Hello %s\nHow are you doing?', name):
    logger.progress(line)

并拥有

def progress(self, txt, *args):
    self.log(self.PROGRESS, txt, *args)

可能会为您节省很多头痛.

Probably it will save you a lot of headache.

不,那无济于事.现在它将向您显示 progress 作为您的调用方函数名称...

EDIT 2: No, that won't help. It now would show you progress as your caller function name...

这篇关于在自定义类中包装记录器功能时,显示正确的funcName的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆