Python-Logger geben alle Nachrichten zusätzlich zur Protokolldatei an stdout aus

450

Gibt es eine Möglichkeit, die Python-Protokollierung mithilfe des loggingModuls automatisch an stdout auszugeben, zusätzlich zu der Protokolldatei, in die sie verschoben werden sollen? Zum Beispiel würde ich alle Anrufe gerne logger.warning, logger.critical, logger.errorum ihre beabsichtigten Orte zu gehen , sondern zusätzlich immer kopiert werden stdout. Dies dient dazu, doppelte Nachrichten wie: zu vermeiden.

mylogger.critical("something failed")
print "something failed"
Ben
quelle
1
Bitte überprüfen Sie diese Antwort stackoverflow.com/questions/9321741/…
SeF

Antworten:

635

Die gesamte Protokollausgabe wird von den Handlern verarbeitet. Fügen Sie einfach ein logging.StreamHandler()zum Root-Logger hinzu.

Hier ist ein Beispiel für die Konfiguration eines Stream-Handlers ( stdoutanstelle der Standardeinstellung stderr) und das Hinzufügen zum Root-Logger:

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)
Martijn Pieters
quelle
4
Das ist in Ordnung, aber wenn es bereits in eine Datei umgeleitet wurde, wie kann ich es zusätzlich drucken lassen stdout?
54
@ user248237: Durch Hinzufügen eines neuen Handlers wie abgebildet. Neue Handler ersetzen nicht die vorhandenen Handler, sondern können auch die Protokolleinträge verarbeiten.
Martijn Pieters
@MartijnPieters Gibt es eine Möglichkeit, jeder ausgedruckten Protokollanweisung eine Zeichenfolge hinzuzufügen?
Prakhar Mohan Srivastava
7
@PrakharMohanSrivastava Ich denke, Sie können es einfach zu dem übergebenen String hinzufügen logging.Formatter.
A.Wan
3
@ himanshu219: Der Anwendungsfall ist, dass Sie normalerweise differenzieren möchten, sobald Sie mehrere Handler hinzufügen. DEBUG an die Konsole, WARNUNG und bis zu einer Datei usw.
Martijn Pieters
505

Der einfachste Weg, sich bei stdout anzumelden:

import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
Eyal
quelle
57
Hm, aber das ist nicht in einer Datei protokolliert, oder? Die Frage war, wie man sich in einer Datei und in einer Konsole anmeldet .
Weidenrinde
Ref Link: Python3 Docs: Logging.basicConfig
Czechnology
Zumindest in Python 3 sieht es so aus, als würde das Auslassen stream=sys.stdoutfür mich immer noch für die Protokollierung an der Konsole funktionieren.
Taylor Edmiston
3
@ TaylorEdmiston Ja, aber es ist der Stderr-Stream AFAIK. Versuchen Sie, die Ausgabe von der Shell umzuleiten.
Sorin
1
OK. Dies beantwortet nicht beide: Protokollierung in Datei und Konsole, aber es war schön, das, was ich brauchte, in 3 Zeilen oder weniger zu finden.
Steve3p0
67

Es ist möglich, mehrere Handler zu verwenden.

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)

Weitere Informationen finden Sie unter: https://docs.python.org/2/howto/logging-cookbook.html

Alok Singh Mahor
quelle
4
Wunderbare Antwort, wenn auch etwas chaotisch. Lieben Sie, wie Sie zeigen, wie Sie verschiedene Ebenen und Formate für Streams und Dateien verwenden. +1, aber +2 im Geist.
Die Unfun Cat
Für mich funktionierte dies nicht ohne den sys.stdoutParameter inch = logging.StreamHandler()
veuncent
64

Sie können zwei Handler für file und stdout erstellen und dann einen Logger mit handlersArgument to erstellen basicConfig. Es kann nützlich sein, wenn Sie für beide Handler dieselbe log_level- und Formatausgabe haben:

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')
Anton Protopopov
quelle
32

Der einfachste Weg, sich in einer Datei und in stderr anzumelden:

import logging

logging.basicConfig(filename="logfile.txt")
stderrLogger=logging.StreamHandler()
stderrLogger.setFormatter(logging.Formatter(logging.BASIC_FORMAT))
logging.getLogger().addHandler(stderrLogger)
Weidenrinde
quelle
Hier werden die Bezeichnungen INFO, DEBUG und ERROR vor der Protokollierungsnachricht in der Konsole nicht angezeigt. Diese Beschriftungen werden in der Datei angezeigt. Irgendwelche Ideen, um auch die Beschriftungen in der Konsole anzuzeigen?
JahMyst
1
Danke, @JahMyst, ich habe den Formatierer hinzugefügt. Leider ist es nicht mehr so ​​kurz, aber immer noch der einfachste Weg. :-)
Weidenrinde
12

Hier ist eine Lösung, die auf der leistungsstarken, aber schlecht dokumentierten logging.config.dictConfigMethode basiert . Anstatt jede Protokollnachricht an zu senden stdout, werden Nachrichten mit Protokollstufe ERRORund höher an stderrund alles andere an gesendet stdout. Dies kann nützlich sein, wenn andere Teile des Systems stderroder hören 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)
Elias Strehle
quelle
musste den Logger in eine leere Zeichenfolge umbenennen, um den Root-Logger tatsächlich zu erhalten. Ansonsten sehr hilfreich, danke!
Newtopian
8

Da niemand einen ordentlichen Zwei-Liner geteilt hat, werde ich meinen eigenen teilen:

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

Hier ist ein äußerst einfaches Beispiel:

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")

Die Ausgabe zeigt "test msg" auf stdout und auch in der Datei.

Kiki Jewell
quelle