[python] 파일에 기록하고 stdout에 인쇄하기위한 로거 구성

파이썬의 로깅 모듈을 사용하여 디버그 문자열을 꽤 잘 작동하는 파일에 로깅합니다. 또한이 모듈을 사용하여 문자열을 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")

여기에 도움을 주셔서 감사합니다!



답변

루트 로거에 대한 핸들을 가져 와서을 추가하십시오 StreamHandler. 는 StreamHandlerstderr에 기록합니다. 실제로 stderr보다 stdout이 필요한지 확실하지 않지만 이것이 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


답변

logging.basicConfig()handlers파이썬 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().


답변

인수없이 StreamHandler를 추가하면 stdout 대신 stderr로 이동합니다. 다른 프로세스가 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)


답변

하나 실행 basicConfigstream=sys.stdout추가 수동으로 다른 핸들러를 설정하거나 메시지를 로깅, 또는 이전에 인수로서 StreamHandler(그 문제에 대한, 또는 당신이 원하는 다른 로거) 루트 로거에 표준 출력이 푸시 메시지를.


답변

여러 Python 패키지에서 Waterboy의 코드를 반복해서 사용한 후에 마침내 작은 독립형 Python 패키지로 캐스트했습니다.

https://github.com/acschaefer/duallog

코드는 잘 문서화되어 있고 사용하기 쉽습니다. .py파일을 다운로드 하여 프로젝트에 포함 시키거나을 통해 전체 패키지를 설치하십시오 pip install duallog.


답변

에 로그인 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.')


답변

다음은 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())