{"version":3,"file":"logging.mjs","sources":["../../../src/lib/utils/logging.ts"],"sourcesContent":["/**\n * @file Logging Utilities\n * @description Centralized logging with levels and context\n */\n\n/**\n * Log levels\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\n\n/**\n * Log level priority\n */\nconst LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {\n  debug: 0,\n  info: 1,\n  warn: 2,\n  error: 3,\n};\n\n/**\n * Logger configuration\n */\nexport interface LoggerConfig {\n  /** Minimum log level */\n  level: LogLevel;\n\n  /** Enable console output */\n  console: boolean;\n\n  /** Enable remote logging */\n  remote: boolean;\n\n  /** Remote logging endpoint */\n  remoteEndpoint?: string;\n\n  /** Include timestamp */\n  timestamp: boolean;\n\n  /** Include caller info */\n  caller: boolean;\n\n  /** Custom log handler */\n  handler?: (entry: LogEntry) => void;\n}\n\n/**\n * Log entry\n */\nexport interface LogEntry {\n  level: LogLevel;\n  message: string;\n  timestamp: Date;\n  context?: Record<string, unknown>;\n  caller?: string;\n  tags?: string[];\n}\n\n/**\n * Default logger configuration\n */\nconst defaultConfig: LoggerConfig = {\n  level: 'info',\n  console: true,\n  remote: false,\n  timestamp: true,\n  caller: false,\n};\n\n/**\n * Current logger configuration\n */\nlet currentConfig: LoggerConfig = { ...defaultConfig };\n\n/**\n * Configure logger\n */\nexport function configureLogger(config: Partial<LoggerConfig>): void {\n  currentConfig = { ...currentConfig, ...config };\n}\n\n/**\n * Get current configuration\n */\nexport function getLoggerConfig(): LoggerConfig {\n  return { ...currentConfig };\n}\n\n/**\n * Check if level should be logged\n */\nfunction shouldLog(level: LogLevel): boolean {\n  return LOG_LEVEL_PRIORITY[level] >= LOG_LEVEL_PRIORITY[currentConfig.level];\n}\n\n/**\n * Get caller information\n */\nfunction getCallerInfo(): string {\n  const error = new Error();\n  const stack = error.stack?.split('\\n');\n\n  if (stack !== undefined && stack.length > 4) {\n    const [callerLine] = stack.slice(4);\n    if (callerLine !== undefined && callerLine !== '') {\n      const match = callerLine.match(/at\\s+(.+)\\s+\\((.+):(\\d+):(\\d+)\\)/);\n\n      if (match) {\n        return `${match[1]} (${match[2]}:${match[3]})`;\n      }\n\n      const simpleMatch = callerLine.match(/at\\s+(.+):(\\d+):(\\d+)/);\n      if (simpleMatch) {\n        return `${simpleMatch[1]}:${simpleMatch[2]}`;\n      }\n    }\n  }\n\n  return 'unknown';\n}\n\n/**\n * Format log entry for console\n */\nfunction formatForConsole(entry: LogEntry): string {\n  const parts: string[] = [];\n\n  if (currentConfig.timestamp) {\n    parts.push(`[${entry.timestamp.toISOString()}]`);\n  }\n\n  parts.push(`[${entry.level.toUpperCase()}]`);\n\n  if (entry.tags !== undefined && entry.tags.length > 0) {\n    parts.push(`[${entry.tags.join(', ')}]`);\n  }\n\n  parts.push(entry.message);\n\n  if (currentConfig.caller && entry.caller !== undefined && entry.caller !== '') {\n    parts.push(`@ ${entry.caller}`);\n  }\n\n  return parts.join(' ');\n}\n\n/**\n * Send log to remote endpoint\n *\n * Note: This function intentionally uses raw fetch() rather than apiClient because:\n * 1. Logging should be independent to avoid circular dependencies\n * 2. Log endpoints may be on different domains (e.g., log aggregation services)\n * 3. Logging must work even when apiClient encounters errors\n *\n * @see {@link @/lib/api/api-client} for application API calls\n */\nasync function sendToRemote(entry: LogEntry): Promise<void> {\n  const endpoint = currentConfig.remoteEndpoint;\n  if (!currentConfig.remote || endpoint === undefined || endpoint === '') {\n    return;\n  }\n\n  try {\n    // Raw fetch is intentional - logging must be independent of apiClient\n    await fetch(endpoint, {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/json' },\n      body: JSON.stringify(entry),\n    });\n  } catch {\n    // Silently fail remote logging\n  }\n}\n\n/**\n * Log function\n */\nfunction log(\n  level: LogLevel,\n  message: string,\n  context?: Record<string, unknown>,\n  tags?: string[]\n): void {\n  if (!shouldLog(level)) {\n    return;\n  }\n\n  const entry: LogEntry = {\n    level,\n    message,\n    timestamp: new Date(),\n    context,\n    tags,\n  };\n\n  if (currentConfig.caller) {\n    entry.caller = getCallerInfo();\n  }\n\n  // Custom handler\n  if (currentConfig.handler !== undefined) {\n    currentConfig.handler(entry);\n    return;\n  }\n\n  // Console output\n  if (currentConfig.console) {\n    const formatted = formatForConsole(entry);\n\n    if (context !== undefined && Object.keys(context).length > 0) {\n      if (level === 'error') {\n        console.error(formatted, context);\n      } else if (level === 'warn') {\n        console.warn(formatted, context);\n      } else {\n        console.info(formatted, context);\n      }\n    } else {\n      if (level === 'error') {\n        console.error(formatted);\n      } else if (level === 'warn') {\n        console.warn(formatted);\n      } else {\n        console.info(formatted);\n      }\n    }\n  }\n\n  // Remote logging\n  if (currentConfig.remote) {\n    void sendToRemote(entry);\n  }\n}\n\n/**\n * Logger object\n */\nexport const logger = {\n  debug: (message: string, context?: Record<string, unknown>, tags?: string[]): void =>\n    log('debug', message, context, tags),\n\n  info: (message: string, context?: Record<string, unknown>, tags?: string[]): void =>\n    log('info', message, context, tags),\n\n  warn: (message: string, context?: Record<string, unknown>, tags?: string[]): void =>\n    log('warn', message, context, tags),\n\n  error: (message: string, context?: Record<string, unknown>, tags?: string[]): void =>\n    log('error', message, context, tags),\n\n  /**\n   * Create a logger with preset tags\n   */\n  withTags: (\n    ...tags: string[]\n  ): {\n    debug: (message: string, context?: Record<string, unknown>) => void;\n    info: (message: string, context?: Record<string, unknown>) => void;\n    warn: (message: string, context?: Record<string, unknown>) => void;\n    error: (message: string, context?: Record<string, unknown>) => void;\n  } => ({\n    debug: (message: string, context?: Record<string, unknown>): void =>\n      log('debug', message, context, tags),\n    info: (message: string, context?: Record<string, unknown>): void =>\n      log('info', message, context, tags),\n    warn: (message: string, context?: Record<string, unknown>): void =>\n      log('warn', message, context, tags),\n    error: (message: string, context?: Record<string, unknown>): void =>\n      log('error', message, context, tags),\n  }),\n\n  /**\n   * Create a logger with preset context\n   */\n  withContext: (\n    baseContext: Record<string, unknown>\n  ): {\n    debug: (message: string, context?: Record<string, unknown>) => void;\n    info: (message: string, context?: Record<string, unknown>) => void;\n    warn: (message: string, context?: Record<string, unknown>) => void;\n    error: (message: string, context?: Record<string, unknown>) => void;\n  } => ({\n    debug: (message: string, context?: Record<string, unknown>) =>\n      log('debug', message, { ...baseContext, ...context }),\n    info: (message: string, context?: Record<string, unknown>) =>\n      log('info', message, { ...baseContext, ...context }),\n    warn: (message: string, context?: Record<string, unknown>) =>\n      log('warn', message, { ...baseContext, ...context }),\n    error: (message: string, context?: Record<string, unknown>) =>\n      log('error', message, { ...baseContext, ...context }),\n  }),\n};\n\n/**\n * Performance logging helper\n */\nexport function logPerformance(label: string): () => void {\n  const start = performance.now();\n\n  return () => {\n    const duration = performance.now() - start;\n    logger.debug(`[Perf] ${label}: ${duration.toFixed(2)}ms`);\n  };\n}\n\n/**\n * Measure async function performance\n */\nexport async function measureAsync<T>(label: string, fn: () => Promise<T>): Promise<T> {\n  const end = logPerformance(label);\n  try {\n    return await fn();\n  } finally {\n    end();\n  }\n}\n"],"names":["LOG_LEVEL_PRIORITY","defaultConfig","currentConfig","configureLogger","config","getLoggerConfig","shouldLog","level","getCallerInfo","stack","callerLine","match","simpleMatch","formatForConsole","entry","parts","sendToRemote","endpoint","log","message","context","tags","formatted","logger","baseContext","logPerformance","label","start","duration","measureAsync","fn","end"],"mappings":"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"}