Capturar Errores y Problemas
Python Automation Cookbook — Logging y Manejo de Excepciones
La característica principal de una tarea automatizada es su cualidad de "disparar y olvidar". No estamos mirando activamente el resultado, sino haciéndola ejecutarse en segundo plano.
La mayoría de las recetas en este libro tratan con información externa, como páginas web u otros informes, por lo que la probabilidad de encontrar un problema inesperado al ejecutarla es alta. Esta receta presentará una tarea automatizada que almacenará de forma segura comportamientos inesperados en un archivo de registro que puede verificarse después.
Sin Manejo de Errores
import argparse import sys def main(number, other_number, output): result = number / other_number # ¡División! print(f'The result is {result}', file=output) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-n1', type=int, help='A number', default=1) parser.add_argument('-n2', type=int, help='Another number', default=1) parser.add_argument('-o', dest='output', type=argparse.FileType('w'), help='output file', default=sys.stdout) args = parser.parse_args() main(args.n1, args.n2, args.output)
Cuando Falla...
File "task_with_error_handling_step1.py", line 20, in <module>
main(args.n1, args.n2, args.output)
File "task_with_error_handling_step1.py", line 6, in main
result = number / other_number
ZeroDivisionError: division by zero
Con Manejo de Errores y Logging
import argparse import sys import logging # Configuración del logging LOG_FORMAT = '%(asctime)s %(name)s %(levelname)s %(message)s' LOG_LEVEL = logging.DEBUG def main(number, other_number, output): logging.info(f'Dividing {number} between {other_number}') result = number / other_number print(f'The result is {result}', file=output) if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('-n1', type=int, help='A number', default=1) parser.add_argument('-n2', type=int, help='Another number', default=1) parser.add_argument('-o', dest='output', type=argparse.FileType('w'), help='output file', default=sys.stdout) parser.add_argument('-l', dest='log', type=str, help='log file', default=None) args = parser.parse_args() if args.log: logging.basicConfig(format=LOG_FORMAT, filename=args.log, level=LOG_LEVEL) else: logging.basicConfig(format=LOG_FORMAT, level=LOG_LEVEL) try: main(args.n1, args.n2, args.output) except Exception as exc: logging.exception("Error running task") exit(1)
Ejecución con Logging
2020-01-19 14:26:15,376 root INFO Dividing 5 between 0
2020-01-19 14:26:15,376 root ERROR division by zero
Traceback (most recent call last):
File "task_with_error_handling_step4.py", line 33, in <module>
main(args.n1, args.n2, args.output)
File "task_with_error_handling_step4.py", line 11, in main
result = number / other_number
ZeroDivisionError: division by zero
Cómo Funciona
Para capturar correctamente cualquier excepción inesperada, la función main debe estar envuelta en un bloque try-except:
try: main(...) except Exception as exc: # Algo salió mal logging.exception("Error running task") exit(1)
Niveles de Log
Los niveles disponibles para registros son—de menos crítico a más crítico:
| Nivel | Uso |
|---|---|
DEBUG |
Información detallada para diagnóstico |
INFO |
Confirmación de que las cosas funcionan como se espera |
WARNING |
Algo inesperado ocurrió, pero el software sigue funcionando |
ERROR |
Debido a un problema, el software no pudo realizar una función |
CRITICAL |
Error grave, el programa puede no poder continuar |
Ejemplo de Uso
>>> import logging >>> logging.basicConfig(level=logging.INFO) >>> logging.warning('a warning message') WARNING:root:a warning message >>> logging.info('an info message') INFO:root:an info message >>> logging.debug('a debug message') # No se muestra porque el nivel es INFO
Capacidades del Módulo Logging
- Formato personalizado: Incluir archivo y número de línea del log
- Múltiples loggers: Cada uno con su propia configuración
- Múltiples destinos: Salida estándar, archivo, o logger remoto
- Rotación automática: Crear nuevos archivos de log por tiempo o tamaño
- Configuración desde archivos: Leer configuraciones estándar
logging.exception() es un caso especial que crea un registro ERROR, pero también incluye información sobre la excepción, como el stack trace. Úsalo dentro de bloques except.
Para detalles completos, consulta la documentación de Python: