คอนฟิกูเรชันตัวบันทึกเพื่อล็อกไฟล์และพิมพ์ไปยัง stdout


353

ฉันใช้โมดูลการบันทึกของ 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")

ขอบคุณสำหรับความช่วยเหลือที่นี่!

คำตอบ:


451

StreamHandlerเพียงแค่ได้รับการจัดการในการตัดไม้รากและเพิ่ม การStreamHandlerเขียนไปยัง stderr ไม่แน่ใจว่าคุณต้องการ stdout ผ่าน stderr จริงๆหรือไม่ แต่นี่คือสิ่งที่ฉันใช้เมื่อตั้งค่า Python logger และฉันก็เพิ่มด้วยFileHandlerเช่นกัน จากนั้นบันทึกทั้งหมดของฉันจะไปที่ทั้งสองแห่ง (ซึ่งเป็นเหมือนที่คุณต้องการ)

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

หากคุณต้องการที่จะส่งออกไปstdoutแทนคุณstderrเพียงแค่ต้องระบุให้กับตัวStreamHandlerสร้าง

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

คุณสามารถเพิ่ม a 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

19
คุณสามารถกำหนดค่าเริ่มต้นStreamHandlerด้วยsys.stdoutแล้วมันจะเข้าสู่ระบบแทน stderr
สิลาสเรย์

1
@ sr2222 logger.addHandler (sys.stdout) ให้ฉัน NameError: ไม่ได้กำหนดชื่อ 'sys'
stdcerr

21
ใช่ ... คุณต้องimport sysก่อน และเริ่มต้นตัวจัดการจริง ๆ เช่นconsoleHandler = logging.StreamHandler(sys.stdout)
Silas Ray

15
เพราะอย่างที่ฉันพูดไปแล้วนั่นไม่ใช่วิธีที่คุณทำ สร้าง HANDLER ด้วย sys.stdout จากนั้นแนบ handler เข้ากับคนตัดไม้
สิลาสเรย์

6
อย่าลืมrootLogger.setLevel(logging.DEBUG)ว่าคุณกำลังพยายามดูข้อมูลหรือข้อความดีบั๊ก
storm_m2138

247

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หากคุณต้องการปรับแต่งรูปแบบการบันทึกและเพิ่มสิ่งต่าง ๆ เช่นชื่อไฟล์ / บรรทัดข้อมูลเธรดเป็นต้น)

การตั้งค่าด้านบนจะต้องทำเพียงครั้งเดียวใกล้ถึงจุดเริ่มต้นของสคริปต์ คุณสามารถใช้การบันทึกจากที่อื่น ๆ ใน codebase ในภายหลังเช่นนี้

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

หมายเหตุ: หากไม่สามารถใช้งานได้อาจมีบุคคลอื่นเริ่มต้นระบบการบันทึกต่างไป ความคิดเห็นขอแนะนำให้ทำก่อนที่จะเรียกร้องให้logging.root.handlers = []basicConfig()


5
อย่าลืมตั้งค่าระดับ = logging.INFO หรือระดับที่ต้องการเช่นกัน
Andy Matteson

5
คำจำกัดความของFileHandler: logging.FileHandler(filename, mode='a', encoding=None, delay=False). FileHandler("mylog.log")ซึ่งหมายความว่าเมื่อคุณเพียงต้องการที่จะเข้าสู่ระบบในโฟลเดอร์เดียวกันคุณก็สามารถใช้ หากคุณต้องการบันทึกทับทุกครั้งให้ตั้งค่า "w" เป็นอาร์กิวเมนต์ที่สอง
user136036

7
ฉันลองสิ่งนี้ แต่ไฟล์เอาต์พุตนั้นว่างเปล่าแม้ว่าคอนโซลจะให้ผลลัพธ์ .. คำแนะนำใด ๆ .. ?
Ramesh-X

4
@ Ramesh-X มันทำให้ฉันเป็นบ้าเช่นกัน ทำlogging.root.handlers = []ก่อนที่จะโทรไปbasicConfigดูฟังก์ชั่น - มันน่ารำคาญ
ihadanny

70

เพิ่ม 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)

ฉันลองสิ่งนี้
Ajay Kumar

19

เรียกใช้basicConfigด้วยstream=sys.stdoutอาร์กิวเมนต์ก่อนที่จะตั้งค่าตัวจัดการอื่น ๆ หรือบันทึกข้อความใด ๆ หรือเพิ่มStreamHandlerข้อความที่ส่งข้อความไปยัง stdout ไปยัง root logger ด้วยตนเอง(หรือตัวบันทึกอื่น ๆ ที่คุณต้องการสำหรับเรื่องนั้น)


5

หลังจากใช้รหัสของ Waterboy ซ้ำแล้วซ้ำอีกในแพ็คเกจ Python หลาย ๆ อันในที่สุดฉันก็นำมันไปทำเป็นแพ็คเกจ Python แบบสแตนด์อโลนขนาดเล็กซึ่งคุณสามารถหาได้ที่นี่:

https://github.com/acschaefer/duallog

รหัสเป็นเอกสารที่ดีและใช้งานง่าย เพียงดาวน์โหลดไฟล์และรวมไว้ในโครงการของคุณหรือติดตั้งแพคเกจทั้งหมดผ่านทาง.pypip install duallog


ด้วยเหตุผลบางอย่างไม่ได้เข้าสู่คอนโซลไม่มีไฟล์ (ว่างเปล่า)
JackTheKnife

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

2

นี่คือคำตอบที่สมบูรณ์และห่อหุ้มอย่างดีตามคำตอบของ 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())

-4

สำหรับ 2.7 ลองทำดังนี้:

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.