import { randomUUID } from "node:crypto"; import pino, { type Logger } from "pino"; const SECRET_KEYS = /(password|secret|token|api[-_]?key|authorization|cookie|credential|private[-_]?key)/i; const SECRET_VALUES = [ /sk-ant-[A-Za-z0-9_-]{10,}/g, /Bearer\s+[A-Za-z0-9._-]{10,}/g, /asw_[A-Za-z0-9_-]{10,}/g, ]; /** Redacts secret-looking keys/values from any JSON-serialisable value (used for logs, events and public provenance). */ export function redact(value: T): T { if (typeof value === "string") { let out: string = value; for (const re of SECRET_VALUES) out = out.replace(re, "[REDACTED]"); return out as unknown as T; } if (value instanceof Date) return value; if (Buffer.isBuffer(value)) return value; if (Array.isArray(value)) return value.map((v) => redact(v)) as T; if (value && typeof value === "object") { const out: Record = {}; for (const [k, v] of Object.entries(value as Record)) { out[k] = SECRET_KEYS.test(k) ? "[REDACTED]" : redact(v); } return out as T; } return value; } export function createLogger(service: string, level = process.env.LOG_LEVEL ?? "info"): Logger { return pino({ level, base: { service, pid: process.pid }, timestamp: pino.stdTimeFunctions.isoTime, formatters: { level: (label) => ({ level: label }) }, hooks: { logMethod(args, method) { const safe = args.map((a) => typeof a === "object" && a !== null ? redact(a) : typeof a === "string" ? redact(a) : a, ); return method.apply(this, safe as Parameters); }, }, }); } export function correlationId(): string { return randomUUID(); } export type { Logger };