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

时间:2010-02-20 13:21:36

标签: python logging

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

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

7 个答案:

答案 0 :(得分:27)

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信息。

答案 1 :(得分:6)

答案 2 :(得分:5)

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

答案 3 :(得分:2)

从更新的文档开始,它现在很好地涵盖了这个案例。

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

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

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

为方便起见,将所有内容和格式化程序一起添加到一个软件包中

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

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

答案 6 :(得分:-3)

App 18197 stderr: Started GET "/letterfilter?title=%D0%8C" for IP at2015-07-30 12:03:46 -0400
App 18197 stderr: Processing by PostsController#letterfilter as HTML
App 18197 stderr:   Parameters: {"title"=>"Ќ"}
App 18197 stderr:   Rendered posts/letterfilter.html.haml within layouts/application (4.3ms)
App 18197 stderr:   Rendered posts/_search.html.haml (0.8ms)
App 18197 stderr:   Rendered shared/_header.html.haml (9.6ms)
App 18197 stderr:   Rendered shared/_footer.html.haml (0.2ms)
App 18197 stderr: Completed 200 OK in 18ms (Views: 16.6ms | ActiveRecord: 0.2ms)

App 18197 stderr: Started GET "/letterfilter?title=%D0%9A" for IP at 2015-07-30 12:03:51 -0400
App 18197 stderr: Processing by PostsController#letterfilter as HTML
App 18197 stderr:   Parameters: {"title"=>"К"}
App 18197 stderr:   Rendered posts/letterfilter.html.haml within layouts/application (4.9ms)
App 18197 stderr:   Rendered posts/_search.html.haml (0.7ms)
App 18197 stderr:   Rendered shared/_header.html.haml (7.7ms)
App 18197 stderr:   Rendered shared/_footer.html.haml (0.2ms)
App 18197 stderr: Completed 200 OK in 17ms (Views: 14.2ms | ActiveRecord: 0.9ms)