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
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
<!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
@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
# 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
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
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
@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. |