Manejo de errores en Flask

Flask proporciona un mecanismo flexible de manejo de errores que permite capturar y gestionar diversos errores en la aplicación.

Un buen manejo de errores no consiste solo en «no tener bugs», sino en dar al usuario una experiencia amigable cuando ocurre un bug.

Este capítulo explica cómo personalizar páginas de error y gestionar logs en Flask.


Páginas de error personalizadas

Por defecto, Flask muestra una página simple de texto blanco sobre fondo negro para errores como el 404.

Usando@app.errorhandlerel decorador puede personalizar páginas de error para códigos de estado HTTP específicos:

Ejemplo

from flask import Flask, render_template

app = Flask(__name__)

# Manejar el error 404: página no encontrada
@app.errorhandler(404)
def page_not_found(error):
    # Nota: debes devolver explícitamente el código de estado 404
    return render_template("404.html"), 404

# Manejar el error 500: error interno del servidor
@app.errorhandler(500)
def internal_error(error):
    return render_template("500.html"), 500

# Manejar el error 403: acceso prohibido
@app.errorhandler(403)
def forbidden(error):
    return render_template("403.html"), 403

Plantilla 404 correspondiente:

Ejemplo

<!-- Ruta de archivo: templates/404.html -->
<!DOCTYPE html>
<html>
<head>
    <title>404 - Página no encontrada</title>
    <style>
        body { text-align: center; padding-top: 60px; font-family: Arial; }
        h1 { font-size: 72px; color: #e74c3c; margin: 0; }
        p { color: #666; font-size: 18px; }
        a { color: #3498db; }
    </style>
</head>
<body>
    <h1>404</h1>
    <p>Lo sentimos, la página que buscas no existe.</p>
    <p><a href="/">Volver a la página de inicio</a></p>
</body>
</html>

EnerrorhandlerEn la función decorada,returndebe incluirse explícitamente el código de estado. Si no se escribe, 404, Flask devolverá por defecto el código de estado 200, y los navegadores y motores de búsqueda no considerarán que es una página de error.


Registro de clases de excepción

Además de los códigos de estado,@app.errorhandlertambién se pueden registrar clases de excepción directamente:

Ejemplo

from werkzeug.exceptions import HTTPException

@app.errorhandler(HTTPException)
def handle_http_exception(error):
    """Maneja de forma unificada todas las excepciones HTTP (como 400, 401, 403, 404, etc.)"""
    return f"""
<h1>Error HTTP {error.code}</h1>
    <p>{error.description}</p>
<p><a href="/">Volver a la página de inicio de EJEMPLO</a></p>
    """
, error.code

Usar abort para disparar errores

En tu función de vista, usaabort()para disparar errores HTTP activamente:

Ejemplo

from flask import abort

# Simular una base de datos de artículos
articles = {
    1: {"title": "Tutorial de introducción a Flask"},
    2: {"title": "Fundamentos de Python"},
}

@app.get("/article/<int:article_id>")
def view_article(article_id):
    article = articles.get(article_id)

    if article is None:
        # El artículo no existe, devolver 404
        # El parámetro description se pasa al errorhandler
        abort(404, description=f"El ID del artículo {article_id} no existe")

    return f"<h1>{article['title']}</h1>"

@app.get("/admin")
def admin_panel():
    # Un usuario sin permisos de administrador accede al panel de administración
    abort(403, description="No tienes permisos de administrador")

Registro de logs

Flask usa la biblioteca estándar de Pythonloggingmódulo, a través deapp.loggerse pueden registrar logs.

Los logs son cruciales para solucionar problemas: no uses soloprint(), los logs ofrecen un contexto más rico y un mejor control.

Ejemplo

from flask import Flask, request

app = Flask(__name__)

@app.route("/")
def index():
    # Diferentes niveles de registro
    app.logger.debug("Visitar página de inicio")                    # Información de depuración, normalmente no se muestra en producción
    app.logger.info("Usuario proveniente de IP: %s", request.remote_addr)  # Información general
    app.logger.warning("Patrón de acceso anómalo detectado")         # Advertencia: requiere atención pero no afecta la operación
    app.logger.error("Error de conexión con la base de datos")               # Error: la funcionalidad se ve afectada
    app.logger.critical("Espacio en disco insuficiente, el servicio se detendrá")  # Error grave: el servicio puede detenerse
    return "OK"

Niveles de registro (de menor a mayor):

Nivel Caso de uso
DEBUG Información detallada durante el desarrollo y la depuración, no se muestra por defecto en producción
INFO Información general de ejecución, como registros de solicitudes, inicio del servicio
WARNING Mensajes de advertencia, problemas potenciales que no afectan la operación actual
ERROR Mensajes de error, una función falla pero el servicio sigue en ejecución
CRITICAL Errores graves, pueden provocar la detención del servicio

En modo Debug, el nivel de registro de Flask se establece automáticamente enDEBUG. En el entorno de producción se debe configurar según sea necesario aINFOoWARNING, para evitar generar demasiada información inútil.


Configurar la salida de logs

Escribir los logs en un archivo para facilitar el análisis y la resolución de problemas posteriores:

Ejemplo

import logging
from logging.handlers import RotatingFileHandler

# Configurar el handler de archivos de log: rotación automática cuando el archivo alcanza 10MB
handler = RotatingFileHandler(
    "ejemplo_app.log",     # Ruta del archivo de log
    maxBytes=10 * 1024 * 1024,  # Tamaño máximo por archivo: 10MB
    backupCount=5          # Conservar las últimas 5 copias de respaldo
)

# Establecer el formato del log
handler.setFormatter(logging.Formatter(
    "[%(asctime)s] %(levelname)s in %(module)s: %(message)s"
))

# Añadir el handler al logger de Flask
app.logger.addHandler(handler)
app.logger.setLevel(logging.INFO)

Capturar excepciones no controladas

Usandoapp.errorhandler(500)puedes capturar excepciones no controladas y registrar logs:

Ejemplo

import traceback

@app.errorhandler(500)
def internal_error(error):
    # Registrar la traza completa de la excepción
    app.logger.error("Error interno del servidor:\n%s", traceback.format_exc())

    # Mostrar al usuario una página de error amigable
    return """
<h1>500 - Error interno del servidor</h1>
<p>Lo sentimos, el servidor encontró un error inesperado. Hemos registrado el problema, por favor inténtalo de nuevo más tarde.</p>
<p>Si el problema persiste, contacta con el soporte técnico de EJEMPLO.</p>
    """
, 500

Referencia rápida de códigos de error HTTP comunes

Código de estado Significado Causas comunes
400 Bad Request Formato incorrecto de los parámetros de solicitud, falta de campos obligatorios
401 Unauthorized Usuario no autenticado o fallo de autenticación
403 Forbidden Usuario autenticado pero sin permisos de acceso
404 Not Found El recurso (artículo, usuario, etc.) no existe
405 Method Not Allowed Se utilizó un método HTTP incorrecto (por ejemplo, usar GET en una interfaz POST)
500 Internal Server Error Errores no controlados como excepciones de código, fallos de conexión a la base de datos, etc.
Otras extensiones