Zmuszenie rejestratorów Pythona do wysyłania wszystkich komunikatów na standardowe wyjście oprócz pliku dziennika


450

Czy istnieje sposób, aby logowanie w Pythonie za pomocą loggingmodułu automatycznie wyświetlało rzeczy na standardowe wyjście oprócz pliku dziennika, do którego powinny się udać? Na przykład, chciałbym wszystkich połączeń do logger.warning, logger.critical, logger.erroraby przejść do zamierzonych miejscach, ale w dodatku zawsze być kopiowane do stdout. Ma to na celu uniknięcie powielania wiadomości, takich jak:

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

1
Sprawdź tę odpowiedź stackoverflow.com/questions/9321741/...
SeF

Odpowiedzi:


635

Wszystkie dane wyjściowe rejestrowania są obsługiwane przez procedury obsługi; po prostu dodaj a logging.StreamHandler()do głównego loggera.

Oto przykład konfigurowania modułu obsługi strumienia (użycie stdoutzamiast domyślnego stderr) i dodawanie go do głównego programu rejestrującego:

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
W porządku, ale jeśli jest już przekierowany do pliku, jak mogę go dodatkowo wydrukować stdout?

54
@ user248237: Dodając nowy moduł obsługi zgodnie z ilustracją. Nowe moduły obsługi nie zastępują istniejących modułów obsługi, mogą także przetwarzać wpisy dziennika.
Martijn Pieters

@MartijnPieters czy istnieje sposób, aby dodać ciąg do każdej wydrukowanej instrukcji dziennika?
Prakhar Mohan Srivastava

7
@PrakharMohanSrivastava Myślę, że możesz po prostu dodać go do przekazanego ciągu logging.Formatter.
A.Wan

3
@ himanshu219: przypadek użycia jest taki, że jak tylko zaczniesz dodawać wiele programów obsługi, zwykle chcesz rozróżnić. DEBUGOWANIE do konsoli, OSTRZEŻENIE i do pliku itp.
Martijn Pieters

505

Najprostszy sposób na zalogowanie się na standardowe wyjście:

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

57
Hm, ale to nie jest zalogowane do pliku, prawda? Pytanie dotyczyło sposobu logowania do pliku i do konsoli.
Weidenrinde


Przynajmniej w Pythonie 3 wygląda na to, że pominięcie stream=sys.stdoutnadal działa w przypadku logowania do konsoli.
Taylor Edmiston,

3
@TaylorEdmiston Tak, ale to strumień AFAIK stderr. Spróbuj przekierować wyjście z powłoki.
Sorin

1
OK. To nie odpowiada na oba: logowanie do pliku i na konsolę, ale miło było znaleźć to, czego potrzebowałem w 3 liniach lub mniej.
Steve3p0

67

Jest to możliwe przy użyciu wielu programów obsługi.

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)

Zobacz: https://docs.python.org/2/howto/logging-cookbook.html


4
Cudowna odpowiedź, choć trochę niechlujna. Uwielbiam to, jak pokazujesz, jak używać różnych poziomów i formatów dla strumieni i plików. +1, ale +2 w duchu.
The Unfun Cat

Dla mnie to nie działa bez sys.stdoutparametru wch = logging.StreamHandler()
veuncent

64

Możesz utworzyć dwie procedury obsługi plików i standardowego wyjścia, a następnie utworzyć jeden program rejestrujący z handlersargumentem do basicConfig. Może to być przydatne, jeśli masz ten sam poziom log_level i format wyjściowy dla obu programów obsługi:

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

Najprostszy sposób na zalogowanie się do pliku i do stderr:

import logging

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

Nie wyświetla etykiet INFO, DEBUG i ERROR przed komunikatem logowania w konsoli. To pokazuje te etykiety w pliku. Jakieś pomysły, aby pokazać etykiety w konsoli?
JahMyst

1
Dzięki, @JahMyst, dodałem formatyzator. Niestety nie jest już tak krótki, ale wciąż najprostszy sposób. :-)
Weidenrinde

12

Oto rozwiązanie oparte na potężnej, ale słabo udokumentowanej logging.config.dictConfigmetodzie . Zamiast wysyłać każdy komunikat dziennika do stdout, wysyła wiadomości o poziomie dziennika wyższym ERRORi do stderrwszystkich innych elementów stdout. Może to być przydatne, jeśli inne części systemu słuchają stderrlub 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)

musiał zmienić nazwę rejestratora na pusty ciąg, aby faktycznie uzyskać rejestrator główny. W przeciwnym razie bardzo pomocne, dzięki!
Newtopian

8

Ponieważ nikt nie udostępnił zgrabnego linera, podzielę się własnym:

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

2

Oto niezwykle prosty przykład:

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

Dane wyjściowe pokażą „test msg” na standardowym wyjściu, a także w pliku.

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.