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

发布于 2024-08-22 11:16:06 字数 94 浏览 4 评论 0原文

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

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

Is there an easy way with python's logging module to send messages with a DEBUG or INFO level and the one with a higher level to different streams?

Is it a good idea anyway?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

白鸥掠海 2024-08-29 11:16:06
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 信息。

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')

Implementation aside, I do think it is a good idea to use the logging facilities in python to output to the terminal, in particular because you can add another handler to additionally log to a file. If you set stdout to be INFO instead of DEBUG, you can even include additional DEBUG information that the user wouldn't standardly see in the log file.

热鲨 2024-08-29 11:16:06

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

# 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(...)

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

Just for your convenience adding everything together with the formatter in one package:

# 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(...)

My use case was to redirect stdout to a datafile while seeing errors on the screen during processing.

白芷 2024-08-29 11:16:06

我遇到了同样的问题,并编写了一个名为 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)

I had the same problem and wrote a custom logging handler called 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)
固执像三岁 2024-08-29 11:16:06

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

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

right from the updated docs, it cover this case pretty well now.

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')

i've mentioned on comments the two changes required from the example to make the output go to stdout. you may also use filters to redirect depending on the level.

more information to understand the changes is at
http://docs.python.org/library/logging.handlers.html#module-logging.handlers

七七 2024-08-29 11:16:06

这是我使用的紧凑解决方案(使用 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)

Here's the compact solution I use (tested with 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)
浊酒尽余欢 2024-08-29 11:16:06

您必须将处理程序与您想要将记录的语句发送到的每个流关联。
Python 有 5 个日志记录级别 - CRITICAL错误警告信息调试

级别数字值
CRITICAL50STDERROR
ERROR40STDERROR
WARNING30STDOUT
INFO20STDOUT
DEBUG10STDOUT
NOTSET0

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

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

Step1 初始化日志模块

import logging, sys
log = logging.getLogger()

Step 2 创建两个流,一个附加到 STDERROR,
其他到 STDOUT

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

第 3 步 设置每个处理程序的日志级别。这告诉处理程序仅记录日志级别等于或高于我们设置的级别的语句。例如。如果我们将其设置为WARNING,则处理程序将仅处理WARNINGERRORCRITICAL级别的日志记录语句。还要确保日志级别仅在处理程序中设置,而不是在 log 对象中设置,这样就不会发生冲突

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

。现在有一个问题。 hStErr 仅输出 ERRORCRITICAL 的日志记录,而 hStOut 将输出所有 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")

当我们运行这个脚本时输出。

error log
info log

Pycharm IDE 将 std 错误的输出颜色显示为红色。下图显示了上面的错误日志语句被发送到stderr

输出于pycharm IDE 控制台

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

error log
error log
info log

输出于pycharm IDE console

请注意,如果没有过滤器,hStOut 将从 INFO 和 ERROR 中输出日志语句,而对于 INFO hStErr 不输出任何内容,并且 hStOut 输出单个语句 - 信息日志

You will have to associated a handler to each stream you want logged statements to be sent to.
Python has 5 logging levels - CRITICAL, ERROR, WARNING, INFO, and DEBUG.

LevelNumeric ValueStream
CRITICAL50STDERROR
ERROR40STDERROR
WARNING30STDOUT
INFO20STDOUT
DEBUG10STDOUT
NOTSET0

Let's assume you want that Critical and Error logs should go to STDERROR, while others should go to STDOUT - as shown in the table above.

Here are the steps you should follow.

Step1 Initialize the logging module

import logging, sys
log = logging.getLogger()

Step 2 Create two streams, one attached to STDERROR,
other to STDOUT

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

Step 3 Set log level for each handler. This tells handler to only log statements which have log level equal to or higher than the level we set. eg. if we set this to WARNING, the handler will only handle logging statements for WARNING, ERROR, and CRITICAL levels. Also make sure that the log level is set in handler only, and not with the log object, so that there is no conflict

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

Now here comes a catch. While hStErr will only output logging for ERROR and CRITICAL, hStOut will output for all 5 levels. Remember that setLevel only tells the minimum logging level which should be handled, so all levels which are greater will also be handled. To limit hStOut to not handle ERROR and CRITICAL, we use a filter.

Step 4 Specify a filter so that ERROR, and CRITICAL aren't handled by hStOut

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

Step 5 Add these handlers to logger

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

Here are all the pieces together.

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 when we run this script.

error log
info log

Pycharm IDE colors output from std error red. The following image shows that the error log statement above was sent to stderr.

output in pycharm IDE console

If we comment the addFilter line in above script, we will see the following output.

error log
error log
info log

output in pycharm IDE console

Note that without filter hStOut will output logging statements from both INFO and ERROR, while for INFO hStErr outputs nothing, and hStOut outputs a single statement - info log

白鸥掠海 2024-08-29 11:16:06

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

Not necessarily a good idea (it could be confusing to see info and debug messages mixed in with normal output!), but feasible, since you can have multiple handler objects and a custom filter for each of them, in order to pick and choose which log records each handler gets to handle.

乖乖兔^ω^ 2024-08-29 11:16:06
#!/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')
#!/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')
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文