簡體   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