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


353

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

19
StreamHandlerwith 만 초기화하면 sys.stdoutstderr 대신에 로그에 기록됩니다.
Silas Ray

1
@ sr2222 logger.addHandler (sys.stdout)는 나에게 NameError를 준다 : name '
sys'is

21
네 .. 먼저해야 import sys해요 그리고 실제로 핸들러를 초기화하십시오.consoleHandler = logging.StreamHandler(sys.stdout)
Silas Ray

15
내가 이미 말했듯이 그렇게하는 것이 아닙니다. sys.stdout을 사용하여 HANDLER를 작성한 다음 핸들러를 로거에 연결하십시오.
Silas Ray

6
rootLogger.setLevel(logging.DEBUG)정보 또는 디버그 메시지를 보려는 경우를 잊지 마십시오
storm_m2138

247

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


5
세트 수준 = logging.INFO 또는 원하는 수준뿐만 아니라 잊지 마세요
앤디 테손 (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를 추가하면 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)

나는 이것을 시도하고있다.
Ajay Kumar

19

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


5

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

https://github.com/acschaefer/duallog

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