Saltar a contenido

Manejo de errores y logging — Backend

Contexto: Trabajo en este repo sigue el ROL de @CURSOR_AUDIT_PROMPT.md (líneas 3-4): Staff Engineer / Arquitecto senior (Angular + Ionic + PHP + Rust), experto en arquitectura hexagonal, testing y sistemas multi-app.


Prohibición de panic: unwrap y expect

  • Queda prohibido usar .unwrap() y .expect() en código que atienda requests (handlers, repos, servicios llamados desde handlers). Ambos pueden provocar panic y tumbar el proceso.
  • Sí permitido: .unwrap_or(), .unwrap_or_else(), .unwrap_or_default() — no paniquean, dan un valor por defecto.
  • Excepción: dentro de #[cfg(test)] (tests unitarios) se puede usar .unwrap()/.expect() cuando el test controla los datos; aun así preferir ? o afirmaciones si el fallo es posible.

Prohibido (riesgo de panic)

let url = state.config.photo_to_full_url(Some(&hash)).unwrap();
let json = serde_json::to_string(&event).unwrap();
let t = NaiveTime::from_hms_opt(h, m, 0).unwrap(); // en código de producción
Correcto
let url = state.config.photo_to_full_url(Some(&hash)).unwrap_or_else(|| hash.clone());
let json = serde_json::to_string(&event).unwrap_or_default();
let t = NaiveTime::from_hms_opt(h, m, 0).ok_or(AppError::BadRequest("hora inválida".into()))?;

  • Cualquier error recuperable debe convertirse en AppError y devolverse (o loguearse con log_internal y devolver AppError::Internal). No ocultar fallos con unwrap().

Tipo de error unificado

  • Usar siempre crate::error::AppError para respuestas HTTP. Errores internos (BD, IO, serialización): usar log_internal("contexto", e)? para registrar la cadena de causas y convertir a AppError::Internal.

Tracing obligatorio

  • En cada handler que realice una operación relevante (crear/actualizar/borrar, login, clock-in, etc.) debe haber al menos un tracing::info o tracing::debug con contexto (place_id, user_id, acción). Así se puede seguir el flujo y diagnosticar sin depender solo del error HTTP.
  • Errores: log_internal y el IntoResponse de AppError ya registran internals; en handlers se puede añadir un log explícito cuando el contexto ayude (ej. parámetros del request que falló).

Prohibido

pub async fn clock_in(...) -> Result<Json<...>, AppError> {
    repository::time_control::insert(...).await?;
    Ok(Json(...))
}
Correcto
tracing::info!("clock_in place_id={} user_id={}", req.place_id, auth.user_id);
repository::time_control::insert(...).await?;
Ok(Json(...))