cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

44 lines
1.6 KiB
TypeScript

import type { MiddlewareHandler } from "hono";
import { correlationBindings, establishRequestCorrelation } from "./correlation.js";
import { formatTraceparent } from "./ids.js";
import { getLogger } from "./logger.js";
import type { Logger } from "./types.js";
export function requestLogging(logger?: Logger): MiddlewareHandler {
return async (c, next) => {
const log = logger ?? getLogger();
const correlation = establishRequestCorrelation({
requestId: c.req.header("x-request-id"),
traceparent: c.req.header("traceparent"),
});
c.header("x-request-id", correlation.requestId);
c.header("traceparent", formatTraceparent(correlation.traceId, correlation.spanId));
const started = performance.now();
return log.withContext(correlationBindings(correlation), async () => {
let thrown: unknown;
try {
await next();
} catch (error) {
thrown = error;
if (!c.error) throw error;
} finally {
const failure = c.error ?? thrown;
if (failure) log.error("http.request.failed", failure);
const status = failure && c.res.status < 400 ? 500 : c.res.status;
const level = status >= 500 ? "error" : status >= 400 ? "warn" : "info";
log[level]("http.request.completed", {
"http.method": c.req.method,
"http.route": matchedRoute(c.req.path, c.req.routePath),
"http.status": status,
"http.duration_ms": Math.round(performance.now() - started),
});
}
});
};
}
function matchedRoute(path: string, routePath: string): string {
if (routePath && routePath !== "/*") return routePath;
return path;
}