logging — Informe de estado, error y mensajes informativos¶
Propósito: | Informe de estado, error y mensajes informativos. |
---|
El módulo de logging
define una interfaz de programación estándar para
informar errores e información de estado de aplicaciones y bibliotecas. El
beneficio clave de tener la interfaz de programación de registro proporcionada
por un módulo de biblioteca estándar es que todos los módulos de Python pueden
participar en el registro, por lo que el registro de una aplicación puede
incluir mensajes de módulos de terceros.
Componentes de registro¶
El sistema de registro está compuesto por cuatro tipos de objetos que
interactúan. Cada módulo o aplicación que desea registrar utiliza una
instancia de Logger
para agregar información a los registros. Invocar al
registrador crea un LogRecord
, que se utiliza para mantener la información
en la memoria hasta que se procesa. Un Logger
puede tener varios objetos
Handler
configurados para recibir y procesar registros. El Handler
usa
un Formatter
para convertir los registros en mensajes de salida.
Registro en aplicaciones versus bibliotecas¶
Los desarrolladores de aplicaciones y los autores de bibliotecas pueden usar
logging
, pero cada audiencia tiene diferentes consideraciones a tener en
cuenta.
Los desarrolladores de aplicaciones configuran el módulo de logging
dirigiendo los mensajes a los canales de salida apropiados. Es posible
registrar mensajes con diferentes niveles de verbosidad o en diferentes
destinos. Se incluyen todos los controladores para escribir mensajes de
registro en archivos, ubicaciones HTTP GET/POST, correo electrónico a través de
SMTP, sockets genéricos o mecanismos de registro específicos del sistema
operativo, y es posible crear clases de destino de registro personalizadas para
requisitos especiales no manejados por ninguno de las clases integradas.
Los desarrolladores de bibliotecas también pueden usar logging
y tienen aún
menos trabajo por hacer. Simplemente crea una instancia de registrador para
cada contexto, usando un nombre apropiado, y luego registra los mensajes usando
los niveles estándar. Siempre que una biblioteca use la interfaz de
programación de registro con nombres y selecciones de nivel consistentes, la
aplicación se puede configurar para mostrar u ocultar mensajes de la
biblioteca, según lo desees.
Registro a un archivo¶
La mayoría de las aplicaciones están configuradas para registrar en un archivo.
Usa la función basicConfig()
para configurar el controlador predeterminado
para que los mensajes de depuración se escriban en un archivo.
import logging
LOG_FILENAME = 'logging_example.out'
logging.basicConfig(
filename=LOG_FILENAME,
level=logging.DEBUG,
)
logging.debug('This message should go to the log file')
with open(LOG_FILENAME, 'rt') as f:
body = f.read()
print('FILE:')
print(body)
Después de ejecutar la secuencia de comandos el mensaje de registro se escribe
en logging_example.out
:
$ python3 logging_file_example.py
FILE:
DEBUG:root:This message should go to the log file
Rotación de archivos de registro¶
Ejecutar la secuencia de comandos repetidamente hace que se agreguen más
mensajes al archivo. Para crear un nuevo archivo cada vez que se ejecuta el
programa, pasa un argumento de filemode
a basicConfig()
con un valor de
'w'
. Sin embargo, en lugar de administrar la creación de archivos de esta
manera, es mejor usar un RotatingFileHandler
, que crea nuevos archivos
automáticamente y conserva el antiguo archivo de registro al mismo tiempo.
import glob
import logging
import logging.handlers
LOG_FILENAME = 'logging_rotatingfile_example.out'
# Set up a specific logger with our desired output level
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)
# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(
LOG_FILENAME,
maxBytes=20,
backupCount=5,
)
my_logger.addHandler(handler)
# Log some messages
for i in range(20):
my_logger.debug('i = %d' % i)
# See what files are created
logfiles = glob.glob('%s*' % LOG_FILENAME)
for filename in sorted(logfiles):
print(filename)
El resultado son seis archivos separados, cada uno con parte del historial de registro de la aplicación.
$ python3 logging_rotatingfile_example.py
logging_rotatingfile_example.out
logging_rotatingfile_example.out.1
logging_rotatingfile_example.out.2
logging_rotatingfile_example.out.3
logging_rotatingfile_example.out.4
logging_rotatingfile_example.out.5
El archivo más actual siempre es logging_rotatingfile_example.out
, y cada
vez que alcanza el límite de tamaño se renombra con el sufijo .1
. Se
cambia el nombre de cada uno de los archivos de copia de seguridad existentes
para incrementar el sufijo (.1
se convierte en .2
, etc.) y se borra el
archivo .5
.
Nota
Obviamente, este ejemplo establece la longitud del registro demasiado pequeña
como un ejemplo extremo. Establece maxBytes
en un valor más apropiado en
un programa real.
Niveles de verbosidad¶
Otra característica útil de la interfaz de programación de logging
es la
capacidad de producir diferentes mensajes en diferentes niveles de registro.
Esto significa que el código se puede instrumentar con mensajes de depuración,
por ejemplo, y el nivel de registro se puede configurar para que esos mensajes
de depuración no se escriban en un sistema de producción. the table below enumera los niveles de registro definidos por logging
.
Nivel | Valor |
---|---|
CRITICAL | 50 |
ERROR | 40 |
WARNING | 30 |
INFO | 20 |
DEBUG | 10 |
UNSET | 0 |
El mensaje de registro solo se emite si el controlador y el registrador están
configurados para emitir mensajes de ese nivel o superior. Por ejemplo, si un
mensaje es CRITICAL
y el registrador está configurado en ERROR
, se
emite el mensaje (50> 40). Si un mensaje es una WARNING
, y el registrador
está configurado para producir solo mensajes configurados en ERROR
, el
mensaje no se emite (30 <40).
import logging
import sys
LEVELS = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL,
}
if len(sys.argv) > 1:
level_name = sys.argv[1]
level = LEVELS.get(level_name, logging.NOTSET)
logging.basicConfig(level=level)
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical error message')
Ejecuta la secuencia de comandos con un argumento como “debug” o “warning” ara ver qué mensajes aparecen en diferentes niveles:
$ python3 logging_level_example.py debug
DEBUG:root:This is a debug message
INFO:root:This is an info message
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical error message
$ python3 logging_level_example.py info
INFO:root:This is an info message
WARNING:root:This is a warning message
ERROR:root:This is an error message
CRITICAL:root:This is a critical error message
Instancias de registro con nombres¶
Todos los mensajes de registro anteriores tienen “raíz” incrustada porque el código usa el registrador raíz. Una manera fácil de saber de dónde proviene un mensaje de registro específico es usar un objeto de registro por separado para cada módulo. Los mensajes de registro enviados a un registrador incluyen el nombre de ese registrador. Aquí hay un ejemplo de cómo registrar desde diferentes módulos para que sea fácil rastrear la fuente del mensaje.
import logging
logging.basicConfig(level=logging.WARNING)
logger1 = logging.getLogger('package1.module1')
logger2 = logging.getLogger('package2.module2')
logger1.warning('This message comes from one module')
logger2.warning('This comes from another module')
La salida muestra los diferentes nombres de módulos para cada línea de salida.
$ python3 logging_modules_example.py
WARNING:package1.module1:This message comes from one module
WARNING:package2.module2:This comes from another module
El árbol de registro¶
Las instancias de Logger
se configuran en una estructura de árbol, en
función de sus nombres, como se ilustra en the figure. Normalmente, cada aplicación o biblioteca define un nombre base,
con registradores para módulos individuales configurados como elementos
secundarios. El registrador raíz no tiene nombre.
![digraph {
rankdir = BT;
node [shape = doublecircle];
"";
node [shape = rect];
"myapp" -> "";
"package1" -> "";
"package1.module1" -> "package1";
"package2" -> "";
"package2.module2" -> "package2";
}](../_images/graphviz-2e39f0529306c11f75a099d1fb3e3bc455c8b6e5.png)
Ejemplo de árbol de registro¶
La estructura de árbol es útil para configurar el registro porque significa que cada registrador no necesita su propio conjunto de controladores. Si un registrador no tiene ningún controlador, el mensaje se entrega a su padre para su procesamiento. Esto significa que para la mayoría de las aplicaciones solo es necesario configurar controladores en el registrador raíz, y toda la información de registro se recopilará y enviará al mismo lugar, como se muestra en la the figure.
![digraph {
rankdir = BT;
node [shape = doublecircle];
"";
node [shape = rect];
"package1" -> "";
"package1.module2" -> "package1";
"package2" -> "";
"package2.module2" -> "package2";
"myapp" -> "";
node [shape = note];
"" -> "/var/log/app.log";
{rank = same; ""; "/var/log/app.log"}
}](../_images/graphviz-6284db203afa8a3d139270d7a642cfb0a72eec43.png)
Un controlador de registro¶
La estructura de árbol también permite establecer diferentes niveles de verbosidad, controladores y formateadores para diferentes partes de la aplicación o biblioteca para controlar qué mensajes se registran y hacia dónde van, como en la the figure.
![digraph {
rankdir = BT;
node [shape = doublecircle];
"";
node [shape = rect];
"package1" -> "" [label="level=INFO"];
"package1.module2" -> "package1";
"package2" -> "" [label="level=WARNING"];
"package2.module2" -> "package2";
"myapp" -> "" [label="level=DEBUG"];
}](../_images/graphviz-ae79e97c1e28266e0cfac5ae087a01eea5e70d61.png)
Diferentes niveles y controladores¶
Integración con el módulo warnings¶
El módulo de registro se integra con warnings
a través de
captureWarnings()
, que configura warnings
para enviar mensajes a través
del sistema de registro en lugar de enviarlos directamente.
import logging
import warnings
logging.basicConfig(
level=logging.INFO,
)
warnings.warn('This warning is not sent to the logs')
logging.captureWarnings(True)
warnings.warn('This warning is sent to the logs')
El mensaje de advertencia se envía a un registrador llamado py.warnings
usando el nivel WARNING
.
$ python3 logging_capture_warnings.py
logging_capture_warnings.py:13: UserWarning: This warning is not
sent to the logs
warnings.warn('This warning is not sent to the logs')
WARNING:py.warnings:logging_capture_warnings.py:17: UserWarning:
This warning is sent to the logs
warnings.warn('This warning is sent to the logs')
Ver también
- Documentación de la biblioteca estándar para logging – La documentation de
logging
es extensa e incluye tutoriales y material de referencia que va más allá de los ejemplos presentados aquí. - Notas para portar Python 2 a 3 para logging
warnings
– Alertas no fatales.- logging_tree – Paquete de Brandon Rhodes para mostrar el árbol de registro para una aplicación.
- Recetas de registro – Parte de
la documentación estándar de la biblioteca, con ejemplos del uso de
logging
para diferentes tareas.