Python es un popular lenguaje de pro­gra­ma­ción orientado a objetos. Este lenguaje de scripting es el favorito de muchos de­sa­rro­lla­do­res, ya que permite elaborar programas más rápido que otros lenguajes como, por ejemplo, Java. Frente a los lenguajes pro­ce­du­ra­les tra­di­cio­na­les, como Perl, Python tiene la ventaja de ser fácil de leer y de mantener. Asimismo, Python puede uti­li­zar­se en una gran variedad de ámbitos, como in­te­li­ge­n­cia ar­ti­fi­cial, in­te­r­fa­ces gráficas de usuario o ad­mi­ni­s­tra­ción de sistemas. No obstante, cuanto más se utiliza un lenguaje de pro­gra­ma­ción, más im­po­r­ta­n­te es contar con una buena cultura de co­m­pre­n­sión de errores. De hecho, el registro de los mismos debería tener lugar desde la primera fase de de­sa­rro­llo hasta la apli­ca­ción, por parte del usuario, en la vida real.

La bi­blio­te­ca de Python incluye un práctico módulo de logging. Tanto para efectuar una de­pu­ra­ción sencilla como para realizar un registro ce­n­tra­li­za­do desde diversos se­r­vi­do­res, el módulo logging de Python puede facilitar eno­r­me­me­n­te el trabajo a los de­sa­rro­lla­do­res y ope­ra­do­res.

¿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 im­po­r­ta­n­tes del historial de eventos. De­pe­n­die­n­do del tipo de se­gui­mie­n­to que queramos hacer, solo se registran ciertas acciones o eventos de un proceso o, por el contrario, se co­m­prue­ban todas las acciones.

Cua­l­quie­ra que esté apre­n­die­n­do un nuevo lenguaje de pro­gra­ma­ción cometerá in­e­vi­ta­ble­me­n­te algún error. Aunque Python es fácil de entender para los usuarios que ya conocen lenguajes de pro­gra­ma­ción como C++ o Java, debido a las si­mi­li­tu­des entre sus es­tru­c­tu­ras (por ejemplo, los tipos de bucles), cada lenguaje tiene sus pe­cu­lia­ri­da­des. Por ejemplo, Python re­pre­se­n­ta las je­ra­r­quías mediante sangrías. Si no dejamos un espacio en blanco con las prisas del momento, incluso la apli­ca­ción más sencilla no fu­n­cio­na­rá. En este caso, al realizar la de­pu­ra­ción, el protocolo de errores sirve a los de­sa­rro­lla­do­res menos ex­pe­ri­me­n­ta­dos para ver la línea co­rre­s­po­n­die­n­te y el error “une­x­pe­c­ted in­de­n­ta­tio­nˮ: 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 de­sa­rro­lla­do­res lo utilizan en proyectos de pro­gra­ma­ción con las apli­ca­cio­nes más diversas:

  • De­pu­ra­ción: se examina todo el código fuente en busca de errores para ga­ra­n­ti­zar que el programa funcione co­rre­c­ta­me­n­te cuando esté terminado.
  • Búsqueda y re­pa­ra­ción de brechas de seguridad: los posibles riesgos se ide­n­ti­fi­can y resuelven de forma pre­ve­n­ti­va.
  • Análisis forense de TI: se puede utilizar para ide­n­ti­fi­car la causa de in­ci­de­n­tes críticos, como ataques de piratas in­fo­r­má­ti­cos, mediante el archivo de registro.
  • Auditoría de TI: esta revisión determina si la seguridad y la in­te­gri­dad de los datos están ga­ra­n­ti­za­das, compara los objetivos de las empresas con sus es­tru­c­tu­ras de TI exi­s­te­n­tes para asegurar que sean co­m­pa­ti­bles y analiza la efi­cie­n­cia de los programas y sistemas ope­ra­ti­vos.
  • Co­m­pa­ra­ción de di­fe­re­n­tes versiones de registros de datos: mediante la creación de un archivo de registro in­de­pe­n­die­n­te para cada proceso, es posible co­m­pa­rar­los.

El registro de Python puede contener una gran cantidad de datos, es­pe­cia­l­me­n­te al de­sa­rro­llar apli­ca­cio­nes 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 de­sa­rro­lla­do­res recopilan estos datos. Es im­po­r­ta­n­te que el archivo de registro funcione de forma asi­n­cró­ni­ca. 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 prio­ri­za­ción de registro

Algunos de­sa­rro­lla­do­res utilizan la salida de impresión para comprobar si hay errores en el código. Para ello, in­tro­du­cen el comando en todos los lugares que sospechan que pueden originar un error. Otros incluso utilizan el comando print de forma pre­ve­n­ti­va 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 di­s­po­ne­mos 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, ob­via­me­n­te puedes hacerlo, aunque los niveles de gravedad incluidos en el módulo logging de Python, de­sa­rro­lla­do por Vinay Sajip, nos parecen bastante adecuados:

Nombre del nivel de registro Uso Posible salida de mensaje
Debug Dia­g­nó­s­ti­co del problema, muy detallado Sangría ine­s­pe­ra­da en la línea X
Info Indica que el sistema funciona co­rre­c­ta­me­n­te La función 1*1 está eje­cu­tá­n­do­se.
Warning La apli­ca­ción funciona co­rre­c­ta­me­n­te, pero se ha producido una situación ine­s­pe­ra­da o se predice un problema futuro. Poco espacio de al­ma­ce­na­mie­n­to
Error No se pudo realizar una función debido a un problema. Ha ocurrido un error y se ha in­te­rru­m­pi­do la acción.
Critical Ha ocurrido un problema grave. Es posible que la apli­ca­ción deba in­te­rru­m­pi­r­se por completo. Error grave: el programa no puede acceder a este servicio y debe cerrarse.

Los di­fe­re­n­tes niveles dan in­fo­r­ma­ción sobre eventos de menor a mayor im­po­r­ta­n­cia. Los niveles del logging de Python son funciones estáticas. En la pro­gra­ma­ción orientada a objetos, estas funciones son co­n­te­ni­dos 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, co­m­po­r­ta­rá un mensaje de error en cada instancia. Si se solicita en el mismo objeto de ejecución, el mensaje de error asociado pe­r­ma­ne­ce­rá igual. Para otras acciones se puede es­ta­ble­cer un mensaje de error distinto.

Debug es el nivel más bajo, por lo que también genera in­fo­r­ma­ció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 bi­blio­te­ca de Python. Por lo tanto, la interfaz de logging no solo in­ter­ac­túa con fluidez con el resto del código fuente, sino que también está siempre lista para usar. Gracias al handler, es posible in­co­r­po­rar rá­pi­da­me­n­te al código existente los registros simples y el envío de in­fo­r­ma­ción a un archivo. Además, el módulo logging de Python tiene ca­ra­c­te­rí­s­ti­cas adi­cio­na­les que permiten pe­r­so­na­li­zar la he­rra­mie­n­ta. Estos son los co­m­po­ne­n­tes pri­n­ci­pa­les del módulo logging:

  • Logger
  • Handler
  • Filter
  • Formatter

Las in­s­ta­n­cias se agrupan en la instancia LogRecord y se in­te­r­ca­m­bian dentro de la misma.

Logger

Los logger registran las acciones durante la ejecución de un programa. No se pueden usar di­re­c­ta­me­n­te 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 je­ra­r­quías de una manera es­tru­c­tu­ra­da. En Python, los di­re­c­to­rios de los paquetes se separan con un punto. Por lo tanto, el paquete log puede contener los di­re­c­to­rios 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 di­re­c­to­rios log.bam y log.bar.loco.

Handler

Los handler recopilan la in­fo­r­ma­ción del logger y la reenvían. El handler es una clase básica que determina cómo actúa la interfaz de las in­s­ta­n­cias del handler. Para es­ta­ble­cer el destino, debes utilizar el tipo de handler co­rre­s­po­n­die­n­te. Strea­mHa­n­d­ler envía los datos a las se­cue­n­cias, mientras que Fi­leHa­n­d­ler 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 de­pu­ra­ción en la consola y los mensajes de error im­po­r­ta­n­tes en un archivo in­de­pe­n­die­n­te.

Mediante el método setLevel() puedes es­ta­ble­cer 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 [ha­n­d­le­r­na­me].setLevel (consulta la quinta línea del código de muestra: fh.setLevel).

Formatter

Los formatter (objetos de formato), a di­fe­re­n­cia de los handler, se pueden utilizar di­re­c­ta­me­n­te como in­s­ta­n­cias en el código de la apli­ca­ción. Con estas in­s­ta­n­cias puedes de­te­r­mi­nar el formato en el que se emitirá la no­ti­fi­ca­ción en el archivo de registro. Si no utilizas ningún formato, solo aparecerá el mensaje es­pe­ci­fi­ca­do del logger. Con el siguiente comando puedes acceder al formatter y co­n­fi­gu­rar 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 es­pe­ci­fi­cas un formato de fecha en el atributo, el formatter es­ta­ble­ce­rá el formato y la hora es­ta­dou­ni­de­n­ses: año-mes-día horas: minutos: segundos.

Filter

Los filter permiten crear de­fi­ni­cio­nes aún más precisas para los mensajes de salida. Establece primero los filtros y, después, añádelos al handler o al logger co­rre­s­po­n­die­n­te mediante el método addFilter(). Si el valor de un filtro es false (erróneo) debido a las pro­pie­da­des del mensaje, no reenviará el mensaje. Utiliza la función logging.Filter(name = fh), donde el atributo fh re­pre­se­n­ta cualquier nombre de logger, para permitir que se envíen úni­ca­me­n­te los datos de registro de un logger concreto y bloquear todos los demás logger.

Ejemplo del módulo logging de Python

Python pro­po­r­cio­na a los de­sa­rro­lla­do­res la he­rra­mie­n­ta de dibujo Turtle para probar comandos básicos de manera práctica. A co­n­ti­nua­ción, te pre­se­n­ta­mos un ejemplo de uso de Turtle. En este caso, la he­rra­mie­n­ta debe dibujar una línea recta sobre un fondo verde, girar a la izquierda, seguir adelante y, fi­na­l­me­n­te, 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 in­s­tru­c­cio­nes. 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 es­pe­ci­fi­ca el mensaje de registro de Python: ERROR:Error:root:Oops, looks like you’re running in circles.

Esto se debe a que el valor pre­de­te­r­mi­na­do del módulo logging de Python es WARNING. Mientras no se cambie esta co­n­fi­gu­ra­ción, el módulo omitirá cualquier in­fo­r­ma­ción más detallada.

Cambiar el nivel en el logging del Python

Introduce el siguiente comando para cambiar la co­n­fi­gu­ra­ció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 co­n­su­l­ta­n­do los datos de registro, debes utilizar un archivo de registro. Este método se llama en inglés logging to file, es decir, al­ma­ce­na­mie­n­to 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 co­n­fi­gu­ra­ción básica o utilizar el handler. Si no es­pe­ci­fi­cas un destino, el logging de Python al­ma­ce­na­rá te­m­po­ra­l­me­n­te los datos en la consola.

Para crear un archivo para el logging de Python, introduce los si­guie­n­tes comandos:

import logging
logging.basicConfig( level=logging.DEBUG, filename='example.log')

Fi­leHa­n­d­ler es una instancia de la clase de logging y actúa junto con la instancia de registro. Se encarga de de­te­r­mi­nar qué datos de registro se envían, adónde y en qué formato. Además de Fi­leHa­n­d­ler, existen otros handler de logging como Strea­mHa­n­d­ler y Nu­llHa­n­d­ler. Sin embargo, para evaluar po­s­te­rio­r­me­n­te los datos de registro, se re­co­mie­n­da crear un archivo de registro.

Para crear un Fi­leHa­n­d­ler 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 Fi­leHa­n­d­ler 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 ad­dHa­n­d­ler() asigna el handler co­rre­s­po­n­die­n­te al logger. Puedes poner al archivo el nombre que quieras.

Para probarlo, introduce los si­guie­n­tes 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 pro­po­r­cio­nar in­fo­r­ma­ció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 cla­si­fi­car mejor los mensajes. La siguiente imagen muestra un ejemplo de cómo puedes co­n­fi­gu­rar el formato uti­li­za­n­do 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 co­n­ti­nua­ción, figura el código que puedes in­tro­du­cir 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 he­rra­mie­n­ta útil para prevenir errores, controlar los ataques de piratas in­fo­r­má­ti­cos o, si­m­ple­me­n­te, llevar a cabo análisis. Mientras que otros lenguajes de pro­gra­ma­ción registran los datos de tercera mano, el módulo logging de Python forma parte de la bi­blio­te­ca estándar del lenguaje. Al in­co­r­po­rar este método en el código, se crean mensajes de registro de di­fe­re­n­tes niveles, tanto en los archivos como en la consola. Las funciones de formato y filtro, así como los handler, permiten al usuario co­n­fi­gu­rar­lo de manera fácil. Para si­m­pli­fi­car 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 di­re­c­to­rios.

Ir al menú principal