configurazione del logger per accedere al file e stampare su stdout


353

Sto usando il modulo di registrazione di Python per registrare alcune stringhe di debug in un file che funziona abbastanza bene. Inoltre, vorrei utilizzare questo modulo per stampare anche le stringhe su stdout. Come faccio a fare questo? Per registrare le mie stringhe in un file uso il seguente codice:

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)

e quindi chiamare una funzione logger come

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

Grazie per l'aiuto qui!

Risposte:


451

Basta avere un handle per il logger di root e aggiungere il StreamHandler. La StreamHandlerscrive a stderr. Non sono sicuro se hai davvero bisogno di stdout su stderr, ma questo è quello che uso quando installo il logger Python e aggiungo anche quello FileHandler. Quindi tutti i miei registri vanno in entrambi i posti (che è quello che sembra che tu voglia).

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

Se si desidera eseguire l'output stdoutinvece di stderr, è sufficiente specificarlo al StreamHandlercostruttore.

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

È inoltre possibile aggiungere un Formatterad esso in modo che tutte le righe del registro abbiano un'intestazione comune.

vale a dire:

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)

Stampa nel formato di:

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

19
Puoi anche inizializzare il StreamHandlercon sys.stdout, e poi accederà a quello invece di stderr.
Silas Ray,

1
@ sr2222 logger.addHandler (sys.stdout) mi dà NameError: il nome 'sys' non è definito
stdcerr il

21
Beh sì ... devi import sysprima. E in realtà inizializza il gestore, ovveroconsoleHandler = logging.StreamHandler(sys.stdout)
Silas Ray

15
Perché come ho già detto, non è così che lo fai. Creare l'HANDLER con sys.stdout, quindi collegare il gestore al logger.
Silas Ray,

6
Non dimenticare rootLogger.setLevel(logging.DEBUG)se stai cercando di vedere informazioni o messaggi di debug
storm_m2138,

247

logging.basicConfig()può accettare un argomento di parole chiave handlersda Python 3.3, il che semplifica molto la configurazione della registrazione, specialmente quando si configurano più gestori con lo stesso formattatore:

handlers- Se specificato, questo dovrebbe essere un iterabile di gestori già creati da aggiungere al logger root. A tutti i gestori che non hanno già un set di formattatori verrà assegnato il formatter predefinito creato in questa funzione.

L'intera configurazione può quindi essere eseguita con una singola chiamata come questa:

import logging

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

(O con import sys+ StreamHandler(sys.stdout)per i requisiti della domanda originale - l'impostazione predefinita per StreamHandler è scrivere su stderr. Guarda gli attributi LogRecord se vuoi personalizzare il formato del registro e aggiungere elementi come nome file / linea, informazioni thread ecc.)

L'impostazione sopra deve essere eseguita solo una volta vicino all'inizio dello script. È possibile utilizzare la registrazione da tutte le altre posizioni nella base di codice in un secondo momento in questo modo:

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

Nota: se non funziona, probabilmente qualcun altro ha già inizializzato il sistema di registrazione in modo diverso. I commenti suggeriscono di fare logging.root.handlers = []prima della chiamata a basicConfig().


5
non dimenticare di impostare level = logging.INFO o anche il livello desiderato
Andy Matteson,

5
Definizione per FileHandler: logging.FileHandler(filename, mode='a', encoding=None, delay=False). Ciò significa che quando vuoi semplicemente accedere alla stessa cartella, puoi semplicemente usare FileHandler("mylog.log"). Se si desidera sovrascrivere il registro ogni volta, impostare "w" come secondo argomento.
user136036,

7
Ho provato questo, ma il file di output è vuoto anche se la console sta dando l'output .. Qualche suggerimento ..?
Ramesh-X,

4
@ Ramesh-X, anche questo mi ha fatto impazzire. basta fare logging.root.handlers = []prima della chiamata per basicConfigdare un'occhiata alla funzione - è fastidioso.
ihadanny, il

70

L'aggiunta di uno StreamHandler senza argomenti va a stderr anziché a stdout. Se qualche altro processo ha una dipendenza dal dump dello stdout (cioè quando si scrive un plug-in NRPE), assicurarsi di specificare lo stdout esplicitamente o si potrebbero riscontrare problemi imprevisti.

Ecco un breve esempio di riutilizzo dei valori assunti e LOGFILE dalla domanda:

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)

Ci sto provando.
Ajay Kumar,

19

O correre basicConfigcon stream=sys.stdoutcome argomento prima di quella di qualsiasi altra gestori o l'accesso alcun messaggio, o manualmente aggiungere StreamHandlerche i messaggi spinge a stdout al logger principale (o qualsiasi altro registratore che si desidera, è per questo).


5

Dopo aver usato il codice di Waterboy più e più volte in più pacchetti Python, finalmente lo ho lanciato in un piccolo pacchetto Python autonomo, che puoi trovare qui:

https://github.com/acschaefer/duallog

Il codice è ben documentato e facile da usare. Basta scaricare il .pyfile e includerlo nel progetto o installare l'intero pacchetto tramite pip install duallog.


Per qualche motivo non viene registrato sulla console né sul file (è vuoto)
JackTheKnife

5

Accesso a stdoute rotating filecon diversi livelli e formati:

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

Ecco una soluzione completa e ben strutturata basata sulla risposta di Waterboy e varie altre fonti. Supporta la registrazione su console e file di registro, consente diverse impostazioni a livello di registro, fornisce output colorato ed è facilmente configurabile (disponibile anche come 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

Per 2.7, prova quanto segue:

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.