我使用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")

谢谢你的帮助!


当前回答

使用stream=sys运行basicConfig。在设置任何其他处理程序或记录任何消息之前,将stdout作为参数,或者手动添加一个StreamHandler,将消息推到stdout到根记录器(或您想要的任何其他记录器)。

其他回答

使用stream=sys运行basicConfig。在设置任何其他处理程序或记录任何消息之前,将stdout作为参数,或者手动添加一个StreamHandler,将消息推到stdout到根记录器(或您想要的任何其他记录器)。

只需获得根记录器的句柄并添加StreamHandler。StreamHandler写入stderr。不确定你是否真的需要stdout而不是stderr,但这是我在设置Python日志记录器时使用的,我也添加了FileHandler。然后我所有的日志都去了这两个地方(这听起来像是你想要的)。

import logging
logging.getLogger().addHandler(logging.StreamHandler())

如果希望输出到stdout而不是stderr,只需将其指定给StreamHandler构造函数。

import sys
# ...
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

您还可以向它添加一个Formatter,以便所有日志行都有一个公共标题。

ie:

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

尽管问题特别要求记录器配置,但有一种替代方法不需要对日志记录配置进行任何更改,也不需要重定向标准输出。

也许有点简单,但它确实有效:

def log_and_print(message: str, level: int, logger: logging.Logger):
    logger.log(level=level, msg=message)  # log as normal
    print(message)  # prints to stdout by default

我们现在调用log_and_print(message='something', level=logging. debug('something')而不是例如logger.debug('something')。调试日志记录器=记录器)。

我们还可以稍微扩展一下,这样它只在必要时才打印到stdout:

def log_print(message: str, level: int, logger: logging.Logger):
    # log the message normally
    logger.log(level=level, msg=message)
    # only print to stdout if the message is not logged to stdout
    msg_logged_to_stdout = False
    current_logger = logger
    while current_logger and not msg_logged_to_stdout:
        is_enabled = current_logger.isEnabledFor(level)
        logs_to_stdout = any(
            getattr(handler, 'stream', None) == sys.stdout
            for handler in current_logger.handlers
        )
        msg_logged_to_stdout = is_enabled and logs_to_stdout
        if not current_logger.propagate:
            current_logger = None
        else:
            current_logger = current_logger.parent            
    if not msg_logged_to_stdout:
        print(message)
    

这将检查日志记录器及其父日志记录器是否有任何流到标准输出的处理程序,并检查日志记录器是否为指定级别启用。

注意,这并没有针对性能进行优化。

在多个Python包中反复使用Waterboy的代码后,我最终将其转换为一个独立的小Python包,你可以在这里找到:

https://github.com/acschaefer/duallog

该代码有良好的文档,易于使用。只需下载.py文件并将其包含在您的项目中,或通过pip install duallog安装整个包。

logging. basicconfig()从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()
    ]
)

(或根据原始问题的要求导入sys + StreamHandler(sys.stdout) -默认的StreamHandler是写入stderr。如果你想自定义日志格式并添加文件名/行、线程信息等,请查看LogRecord属性。)

上面的设置只需要在脚本的开头执行一次。你可以在代码库的其他地方使用日志,如下所示:

logging.info('Useful message')
logging.error('Something bad happened')
...

注意:如果它不起作用,那么其他人可能已经对日志系统进行了不同的初始化。注释建议在调用basicConfig()之前执行logging.root.handlers =[]。