繁体   English   中英

将sys.stdout重定向到python日志记录

[英]redirecting sys.stdout to python logging

所以现在我们有很多python脚本,我们正在尝试整合它们并修复和裁员。 我们要做的一件事是确保所有sys.stdout / sys.stderr进入python日志记录模块。

现在最重要的是,我们希望打印出以下内容:

[<ERROR LEVEL>] | <TIME> | <WHERE> | <MSG>

现在所有的python错误消息中的所有sys.stdout / sys.stderr消息都是[LEVEL] - MSG的格式,它们都是使用sys.stdout / sys.stderr编写的。 我可以在我的sys.stdout包装器和sys.stderr包装器中解析这个问题。 然后根据解析的输入调用相应的日志记录级别。

所以基本上我们有一个名为foo的包,以及一个名为log的子包。 __init__.py我们定义以下内容:

def initLogging(default_level = logging.INFO, stdout_wrapper = None, \
                stderr_wrapper = None):
    """
        Initialize the default logging sub system
    """
    root_logger = logging.getLogger('')
    strm_out = logging.StreamHandler(sys.__stdout__)
    strm_out.setFormatter(logging.Formatter(DEFAULT_LOG_TIME_FORMAT, \
                                            DEFAULT_LOG_TIME_FORMAT))
    root_logger.setLevel(default_level)
    root_logger.addHandler(strm_out)

    console_logger = logging.getLogger(LOGGER_CONSOLE)
    strm_out = logging.StreamHandler(sys.__stdout__)
    #strm_out.setFormatter(logging.Formatter(DEFAULT_LOG_MSG_FORMAT, \
    #                                        DEFAULT_LOG_TIME_FORMAT))
    console_logger.setLevel(logging.INFO)
    console_logger.addHandler(strm_out)

    if stdout_wrapper:
        sys.stdout = stdout_wrapper
    if stderr_wrapper:
        sys.stderr = stderr_wrapper


def cleanMsg(msg, is_stderr = False):
    logy = logging.getLogger('MSG')
    msg = msg.rstrip('\n').lstrip('\n')
    p_level = r'^(\s+)?\[(?P<LEVEL>\w+)\](\s+)?(?P<MSG>.*)$'
    m = re.match(p_level, msg)
    if m:
        msg = m.group('MSG')
        if m.group('LEVEL') in ('WARNING'):
            logy.warning(msg)
            return
        elif m.group('LEVEL') in ('ERROR'):
            logy.error(msg)
            return
    if is_stderr:
        logy.error(msg)
    else:
        logy.info(msg)

class StdOutWrapper:
    """
        Call wrapper for stdout
    """
    def write(self, s):
        cleanMsg(s, False)

class StdErrWrapper:
    """
        Call wrapper for stderr
    """
    def write(self, s):
        cleanMsg(s, True)

现在我们将在我们的一个脚本中调用它,例如:

import foo.log
foo.log.initLogging(20, foo.log.StdOutWrapper(), foo.log.StdErrWrapper())
sys.stdout.write('[ERROR] Foobar blew')

哪个将转换为错误日志消息。 喜欢:

[ERROR] | 20090610 083215 | __init__.py | Foobar Blew

现在的问题是,当我们这样做时,记录错误消息的模块现在是__init__ (对应于foo.log.__init__.py文件),这违背了整个目的。

我尝试对stderr / stdout对象执行deepCopy / shallowCopy,但这没有做任何事情,它仍然在模块中显示__init__.py的消息。 我怎么能这样做,所以这不会发生?

问题是日志记录模块在调用堆栈中查找单个层以查找谁调用它,但现在您的函数是该点的中间层(虽然我cleanMsg希望它报告cleanMsg ,而不是__init__ ,因为它是你在哪里调用log())。 相反,你需要它上升两个级别,否则将你的调用者传递给记录的消息。 您可以通过自己检查堆栈帧并抓取调用函数,将其插入到消息中来完成此操作。

要查找调用框架,可以使用inspect模块:

import inspect
f = inspect.currentframe(N)

将查找N帧,并返回帧指针。 即你的直接调用者是currentframe(1),但如果这是stdout.write方法,你可能不得不再去另一帧。 拥有调用框架后,您可以获取正在执行的代码对象,并查看与其关联的文件和函数名称。 例如:

code = f.f_code
caller = '%s:%s' % (code.co_filename, code.co_name)

您可能还需要放置一些代码来处理调用您的非python代码(即C函数或内置函数),因为这些代码可能缺少f_code对象。

或者,按照mikej的回答 ,您可以在继承自logging.Logger的自定义Logger类中使用相同的方法,该类覆盖findCaller以向上导航多个帧,而不是一个。

我认为问题是您的实际日志消息现在由logy.errorlogy.info调用cleanMsg ,因此该方法是日志消息的来源,您将其视为__init__.py

如果查看Python的lib/logging/__init__.py的源代码,您将看到一个名为findCaller的方法,它是日志记录模块用来派生日志记录请求调用者的方法。
也许您可以在日志记录对象上覆盖它以自定义行为?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM