日志记录、StreamHandler 和标准流

发布于 2024-08-03 18:50:51 字数 178 浏览 3 评论 0原文

我不知道如何将信息级消息记录到标准输出,但将其他所有内容记录到标准错误。我已经读过这个 http://docs.python.org/library/logging.html 。有什么建议吗?

I can't figure out how to log info-level messages to stdout, but everything else to stderr. I already read this http://docs.python.org/library/logging.html. Any suggestion?

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

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

发布评论

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

评论(6

燕归巢 2024-08-10 18:50:51

以下脚本 log1.py

import logging, sys

class SingleLevelFilter(logging.Filter):
    def __init__(self, passlevel, reject):
        self.passlevel = passlevel
        self.reject = reject

    def filter(self, record):
        if self.reject:
            return (record.levelno != self.passlevel)
        else:
            return (record.levelno == self.passlevel)

h1 = logging.StreamHandler(sys.stdout)
f1 = SingleLevelFilter(logging.INFO, False)
h1.addFilter(f1)
rootLogger = logging.getLogger()
rootLogger.addHandler(h1)
h2 = logging.StreamHandler(sys.stderr)
f2 = SingleLevelFilter(logging.INFO, True)
h2.addFilter(f2)
rootLogger.addHandler(h2)
logger = logging.getLogger("my.logger")
logger.setLevel(logging.DEBUG)
logger.debug("A DEBUG message")
logger.info("An INFO message")
logger.warning("A WARNING message")
logger.error("An ERROR message")
logger.critical("A CRITICAL message")

运行时会产生以下结果。

C:\temp>log1.py
A DEBUG message
An INFO message
A WARNING message
An ERROR message
A CRITICAL message

正如您所期望的,因为在终端上 sys.stdoutsys.stderr 是相同的。现在,让我们将 stdout 重定向到文件 tmp

C:\temp>log1.py >tmp
A DEBUG message
A WARNING message
An ERROR message
A CRITICAL message

因此 INFO 消息尚未打印到终端 - 但定向到 sys.stderr 的消息有< /em> 已打印。让我们看看 tmp 中的内容:

C:\temp>type tmp
An INFO message

因此该方法似乎可以满足您的要求。

The following script, log1.py:

import logging, sys

class SingleLevelFilter(logging.Filter):
    def __init__(self, passlevel, reject):
        self.passlevel = passlevel
        self.reject = reject

    def filter(self, record):
        if self.reject:
            return (record.levelno != self.passlevel)
        else:
            return (record.levelno == self.passlevel)

h1 = logging.StreamHandler(sys.stdout)
f1 = SingleLevelFilter(logging.INFO, False)
h1.addFilter(f1)
rootLogger = logging.getLogger()
rootLogger.addHandler(h1)
h2 = logging.StreamHandler(sys.stderr)
f2 = SingleLevelFilter(logging.INFO, True)
h2.addFilter(f2)
rootLogger.addHandler(h2)
logger = logging.getLogger("my.logger")
logger.setLevel(logging.DEBUG)
logger.debug("A DEBUG message")
logger.info("An INFO message")
logger.warning("A WARNING message")
logger.error("An ERROR message")
logger.critical("A CRITICAL message")

when run, produces the following results.

C:\temp>log1.py
A DEBUG message
An INFO message
A WARNING message
An ERROR message
A CRITICAL message

As you'd expect, since on a terminal sys.stdout and sys.stderr are the same. Now, let's redirect stdout to a file, tmp:

C:\temp>log1.py >tmp
A DEBUG message
A WARNING message
An ERROR message
A CRITICAL message

So the INFO message has not been printed to the terminal - but the messages directed to sys.stderr have been printed. Let's look at what's in tmp:

C:\temp>type tmp
An INFO message

So that approach appears to do what you want.

套路撩心 2024-08-10 18:50:51

一般来说,我认为将低于WARNING的消息重定向到stdout是有意义的,而不仅仅是INFO消息

根据 Vinay Sajip 的出色回答,我得出了以下结论:

class MaxLevelFilter(Filter):
    '''Filters (lets through) all messages with level < LEVEL'''
    def __init__(self, level):
        self.level = level

    def filter(self, record):
        return record.levelno < self.level # "<" instead of "<=": since logger.setLevel is inclusive, this should be exclusive


MIN_LEVEL= DEBUG
#...
stdout_hdlr = StreamHandler(sys.stdout)
stderr_hdlr = StreamHandler(sys.stderr)
lower_than_warning= MaxLevelFilter(WARNING)
stdout_hdlr.addFilter( lower_than_warning )     #messages lower than WARNING go to stdout
stdout_hdlr.setLevel( MIN_LEVEL )
stderr_hdlr.setLevel( max(MIN_LEVEL, WARNING) ) #messages >= WARNING ( and >= STDOUT_LOG_LEVEL ) go to stderr
#...

Generally, I think it makes sense to redirect messages lower than WARNING to stdout, instead of only INFO messages.

Based on Vinay Sajip's excellent answer, I came up with this:

class MaxLevelFilter(Filter):
    '''Filters (lets through) all messages with level < LEVEL'''
    def __init__(self, level):
        self.level = level

    def filter(self, record):
        return record.levelno < self.level # "<" instead of "<=": since logger.setLevel is inclusive, this should be exclusive


MIN_LEVEL= DEBUG
#...
stdout_hdlr = StreamHandler(sys.stdout)
stderr_hdlr = StreamHandler(sys.stderr)
lower_than_warning= MaxLevelFilter(WARNING)
stdout_hdlr.addFilter( lower_than_warning )     #messages lower than WARNING go to stdout
stdout_hdlr.setLevel( MIN_LEVEL )
stderr_hdlr.setLevel( max(MIN_LEVEL, WARNING) ) #messages >= WARNING ( and >= STDOUT_LOG_LEVEL ) go to stderr
#...
羁拥 2024-08-10 18:50:51

由于我的编辑被拒绝,这是我的答案。 @goncalopp 的答案很好,但并不独立或开箱即用。这是我的改进版本:

import sys, logging


class LogFilter(logging.Filter):
    """Filters (lets through) all messages with level < LEVEL"""
    # http://stackoverflow.com/a/24956305/408556
    def __init__(self, level):
        self.level = level

    def filter(self, record):
        # "<" instead of "<=": since logger.setLevel is inclusive, this should
        # be exclusive
        return record.levelno < self.level

MIN_LEVEL = logging.DEBUG
stdout_hdlr = logging.StreamHandler(sys.stdout)
stderr_hdlr = logging.StreamHandler(sys.stderr)
log_filter = LogFilter(logging.WARNING)
stdout_hdlr.addFilter(log_filter)
stdout_hdlr.setLevel(MIN_LEVEL)
stderr_hdlr.setLevel(max(MIN_LEVEL, logging.WARNING))
# messages lower than WARNING go to stdout
# messages >= WARNING (and >= STDOUT_LOG_LEVEL) go to stderr

rootLogger = logging.getLogger()
rootLogger.addHandler(stdout_hdlr)
rootLogger.addHandler(stderr_hdlr)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Example Usage
>>> logger.debug("A DEBUG message")
>>> logger.info("An INFO message")
>>> logger.warning("A WARNING message")
>>> logger.error("An ERROR message")
>>> logger.critical("A CRITICAL message")

Since my edit was rejected, here's my answer. @goncalopp's answer is good but doesn't stand alone or work out of the box. Here's my improved version:

import sys, logging


class LogFilter(logging.Filter):
    """Filters (lets through) all messages with level < LEVEL"""
    # http://stackoverflow.com/a/24956305/408556
    def __init__(self, level):
        self.level = level

    def filter(self, record):
        # "<" instead of "<=": since logger.setLevel is inclusive, this should
        # be exclusive
        return record.levelno < self.level

MIN_LEVEL = logging.DEBUG
stdout_hdlr = logging.StreamHandler(sys.stdout)
stderr_hdlr = logging.StreamHandler(sys.stderr)
log_filter = LogFilter(logging.WARNING)
stdout_hdlr.addFilter(log_filter)
stdout_hdlr.setLevel(MIN_LEVEL)
stderr_hdlr.setLevel(max(MIN_LEVEL, logging.WARNING))
# messages lower than WARNING go to stdout
# messages >= WARNING (and >= STDOUT_LOG_LEVEL) go to stderr

rootLogger = logging.getLogger()
rootLogger.addHandler(stdout_hdlr)
rootLogger.addHandler(stderr_hdlr)
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Example Usage
>>> logger.debug("A DEBUG message")
>>> logger.info("An INFO message")
>>> logger.warning("A WARNING message")
>>> logger.error("An ERROR message")
>>> logger.critical("A CRITICAL message")
静若繁花 2024-08-10 18:50:51

将彩色输出发送到 stderr 的最简单处理程序:

class ColorStderr(logging.StreamHandler):
    def __init__(self):
        class AddColor(logging.Formatter):
            def format(self, record: logging.LogRecord):
                msg = super().format(record)
                # Green/Cyan/Yellow/Red/Redder based on log level:
                color = '\033[1;' + ('32m', '36m', '33m', '31m', '41m')[
                   min(4,int(4 * record.levelno / logging.FATAL))]
                return color + record.levelname + '\033[1;0m: ' + msg
        super().__init__(sys.stderr)
        self.setFormatter(AddColor())

与以下方式一起使用:

logging.basicConfig(level=logging.INFO, handlers=[ColorStderr()])

或者您甚至可以将格式化程序直接应用于当前日志处理程序,而不需要 ColorStderr

logging.getLogger().handlers[0].setFormatter(AddColor())

Simplest handler to send colored output to stderr:

class ColorStderr(logging.StreamHandler):
    def __init__(self):
        class AddColor(logging.Formatter):
            def format(self, record: logging.LogRecord):
                msg = super().format(record)
                # Green/Cyan/Yellow/Red/Redder based on log level:
                color = '\033[1;' + ('32m', '36m', '33m', '31m', '41m')[
                   min(4,int(4 * record.levelno / logging.FATAL))]
                return color + record.levelname + '\033[1;0m: ' + msg
        super().__init__(sys.stderr)
        self.setFormatter(AddColor())

Use with:

logging.basicConfig(level=logging.INFO, handlers=[ColorStderr()])

Or you can even apply the formatter directly to the current log handler, with no need for ColorStderr:

logging.getLogger().handlers[0].setFormatter(AddColor())
箹锭⒈辈孓 2024-08-10 18:50:51

这是我的答案(其他答案的组合):

logger = logging.getLogger(__name__)

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

    def filter(self, record):
        return record.levelno < self.level

info_handler = logging.StreamHandler(stdout)
error_handler = logging.StreamHandler(stderr)

info_handler.addFilter(LogFilter(logging.WARNING))
error_handler.setLevel(max(logging.DEBUG, logging.WARNING))

logger.handlers = [info_handler, error_handler]
logger.setLevel(logging.DEBUG)

Here is my answer (combination of the others):

logger = logging.getLogger(__name__)

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

    def filter(self, record):
        return record.levelno < self.level

info_handler = logging.StreamHandler(stdout)
error_handler = logging.StreamHandler(stderr)

info_handler.addFilter(LogFilter(logging.WARNING))
error_handler.setLevel(max(logging.DEBUG, logging.WARNING))

logger.handlers = [info_handler, error_handler]
logger.setLevel(logging.DEBUG)
凝望流年 2024-08-10 18:50:51

试试这个猴子补丁~~

import sys
import logging
import threading

def _logging_handle(self, record):
    self.STREAM_LOCKER = getattr(self, "STREAM_LOCKER", threading.RLock())
    if self.stream in (sys.stdout, sys.stderr) and record.levelname in self.FIX_LEVELS:
        try:
            self.STREAM_LOCKER.acquire()
            self.stream = sys.stdout
            self.old_handle(record)
            self.stream = sys.stderr
        finally:
            self.STREAM_LOCKER.release()
    else:
        self.old_handle(record)


def patch_logging_stream(*levels):
    """
    writing some logging level message to sys.stdout

    example:
    patch_logging_stream(logging.INFO, logging.DEBUG)
    logging.getLogger('root').setLevel(logging.DEBUG)

    logging.getLogger('root').debug('test stdout')
    logging.getLogger('root').error('test stderr')
    """
    stream_handler = logging.StreamHandler
    levels = levels or [logging.DEBUG, logging.INFO]
    stream_handler.FIX_LEVELS = [logging.getLevelName(i) for i in levels]
    if hasattr(stream_handler, "old_handle"):
        stream_handler.handle = stream_handler.old_handle
    stream_handler.old_handle = stream_handler.handle
    stream_handler.handle = _logging_handle

测试

#
patch_logging_stream(logging.INFO, logging.DEBUG)
logging.getLogger('root').setLevel(logging.DEBUG)

logging.getLogger('root').debug('test root stdout')
logging.getLogger('root').error('test root stderr')

测试输出

$ python3 test_patch_logging.py 2>/dev/null
DEBUG:root:test root stdout

$ python3 test_patch_logging.py 1>/dev/null
ERROR:root:test root stderr

Try This Monkey Patch ~~

import sys
import logging
import threading

def _logging_handle(self, record):
    self.STREAM_LOCKER = getattr(self, "STREAM_LOCKER", threading.RLock())
    if self.stream in (sys.stdout, sys.stderr) and record.levelname in self.FIX_LEVELS:
        try:
            self.STREAM_LOCKER.acquire()
            self.stream = sys.stdout
            self.old_handle(record)
            self.stream = sys.stderr
        finally:
            self.STREAM_LOCKER.release()
    else:
        self.old_handle(record)


def patch_logging_stream(*levels):
    """
    writing some logging level message to sys.stdout

    example:
    patch_logging_stream(logging.INFO, logging.DEBUG)
    logging.getLogger('root').setLevel(logging.DEBUG)

    logging.getLogger('root').debug('test stdout')
    logging.getLogger('root').error('test stderr')
    """
    stream_handler = logging.StreamHandler
    levels = levels or [logging.DEBUG, logging.INFO]
    stream_handler.FIX_LEVELS = [logging.getLevelName(i) for i in levels]
    if hasattr(stream_handler, "old_handle"):
        stream_handler.handle = stream_handler.old_handle
    stream_handler.old_handle = stream_handler.handle
    stream_handler.handle = _logging_handle

Test

#
patch_logging_stream(logging.INFO, logging.DEBUG)
logging.getLogger('root').setLevel(logging.DEBUG)

logging.getLogger('root').debug('test root stdout')
logging.getLogger('root').error('test root stderr')

Test Output

$ python3 test_patch_logging.py 2>/dev/null
DEBUG:root:test root stdout

$ python3 test_patch_logging.py 1>/dev/null
ERROR:root:test root stderr

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文