69 lines · 1.9 KB · TypeScript
RawDownload
import type { MiddlewareHandler } from "hono";import { isHttpStatus } from "../core/errors";import type { AppContext } from "../app-context"; // High-frequency polling and streaming paths; recording every hit would grow// the telemetry tables without bound and drown real traffic in the stats.export const TELEMETRY_SKIP_PATHS = new Set([  "/health",  "/metrics",  "/events",  "/status",  "/api/docs",  "/api/spec",]); function elapsedMs(start: number): number {  return Math.round(performance.now() - start);} function errorClass(error: unknown): string {  if (isHttpStatus(error)) return `Http${error.status}`;  return (error as { name?: string } | null)?.name || "Error";} function errorMessage(error: unknown): string {  if (isHttpStatus(error)) return error.detail;  if (error instanceof Error) return error.message;  return String(error);} export function createControllerRequestObservabilityMiddleware(  context: AppContext,): MiddlewareHandler {  return async (ctx, next) => {    if (TELEMETRY_SKIP_PATHS.has(ctx.req.path)) {      await next();      return;    }    const start = performance.now();    const method = ctx.req.method.toUpperCase();    const path = ctx.req.path;    const userAgent = ctx.req.header("user-agent") ?? null;     try {      await next();      const status = ctx.res.status || 200;      context.stores.controllerRequestStore.record({        method,        path,        status,        duration_ms: elapsedMs(start),        success: status >= 200 && status < 400,        user_agent: userAgent,      });    } catch (error) {      context.stores.controllerRequestStore.record({        method,        path,        status: isHttpStatus(error) ? error.status : 500,        duration_ms: elapsedMs(start),        success: false,        error_class: errorClass(error),        error_message: errorMessage(error),        user_agent: userAgent,      });      throw error;    }  };}