问题:记录器配置以记录到文件并打印到stdout
我正在使用Python的日志记录模块将一些调试字符串记录到运行良好的文件中。现在,此外,我想使用此模块还将字符串输出到stdout。我该怎么做呢?为了将我的字符串记录到文件中,我使用以下代码:
import logging
import logging.handlers
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
LOGFILE, maxBytes=(1048576*5), backupCount=7
)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
然后调用记录器功能,例如
logger.debug("I am written to the file")
谢谢您的帮助!
回答 0
只需获取根记录器的句柄并添加即可StreamHandler
。在StreamHandler
写至标准错误。不知道您是否真的需要stdout而不是stderr,但这是我在设置Python记录器时使用的方法,我也添加了FileHandler
它。然后,我所有的日志都转到两个地方(这听起来像您想要的)。
import logging
logging.getLogger().addHandler(logging.StreamHandler())
如果要输出到stdout
而不是stderr
,则只需将其指定给StreamHandler
构造函数。
import sys
# ...
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))
您也可以在其中添加一个Formatter
,以便所有日志行都有一个公共标题。
即:
import logging
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] %(message)s")
rootLogger = logging.getLogger()
fileHandler = logging.FileHandler("{0}/{1}.log".format(logPath, fileName))
fileHandler.setFormatter(logFormatter)
rootLogger.addHandler(fileHandler)
consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
rootLogger.addHandler(consoleHandler)
打印为以下格式:
2012-12-05 16:58:26,618 [MainThread ] [INFO ] my message
回答 1
logging.basicConfig()
handlers
从Python 3.3开始可以使用关键字参数,这大大简化了日志记录设置,尤其是在使用同一格式化程序设置多个处理程序时:
handlers
–如果指定,则这应该是已经创建的处理程序的迭代,以添加到根记录器。任何尚未设置格式器的处理程序都将被分配此函数中创建的默认格式器。
因此,整个设置可以通过以下单个调用完成:
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("debug.log"),
logging.StreamHandler()
]
)
(或按照原始问题的要求加上import sys
+ StreamHandler(sys.stdout)
– StreamHandler的默认值是写入stderr。查看LogRecord属性如果要自定义日志格式并添加诸如文件名/行,线程信息等内容,请。)
上面的设置只需要在脚本开始处执行一次即可。您可以稍后在代码库的所有其他位置使用日志记录,如下所示:
logging.info('Useful message')
logging.error('Something bad happened')
...
注意:如果它不起作用,则可能是其他人已经用不同的方式初始化了日志系统。建议logging.root.handlers = []
在调用之前先做评论basicConfig()
。
回答 2
添加不带参数的StreamHandler转到stderr而不是stdout。如果某些其他进程依赖于stdout转储(即,在编写NRPE插件时),则请确保明确指定stdout,否则您可能会遇到一些意想不到的麻烦。
这是一个快速示例,该示例重用了问题中的假定值和LOGFILE:
import logging
from logging.handlers import RotatingFileHandler
from logging import handlers
import sys
log = logging.getLogger('')
log.setLevel(logging.DEBUG)
format = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(format)
log.addHandler(ch)
fh = handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
fh.setFormatter(format)
log.addHandler(fh)
回答 3
在设置任何其他处理程序或记录任何消息之前,可以basicConfig
使用stream=sys.stdout
作为参数运行,或者手动添加一个StreamHandler
将消息推入stdout的根记录器(或与此相关的任何其他记录器)。
回答 4
在多个Python包中反复使用Waterboy的代码后,我最终将其转换为一个很小的独立Python包,您可以在这里找到:
https://github.com/acschaefer/duallog
该代码有据可查且易于使用。只需下载.py
文件并将其包含在您的项目中,或通过安装整个软件包pip install duallog
。
回答 5
登录stdout
并rotating file
使用不同的级别和格式:
import logging
import logging.handlers
import sys
if __name__ == "__main__":
# Change root logger level from WARNING (default) to NOTSET in order for all messages to be delegated.
logging.getLogger().setLevel(logging.NOTSET)
# Add stdout handler, with level INFO
console = logging.StreamHandler(sys.stdout)
console.setLevel(logging.INFO)
formater = logging.Formatter('%(name)-13s: %(levelname)-8s %(message)s')
console.setFormatter(formater)
logging.getLogger().addHandler(console)
# Add file rotating handler, with level DEBUG
rotatingHandler = logging.handlers.RotatingFileHandler(filename='rotating.log', maxBytes=1000, backupCount=5)
rotatingHandler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
rotatingHandler.setFormatter(formatter)
logging.getLogger().addHandler(rotatingHandler)
log = logging.getLogger("app." + __name__)
log.debug('Debug message, should only appear in the file.')
log.info('Info message, should appear in file and stdout.')
log.warning('Warning message, should appear in file and stdout.')
log.error('Error message, should appear in file and stdout.')
回答 6
这是一个完整的包装好的解决方案,基于Waterboy的答案和其他各种来源。它支持同时记录到控制台和日志文件,允许进行不同的日志级别设置,提供彩色输出,并且易于配置(也可以作为Gist使用):
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -------------------------------------------------------------------------------
# -
# Python dual-logging setup (console and log file), -
# supporting different log levels and colorized output -
# -
# Created by Fonic <https://github.com/fonic> -
# Date: 04/05/20 -
# -
# Based on: -
# https://stackoverflow.com/a/13733863/1976617 -
# https://uran198.github.io/en/python/2016/07/12/colorful-python-logging.html -
# https://en.wikipedia.org/wiki/ANSI_escape_code#Colors -
# -
# -------------------------------------------------------------------------------
# Imports
import os
import sys
import logging
# Logging formatter supporting colored output
class LogFormatter(logging.Formatter):
COLOR_CODES = {
logging.CRITICAL: "\033[1;35m", # bright/bold magenta
logging.ERROR: "\033[1;31m", # bright/bold red
logging.WARNING: "\033[1;33m", # bright/bold yellow
logging.INFO: "\033[0;37m", # white / light gray
logging.DEBUG: "\033[1;30m" # bright/bold black / dark gray
}
RESET_CODE = "\033[0m"
def __init__(self, color, *args, **kwargs):
super(LogFormatter, self).__init__(*args, **kwargs)
self.color = color
def format(self, record, *args, **kwargs):
if (self.color == True and record.levelno in self.COLOR_CODES):
record.color_on = self.COLOR_CODES[record.levelno]
record.color_off = self.RESET_CODE
else:
record.color_on = ""
record.color_off = ""
return super(LogFormatter, self).format(record, *args, **kwargs)
# Setup logging
def setup_logging(console_log_output, console_log_level, console_log_color, logfile_file, logfile_log_level, logfile_log_color, log_line_template):
# Create logger
# For simplicity, we use the root logger, i.e. call 'logging.getLogger()'
# without name argument. This way we can simply use module methods for
# for logging throughout the script. An alternative would be exporting
# the logger, i.e. 'global logger; logger = logging.getLogger("<name>")'
logger = logging.getLogger()
# Set global log level to 'debug' (required for handler levels to work)
logger.setLevel(logging.DEBUG)
# Create console handler
console_log_output = console_log_output.lower()
if (console_log_output == "stdout"):
console_log_output = sys.stdout
elif (console_log_output == "stderr"):
console_log_output = sys.stderr
else:
print("Failed to set console output: invalid output: '%s'" % console_log_output)
return False
console_handler = logging.StreamHandler(console_log_output)
# Set console log level
try:
console_handler.setLevel(console_log_level.upper()) # only accepts uppercase level names
except:
print("Failed to set console log level: invalid level: '%s'" % console_log_level)
return False
# Create and set formatter, add console handler to logger
console_formatter = LogFormatter(fmt=log_line_template, color=console_log_color)
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# Create log file handler
try:
logfile_handler = logging.FileHandler(logfile_file)
except Exception as exception:
print("Failed to set up log file: %s" % str(exception))
return False
# Set log file log level
try:
logfile_handler.setLevel(logfile_log_level.upper()) # only accepts uppercase level names
except:
print("Failed to set log file log level: invalid level: '%s'" % logfile_log_level)
return False
# Create and set formatter, add log file handler to logger
logfile_formatter = LogFormatter(fmt=log_line_template, color=logfile_log_color)
logfile_handler.setFormatter(logfile_formatter)
logger.addHandler(logfile_handler)
# Success
return True
# Main function
def main():
# Setup logging
script_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
if (not setup_logging(console_log_output="stdout", console_log_level="warning", console_log_color=True,
logfile_file=script_name + ".log", logfile_log_level="debug", logfile_log_color=False,
log_line_template="%(color_on)s[%(created)d] [%(threadName)s] [%(levelname)-8s] %(message)s%(color_off)s")):
print("Failed to setup logging, aborting.")
return 1
# Log some messages
logging.debug("Debug message")
logging.info("Info message")
logging.warning("Warning message")
logging.error("Error message")
logging.critical("Critical message")
# Call main function
if (__name__ == "__main__"):
sys.exit(main())
回答 7
对于2.7,请尝试以下操作:
fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)