- InfinitePy Newsletter 🇪🇸
- Posts
- Transforma Tus Proyectos Python con Técnicas Robustas de Registro - Parte I
Transforma Tus Proyectos Python con Técnicas Robustas de Registro - Parte I
El registro es una habilidad crucial para cualquier desarrollador de Python, ayudando en la depuración, monitorización del rendimiento y diagnóstico de fallos. Aquí profundizaremos en el registro en Python, cubriéndolo desde niveles básicos hasta avanzados, e incluyendo ejemplos prácticos para consolidar tu comprensión.
🕒 Tiempo estimado de lectura: 11 minutos
El registro es la práctica de registrar información sobre la ejecución de un programa. Esto puede incluir detalles como errores, mensajes informativos, advertencias e información de depuración. Python proporciona un módulo de registro incorporado que los desarrolladores pueden usar para implementar el registro dentro de sus aplicaciones.
¿Por qué es importante el registro?
Depuración: El registro te permite entender el flujo de tu aplicación y te ayuda a identificar dónde pueden estar ocurriendo problemas. Es mucho más fácil rastrear problemas cuando tienes un registro de lo que la aplicación estaba haciendo en varios momentos.
Monitorización: Los registros pueden usarse para supervisar la salud de una aplicación en tiempo real o revisando los registros posteriormente. Pueden proporcionar información sobre cuellos de botella en el rendimiento, uso de recursos y otros aspectos operacionales.
Auditoría y Cumplimiento: Muchas industrias tienen requisitos regulatorios que obligan a mantener registros para fines de cumplimiento. El registro es una parte esencial de mantener un rastro de auditoría, lo cual puede ser crítico para auditorías de seguridad y revisiones de cumplimiento.
Soporte al Usuario: Los registros detallados pueden proporcionar información invaluable para los equipos de soporte al cliente cuando los usuarios reportan problemas. Saber qué estaba haciendo el sistema cuando se produjo un error puede ayudar a identificar y resolver rápidamente los problemas.
Persistencia: A diferencia de las declaraciones
print
que son transitorias y solo visibles en la consola, los registros pueden guardarse en archivos, bases de datos, u otros medios de almacenamiento para análisis e inspección a largo plazo.
Ahora, sumerjámonos en el mundo de los Datos en Python con algunos ejemplos prácticos que también están disponibles en Google Colab aquí 👨🔬.
Configuración del Registro Básico
La biblioteca de registro integrada de Python facilita la configuración del registro. Aquí te explicamos cómo empezar:
import logging # Configurar el sistema de registro # Establece el umbral para el logger en el nivel DEBUG # Esto significa que todos los mensajes de registro en este nivel y superiores serán mostrados logging.basicConfig(level=logging.DEBUG) # A continuación se muestran diferentes niveles de mensajes de registro en orden creciente de severidad. # DEBUG: Información detallada, típicamente de interés solo al diagnosticar problemas. logging.debug("Este es un mensaje de depuración") # INFO: Confirmación de que las cosas están funcionando como se espera. logging.info("Este es un mensaje informativo") # WARNING: Una indicación de que algo inesperado ocurrió, o indicativo de algún problema en el futuro cercano # (por ejemplo, 'espacio en disco bajo'). El software sigue funcionando como se espera. logging.warning("Este es un mensaje de advertencia") # ERROR: Debido a un problema más grave, el software no ha podido realizar alguna función. logging.error("Este es un mensaje de error") # CRITICAL: Un error muy grave, indicando que el programa en sí puede no ser capaz de seguir ejecutándose. logging.critical("Este es un mensaje crítico")
Ejemplo Práctico: Registro en un Script Simple
Escribamos un script básico que demuestre el registro:
import logging # Importar el módulo de registro para habilitar el registro de eventos, errores y mensajes informativos. def dividir(a, b): """Función para dividir dos números. Args: a (float): El numerador. b (float): El denominador. Returns: float: El resultado de la división si tiene éxito, de lo contrario None. """ logging.info(f"Dividiendo {a} por {b}") # Registrar un mensaje informativo indicando la operación que se está realizando. if b == 0: # Verificar si el denominador es cero para evitar error de división por cero. logging.error("Intento de división por cero") # Registrar un mensaje de error si se intenta una división por cero. return None # Devolver None para indicar que la operación falló. return a / b # Realizar la división y devolver el resultado. if __name__ == "__main__": # Configurar el registro para mostrar mensajes de nivel DEBUG y superiores logging.basicConfig(level=logging.DEBUG) # Probar la función dividir con entrada válida resultado = dividir(10, 2) print(f"Resultado: {resultado}") # Imprimir el resultado de la división # Probar la función dividir con división por cero resultado = dividir(10, 0) print(f"Resultado: {resultado}") # Imprimir el resultado, que debería ser None debido a la división por cero
Configuración de Loggers, Handlers y Formatters
El registro en Python tiene varios niveles (DEBUG
, INFO
, WARNING
, ERROR
, CRITICAL
). Por defecto, el registro captura niveles WARNING
y superiores a menos que se configure de otra manera.
Un
logger
es un punto de entrada para el registro;los
handlers
envían mensajes de registro a destinos configurados como archivos o consolas;los
formatters
definen el formato de los mensajes de registro.
import logging # Crear un objeto logger con el nombre 'ejemplo_logger' logger = logging.getLogger('ejemplo_logger') # Establecer el nivel de registro en DEBUG. Esto significa que todos los mensajes con el nivel DEBUG y superiores serán registrados logger.setLevel(logging.DEBUG) # Crear un handler que imprimirá los mensajes de registro en la consola (salida estándar) console_handler = logging.StreamHandler() # Crear un handler que escribirá los mensajes de registro en un archivo llamado 'ejemplo.log' file_handler = logging.FileHandler('ejemplo.log') # Definir un formatter que especifica el formato de los mensajes de registro formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # Establecer el formatter para el console handler console_handler.setFormatter(formatter) # Establecer el formatter para el file handler file_handler.setFormatter(formatter) # Añadir el console handler al logger. Esto significa que los mensajes de registro se mostrarán en la consola logger.addHandler(console_handler) # Añadir el file handler al logger. Esto significa que los mensajes de registro se escribirán en el archivo 'ejemplo.log' logger.addHandler(file_handler) # Registrar un mensaje informativo con el logger logger.info('Este es un mensaje informativo')
La ejecución del script anterior producirá los siguientes registros, tanto en la consola como en el archivo example.log.
2024-06-14 13:29:42,651 - ejemplo_logger - INFO - Este es un mensaje informativo
INFO:ejemplo_logger:Este es un mensaje informativo
⚠️ En Google Colab, si desea consultar el contenido del archivo, seleccione el ícono de la carpeta llamada "Archivos" en la barra lateral izquierda.
Rotación de Archivos de Registro
Rotar archivos de registro es esencial para mantener un sistema de registro eficiente y manejable en cualquier aplicación. A medida que una aplicación se ejecuta, sus archivos de registro pueden crecer indefinidamente, lo que lleva a un mayor uso de disco y, potencialmente, a problemas de rendimiento o falta de espacio en disco.
Mediante la rotación de archivos de registro, se limita el tamaño y el número de archivos de registro retenidos, asegurando que sólo se conserven los registros más recientes y relevantes para la solución de problemas y la monitorización. La rotación de registros también mejora la legibilidad de los mismos al evitar que cualquier archivo de registro se vuelva excesivamente grande e inmanejable. Además, mejora la seguridad asegurando que la información sensible no permanezca accesible indefinidamente.
import logging # Importar el módulo de registro para manejar el registro from logging.handlers import RotatingFileHandler # Importar el RotatingFileHandler para crear un sistema de registro rotativo # Crear un objeto logger llamado 'logger_rotativo' logger = logging.getLogger('logger_rotativo') # Configurar el nivel de registro en DEBUG para que todos los niveles (DEBUG, INFO, WARNING, ERROR y CRITICAL) sean registrados logger.setLevel(logging.DEBUG) # Crear un handler de archivo rotativo que escribirá en 'registro_rotativo.log' # maxBytes=2000 significa que cada archivo de registro puede contener hasta 2000 bytes # backupCount=5 significa que el handler conservará hasta 5 archivos de registro de respaldo antes de sobrescribir el más antiguo rotating_handler = RotatingFileHandler('registro_rotativo.log', maxBytes=2000, backupCount=5) # Definir un formato para los mensajes de registro # El formato incluye la marca de tiempo, el nombre del logger, el nivel de registro y el mensaje formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # Aplicar el formatter al handler rotativo rotating_handler.setFormatter(formatter) # Añadir el handler rotativo al logger para que sepa cómo manejar el registro logger.addHandler(rotating_handler) # Generar 100 mensajes de depuración for i in range(100): # Cada mensaje de depuración contendrá el índice del bucle logger.debug(f"Mensaje de depuración {i}")
Trabajo en Proyecto y Práctica
Para consolidar tu comprensión, vamos a crear un proyecto donde combinamos los conceptos. Supongamos que estamos construyendo un simple raspador web que extrae datos de un sitio web. Queremos registrar varios eventos como el inicio del raspador, errores encontrados, y la extracción exitosa de datos. Aquí hay un ejemplo paso a paso:
Paso 1: Configuración de la Configuración de Registro
import logging # Crea un objeto logger con el nombre 'web_scraper_logger' web_scraper_logger = logging.getLogger('web_scraper') # Define el nivel de logging a DEBUG. Esto significa que todos los mensajes con el nivel DEBUG y superior serán registrados web_scraper_logger.setLevel(logging.DEBUG) # Crea handlers que imprimirán mensajes de log en la consola (salida estándar) y escribirán mensajes de log en un archivo llamado 'web_scraper.log' console_handler = logging.StreamHandler() file_handler = logging.FileHandler('web_scraper.log') # Define un formatter que especifica el formato de los mensajes de log formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # Define el formatter para el console handler y para el file handler console_handler.setFormatter(formatter) file_handler.setFormatter(formatter) # Añade el console handler al logger. Esto significa que los mensajes de log se mostrarán en la consola web_scraper_logger.addHandler(console_handler) # Añade el file handler al logger. Esto significa que los mensajes de log se escribirán en el archivo 'web_scraper.log' web_scraper_logger.addHandler(file_handler)
Paso 2: Creación de una Función de Raspado Web
Para simplificar, creamos un raspador web básico que utiliza requests y BeautifulSoup para obtener el título de una página web.
import requests # Importa la biblioteca requests para hacer solicitudes HTTP from bs4 import BeautifulSoup # Importa la clase BeautifulSoup de la biblioteca bs4 para analizar contenido HTML def buscar_titulo(url): """ Función para obtener el título de una página web dado su URL. Args: url (str): La URL de la página web a ser scrapeada. Retorna: str o None: El título de la página web, o None si ocurre un error. """ # Registra el inicio del proceso de scraping web web_scraper_logger.info(f"Iniciando el scraping de la URL: {url}") try: # Envía una solicitud GET a la URL especificada respuesta = requests.get(url) # Lanza una excepción HTTPError para códigos de respuesta 4xx o 5xx respuesta.raise_for_status() except requests.RequestException as e: # Registra el error si la solicitud HTTP falla web_scraper_logger.error(f"Fallo en la solicitud HTTP: {e}") return None try: # Analiza el contenido HTML de la respuesta usando BeautifulSoup sopa = BeautifulSoup(respuesta.content, 'html.parser') # Extrae el contenido de la etiqueta <title> titulo = sopa.title.string # Registra un mensaje indicando la extracción exitosa del título web_scraper_logger.info(f"Título obtenido con éxito: {titulo}") return titulo except Exception as e: # Registra cualquier error que ocurra durante el análisis de HTML o extracción del título web_scraper_logger.error(f"Fallo al analizar HTML o extraer título: {e}") return None
if __name__ == '__main__': urls = [ 'https://www.python.org', 'https://www.example.com', 'https://urlinexistente.com.br' # Una URL que causará una solicitud fallida ] for url in urls: titulo = buscar_titulo(url) if titulo: web_scraper_logger.debug(f"Título scrapeado para {url}: {titulo}") else: web_scraper_logger.warning(f"Ningún título encontrado para {url}")
Paso 3: Uso del Registro Dentro de la Función
Ahora probemos nuestro raspador web y veamos el registro en acción.
La ejecución del script anterior producirá los siguientes registros, tanto en la consola como en el archivo example.log.
2024-06-14 13:34:21,508 - web_scraper - INFO - Iniciando el scraping de la URL: https://www.python.org
INFO:web_scraper:Iniciando el scraping de la URL: https://www.python.org
2024-06-14 13:34:21,744 - web_scraper - INFO - Título obtenido con éxito: Welcome to Python.org
INFO:web_scraper:Título obtenido con éxito: Welcome to Python.org
2024-06-14 13:34:21,750 - web_scraper - DEBUG - Título scrapeado para https://www.python.org: Welcome to Python.org
DEBUG:web_scraper:Título scrapeado para https://www.python.org: Welcome to Python.org
2024-06-14 13:34:21,755 - web_scraper - INFO - Iniciando el scraping de la URL: https://www.example.com
INFO:web_scraper:Iniciando el scraping de la URL: https://www.example.com
2024-06-14 13:34:21,880 - web_scraper - INFO - Título obtenido con éxito: Example Domain
INFO:web_scraper:Título obtenido con éxito: Example Domain
2024-06-14 13:34:21,888 - web_scraper - DEBUG - Título scrapeado para https://www.example.com: Example Domain
DEBUG:web_scraper:Título scrapeado para https://www.example.com: Example Domain
2024-06-14 13:34:21,893 - web_scraper - INFO - Iniciando el scraping de la URL: https://urlinexistente.com.br
INFO:web_scraper:Iniciando el scraping de la URL: https://urlinexistente.com.br
2024-06-14 13:34:22,030 - web_scraper - ERROR - Fallo en la solicitud HTTP: HTTPSConnectionPool(host='urlinexistente.com.br', port=443): Max retries exceeded with url: / (Caused by NameResolutionError("<urllib3.connection.HTTPSConnection object at 0x7b2e84c20d60>: Failed to resolve 'urlinexistente.com.br' ([Errno -2] Name or service not known)"))
ERROR:web_scraper:Fallo en la solicitud HTTP: HTTPSConnectionPool(host='urlinexistente.com.br', port=443): Max retries exceeded with url: / (Caused by NameResolutionError("<urllib3.connection.HTTPSConnection object at 0x7b2e84c20d60>: Failed to resolve 'urlinexistente.com.br' ([Errno -2] Name or service not known)"))
2024-06-14 13:34:22,034 - web_scraper - WARNING - Ningún título encontrado para https://urlinexistente.com.br
WARNING:web_scraper:Ningún título encontrado para https://urlinexistente.com.br
Conclusión
Hemos cubierto el espectro de registro en Python — desde los conceptos básicos de configuración del registro hasta conceptos más avanzados como la rotación de archivos de registro. Con ejemplos prácticos y un proyecto completo, ahora tienes todo lo que necesitas para incorporar un registro efectivo en tus aplicaciones Python, asegurando que tu código sea mantenible, depurable y listo para producción.
Si tienes alguna pregunta o temas que te gustaría que cubriéramos en el futuro, no dudes en responder a este boletín.
Si te gustó este boletín, no olvides suscribirte para recibir actualizaciones regulares. Comparte con tus amigos y colegas interesados en Python y crezcamos juntos en nuestra comunidad de programadores.
Recuerda, la clave de la maestría es la práctica y la persistencia. ¡Feliz codificación! Hasta la próxima edición, ¡sigue programando! 👨💻
Newsletter InfinitePy - Tu fuente de aprendizaje e inspiración en Python.