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

谢谢你的帮助!


当前回答

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

https://github.com/acschaefer/duallog

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

其他回答

记录到标准输出和旋转文件的不同级别和格式:

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

对于2.7,尝试以下操作:

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)

添加不带参数的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)

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

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

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安装整个包。