setlevel - python logging best practices
Cómo redireccionar stdout y stderr a logger en Python (6)
Tengo un registrador que tiene un RotatingFileHandler
. Quiero redireccionar todos los Stdout
y Stderr
al registrador. ¿Cómo hacerlo?
Como evolución a la respuesta de Cameron Gagnon, he mejorado la clase LoggerWriter
para:
class LoggerWriter(object):
def __init__(self, writer):
self._writer = writer
self._msg = ''''
def write(self, message):
self._msg = self._msg + message
while ''/n'' in self._msg:
pos = self._msg.find(''/n'')
self._writer(self._msg[:pos])
self._msg = self._msg[pos+1:]
def flush(self):
if self._msg != '''':
self._writer(self._msg)
self._msg = ''''
Ahora las excepciones no controladas se ven mejor:
2018-07-31 13:20:37,482 - ERROR - Traceback (most recent call last):
2018-07-31 13:20:37,483 - ERROR - File "mf32.py", line 317, in <module>
2018-07-31 13:20:37,485 - ERROR - main()
2018-07-31 13:20:37,486 - ERROR - File "mf32.py", line 289, in main
2018-07-31 13:20:37,488 - ERROR - int('''')
2018-07-31 13:20:37,489 - ERROR - ValueError: invalid literal for int() with base 10: ''''
Con color añadido a la respuesta de Vinay Sajip:
class LoggerWriter:
def __init__(self, logger, level):
self.logger = logger
self.level = level
def write(self, message):
if message != ''/n'':
self.logger.log(self.level, message)
def flush(self):
pass
No tengo suficiente representante para comentar, pero quería agregar la versión de esto que funcionó para mí en caso de que otros se encuentren en una situación similar.
class LoggerWriter:
def __init__(self, level):
# self.level is really like using log.debug(message)
# at least in my case
self.level = level
def write(self, message):
# if statement reduces the amount of newlines that are
# printed to the logger
if message != ''/n'':
self.level(message)
def flush(self):
# create a flush method so things can be flushed when
# the system wants to. Not sure if simply ''printing''
# sys.stderr is the correct way to do it, but it seemed
# to work properly for me.
self.level(sys.stderr)
y esto se vería algo así como:
log = logging.getLogger(''foobar'')
sys.stdout = LoggerWriter(log.debug)
sys.stderr = LoggerWriter(log.warning)
Puedes usar el administrador de contexto redirect_stdout:
import logging
from contextlib import redirect_stdout
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
logging.write = lambda msg: logging.info(msg) if msg != ''/n'' else None
with redirect_stdout(logging):
print(''Test'')
o así
import logging
from contextlib import redirect_stdout
logger = logging.getLogger(''Meow'')
logger.setLevel(logging.INFO)
formatter = logging.Formatter(
fmt=''[{name}] {asctime} {levelname}: {message}'',
datefmt=''%m/%d/%Y %H:%M:%S'',
style=''{''
)
ch = logging.StreamHandler()
ch.setLevel(logging.INFO)
ch.setFormatter(formatter)
logger.addHandler(ch)
logger.write = lambda msg: logger.info(msg) if msg != ''/n'' else None
with redirect_stdout(logger):
print(''Test'')
Si se trata de un sistema todo en Python (es decir, no hay bibliotecas C que escriban directamente en fds, como preguntó Ignacio Vázquez-Abrams), entonces es posible que pueda utilizar un enfoque como se sugiere here :
class LoggerWriter:
def __init__(self, logger, level):
self.logger = logger
self.level = level
def write(self, message):
if message != ''/n'':
self.logger.log(self.level, message)
y luego configure sys.stdout
y sys.stderr
en instancias de LoggerWriter
.
Todas las respuestas anteriores parecen tener problemas para agregar nuevas líneas adicionales donde no son necesarias. La solución que mejor me funciona es de http://www.electricmonk.nl/log/2011/08/14/redirect-stdout-and-stderr-to-a-logger-in-python/ , donde demuestra cómo envíe tanto stdout como stderr al registrador:
import logging
import sys
class StreamToLogger(object):
"""
Fake file-like stream object that redirects writes to a logger instance.
"""
def __init__(self, logger, log_level=logging.INFO):
self.logger = logger
self.log_level = log_level
self.linebuf = ''''
def write(self, buf):
for line in buf.rstrip().splitlines():
self.logger.log(self.log_level, line.rstrip())
logging.basicConfig(
level=logging.DEBUG,
format=''%(asctime)s:%(levelname)s:%(name)s:%(message)s'',
filename="out.log",
filemode=''a''
)
stdout_logger = logging.getLogger(''STDOUT'')
sl = StreamToLogger(stdout_logger, logging.INFO)
sys.stdout = sl
stderr_logger = logging.getLogger(''STDERR'')
sl = StreamToLogger(stderr_logger, logging.ERROR)
sys.stderr = sl
print "Test to standard out"
raise Exception(''Test to standard error'')
La salida se ve como:
2011-08-14 14:46:20,573:INFO:STDOUT:Test to standard out
2011-08-14 14:46:20,573:ERROR:STDERR:Traceback (most recent call last):
2011-08-14 14:46:20,574:ERROR:STDERR: File "redirect.py", line 33, in
2011-08-14 14:46:20,574:ERROR:STDERR:raise Exception(''Test to standard error'')
2011-08-14 14:46:20,574:ERROR:STDERR:Exception
2011-08-14 14:46:20,574:ERROR:STDERR::
2011-08-14 14:46:20,574:ERROR:STDERR:Test to standard error
Tenga en cuenta que self.linebuf = '''' es donde se maneja la descarga, en lugar de implementar una función de descarga.