/** * Lightweight logger that suppresses output in production. * * In development (npm run dev): logs are shown with a prefix tag. * In production (npm run build): all console output is suppressed * so logs are never exposed to the browser inspector. * * Usage: * import { logger } from '$lib/core/utils/logger'; * logger.info('hello', someVar); * logger.error('failed', err); */ type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error'; const LOG_LEVEL_RANK: Record = { trace: -1, debug: 0, info: 1, warn: 2, error: 3 }; const ENV_LOG_LEVEL: LogLevel = (import.meta.env.PUBLIC_APP_LOG_LEVEL as LogLevel) || 'info'; const IS_DEV = import.meta.env.DEV; function shouldLog(level: LogLevel): boolean { return IS_DEV && LOG_LEVEL_RANK[level] >= LOG_LEVEL_RANK[ENV_LOG_LEVEL]; } function formatArgs(level: LogLevel, args: any[]): any[] { return [`[${level.toUpperCase()}]`, ...args]; } /** * Extract caller location (file:line) from a stack trace. * Stack frame format: " at method (file:///path/file.ts:10:20)" * The 3rd frame (index 2) is the caller of the method that created the Error. */ function getCallerLocation(): string { try { const stack = new Error().stack?.split('\n'); if (!stack || stack.length < 3) return 'unknown'; // stack[0] = "Error", stack[1] = " at getCallerLocation (...)", // stack[2] = " at Logger.trace (...)" — skip both const caller = stack[2]?.trim(); // Extract path from common formats: // "at method (file:///path/file.ts:10:20)" or "at file:///path/file.ts:10:20" const match = caller.match(/(?:\(|at\s+)([^(]+?\.\w+):(\d+)/); if (match) return `${match[1]}:${match[2]}`; return caller || 'unknown'; } catch { return 'unknown'; } } class Logger { trace(...args: any[]) { if (shouldLog('trace')) { const location = getCallerLocation(); console.debug(`[TRACE:${location}]`, ...args); } } debug(...args: any[]) { if (shouldLog('debug')) { console.debug(...formatArgs('debug', args)); } } info(...args: any[]) { if (shouldLog('info')) { console.info(...formatArgs('info', args)); } } warn(...args: any[]) { if (shouldLog('warn')) { console.warn(...formatArgs('warn', args)); } } error(...args: any[]) { if (shouldLog('error')) { console.error(...formatArgs('error', args)); } } } export const logger = new Logger();