/** * Structured logging via Winston. * * One root logger is created at module load. Each domain gets a child logger * that adds a `domain` field to every log entry so log lines can be filtered * and routed independently. * * A custom format reads the current request context from AsyncLocalStorage and * injects `requestId` into every log entry automatically — no manual threading needed. * * Optional env vars: * LOG_LEVEL — winston level (default: "info"; use "debug" for verbose output) * NODE_ENV — when "development", logs are pretty-printed; otherwise JSON */ import { createLogger, format, transports } from 'winston'; import { getContext } from '$lib/server/context'; import { env } from '$env/dynamic/private'; const level = env.LOG_LEVEL ?? 'info'; // Injects requestId from AsyncLocalStorage into every log entry. const requestContextFormat = format((info) => { const ctx = getContext(); if (ctx) info['requestId'] = ctx.requestId; return info; }); const productionFormat = format.combine( requestContextFormat(), format.timestamp(), format.errors({ stack: true }), format.json(), ); const developmentFormat = format.combine( requestContextFormat(), format.timestamp({ format: 'HH:mm:ss' }), format.errors({ stack: true }), format.colorize(), format.printf(({ timestamp, level, message, domain, requestId, ...rest }) => { const domainTag = domain ? `[${domain}] ` : ''; const rid = requestId ? `(${requestId}) ` : ''; const extra = Object.keys(rest).length ? ' ' + JSON.stringify(rest) : ''; return `${timestamp} ${level} ${domainTag}${rid}${message}${extra}`; }), ); const root = createLogger({ level, format: env.NODE_ENV === 'development' ? developmentFormat : productionFormat, transports: [new transports.Console()], }); const child = (domain: string) => root.child({ domain }); // --- Domain loggers --- const serverLogger = child('server'); const authLogger = child('auth'); const gitLogger = child('git'); const k8sLogger = child('k8s'); export { authLogger, gitLogger, k8sLogger, root as logger, serverLogger };