Hacer que los registradores de Python envíen todos los mensajes a la salida estándar además del archivo de registro

¿Hay alguna manera de hacer que el registro de Python utilizando el módulo de logging genere automáticamente las cosas a la salida estándar además del archivo de registro donde se supone que deben ir? Por ejemplo, me gustaría que todas las llamadas a logger.warning , logger.critical , logger.error vayan a sus lugares logger.error pero, además, siempre se copien en stdout . Esto es para evitar la duplicación de mensajes como:

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

Toda la salida de registro es manejada por los manejadores; simplemente agregue un logging.StreamHandler() al registrador raíz.

Aquí hay un ejemplo de configuración de un controlador de flujo (usando stdout lugar del stderr predeterminado) y agregarlo al registrador raíz:

 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) 

La forma más sencilla:

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

Es posible usar múltiples manejadores.

 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) 

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

Puede crear dos controladores para el archivo y la basicConfig y luego crear un registrador con el argumento de los handlers para basicConfig . Podría ser útil si tiene el mismo log_level y formato de salida para ambos controladores:

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

La forma más sencilla de iniciar sesión en un archivo y en stderr:

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

Ya que nadie ha compartido un buen forro de dos líneas, compartiré el mío:

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

Aquí hay una solución basada en la función de configuración dictConfig . En lugar de enviar todos los mensajes de registro a la salida estándar, envía mensajes con un nivel de registro ERROR y superior a stderr y todo lo demás a la salida estándar. Esto puede ser útil si otras partes del sistema están escuchando stderr / 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': { 'my_formatter': { 'format': '(%(process)d) %(asctime)s %(name)s (line %(lineno)s) | %(levelname)s %(message)s' } }, 'handlers': { 'console_stderr': { # Directs log messages with log level ERROR or higher to stderr 'class': 'logging.StreamHandler', 'level': 'ERROR', 'formatter': 'my_formatter', 'stream': sys.stderr }, 'console_stdout': { # Directs 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': { # Directs all log messages to a file 'class': 'logging.FileHandler', 'level': 'DEBUG', 'formatter': 'my_formatter', 'filename': 'my.log', 'encoding': 'utf8' } }, 'root': { 'level': 'NOTSET', 'handlers': ['console_stderr', 'console_stdout', 'file'] }, } logging.config.dictConfig(config) 

Echa un vistazo al módulo loguru .

 from loguru import logger logger.debug("That's it, beautiful and simple logging!") 

Aquí hay un ejemplo extremadamente simple:

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

La salida mostrará “test msg” en la salida estándar y también en el archivo.