export type LogLevel = "debug" | "info" | "warn" | "error"; type Fields = Record; const LEVELS: Record = { debug: 10, info: 20, warn: 30, error: 40 }; let minLevel: LogLevel = "info"; function parseLevel(input?: string | null): LogLevel { const value = (input || "").toLowerCase(); if (value === "debug" || value === "info" || value === "warn" || value === "error") return value; return "info"; } export function setLevel(input?: string): void { minLevel = parseLevel(input); } export interface Logger { debug: (event: string, fields?: Fields) => void; info: (event: string, fields?: Fields) => void; warn: (event: string, fields?: Fields) => void; error: (event: string, fields?: Fields) => void; child: (fields: Fields) => Logger; } function write(level: LogLevel, component: string, base: Fields, event: string, fields: Fields): void { if (LEVELS[level] < LEVELS[minLevel]) return; const entry = JSON.stringify({ ts: new Date().toISOString(), level, component, event, eventType: event, ...base, ...fields, }); if (level === "error") { console.error(entry); return; } if (level === "warn") { console.warn(entry); return; } console.log(entry); } export function createLogger(component: string, baseFields: Fields = {}): Logger { return { debug: (event, fields = {}) => write("debug", component, baseFields, event, fields), info: (event, fields = {}) => write("info", component, baseFields, event, fields), warn: (event, fields = {}) => write("warn", component, baseFields, event, fields), error: (event, fields = {}) => write("error", component, baseFields, event, fields), child: (fields) => createLogger(component, { ...baseFields, ...fields }), }; } export function errorContext(error: unknown): Fields { if (error instanceof Error) { return { errorName: error.name, errorMessage: error.message, errorStack: error.stack }; } return { errorMessage: String(error) }; }