使Python日志记录器输出所有消息到标准输出除日志文件

是否有一种方法使Python日志记录使用logging模块自动输出东西到标准输出除了到日志文件,他们应该去哪里?例如,我想所有调用logger.warninglogger.criticallogger.error去他们预定的地方,但总是复制到stdout。这是为了避免重复以下信息:

mylogger.critical("something failed")
print "something failed"
611682 次浏览

所有日志输出都由处理程序处理;只需添加logging.StreamHandler()到根日志记录器。

下面是一个配置流处理程序的例子(使用stdout而不是默认的stderr),并将其添加到根日志记录器:

import logging
import sys


root = logging.getLogger()
root.setLevel(logging.DEBUG)


handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
root.addHandler(handler)

可以使用多个处理程序。

import logging
import auxiliary_module


# create logger with 'spam_application'
log = logging.getLogger('spam_application')
log.setLevel(logging.DEBUG)


# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')


# create file handler which logs even debug messages
fh = logging.FileHandler('spam.log')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
log.addHandler(fh)


# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
ch.setFormatter(formatter)
log.addHandler(ch)


log.info('creating an instance of auxiliary_module.Auxiliary')
a = auxiliary_module.Auxiliary()
log.info('created an instance of auxiliary_module.Auxiliary')


log.info('calling auxiliary_module.Auxiliary.do_something')
a.do_something()
log.info('finished auxiliary_module.Auxiliary.do_something')


log.info('calling auxiliary_module.some_function()')
auxiliary_module.some_function()
log.info('done with auxiliary_module.some_function()')


# remember to close the handlers
for handler in log.handlers:
handler.close()
log.removeFilter(handler)

请参见:https://docs.python.org/2/howto/logging-cookbook.html

登录到stdout的最简单方法:

import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

记录文件和stderr的最简单方法:

import logging


logging.basicConfig(filename="logfile.txt")
stderrLogger=logging.StreamHandler()
stderrLogger.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
logging.getLogger().addHandler(stderrLogger)

您可以为文件和stdout创建两个处理程序,然后创建一个记录器,参数从handlersbasicConfig。如果两个处理程序有相同的log_level和format输出,这可能会很有用:

import logging
import sys


file_handler = logging.FileHandler(filename='tmp.log')
stdout_handler = logging.StreamHandler(stream=sys.stdout)
handlers = [file_handler, stdout_handler]


logging.basicConfig(
level=logging.DEBUG,
format='[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s',
handlers=handlers
)


logger = logging.getLogger('LOGGER_NAME')
这里有一个基于强大但缺乏文档记录的# EYZ0方法的解决方案。 它不是将每个日志消息发送到stdout,而是将日志级别为ERROR或更高的消息发送到stderr,将其他所有日志消息发送到stdout。 如果系统的其他部分正在监听stderrstdout,这可能很有用
import logging
import logging.config
import sys


class _ExcludeErrorsFilter(logging.Filter):
def filter(self, record):
"""Only lets through log messages with log level below ERROR ."""
return record.levelno < logging.ERROR




config = {
'version': 1,
'filters': {
'exclude_errors': {
'()': _ExcludeErrorsFilter
}
},
'formatters': {
# Modify log message format here or replace with your custom formatter class
'my_formatter': {
'format': '(%(process)d) %(asctime)s %(name)s (line %(lineno)s) | %(levelname)s %(message)s'
}
},
'handlers': {
'console_stderr': {
# Sends log messages with log level ERROR or higher to stderr
'class': 'logging.StreamHandler',
'level': 'ERROR',
'formatter': 'my_formatter',
'stream': sys.stderr
},
'console_stdout': {
# Sends log messages with log level lower than ERROR to stdout
'class': 'logging.StreamHandler',
'level': 'DEBUG',
'formatter': 'my_formatter',
'filters': ['exclude_errors'],
'stream': sys.stdout
},
'file': {
# Sends all log messages to a file
'class': 'logging.FileHandler',
'level': 'DEBUG',
'formatter': 'my_formatter',
'filename': 'my.log',
'encoding': 'utf8'
}
},
'root': {
# In general, this should be kept at 'NOTSET'.
# Otherwise it would interfere with the log levels set for each handler.
'level': 'NOTSET',
'handlers': ['console_stderr', 'console_stdout', 'file']
},
}


logging.config.dictConfig(config)

既然没有人分享过简洁的两款眼线笔,我就分享我自己的:

logging.basicConfig(filename='logs.log', level=logging.DEBUG, format="%(asctime)s:%(levelname)s: %(message)s")
logging.getLogger().addHandler(logging.StreamHandler())

这里有一个非常简单的例子:

import logging
l = logging.getLogger("test")


# Add a file logger
f = logging.FileHandler("test.log")
l.addHandler(f)


# Add a stream logger
s = logging.StreamHandler()
l.addHandler(s)


# Send a test message to both -- critical will always log
l.critical("test msg")

输出将在stdout和文件中显示“test msg”。

我简化了我的源代码(其原始版本是OOP并使用配置文件),为您提供了@EliasStrehle的一个替代解决方案,而不使用dictConfig(因此最容易与现有源代码集成):

import logging
import sys




def create_stream_handler(stream, formatter, level, message_filter=None):
handler = logging.StreamHandler(stream=stream)
handler.setLevel(level)
handler.setFormatter(formatter)
if message_filter:
handler.addFilter(message_filter)
return handler




def configure_logger(logger: logging.Logger, enable_console: bool = True, enable_file: bool = True):
if not logger.handlers:
if enable_console:
message_format: str = '{asctime:20} {name:16} {levelname:8} {message}'
date_format: str = '%Y/%m/%d %H:%M:%S'
level: int = logging.DEBUG
formatter = logging.Formatter(message_format, date_format, '{')


# Configures error output (from Warning levels).
error_output_handler = create_stream_handler(sys.stderr, formatter,
max(level, logging.WARNING))
logger.addHandler(error_output_handler)


# Configures standard output (from configured Level, if lower than Warning,
#  and excluding everything from Warning and higher).
if level < logging.WARNING:
standard_output_filter = lambda record: record.levelno < logging.WARNING
standard_output_handler = create_stream_handler(sys.stdout, formatter, level,
standard_output_filter)
logger.addHandler(standard_output_handler)


if enable_file:
message_format: str = '{asctime:20} {name:16} {levelname:8} {message}'
date_format: str = '%Y/%m/%d %H:%M:%S'
level: int = logging.DEBUG
output_file: str = '/tmp/so_test.log'


handler = logging.FileHandler(output_file)
formatter = logging.Formatter(message_format, date_format, '{')
handler.setLevel(level)
handler.setFormatter(formatter)
logger.addHandler(handler)

这是一个非常简单的测试方法:

logger: logging.Logger = logging.getLogger('MyLogger')
logger.setLevel(logging.DEBUG)
configure_logger(logger, True, True)
logger.debug('Debug message ...')
logger.info('Info message ...')
logger.warning('Warning ...')
logger.error('Error ...')
logger.fatal('Fatal message ...')
更多详细的解释-伟大的文档在链接。 例如:这很简单,你只需要设置两个记录器
import sys
import logging


logger = logging.getLogger('')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('my_log_info.log')
sh = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('[%(asctime)s] %(levelname)s [%(filename)s.%(funcName)s:%(lineno)d] %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')
fh.setFormatter(formatter)
sh.setFormatter(formatter)
logger.addHandler(fh)
logger.addHandler(sh)


def hello_logger():
logger.info("Hello info")
logger.critical("Hello critical")
logger.warning("Hello warning")
logger.debug("Hello debug")


if __name__ == "__main__":
print(hello_logger())

输出端:

[Mon, 10 Aug 2020 12:44:25] INFO [TestLoger.py.hello_logger:15] Hello info
[Mon, 10 Aug 2020 12:44:25] CRITICAL [TestLoger.py.hello_logger:16] Hello critical
[Mon, 10 Aug 2020 12:44:25] WARNING [TestLoger.py.hello_logger:17] Hello warning
[Mon, 10 Aug 2020 12:44:25] DEBUG [TestLoger.py.hello_logger:18] Hello debug
None

输出文件:

log in file


更新:彩色终端

包:

pip install colorlog

代码:

import sys
import logging
import colorlog


logger = logging.getLogger('')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('my_log_info.log')
sh = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('[%(asctime)s] %(levelname)s [%(filename)s.%(funcName)s:%(lineno)d] %(message)s', datefmt='%a, %d %b %Y %H:%M:%S')
fh.setFormatter(formatter)
sh.setFormatter(colorlog.ColoredFormatter('%(log_color)s [%(asctime)s] %(levelname)s [%(filename)s.%(funcName)s:%(lineno)d] %(message)s', datefmt='%a, %d %b %Y %H:%M:%S'))
logger.addHandler(fh)
logger.addHandler(sh)


def hello_logger():
logger.info("Hello info")
logger.critical("Hello critical")
logger.warning("Hello warning")
logger.debug("Hello debug")
logger.error("Error message")


if __name__ == "__main__":
hello_logger()
< p >输出: # EYZ0 < / p >

推荐:

INI文件中的完整的记录器配置,其中还包括stdoutdebug.log的设置:

    <李> # EYZ0
    • # EYZ0
    <李> # EYZ0
    • # EYZ0