spb/autoscholar
Public
1import { randomUUID } from "node:crypto";2import pino, { type Logger } from "pino";34const SECRET_KEYS = /(password|secret|token|api[-_]?key|authorization|cookie|credential|private[-_]?key)/i;5const SECRET_VALUES = [6 /sk-ant-[A-Za-z0-9_-]{10,}/g,7 /Bearer\s+[A-Za-z0-9._-]{10,}/g,8 /asw_[A-Za-z0-9_-]{10,}/g,9];1011/** Redacts secret-looking keys/values from any JSON-serialisable value (used for logs, events and public provenance). */12export function redact<T>(value: T): T {13 if (typeof value === "string") {14 let out: string = value;15 for (const re of SECRET_VALUES) out = out.replace(re, "[REDACTED]");16 return out as unknown as T;17 }18 if (value instanceof Date) return value;19 if (Buffer.isBuffer(value)) return value;20 if (Array.isArray(value)) return value.map((v) => redact(v)) as T;21 if (value && typeof value === "object") {22 const out: Record<string, unknown> = {};23 for (const [k, v] of Object.entries(value as Record<string, unknown>)) {24 out[k] = SECRET_KEYS.test(k) ? "[REDACTED]" : redact(v);25 }26 return out as T;27 }28 return value;29}3031export function createLogger(service: string, level = process.env.LOG_LEVEL ?? "info"): Logger {32 return pino({33 level,34 base: { service, pid: process.pid },35 timestamp: pino.stdTimeFunctions.isoTime,36 formatters: { level: (label) => ({ level: label }) },37 hooks: {38 logMethod(args, method) {39 const safe = args.map((a) =>40 typeof a === "object" && a !== null ? redact(a) : typeof a === "string" ? redact(a) : a,41 );42 return method.apply(this, safe as Parameters<typeof method>);43 },44 },45 });46}4748export function correlationId(): string {49 return randomUUID();50}5152export type { Logger };53