繁体   English   中英

如何将 INFO 和 DEBUG 日志消息发送到 stdout 并将更高级别的消息发送到 stderr

[英]How can INFO and DEBUG logging message be sent to stdout and higher level message to stderr

python的日志记录模块是否有一种简单的方法可以将具有DEBUG或INFO级别的消息以及具有更高级别的消息发送到不同的流?

无论如何,这是个好主意吗?

import logging
import sys

class LessThanFilter(logging.Filter):
    def __init__(self, exclusive_maximum, name=""):
        super(LessThanFilter, self).__init__(name)
        self.max_level = exclusive_maximum

    def filter(self, record):
        #non-zero return means we log this message
        return 1 if record.levelno < self.max_level else 0

#Get the root logger
logger = logging.getLogger()
#Have to set the root logger level, it defaults to logging.WARNING
logger.setLevel(logging.NOTSET)

logging_handler_out = logging.StreamHandler(sys.stdout)
logging_handler_out.setLevel(logging.DEBUG)
logging_handler_out.addFilter(LessThanFilter(logging.WARNING))
logger.addHandler(logging_handler_out)

logging_handler_err = logging.StreamHandler(sys.stderr)
logging_handler_err.setLevel(logging.WARNING)
logger.addHandler(logging_handler_err)

#demonstrate the logging levels
logger.debug('DEBUG')
logger.info('INFO')
logger.warning('WARNING')
logger.error('ERROR')
logger.critical('CRITICAL')

除了实现之外,我确实认为使用 python 中的日志记录工具输出到终端是一个好主意,特别是因为您可以添加另一个处理程序来另外记录到文件。 如果您将 stdout 设置为 INFO 而不是 DEBUG,您甚至可以包含用户通常不会在日志文件中看到的其他 DEBUG 信息。

我遇到了同样的问题并编写了一个名为 SplitStreamHandler 的自定义日志处理程序:

import sys
import logging

class SplitStreamHandler(logging.Handler):
    def __init__(self):
        logging.Handler.__init__(self)

    def emit(self, record):
        # mostly copy-paste from logging.StreamHandler
        try:
            msg = self.format(record)
            if record.levelno < logging.WARNING:
                stream = sys.stdout
            else:
                stream = sys.stderr
            fs = "%s\n"

            try:
                if (isinstance(msg, unicode) and
                    getattr(stream, 'encoding', None)):
                    ufs = fs.decode(stream.encoding)
                    try:
                        stream.write(ufs % msg)
                    except UnicodeEncodeError:
                        stream.write((ufs % msg).encode(stream.encoding))
                else:
                    stream.write(fs % msg)
            except UnicodeError:
                stream.write(fs % msg.encode("UTF-8"))

            stream.flush()
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.handleError(record)

为了您的方便,将所有内容与格式化程序一起添加到一个包中:

# shared formatter, but you can use separate ones:
FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(threadName)s - %(message)s'
formatter = logging.Formatter(FORMAT)

# single app logger:
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)

# 2 handlers for the same logger:
h1 = logging.StreamHandler(sys.stdout)
h1.setLevel(logging.DEBUG)
# filter out everything that is above INFO level (WARN, ERROR, ...)
h1.addFilter(lambda record: record.levelno <= logging.INFO)
h1.setFormatter(formatter)
log.addHandler(h1)

h2 = logging.StreamHandler(sys.stderr)
# take only warnings and error logs
h2.setLevel(logging.WARNING)
h2.setFormatter(formatter)
log.addHandler(h2)

# profit:
log.info(...)
log.debug(...)

我的用例是将标准输出重定向到数据文件,同时在处理过程中在屏幕上看到错误。

从更新的文档来看,它现在很好地涵盖了这种情况。

http://docs.python.org/howto/logging.html#logging-advanced-tutorial

import sys # Add this.
import logging

# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)

# create console handler and set level to debug
ch = logging.StreamHandler( sys.__stdout__ ) # Add this
ch.setLevel(logging.DEBUG)

# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# add formatter to ch
ch.setFormatter(formatter)

# add ch to logger
logger.addHandler(ch)

# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

我在评论中提到了示例中使输出转到标准输出所需的两个更改。 您还可以根据级别使用过滤器进行重定向。

了解更改的更多信息位于http://docs.python.org/library/logging.handlers.html#module-logging.handlers

这是我使用的紧凑型解决方案(使用 Python 3.10 测试):

import logging
import sys

root_logger = logging.getLogger()

# configure default StreamHandler to stderr
logging.basicConfig(
    format="%(asctime)s | %(levelname)-8s | %(filename)s:%(funcName)s(%(lineno)d) | %(message)s",
    level=logging.INFO,  # overall log level; DEBUG or INFO make most sense
)
stderr_handler = root_logger.handlers[0]  # get default handler
stderr_handler.setLevel(logging.WARNING)  # only print WARNING+

# add another StreamHandler to stdout which only emits below WARNING
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.addFilter(lambda rec: rec.levelno < logging.WARNING)
stdout_handler.setFormatter(stderr_handler.formatter)  # reuse the stderr formatter
root_logger.addHandler(stdout_handler)

您必须将处理程序关联到您希望将记录的语句发送到的每个 stream。 Python 有 5 个日志记录级别- CRITICALERRORWARNINGINFODEBUG

等级 数值 Stream
批判的 50 标准错误
错误 40 标准错误
警告 30 标准输出
信息 20 标准输出
调试 10 标准输出
没有设置 0

假设您希望关键和错误日志应该 go 到STDERROR ,而其他日志应该 go 到STDOUT - 如上表所示。

以下是您应该遵循的步骤。

Step1初始化日志模块

import logging, sys
log = logging.getLogger()

步骤 2创建两个流,一个附加到 STDERROR,另一个附加到 STDOUT

hStErr = logging.StreamHandler(sys.stderr)
hStOut = logging.StreamHandler(sys.stdout)

步骤 3为每个处理程序设置日志级别。 这告诉处理程序只记录日志级别等于或高于我们设置的级别的语句。 例如。 如果我们将此设置为WARNING ,则处理程序将仅处理WARNINGERRORCRITICAL级别的日志记录语句。 还要确保只在 handler 中设置日志级别,而不是在log object 中设置,以免发生冲突

hStErr.setLevel('ERROR')
hStOut.setLevel('DEBUG')
log.setLevel('NOTSET')

现在有一个问题。 虽然hStErr只会 output 记录ERRORCRITICAL ,但hStOut将 output 记录所有 5 个级别。 请记住, setLevel只告诉应处理的最小日志记录级别,因此所有更高的级别也将被处理。 为了限制hStOut不处理ERRORCRITICAL ,我们使用了一个过滤器。

步骤 4指定一个过滤器,以便hStOut不处理ERRORCRITICAL

hStOut.addFilter(lambda x : x.levelno < logging.ERROR)

第 5 步将这些处理程序添加到记录器

log.addHandler(hStErr)
log.addHandler(hStOut)

这是所有的部分。

import logging, sys
log = logging.getLogger()
hStErr = logging.StreamHandler(sys.stderr)
hStOut = logging.StreamHandler(sys.stdout)

hStErr.setLevel('ERROR')
hStOut.setLevel('DEBUG')
log.setLevel('NOTSET')

hStOut.addFilter(lambda x : x.levelno < logging.ERROR)

log.addHandler(hStErr)
log.addHandler(hStOut)

log.error("error log")
log.info("info log")

Output 当我们运行这个脚本时。

error log
info log

Pycharm IDE colors Z78E6221F6393D1356681DB398F14CE6从stdDZ6错误红色。 下图显示上面的error log语句被发送到stderr

在 pycharm IDE 控制台中输出

如果我们在上面的脚本中注释addFilter行,我们将看到以下 output。

error log
error log
info log

在 pycharm IDE 控制台中输出

请注意,没有过滤器hStOut将 output 记录来自 INFO 和 ERROR 的语句,而对于 INFO hStErr输出任何内容,并且hStOut输出单个语句 - info log

不一定是个好主意(看到信息和调试消息与正常输出混合可能会令人困惑!),但可行,因为您可以为每个处理程序对象和一个自定义过滤器,以便挑选和选择哪个日志记录每个处理程序得到处理。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import logging
import sys

class LessThenFilter(logging.Filter):
    def __init__(self, level):
        self._level = level
        logging.Filter.__init__(self)

    def filter(self, rec):
        return rec.levelno < self._level

log = logging.getLogger()
log.setLevel(logging.NOTSET)

sh_out = logging.StreamHandler(stream=sys.stdout)
sh_out.setLevel(logging.DEBUG)
sh_out.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
sh_out.addFilter(LessThenFilter(logging.WARNING))
log.addHandler(sh_out)

sh_err = logging.StreamHandler(stream=sys.stderr)
sh_err.setLevel(logging.WARNING)
sh_err.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
log.addHandler(sh_err)

logging.critical('x')
logging.error('x')
logging.warning('x')
logging.info('x')
logging.debug('x')

暂无
暂无

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

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