Loguru es una biblioteca que tiene como objetivo traer un registro agradable en Python.
¿Alguna vez se sintió flojo por configurar un registrador y usó print()
en su lugar? ... Lo hice, pero el registro es fundamental para cada aplicación y facilita el proceso de depuración. Usando Loguru, no tiene excusa para no usar el inicio de registro desde el inicio, esto es tan simple como from loguru import logger
.
Además, esta biblioteca está destinada a hacer que el registro de Python sea menos doloroso agregando un montón de funcionalidades útiles que resuelven advertencias de los registradores estándar. El uso de registros en su aplicación debe ser un automatismo, Loguru intenta hacerlo tanto agradable como poderoso.
pip install loguru
El concepto principal de loguru es que hay un solo logger
.
Para conveniencia, para comenzar con prefiguración y salida a stderr
(pero eso es completamente configurable).
from loguru import logger
logger . debug ( "That's it, beautiful and simple logging!" )
El logger
es solo una interfaz que envía mensajes de registro a los manejadores configurados. Simple, ¿verdad?
¿Cómo agregar un controlador? ¿Cómo configurar el formato de registros? ¿Cómo filtrar mensajes? ¿Cómo establecer el nivel?
Una respuesta: la función add()
.
logger . add ( sys . stderr , format = "{time} {level} {message}" , filter = "my_module" , level = "INFO" )
Esta función debe usarse para registrar sumideros responsables de administrar mensajes de registro contextualizados con un DICT de registro. Un fregadero puede tomar muchas formas: una función simple, una ruta de cadena, un objeto similar a un archivo, una función de Coroutine o un controlador incorporado.
Tenga en cuenta que también puede remove()
un controlador previamente agregado utilizando el identificador devuelto al agregarlo. Esto es particularmente útil si desea reemplazar al controlador stderr
predeterminado: solo llame logger.remove()
para comenzar de nuevo.
Si desea enviar mensajes registrados a un archivo, solo tiene que usar una ruta de cadena como sumidero. También se puede cronometrar automáticamente por conveniencia:
logger . add ( "file_{time}.log" )
También es fácilmente configurable si necesita registrador giratorio, si desea eliminar registros más antiguos o si desea comprimir sus archivos en el cierre.
logger . add ( "file_1.log" , rotation = "500 MB" ) # Automatically rotate too big file
logger . add ( "file_2.log" , rotation = "12:00" ) # New file is created each day at noon
logger . add ( "file_3.log" , rotation = "1 week" ) # Once the file is too old, it's rotated
logger . add ( "file_X.log" , retention = "10 days" ) # Cleanup after some time
logger . add ( "file_Y.log" , compression = "zip" ) # Save some loved space
Loguru favorece el formato {}
mucho más elegante y potente en %
, las funciones de registro son realmente equivalentes a str.format()
.
logger . info ( "If you're using Python {}, prefer {feature} of course!" , 3.6 , feature = "f-strings" )
¿Alguna vez ha visto que su programa se bloquea inesperadamente sin ver nada en el archivo de registro? ¿Alguna vez notó que no se registraron las excepciones en los hilos? Esto se puede resolver utilizando el decorador / administrador de contexto catch()
que garantiza que cualquier error se propague correctamente al logger
.
@ logger . catch
def my_function ( x , y , z ):
# An error? It's caught anyway!
return 1 / ( x + y + z )
Loguru agrega automáticamente colores a sus registros si su terminal es compatible. Puede definir su estilo favorito usando etiquetas de marcado en el formato de sumidero.
logger . add ( sys . stdout , colorize = True , format = "<green>{time}</green> <level>{message}</level>" )
Todos los sumideros agregados al logger
son seguros de hilo de forma predeterminada. No son seguros al multiprocesamiento, pero puede enqueue
los mensajes para garantizar la integridad de los registros. Este mismo argumento también se puede usar si desea registrar async.
logger . add ( "somefile.log" , enqueue = True )
Las funciones de coroutina utilizadas como fregaderos también son compatibles y deben esperarse con complete()
.
Las excepciones de registro que ocurren en su código es importante para rastrear errores, pero es bastante inútil si no sabe por qué falló. Loguru lo ayuda a identificar problemas al permitir que se muestre todo el rastro de la pila, incluidos los valores de las variables (¡gracias better_exceptions
por esto!).
El código:
# Caution, "diagnose=True" is the default and may leak sensitive data in prod
logger . add ( "out.log" , backtrace = True , diagnose = True )
def func ( a , b ):
return a / b
def nested ( c ):
try :
func ( 5 , c )
except ZeroDivisionError :
logger . exception ( "What?!" )
nested ( 0 )
Resultaría en:
2018-07-17 01: 38: 43.975 | Error | __main __: anidado: 10 - ¿Qué?
Traza (la llamada más reciente la última):
Archivo "test.py", línea 12, en <smodule>
anidado (0)
└ <función anidada en 0x7f5c755322f0>
> Archivo "Test.py", línea 8, en anidados
func (5, c)
│ └ 0
└ <function func en 0x7f5c79fc2e18>
Archivo "test.py", línea 4, en func
regresar A / B
│ └ 0
└ 5
ZeroDivisionError: División por cero
Tenga en cuenta que esta característica no funcionará en Python Reply predeterminado debido a datos de cuadros no disponibles.
Ver también: Consideraciones de seguridad cuando se usa Loguru.
¿Quiere que sus registros se serializaran para un análisis más fácil o para pasarlos? Usando el argumento serialize
, cada mensaje de registro se convertirá en una cadena JSON antes de ser enviado al sumidero configurado.
logger . add ( custom_sink_function , serialize = True )
Usando bind()
puede contextualizar sus mensajes de registrador modificando el atributo de registro adicional.
logger . add ( "file.log" , format = "{extra[ip]} {extra[user]} {message}" )
context_logger = logger . bind ( ip = "192.168.0.1" , user = "someone" )
context_logger . info ( "Contextualize your logger easily" )
context_logger . bind ( user = "someone_else" ). info ( "Inline binding of extra attribute" )
context_logger . info ( "Use kwargs to add context during formatting: {user}" , user = "anybody" )
Es posible modificar un estado local de contexto temporalmente con contextualize()
::
with logger . contextualize ( task = task_id ):
do_something ()
logger . info ( "End of task" )
También puede tener más control de grano fino sobre sus registros combinando bind()
y filter
:
logger . add ( "special.log" , filter = lambda record : "special" in record [ "extra" ])
logger . debug ( "This message is not logged to the file" )
logger . bind ( special = True ). info ( "This message, though, is logged to the file!" )
Finalmente, el método patch()
permite que los valores dinámicos se conecten al registro de cada nuevo mensaje:
logger . add ( sys . stderr , format = "{extra[utc]} {message}" )
logger = logger . patch ( lambda record : record [ "extra" ]. update ( utc = datetime . utcnow ()))
En algún momento le gustaría registrar información verbosa sin penalización de rendimiento en la producción, puede usar el método opt()
para lograrlo.
logger . opt ( lazy = True ). debug ( "If sink level <= DEBUG: {x}" , x = lambda : expensive_function ( 2 ** 64 ))
# By the way, "opt()" serves many usages
logger . opt ( exception = True ). info ( "Error stacktrace added to the log message (tuple accepted too)" )
logger . opt ( colors = True ). info ( "Per message <blue>colors</blue>" )
logger . opt ( record = True ). info ( "Display values from the record (eg. {record[thread]})" )
logger . opt ( raw = True ). info ( "Bypass sink formatting n " )
logger . opt ( depth = 1 ). info ( "Use parent stack context (useful within wrapped functions)" )
logger . opt ( capture = False ). info ( "Keyword arguments not added to {dest} dict" , dest = "extra" )
Loguru viene con todos los niveles de registro estándar a los que se agregan trace()
y success()
. ¿Necesitas más? Luego, simplemente cree usando la función level()
.
new_level = logger . level ( "SNAKY" , no = 38 , color = "<yellow>" , icon = "?" )
logger . log ( "SNAKY" , "Here we go!" )
El registro estándar está hinchado con argumentos como datefmt
o msecs
, %(asctime)s
%(created)s
, data de datos ingenuas sin información de zona horaria, no formateo intuitivo, etc. Loguru lo arregla:
logger . add ( "file.log" , format = "{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}" )
Usar el registrador en sus scripts es fácil, y puede configure()
al inicio. Para usar loguru desde el interior de una biblioteca, recuerde nunca llamar add()
pero use disable()
para que las funciones de registro se vuelvan sin op. Si un desarrollador desea ver los registros de su biblioteca, puede enable()
nuevamente.
# For scripts
config = {
"handlers" : [
{ "sink" : sys . stdout , "format" : "{time} - {message}" },
{ "sink" : "file.log" , "serialize" : True },
],
"extra" : { "user" : "someone" }
}
logger . configure ( ** config )
# For libraries, should be your library's `__name__`
logger . disable ( "my_library" )
logger . info ( "No matter added sinks, this message is not displayed" )
# In your application, enable the logger in the library
logger . enable ( "my_library" )
logger . info ( "This message however is propagated to the sinks" )
Para una conveniencia adicional, también puede usar la biblioteca loguru-config
para configurar el logger
directamente desde un archivo de configuración.
¿Desea usar Handler
de registro incorporado como fregadero de loguru?
handler = logging . handlers . SysLogHandler ( address = ( 'localhost' , 514 ))
logger . add ( handler )
¿Necesita propagar los mensajes de logo al registro estándar?
class PropagateHandler ( logging . Handler ):
def emit ( self , record : logging . LogRecord ) -> None :
logging . getLogger ( record . name ). handle ( record )
logger . add ( PropagateHandler (), format = "{message}" )
¿Quiere interceptar mensajes de registro estándar hacia sus sumideros de logos?
class InterceptHandler ( logging . Handler ):
def emit ( self , record : logging . LogRecord ) -> None :
# Get corresponding Loguru level if it exists.
level : str | int
try :
level = logger . level ( record . levelname ). name
except ValueError :
level = record . levelno
# Find caller from where originated the logged message.
frame , depth = inspect . currentframe (), 0
while frame and ( depth == 0 or frame . f_code . co_filename == logging . __file__ ):
frame = frame . f_back
depth += 1
logger . opt ( depth = depth , exception = record . exc_info ). log ( level , record . getMessage ())
logging . basicConfig ( handlers = [ InterceptHandler ()], level = 0 , force = True )
¿No te gusta el formato de registrador predeterminado? ¿Preferiría otro color DEBUG
? Ningún problema:
# Linux / OSX
export LOGURU_FORMAT = "{time} | <lvl>{message}</lvl>"
# Windows
setx LOGURU_DEBUG_COLOR "<green>"
A menudo es útil extraer información específica de los registros generados, es por eso que Loguru proporciona un método de parse()
que ayuda a lidiar con registros y reglas.
pattern = r"(?P<time>.*) - (?P<level>[0-9]+) - (?P<message>.*)" # Regex with named groups
caster_dict = dict ( time = dateutil . parser . parse , level = int ) # Transform matching groups
for groups in logger . parse ( "file.log" , pattern , cast = caster_dict ):
print ( "Parsed:" , groups )
# {"level": 30, "message": "Log example", "time": datetime(2018, 12, 09, 11, 23, 55)}
Loguru se puede combinar fácilmente con la biblioteca notifiers
Great (debe instalarse por separado) para recibir un correo electrónico cuando su programa fallan inesperadamente o para enviar muchos otros tipos de notificaciones.
import notifiers
params = {
"username" : "[email protected]" ,
"password" : "abc123" ,
"to" : "[email protected]"
}
# Send a single notification
notifier = notifiers . get_notifier ( "gmail" )
notifier . notify ( message = "The application is running!" , ** params )
# Be alerted on each error message
from notifiers . logging import NotificationHandler
handler = NotificationHandler ( "gmail" , defaults = params )
logger . add ( handler , level = "ERROR" )
Aunque el impacto de registro en el rendimiento es insignificante en la mayoría de los casos, un registrador de costo cero permitiría usarlo en cualquier lugar sin mucha preocupación. En un próximo lanzamiento, las funciones críticas de Loguru se implementarán en C para la máxima velocidad.