import { redactLogArgs, redactValue } from "@/lib/observability/redaction"; type LogLevel = "debug" | "info" | "warn" | "error" | "silent"; const LEVEL_ORDER: Record = { debug: 10, info: 20, warn: 30, error: 40, silent: 50, }; const SENSITIVE_EVENT_PATTERN = /(authorization|body|cookie|header|image url|messages received|password|prompt|request body|session|token)/i; function resolveLogLevel(): LogLevel { const configured = String(process.env.LOG_LEVEL || "").toLowerCase(); if (configured in LEVEL_ORDER) return configured as LogLevel; return process.env.NODE_ENV === "production" ? "info" : "debug"; } function shouldLog(level: Exclude) { return LEVEL_ORDER[level] >= LEVEL_ORDER[resolveLogLevel()]; } function normalizeEvent(event: string) { return event .trim() .replace(/^\[[^\]]+\]\s*/, "") .replace(/[^a-zA-Z0-9]+/g, "_") .replace(/^_+|_+$/g, "") .toLowerCase() .slice(0, 80) || "event"; } function writeLog(level: Exclude, scope: string, event: string, args: unknown[]) { if (!shouldLog(level)) return; const payload = { level, scope, event: normalizeEvent(event), message: event, timestamp: new Date().toISOString(), ...(args.length > 0 ? { meta: SENSITIVE_EVENT_PATTERN.test(event) ? { omitted: true, argCount: args.length } : redactLogArgs(args), } : {}), }; const serialized = JSON.stringify(redactValue(payload)); if (level === "error") { console.error(serialized); return; } if (level === "warn") { console.warn(serialized); return; } console.log(serialized); } export function createLogger(scope: string) { return { debug(event: string, ...args: unknown[]) { writeLog("debug", scope, event, args); }, info(event: string, ...args: unknown[]) { writeLog("info", scope, event, args); }, warn(event: string, ...args: unknown[]) { writeLog("warn", scope, event, args); }, error(event: string, ...args: unknown[]) { writeLog("error", scope, event, args); }, }; }