【问题标题】:Redirecting Loggers`messages to sys.stdout and sys.stderr将 Loggers 的消息重定向到 sys.stdout 和 sys.stderr
【发布时间】:2018-08-09 19:24:03
【问题描述】:

我刚开始学习 Python,遇到了一个我无法解决的问题。 我想将 CRITICAL 以上的每个级别重定向到 sys.stderr,将 WARNING 以上的所有内容重定向到 sys.stdout。 我想出了这个脚本...

import logging
import sys


print("imported module {}".format(__name__))


class PyLogger(logging.Logger):
    """Wrapper for logging.Logger to redirect its message to
    sys.stdout or sys.stderr accordingly """

    def __init__(self, *args):
        super(PyLogger, self).__init__(self, *args)

        # get Logger
        logger = logging.getLogger()
        logger.setLevel(logging.DEBUG)

        # build Formatter
        formatter = logging.Formatter(fmt="%(asctime)s:%(name)s   %(message)s")

        # build StreamHandler for sys.stderr
        error = logging.StreamHandler(stream=sys.stderr)
        error.setLevel(logging.CRITICAL)
        error.setFormatter(formatter)
        logger.addHandler(error)

        # build StreamHandler for sys.stdin
        out = logging.StreamHandler(stream=sys.stdout)
        out.setFormatter(formatter)
        out.setLevel(logging.WARNING)
        logger.addHandler(out)


def main():
    logger = PyLogger()
    # help(logger)
    logger.info("INFO")


if __name__ == "__main__":
    main()

直接运行此脚本时,出现以下错误:

No handlers could be found for logger "<__main__.PyLogger object at 0x105f23c50>"

我用谷歌搜索了很多人说 logging.basicConfig() 可以完成这项工作,但这对我没有用。

也许你们中的某个人可以帮助我。 谢谢!

【问题讨论】:

  • 无关:这是我第一次在 SO 上看到“运行代码 sn-p”和“复制 sn-p 以回答”按钮。它完全破坏了我复制代码块以在 Python 环境中本地运行它的能力。我无法突出显示滚动,它仅滚动页面而不是代码框(这是在最新版本的 Chrome 上,也在 Firefox 上尝试过,禁用任何可能与它混淆的扩展)。
  • 您可以尝试在代码开头单击一下,然后在结尾处 shift 单击,这将突出显示所有内容。

标签: python redirect logging stdout stderr


【解决方案1】:

您的类是logging.Logger 的子类,因此您不应调用getLogger 或将记录器作为属性进行操作。相反,记录器在类内部是self,应该直接调整:

import logging
import sys


print("imported module {}".format(__name__))


class PyLogger(logging.Logger):
    """Wrapper for logging.Logger to redirect its message to                                                                                   
    sys.stdout or sys.stderr accordingly """

    def __init__(self, *args):
        super(PyLogger, self).__init__(self, *args)

        #####
        # self *is* the logger!                                                                                                                          
        self.setLevel(logging.DEBUG)

        # build Formatter                                                                                                                      
        formatter = logging.Formatter(fmt="%(asctime)s:%(name)s   %(message)s")

        # build StreamHandler for sys.stderr                                                                                                   
        error = logging.StreamHandler(stream=sys.stderr)
        error.setLevel(logging.CRITICAL)
        error.setFormatter(formatter)

        #####
        # Assign the handler to self
        self.addHandler(error)

        # build StreamHandler for sys.stdin                                                                                                    
        out = logging.StreamHandler(stream=sys.stdout)
        out.setFormatter(formatter)
        out.setLevel(logging.WARNING)

        #####
        # Assign the handler to self
        self.addHandler(out)


def main():
    logger = PyLogger()
    # help(logger)                                                                                                                             
    logger.info("INFO")
    logger.warning("WARN")
    logger.critical("CRIT")


if __name__ == "__main__":
    main()

这将按预期显示以下内容:

ely@eschaton:~/programming$ python test_logger.py 
imported module __main__
2018-03-01 11:59:41,896:<__main__.PyLogger object at 0x7fa236aa4a50>   WARN
2018-03-01 11:59:41,896:<__main__.PyLogger object at 0x7fa236aa4a50>   CRIT
2018-03-01 11:59:41,896:<__main__.PyLogger object at 0x7fa236aa4a50>   CRIT

请注意关键消息如何触发两个不同的输出处理程序,因此它出现了两次(一次是因为它满足警告级别,一次是因为它满足关键级别)。

在您的原始代码中,请注意您在__init__ 内部创建了一个名为logger 的变量,但它没有分配给self 或任何东西。此变量在超出__init__ 函数的范围时会被销毁,因此任何处理程序的分配都是没有意义的。另外,因为处理程序没有分配给self,但self 引用的对象稍后将调用的记录器,这就是为什么您会看到关于没有处理程序的错误。

【讨论】:

  • 好的,我明白了!谢谢 :) 所以基本上如果我从任何类继承......通过调用 super(class, self).__init__() 我总是会得到该类的一个实例。所以不需要任何“self.getAnything()”,因为我已经有了那个实例?!如果我现在想摆脱双重日志记录,我需要为其中一个 StreamHandler 指定一个过滤器,对吗?
  • @TonyDorfmeister 如果你想从Logger类访问方法,比如addHandlersetLevel,那么你需要使用self,因为self代表的实例PyLogger(类似于 C++ 或 Java 中的this),而PyLogger 继承了Logger 的那些方法。但是您不需要使用logging.getLogger 创建记录器。
  • 您是正确的,您可以将addFilter 与自定义过滤器一起使用,以便自定义如何处理每个记录器中的级别。 Here is an example 我相信它会直接适用于您的用例。
  • 您肯定不希望同一日志记录两次。 EG 关键日志应该只进入标准错误。
  • @JonathanWylie 不,你不正确。在此示例中,设置了两个处理程序。一个报告任何警告或更高级别的内容,另一个输出任何严重或更高级别的内容,并且它们彼此独立运行。这可能导致多次日志记录,但在这种情况下是有意的。如果您需要不同的行为,您可能会选择使用日志记录Filters 来自定义不在一个处理程序或另一个处理程序中重复记录的业务逻辑。
猜你喜欢
  • 2019-11-23
  • 1970-01-01
  • 2016-05-13
  • 1970-01-01
  • 2016-05-29
  • 1970-01-01
  • 1970-01-01
  • 2015-11-10
  • 1970-01-01
相关资源
最近更新 更多