Rendere i logger Python emettono tutti i messaggi su stdout oltre al file di registro


450

C'è un modo per fare la registrazione di Python usando il loggingmodulo che emette automaticamente le cose su stdout oltre al file di registro dove dovrebbero andare? Ad esempio, mi piacerebbe tutte le chiamate a logger.warning, logger.critical, logger.errorper andare ai loro luoghi destinati, ma in aggiunta sempre essere copiati stdout. Questo per evitare la duplicazione di messaggi come:

mylogger.critical("something failed")
print "something failed"

1
Si prega di verificare questa risposta stackoverflow.com/questions/9321741/...
SeF

Risposte:


635

Tutto l'output di registrazione è gestito dai gestori; basta aggiungere logging.StreamHandler()a al logger di root.

Ecco un esempio di configurazione di un gestore di flusso (utilizzando al stdoutposto del predefinito stderr) e aggiunta al logger di root:

import logging
import sys

root = logging.getLogger()
root.setLevel(logging.DEBUG)

handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
root.addHandler(handler)

4
Va bene ma se è già reindirizzato a un file come posso stamparlo stdoutin aggiunta?

54
@ user248237: aggiungendo un nuovo gestore come illustrato. I nuovi gestori non sostituiscono i gestori esistenti, ma possono anche elaborare le voci del registro.
Martijn Pieters

@MartijnPieters c'è un modo per aggiungere una stringa a ogni istruzione di registro stampata?
Prakhar Mohan Srivastava,

7
@PrakharMohanSrivastava Immagino che puoi semplicemente aggiungerlo alla stringa passata logging.Formatter.
A.Wan

3
@ himanshu219: il caso d'uso è che non appena inizi ad aggiungere più gestori, di solito vuoi differenziare. DEBUG alla console, AVVISO e fino a un file, ecc.
Martijn Pieters

505

Il modo più semplice per accedere a stdout:

import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

57
Hm, ma questo non è registrato in un file, giusto? La domanda era: come effettuare la registrazione su file e console.
Weidenrinde,


Almeno in Python 3, sembra che l'omissione stream=sys.stdoutcontinui a funzionare per me.
Taylor Edmiston,

3
@TaylorEdmiston Sì, ma è il flusso stderr AFAIK. Prova a reindirizzare l'output dalla shell.
Sorin,

1
OK. Questo non risponde ad entrambi: la registrazione su file e su console, ma è stato bello trovare ciò di cui avevo bisogno in 3 righe o meno.
Steve3p0

67

È possibile utilizzare più gestori.

import logging
import auxiliary_module

# create logger with 'spam_application'
log = logging.getLogger('spam_application')
log.setLevel(logging.DEBUG)

# create formatter and add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# create file handler which logs even debug messages
fh = logging.FileHandler('spam.log')
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)
log.addHandler(fh)

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.ERROR)
ch.setFormatter(formatter)
log.addHandler(ch)

log.info('creating an instance of auxiliary_module.Auxiliary')
a = auxiliary_module.Auxiliary()
log.info('created an instance of auxiliary_module.Auxiliary')

log.info('calling auxiliary_module.Auxiliary.do_something')
a.do_something()
log.info('finished auxiliary_module.Auxiliary.do_something')

log.info('calling auxiliary_module.some_function()')
auxiliary_module.some_function()
log.info('done with auxiliary_module.some_function()')

# remember to close the handlers
for handler in log.handlers:
    handler.close()
    log.removeFilter(handler)

Si prega di consultare: https://docs.python.org/2/howto/logging-cookbook.html


4
Risposta meravigliosa, anche se un po 'confusa. Adoro il modo in cui mostri come utilizzare livelli e formati diversi per stream e file. +1, ma +2 nello spirito.
The Unfun Cat,

Per me questo non ha funzionato senza il sys.stdoutparametroch = logging.StreamHandler()
veuncent

64

È possibile creare due gestori per file e stdout e quindi creare un logger con handlersargomento su basicConfig. Potrebbe essere utile se hai lo stesso log_level e lo stesso formato di output per entrambi i gestori:

import logging
import sys

file_handler = logging.FileHandler(filename='tmp.log')
stdout_handler = logging.StreamHandler(sys.stdout)
handlers = [file_handler, stdout_handler]

logging.basicConfig(
    level=logging.DEBUG, 
    format='[%(asctime)s] {%(filename)s:%(lineno)d} %(levelname)s - %(message)s',
    handlers=handlers
)

logger = logging.getLogger('LOGGER_NAME')

32

Il modo più semplice per accedere al file e allo stderr:

import logging

logging.basicConfig(filename="logfile.txt")
stderrLogger=logging.StreamHandler()
stderrLogger.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
logging.getLogger().addHandler(stderrLogger)

Questo non mostra le etichette INFO, DEBUG ed ERROR prima del messaggio di log nella console. Mostra quelle etichette nel file. Qualche idea per mostrare anche le etichette nella console?
JahMyst,

1
Grazie, @JahMyst, ho aggiunto il Formatter. Sfortunatamente, non è più così corto, ma è ancora il modo più semplice. :-)
Weidenrinde,

12

Ecco una soluzione basata sul logging.config.dictConfigmetodo potente ma scarsamente documentato . Invece di inviare tutti i messaggi di registro a stdout, invia messaggi con livello di registro ERRORe superiore a stderre tutto il resto a stdout. Questo può essere utile se altre parti del sistema stanno ascoltando stderro stdout.

import logging
import logging.config
import sys

class _ExcludeErrorsFilter(logging.Filter):
    def filter(self, record):
        """Filters out log messages with log level ERROR (numeric value: 40) or higher."""
        return record.levelno < 40


config = {
    'version': 1,
    'filters': {
        'exclude_errors': {
            '()': _ExcludeErrorsFilter
        }
    },
    'formatters': {
        # Modify log message format here or replace with your custom formatter class
        'my_formatter': {
            'format': '(%(process)d) %(asctime)s %(name)s (line %(lineno)s) | %(levelname)s %(message)s'
        }
    },
    'handlers': {
        'console_stderr': {
            # Sends log messages with log level ERROR or higher to stderr
            'class': 'logging.StreamHandler',
            'level': 'ERROR',
            'formatter': 'my_formatter',
            'stream': sys.stderr
        },
        'console_stdout': {
            # Sends log messages with log level lower than ERROR to stdout
            'class': 'logging.StreamHandler',
            'level': 'DEBUG',
            'formatter': 'my_formatter',
            'filters': ['exclude_errors'],
            'stream': sys.stdout
        },
        'file': {
            # Sends all log messages to a file
            'class': 'logging.FileHandler',
            'level': 'DEBUG',
            'formatter': 'my_formatter',
            'filename': 'my.log',
            'encoding': 'utf8'
        }
    },
    'root': {
        # In general, this should be kept at 'NOTSET'.
        # Otherwise it would interfere with the log levels set for each handler.
        'level': 'NOTSET',
        'handlers': ['console_stderr', 'console_stdout', 'file']
    },
}

logging.config.dictConfig(config)

ha dovuto rinominare il logger in una stringa vuota per ottenere effettivamente il logger root. Altrimenti molto utile, grazie!
Newtopian,

8

Dal momento che nessuno ha condiviso due linee ordinate, condividerò la mia:

logging.basicConfig(filename='logs.log', level=logging.DEBUG, format="%(asctime)s:%(levelname)s: %(message)s")
logging.getLogger().addHandler(logging.StreamHandler())

2

Ecco un esempio estremamente semplice:

import logging
l = logging.getLogger("test")

# Add a file logger
f = logging.FileHandler("test.log")
l.addHandler(f)

# Add a stream logger
s = logging.StreamHandler()
l.addHandler(s)

# Send a test message to both -- critical will always log
l.critical("test msg")

L'output mostrerà "test msg" su stdout e anche nel file.

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.