import { context, metrics, SpanStatusCode, trace } from "@opentelemetry/api"; type TelemetryContext = { req: { method: string; path: string; }; res: { status: number; }; }; export function createHttpTelemetryMiddleware(serviceName: string) { const tracer = trace.getTracer(`${serviceName}.http`); const meter = metrics.getMeter(`${serviceName}.http`); const requestCount = meter.createCounter(`${serviceName}.http.server.requests`, { description: `Total number of ${serviceName} HTTP requests`, }); const requestDuration = meter.createHistogram( `${serviceName}.http.server.duration`, { description: `${serviceName} HTTP request duration`, unit: "ms", }, ); const activeRequests = meter.createUpDownCounter( `${serviceName}.http.server.active_requests`, { description: `Number of in-flight ${serviceName} HTTP requests`, }, ); return async (c: TelemetryContext, next: () => Promise) => { const startedAt = performance.now(); const method = c.req.method; const route = c.req.path; const span = tracer.startSpan(`http ${method} ${route}`, { attributes: { "http.request.method": method, "url.path": route, }, }); activeRequests.add(1, { "http.request.method": method, "url.path": route, }); try { await context.with(trace.setSpan(context.active(), span), next); span.setAttribute("http.response.status_code", c.res.status); span.setStatus({ code: c.res.status >= 500 ? SpanStatusCode.ERROR : SpanStatusCode.OK, }); } catch (error) { span.recordException(error as Error); span.setStatus({ code: SpanStatusCode.ERROR, message: `Unhandled ${serviceName} request error`, }); throw error; } finally { const durationMs = performance.now() - startedAt; const attrs = { "http.request.method": method, "http.response.status_code": c.res.status, "url.path": route, }; requestCount.add(1, attrs); requestDuration.record(durationMs, attrs); activeRequests.add(-1, { "http.request.method": method, "url.path": route, }); span.end(); } }; }