konfiguracja rejestratora do logowania do pliku i drukowania na standardowe wyjście


353

Korzystam z modułu rejestrującego Pythona, aby zalogować niektóre ciągi debugowania do pliku, który działa całkiem dobrze. Teraz dodatkowo chciałbym użyć tego modułu do wydrukowania ciągów na standardowe wyjście. Jak mam to zrobic? Aby zalogować moje ciągi do pliku, używam następującego kodu:

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)

a następnie wywołać funkcję rejestratora, np

logger.debug("I am written to the file")

Dziękuję za pomoc tutaj!

Odpowiedzi:


451

Po prostu weź uchwyt do głównego loggera i dodaj StreamHandler. StreamHandlerPisze na stderr. Nie jestem pewien, czy naprawdę potrzebujesz stdout nad stderr, ale tego właśnie używam, gdy konfiguruję program rejestrujący w języku Python i również go dodaję FileHandler. Następnie wszystkie moje logi trafiają do obu miejsc (tak brzmi, jak chcesz).

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

Jeśli chcesz wyświetlać dane wyjściowe stdoutzamiast stderr, musisz tylko podać je StreamHandlerkonstruktorowi.

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

Możesz także dodać Formatterdo niego, aby wszystkie wiersze dziennika miały wspólny nagłówek.

to znaczy:

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)

Drukuje do formatu:

2012-12-05 16:58:26,618 [MainThread  ] [INFO ]  my message

19
Możesz także po prostu zainicjować za StreamHandlerpomocą sys.stdout, a następnie zaloguje się do tego zamiast stderr.
Silas Ray,

1
@ sr2222 logger.addHandler (sys.stdout) daje mi NameError: nazwa 'sys' nie jest zdefiniowana
stdcerr

21
No tak ... musisz import sysnajpierw. I faktycznie zainicjuj moduł obsługi, tj.consoleHandler = logging.StreamHandler(sys.stdout)
Silas Ray

15
Ponieważ, jak już powiedziałem, nie tak to robisz. Utwórz HANDLER za pomocą sys.stdout, a następnie podłącz moduł obsługi do programu rejestrującego.
Silas Ray

6
Nie zapomnij, rootLogger.setLevel(logging.DEBUG)jeśli próbujesz zobaczyć informacje lub wiadomości debugowania
storm_m2138

247

logging.basicConfig()może wziąć argument słowa kluczowego handlersod Pythona 3.3, co znacznie upraszcza konfigurację rejestrowania, szczególnie przy konfigurowaniu wielu programów obsługi z tym samym formatyzatorem:

handlers- Jeśli jest określony, powinien to być iterowalny już utworzony moduł obsługi, który ma zostać dodany do głównego programu rejestrującego. Wszelkie programy obsługi, które nie mają jeszcze zestawu formatowania, zostaną przypisane do domyślnego formatyzatora utworzonego w tej funkcji.

Całą konfigurację można zatem wykonać za pomocą jednego połączenia takiego jak to:

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("debug.log"),
        logging.StreamHandler()
    ]
)

(Lub z import sys+ StreamHandler(sys.stdout)dla oryginalnych wymagań pytania - domyślnie StreamHandler zapisuje do stderr. Spójrz na atrybuty LogRecord, jeśli chcesz dostosować format dziennika i dodać takie rzeczy, jak nazwa pliku / linii, informacje o wątku itp.)

Powyższą konfigurację należy wykonać tylko raz w pobliżu początku skryptu. Możesz użyć rejestrowania ze wszystkich innych miejsc w bazie kodu później w następujący sposób:

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

Uwaga: jeśli to nie zadziała, prawdopodobnie ktoś już zainicjował system rejestrowania inaczej. Komentarze sugerują zrobienie tego logging.root.handlers = []przed wezwaniem do basicConfig().


5
nie zapomnij również ustawić poziomu = logowanie lub pożądanego poziomu
Andy Matteson,

5
Definicja FileHandler: logging.FileHandler(filename, mode='a', encoding=None, delay=False). Oznacza to, że jeśli chcesz zalogować się do tego samego folderu, możesz po prostu użyć FileHandler("mylog.log"). Jeśli chcesz zastępować dziennik za każdym razem, ustaw „w” jako drugi argument.
user136036

7
Próbowałem tego, ale plik wyjściowy jest pusty, chociaż konsola podaje dane wyjściowe .. Wszelkie sugestie ..?
Ramesh-X

4
@ Ramesh-X, to też doprowadziło mnie do szału. po prostu zrób to logging.root.handlers = []przed wezwaniem basicConfig, spójrz na funkcję - to denerwujące.
ihadanny,

70

Dodanie StreamHandler bez argumentów przechodzi do stderr zamiast stdout. Jeśli jakiś inny proces jest zależny od zrzutu standardowego (np. Podczas pisania wtyczki NRPE), upewnij się, że określono standardowe wyjście standardowe, ponieważ możesz napotkać nieoczekiwane problemy.

Oto szybki przykład ponownego wykorzystania przyjętych wartości i pliku LOGFILE z pytania:

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)

Próbuję tego.
Ajay Kumar

19

Uruchom basicConfigz stream=sys.stdoutjako argument przed konfigurowania innych ładowarki lub zalogowaniu żadnych wiadomości lub ręcznie dodać StreamHandler, że popycha wiadomości na standardowe wyjście do rejestratora root (lub dowolnego innego rejestratora chcesz, o to chodzi).


5

Po wielokrotnym używaniu kodu Waterboya w wielu pakietach Python, w końcu wrzuciłem go do małego samodzielnego pakietu Python, który można znaleźć tutaj:

https://github.com/acschaefer/duallog

Kod jest dobrze udokumentowany i łatwy w użyciu. Wystarczy pobrać .pyplik i dołączyć go do projektu lub zainstalować cały pakiet za pośrednictwem pip install duallog.


Z jakiegoś powodu nie
loguje

5

Zalogowaniu do stdouti rotating filez różnych poziomów i formatów:

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

Oto kompletne, ładnie opakowane rozwiązanie oparte na odpowiedzi Waterboya i różnych innych źródłach. Obsługuje logowanie zarówno do pliku konsoli, jak i pliku dziennika, pozwala na różne ustawienia poziomu dziennika, zapewnia kolorowe wyjście i jest łatwo konfigurowalny (dostępny również jako 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

W przypadku wersji 2.7 spróbuj wykonać następujące czynności:

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.