Ir al contenido principal
Laravel, shipping fast.
Capitulo 9 · Monitoreo y observabilidad

Trazar una peticion que no puedes reproducir

Julian Beaujardin

¿Qué haces cuando el fallo ya ocurrió y no puedes hacer que vuelva a ocurrir? No lo reproduces. Lo reconstruyes a partir de lo que ya estaba registrado, que es todo el sentido de recoger estas señales antes de necesitarlas.

Request hits the API app
LogApiRequestsMiddleware records the start time, lets the request through
Response returns to the caller immediately
defer() runs after the response has already been sent
SensitiveDataSanitizer redacts the request and response payloads
SendToLogsJob queues the sanitized record
LogFacade ships it to the shared logs sink
  • defer(): el saneado, la serialización y el encolado ocurren después de que la respuesta ya haya vuelto a quien llamó, así que la sobrecarga del registro nunca se suma al tiempo de respuesta de un comerciante.
  • SensitiveDataSanitizer: cada cuerpo de petición y respuesta se redacta buscando claves con forma de contraseña, token o credencial antes de encolarse, para que la traza que consultes dentro de seis meses no te entregue un token vivo en un hilo de soporte.
  • SendToLogsJob: lleva method, endpoint, status, execution_time y el user_id del bearer, e implementa ShouldBeEncrypted porque la carga que se sienta en la tabla de la cola sigue siendo sensible incluso redactada.

Eso cubre una sola petición HTTP. Un job en segundo plano es una traza más difícil, porque para cuando falla puede haber llamado ya a dos o tres servicios en tu nombre, y ninguna de esas llamadas es visible en el registro original. TrackJobExecutionMiddleware cierra ese hueco.

// api-server/app/Http/Middleware/TrackJobExecutionMiddleware.php
public function handle(object $job, callable $next): mixed
{
    if (! $this->shouldTrackJob($job)) {
        return $next($job);
    }

    try {
        $log = $this->createJobLog($job);
    } catch (Throwable $e) {
        return $next($job);
    }

    $this->tracker->clear();

    return $this->executeAndTrack($job, $next, $log);
}

Crear esa fila de registro puede fallar por sí misma —un parpadeo de la base de datos, una migración a medias— y cuando ocurre, el job tiene que correr igualmente. El catch cae a $next($job) sin trazar: pierdes la traza de esa ejecución, no la ejecución. El trazado es una capa de diagnóstico atornillada al job, nunca una dependencia de la que el trabajo real del job dependa.

  • JobsLog: una fila por ejecución de job, ligada al site_id sobre el que trabajaba, con status, duration_ms y error_message cuando el job se resuelve.
  • ApiCallTracker: se limpia al inicio de cada job trazado y se rellena según el job llama a otros servicios. Al terminar, si el rastreador registró algo, todas esas llamadas —con su método, URL, estado y ambos cuerpos— se escriben en metadata.api_calls de esa misma fila.
  • Por qué importa: cuando falla la activación de la licencia de un sitio y soporte pregunta qué pasó, no reproduces el fallo. Sacas la fila de JobsLog de ese site_id y lees las llamadas externas exactas, en el orden en que ocurrieron de verdad, con el código de estado que devolvió cada una.