记录器配置以记录到文件并打印到stdout

std*_*err 310 python logging stdout file

我正在使用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)
Run Code Online (Sandbox Code Playgroud)

然后调用一个记录器函数

logger.debug("I am written to the file")
Run Code Online (Sandbox Code Playgroud)

谢谢你的帮助!

小智 403

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

import logging
logging.getLogger().addHandler(logging.StreamHandler())
Run Code Online (Sandbox Code Playgroud)

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

即:

import sys
# ...
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))
Run Code Online (Sandbox Code Playgroud)

打印到以下格式:

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)
Run Code Online (Sandbox Code Playgroud)

  • 嗯...你必须首先`导入sys`.并实际初始化处理程序,即`consoleHandler = logging.StreamHandler(sys.stdout)` (21认同)
  • 您也可以使用`sys.stdout`初始化`StreamHandler`,然后它将记录到而不是stderr. (19认同)
  • 因为正如我已经说过的那样,你不是这样做的.使用sys.stdout创建HANDLER,然后将处理程序附加到记录器. (15认同)
  • 如果您尝试查看信息或调试消息,请不要忘记`rootLogger.setLevel(logging.DEBUG)` (5认同)

Yir*_*kha 188

logging.basicConfig()可以handlers从Python 3.3 开始使用关键字参数,这简化了日志记录设置,尤其是在使用相同的格式化程序设置多个处理程序时:

handlers - 如果指定,则应该是已创建的处理程序的可迭代,以添加到根记录器.任何没有格式化程序集的处理程序都将被分配在此函数中创建的默认格式化程序.

因此,接受的答案中相当长且冗长的示例代码就变成了这样:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s",
    handlers=[
        logging.FileHandler("{0}/{1}.log".format(logPath, fileName)),
        logging.StreamHandler()
    ])
Run Code Online (Sandbox Code Playgroud)

(或者根据原始问题的要求加上import sys+ StreamHandler(sys.stdout).)

要获取记录器,请使用

logger = logging.getLogger()
Run Code Online (Sandbox Code Playgroud)

稍后在脚本中,用于logger.info()输出有用的日志消息.

  • @Ramesh-X,这也让我发疯。只需在调用“basicConfig”之前执行“logging.root.handlers = []”,看看该函数 - 这很烦人。 (7认同)
  • 不要忘记设置level = logging.INFO或所需的级别 (5认同)
  • `FileHandler`的定义:`logging.FileHandler(filename,mode ='a',encoding = None,delay = False)`.这意味着,当你只想登录同一个文件夹时,你可以使用`FileHandler("mylog.log")`.如果要每次都覆盖日志,请将"w"设置为第二个参数. (5认同)
  • 我试过这个,但输出文件是空的虽然控制台正在给出输出..任何建议..? (2认同)
  • 我还需要 `noinspection PyArgumentList` 来让 PyC​​harm 满意,因为 https://youtrack.jetbrains.com/issue/PY-39762 (2认同)

Haz*_*zok 63

添加不带参数的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)
Run Code Online (Sandbox Code Playgroud)


Sil*_*Ray 19

无论是运行basicConfigstream=sys.stdout如设置任何其他处理程序或记录任何消息,或手动添加之前的说法StreamHandler是推动信息到标准输出至根记录器(或任何你想要的其他记录器,对于这个问题).

  • 当我尝试一起使用“stream”和“filename”参数或与处理程序一起使用时出现错误 (2认同)

Max*_*xim 16

这是一个基于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:                                                                    -
#  /sf/answers/961370441/                                 -
#  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())
Run Code Online (Sandbox Code Playgroud)

有关 Microsoft Windows 10 的注意事项:
要在 Microsoft Windows 10 上实际显示颜色,必须先启用 ANSI 终端模式。这是一个可以做到这一点的函数:

# Enable ANSI terminal on Microsoft Windows 10
# /sf/answers/2573261701/
# https://docs.microsoft.com/en-us/windows/console/setconsolemode
def windows_enable_ansi_terminal():
    if (sys.platform != "win32"):
        return None
    try:
        import ctypes
        kernel32 = ctypes.windll.kernel32
        result = kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
        if (result == 0): raise Exception
        return True
    except:
        return False
Run Code Online (Sandbox Code Playgroud)


And*_*jak 13

记录到stdoutrotating 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.')
Run Code Online (Sandbox Code Playgroud)


Lex*_*xer 6

在多个 Python 包中一遍又一遍地使用 Waterboy 的代码后,我终于将其转换为一个小型的独立 Python 包,您可以在这里找到它:

https://github.com/acschaefer/duallog

该代码有据可查且易于使用。只需下载该.py文件并将其包含在您的项目中,或者通过pip install duallog.