leistungsbilanz-ts/src/shared/logging/logger.ts
Julian Appel ea3c02cd6b Add configurable structured logging
Adds a leveled JSON logger (error/warn/info/verbose/debug, controlled via
LOG_LEVEL) wired into the Express API (access log, error middleware,
crash handlers, memory heartbeat) and the Next.js server (page-request
proxy, instrumentation crash handlers, heartbeat). LOG_LEVEL is exposed
through compose.yaml, and both services now rotate their Docker logs
(json-file, 20m x 10 files) instead of growing unbounded. Intended to
capture long-running diagnostic data for the intermittent 502s seen on
the Docker host.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 19:45:39 +02:00

71 lines
2 KiB
TypeScript

export type LogLevel = "error" | "warn" | "info" | "verbose" | "debug";
const LEVEL_SEVERITY: Record<LogLevel, number> = {
error: 0,
warn: 1,
info: 2,
verbose: 3,
debug: 4,
};
const DEFAULT_LEVEL: LogLevel = "info";
export function resolveLogLevel(raw: string | undefined): LogLevel {
const candidate = raw?.trim().toLowerCase();
if (candidate && candidate in LEVEL_SEVERITY) {
return candidate as LogLevel;
}
return DEFAULT_LEVEL;
}
export function toErrorMeta(error: unknown): Record<string, unknown> {
if (error instanceof Error) {
return {
errorName: error.name,
errorMessage: error.message,
stack: error.stack,
};
}
return { error: typeof error === "string" ? error : JSON.stringify(error) };
}
export interface Logger {
error(message: string, meta?: Record<string, unknown>): void;
warn(message: string, meta?: Record<string, unknown>): void;
info(message: string, meta?: Record<string, unknown>): void;
verbose(message: string, meta?: Record<string, unknown>): void;
debug(message: string, meta?: Record<string, unknown>): void;
}
export function createLogger(
scope: string,
options?: { level?: LogLevel }
): Logger {
const threshold = options?.level ?? resolveLogLevel(process.env.LOG_LEVEL);
const write = (level: LogLevel, message: string, meta?: Record<string, unknown>) => {
if (LEVEL_SEVERITY[level] > LEVEL_SEVERITY[threshold]) {
return;
}
const line = JSON.stringify({
timestamp: new Date().toISOString(),
level,
scope,
message,
...meta,
});
if (level === "error" || level === "warn") {
console.error(line);
} else {
console.log(line);
}
};
return {
error: (message, meta) => write("error", message, meta),
warn: (message, meta) => write("warn", message, meta),
info: (message, meta) => write("info", message, meta),
verbose: (message, meta) => write("verbose", message, meta),
debug: (message, meta) => write("debug", message, meta),
};
}