import { TRPCError } from "@trpc/server"; import { logger } from "../lib/logger.js"; const SLOW_THRESHOLD_MS = 500; /** * Core logging logic for tRPC procedure calls. * * Designed to be wrapped with `t.middleware()` in trpc.ts. * Generates a requestId (UUID) per call and attaches it to the context. * * Log levels: * - debug: normal requests * - warn: slow requests (>500ms) * - error: failed requests */ export async function loggingMiddleware(opts: { ctx: { dbUser?: { id: string } | null; requestId?: string }; type: "query" | "mutation" | "subscription"; path: string; next: (opts: { ctx: Record }) => Promise<{ ok: boolean }>; }) { const { ctx, type, path, next } = opts; const requestId = crypto.randomUUID(); const userId = ctx.dbUser?.id ?? "anonymous"; const start = performance.now(); const logBase = { requestId, type, path, userId, }; try { const result = await next({ ctx: { ...ctx, requestId }, }); const durationMs = Math.round(performance.now() - start); const logData = { ...logBase, durationMs, status: "ok" as const }; if (durationMs > SLOW_THRESHOLD_MS) { logger.warn(logData, "Slow tRPC call"); } else { logger.debug(logData, "tRPC call"); } return result; } catch (error) { const durationMs = Math.round(performance.now() - start); const errorCode = error instanceof TRPCError ? error.code : "INTERNAL_SERVER_ERROR"; const errorMessage = error instanceof Error ? error.message : "Unknown error"; // Log input validation failures at warn level (not error) if (errorCode === "BAD_REQUEST") { logger.warn( { ...logBase, durationMs, status: "error" as const, errorCode, errorMessage }, "Input validation failure", ); } else { logger.error( { ...logBase, durationMs, status: "error" as const, errorCode, errorMessage }, "tRPC call failed", ); } throw error; } }