import pino from "pino"; import { env } from "@/env"; import { captureCmsError } from "./error-monitor"; import { getOperationContext } from "./foundation/request-context"; type LogLevel = "debug" | "info" | "warn" | "error"; const isBrowser = typeof window !== "undefined" && typeof window.console !== "undefined"; const level: LogLevel = (env.LOG_LEVEL as LogLevel | undefined) ?? (env.NODE_ENV === "production" ? "info" : "debug"); const isProd = env.NODE_ENV === "production"; const isTest = env.NODE_ENV === "test" || process.env.VITEST === "true"; const pinoLogger = pino({ level, base: { service: "atomcms-next" }, ...(isProd || isTest ? {} : { transport: { target: "pino-pretty", options: { colorize: true, translateTime: "SYS:standard" }, }, }), }); let requestIdCounter = 0; export function generateRequestId(): string { requestIdCounter += 1; return `${Date.now().toString(36)}-${requestIdCounter.toString(36)}`; } function formatMeta(meta: Record = {}): string { const keys = Object.keys(meta); if (keys.length === 0) return ""; return keys .map((k) => { const v = meta[k]; return `${k}=${v instanceof Error ? v.message : String(v)}`; }) .join(" "); } /** Universal logger: pino on server, console in browser. Same interface everywhere. */ export const logger = { debug(message: string, meta: Record = {}): void { if (isBrowser) { console.debug(`[debug] ${message} ${formatMeta(meta)}`.trim()); return; } pinoLogger.debug({ ...meta, ...getOperationContext() }, message); }, info(message: string, meta: Record = {}): void { if (isBrowser) { console.info(`[info] ${message} ${formatMeta(meta)}`.trim()); return; } pinoLogger.info({ ...meta, ...getOperationContext() }, message); }, warn(message: string, meta: Record = {}): void { if (isBrowser) { console.warn(`[warn] ${message} ${formatMeta(meta)}`.trim()); return; } pinoLogger.warn({ ...meta, ...getOperationContext() }, message); }, error(message: string, meta: Record = {}): string { if (isBrowser) { console.error(`[error] ${message} ${formatMeta(meta)}`.trim()); return ""; } const error = meta.err instanceof Error ? meta.err : meta.error instanceof Error ? meta.error : new Error( typeof meta.err === "string" ? meta.err : typeof meta.error === "string" ? meta.error : message, ); const operation = getOperationContext(); const context = { ...operation, ...meta, ...operation }; const errorId = captureCmsError("server", message, error, context); pinoLogger.error({ ...context, errorId }, message); return errorId; }, };