Módulo logging de Python: cómo encontrar errores de código
Python es un popular lenguaje de programación orientado a objetos. Este lenguaje de scripting es el favorito de muchos desarrolladores, ya que permite elaborar programas más rápido que otros lenguajes como, por ejemplo, Java. Frente a los lenguajes procedurales tradicionales, como Perl, Python tiene la ventaja de ser fácil de leer y de mantener. Asimismo, Python puede utilizarse en una gran variedad de ámbitos, como inteligencia artificial, interfaces gráficas de usuario o administración de sistemas. No obstante, cuanto más se utiliza un lenguaje de programación, más importante es contar con una buena cultura de comprensión de errores. De hecho, el registro de los mismos debería tener lugar desde la primera fase de desarrollo hasta la aplicación, por parte del usuario, en la vida real.
La biblioteca de Python incluye un práctico módulo de logging. Tanto para efectuar una depuración sencilla como para realizar un registro centralizado desde diversos servidores, el módulo logging de Python puede facilitar enormemente el trabajo a los desarrolladores y operadores.
¿Qué es logging en Python?
Logging proviene del término en inglés “logˮ y, en este contexto, se refiere a un protocolo. Al igual que un libro de registro, contiene todos los registros importantes del historial de eventos. Dependiendo del tipo de seguimiento que queramos hacer, solo se registran ciertas acciones o eventos de un proceso o, por el contrario, se comprueban todas las acciones.
Cualquiera que esté aprendiendo un nuevo lenguaje de programación cometerá inevitablemente algún error. Aunque Python es fácil de entender para los usuarios que ya conocen lenguajes de programación como C++ o Java, debido a las similitudes entre sus estructuras (por ejemplo, los tipos de bucles), cada lenguaje tiene sus peculiaridades. Por ejemplo, Python representa las jerarquías mediante sangrías. Si no dejamos un espacio en blanco con las prisas del momento, incluso la aplicación más sencilla no funcionará. En este caso, al realizar la depuración, el protocolo de errores sirve a los desarrolladores menos experimentados para ver la línea correspondiente y el error “unexpected indentationˮ: el logging de Python registra errores de código simples y genera un mensaje. No obstante, este módulo es capaz de hacer mucho más. Los desarrolladores lo utilizan en proyectos de programación con las aplicaciones más diversas:
- Depuración: se examina todo el código fuente en busca de errores para garantizar que el programa funcione correctamente cuando esté terminado.
- Búsqueda y reparación de brechas de seguridad: los posibles riesgos se identifican y resuelven de forma preventiva.
- Análisis forense de TI: se puede utilizar para identificar la causa de incidentes críticos, como ataques de piratas informáticos, mediante el archivo de registro.
- Auditoría de TI: esta revisión determina si la seguridad y la integridad de los datos están garantizadas, compara los objetivos de las empresas con sus estructuras de TI existentes para asegurar que sean compatibles y analiza la eficiencia de los programas y sistemas operativos.
- Comparación de diferentes versiones de registros de datos: mediante la creación de un archivo de registro independiente para cada proceso, es posible compararlos.
El registro de Python puede contener una gran cantidad de datos, especialmente al desarrollar aplicaciones complejas. Mediante el logging to file de Python (es decir, un archivo de registro creado por el módulo logging de Python y en el que un handler anota los datos de registro), los desarrolladores recopilan estos datos. Es importante que el archivo de registro funcione de forma asincrónica. De lo contrario, el logging de Python puede bloquear la ejecución del código.
Análisis de errores con el logging de Python: cinco niveles de priorización de registro
Algunos desarrolladores utilizan la salida de impresión para comprobar si hay errores en el código. Para ello, introducen el comando en todos los lugares que sospechan que pueden originar un error. Otros incluso utilizan el comando print de forma preventiva en el código. Uno de los problemas de este método es el hecho de tener que revisar todo el código más adelante para comentar o eliminar el comando. De lo contrario, el texto de salida puede aparecer cuando los usuarios utilicen el programa. Además, en este caso, el código fuente presenta un aspecto algo caótico.
Con el sencillo módulo logging disponemos de una solución más elegante para analizar errores que, además, nos ahorra mucho trabajo. El logging de Python presenta cinco niveles de gravedad distintos, que en inglés reciben el nombre de “levels of severityˮ. Si deseas crear tu propio filtro de registro, obviamente puedes hacerlo, aunque los niveles de gravedad incluidos en el módulo logging de Python, desarrollado por Vinay Sajip, nos parecen bastante adecuados:
Nombre del nivel de registro | Uso | Posible salida de mensaje |
---|---|---|
Debug | Diagnóstico del problema, muy detallado | Sangría inesperada en la línea X |
Info | Indica que el sistema funciona correctamente | La función 1*1 está ejecutándose. |
Warning | La aplicación funciona correctamente, pero se ha producido una situación inesperada o se predice un problema futuro. | Poco espacio de almacenamiento |
Error | No se pudo realizar una función debido a un problema. | Ha ocurrido un error y se ha interrumpido la acción. |
Critical | Ha ocurrido un problema grave. Es posible que la aplicación deba interrumpirse por completo. | Error grave: el programa no puede acceder a este servicio y debe cerrarse. |
Los diferentes niveles dan información sobre eventos de menor a mayor importancia. Los niveles del logging de Python son funciones estáticas. En la programación orientada a objetos, estas funciones son contenidos de una clase. Para cada instancia de la clase en relación con un objeto, las funciones estáticas siempre son las mismas; no cambian y están presentes incluso si no se solicita ninguna instancia. Un error, por ejemplo, comportará un mensaje de error en cada instancia. Si se solicita en el mismo objeto de ejecución, el mensaje de error asociado permanecerá igual. Para otras acciones se puede establecer un mensaje de error distinto.
Debug es el nivel más bajo, por lo que también genera información de baja prioridad. Esto no significa, sin embargo, que la gravedad de un error sea superior a la de critical. Debug incluye todos los demás niveles y, por lo tanto, genera todos los mensajes hasta los de nivel critical.
El módulo logging de Python
El módulo logging de Python forma parte de la biblioteca de Python. Por lo tanto, la interfaz de logging no solo interactúa con fluidez con el resto del código fuente, sino que también está siempre lista para usar. Gracias al handler, es posible incorporar rápidamente al código existente los registros simples y el envío de información a un archivo. Además, el módulo logging de Python tiene características adicionales que permiten personalizar la herramienta. Estos son los componentes principales del módulo logging:
- Logger
- Handler
- Filter
- Formatter
Las instancias se agrupan en la instancia LogRecord y se intercambian dentro de la misma.
Logger
Los logger registran las acciones durante la ejecución de un programa. No se pueden usar directamente como instancia, sino que se los solicita con la función logging.getLogger (nombre del logger). Se asigna un nombre concreto al logger, por ejemplo, para mostrar las jerarquías de una manera estructurada. En Python, los directorios de los paquetes se separan con un punto. Por lo tanto, el paquete log puede contener los directorios log.bam o log.bar.loco. Los logger funcionan de manera análoga, de modo que, en este caso, el objeto log recibirá los datos de los directorios log.bam y log.bar.loco.
Handler
Los handler recopilan la información del logger y la reenvían. El handler es una clase básica que determina cómo actúa la interfaz de las instancias del handler. Para establecer el destino, debes utilizar el tipo de handler correspondiente. StreamHandler envía los datos a las secuencias, mientras que FileHandler los envía a los archivos. Para un programa, puedes utilizar varios handler que envíen mensajes del mismo logger. Esto te puede ser útil, por ejemplo, si deseas mostrar los datos de depuración en la consola y los mensajes de error importantes en un archivo independiente.
Mediante el método setLevel() puedes establecer el nivel mínimo de gravedad que un mensaje de registro requiere para ser reenviado a dicho handler. En lugar de logger.setLevel (que determina el nivel de registro), el método recibe el nombre de [handlername].setLevel (consulta la quinta línea del código de muestra: fh.setLevel).
Formatter
Los formatter (objetos de formato), a diferencia de los handler, se pueden utilizar directamente como instancias en el código de la aplicación. Con estas instancias puedes determinar el formato en el que se emitirá la notificación en el archivo de registro. Si no utilizas ningún formato, solo aparecerá el mensaje especificado del logger. Con el siguiente comando puedes acceder al formatter y configurar el formato del mensaje y la fecha:
logging.Formatter.__init__(fmt=[formato de mensaje], datefmt=[formato de fecha])
O también:
logging.Formatter.__init__(fmt=None, datefmt=None)
Si no especificas un formato de fecha en el atributo, el formatter establecerá el formato y la hora estadounidenses: año-mes-día horas: minutos: segundos.
Filter
Los filter permiten crear definiciones aún más precisas para los mensajes de salida. Establece primero los filtros y, después, añádelos al handler o al logger correspondiente mediante el método addFilter(). Si el valor de un filtro es false (erróneo) debido a las propiedades del mensaje, no reenviará el mensaje. Utiliza la función logging.Filter(name = fh), donde el atributo fh representa cualquier nombre de logger, para permitir que se envíen únicamente los datos de registro de un logger concreto y bloquear todos los demás logger.
Ejemplo del módulo logging de Python
Python proporciona a los desarrolladores la herramienta de dibujo Turtle para probar comandos básicos de manera práctica. A continuación, te presentamos un ejemplo de uso de Turtle. En este caso, la herramienta debe dibujar una línea recta sobre un fondo verde, girar a la izquierda, seguir adelante y, finalmente, dibujar un círculo. En el ejemplo, incluimos los comandos de registro info y error de Python:
# -*- coding: UTF-8 -*-
import turtle
import logging
turtle.bgcolor("green")
turtle.fd(30)
turtle.lt(90)
turtle.fd(50)
logging.info('It is going well.’)
turtle.circle(50)
logging.error('Oops, looks like you’re running in circles.')
En la imagen de arriba puedes ver el resultado. El módulo Turtle (ventana de la izquierda) ha aceptado los comandos y se ejecuta según las instrucciones. En la ventana de la derecha, el código también incluye comandos de logging de los niveles INFO y ERROR, además de los comandos de Turtle. La forma de salida típica de un mensaje de registro es la siguiente: [nivel de gravedad]:[origen del mensaje]:[mensaje].
Sin embargo, en el ejemplo, la consola (console 1/A) solo especifica el mensaje de registro de Python: ERROR:Error:root:Oops, looks like you’re running in circles.
Esto se debe a que el valor predeterminado del módulo logging de Python es WARNING. Mientras no se cambie esta configuración, el módulo omitirá cualquier información más detallada.
Cambiar el nivel en el logging del Python
Introduce el siguiente comando para cambiar la configuración al nivel DEBUG:
import logging
logging.basicConfig(level=logging.DEBUG)
En la imagen de arriba, la consola muestra el registro para cada nueva solicitud. Si se cierra el programa, la consola elimina todos los registros. Para poder seguir consultando los datos de registro, debes utilizar un archivo de registro. Este método se llama en inglés logging to file, es decir, almacenamiento de los registros en un archivo.
Guardar el logging de Python en un archivo
El método logging to file de Python funciona de dos maneras: puedes crear un archivo de registro mediante la configuración básica o utilizar el handler. Si no especificas un destino, el logging de Python almacenará temporalmente los datos en la consola.
Para crear un archivo para el logging de Python, introduce los siguientes comandos:
import logging
logging.basicConfig( level=logging.DEBUG, filename='example.log')
FileHandler es una instancia de la clase de logging y actúa junto con la instancia de registro. Se encarga de determinar qué datos de registro se envían, adónde y en qué formato. Además de FileHandler, existen otros handler de logging como StreamHandler y NullHandler. Sin embargo, para evaluar posteriormente los datos de registro, se recomienda crear un archivo de registro.
Para crear un FileHandler que inserte mensajes DEBUG en un archivo, sigue estos pasos:
En la imagen de arriba, el comando logging.getLogger() ejecuta el módulo logging de Python. Fh se define como un FileHandler que tiene el atributo debug.log. De esta manera, fh crea el archivo de registro debug.log y te envía los mensajes de registro que se van generando. El método addHandler() asigna el handler correspondiente al logger. Puedes poner al archivo el nombre que quieras.
Para probarlo, introduce los siguientes comandos:
import logging
logger = logging.getLogger('ejemplo_Log')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('debug.log')
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)
logger.debug('mensaje debug')
logger.info('mensaje info')
logger.warning('mensaje warning')
logger.error('mensaje error')
logger.critical('mensaje critical')
Si el archivo de registro que hayas creado mediante el método logging to file de Python tiene que proporcionar información útil para tareas concretas, es posible que no baste con los mensajes simples. En este caso, una marca de tiempo y el nombre del logger pueden ayudar a clasificar mejor los mensajes. La siguiente imagen muestra un ejemplo de cómo puedes configurar el formato utilizando los atributos del formatter. En la ventana del bloc de notas del archivo debug.log se indican los mensajes de registro con los datos de fecha, hora, nombre del logger, nivel de registro y mensaje.
De nuevo, a continuación, figura el código que puedes introducir para probarlo:
import logging
logger = logging.getLogger('ejemplo_Log')
logger.setLevel(logging.DEBUG)
fh = logging.FileHandler('debug.log')
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.debug('mensaje debug')
logger.info('mensaje info')
logger.warning('mensaje warning')
logger.error('mensaje error')
logger.critical('mensaje critical')
En resumen
El logging de Python es una herramienta útil para prevenir errores, controlar los ataques de piratas informáticos o, simplemente, llevar a cabo análisis. Mientras que otros lenguajes de programación registran los datos de tercera mano, el módulo logging de Python forma parte de la biblioteca estándar del lenguaje. Al incorporar este método en el código, se crean mensajes de registro de diferentes niveles, tanto en los archivos como en la consola. Las funciones de formato y filtro, así como los handler, permiten al usuario configurarlo de manera fácil. Para simplificar todavía más el trabajo con los archivos de registro en Python, asegúrate de asignar nombres sencillos a los logger y a sus directorios.