'use strict'; /* ── Structured logger — JSON in prod, pretty in dev ────────────────────── Usage: logger.info('msg', { key: val }) logger.error('msg', { err: e.message }) ──────────────────────────────────────────────────────────────────────── */ const LEVELS = { error: 0, warn: 1, info: 2, debug: 3 }; const COLORS = { error: '\x1b[31m', // red warn: '\x1b[33m', // yellow info: '\x1b[36m', // cyan debug: '\x1b[90m', // gray }; const RESET = '\x1b[0m'; const isProd = process.env.NODE_ENV === 'production'; function _currentLevel() { const env = process.env.LOG_LEVEL; if (env && LEVELS[env] !== undefined) return LEVELS[env]; return isProd ? LEVELS.info : LEVELS.debug; } function log(level, msg, meta) { if (LEVELS[level] > _currentLevel()) return; const ts = new Date().toISOString(); if (isProd) { /* JSON — one line per entry, parseable by log aggregators */ const entry = { level, ts, msg }; if (meta && typeof meta === 'object') Object.assign(entry, meta); process.stdout.write(JSON.stringify(entry) + '\n'); } else { /* Pretty — coloured label + message + optional meta */ const color = COLORS[level] || ''; const label = `[${ts}] ${color}${level.toUpperCase().padEnd(5)}${RESET}`; const metaStr = meta && Object.keys(meta).length ? ' ' + JSON.stringify(meta, null, 0) : ''; process.stdout.write(`${label} ${msg}${metaStr}\n`); } } const logger = { error: (msg, meta) => log('error', msg, meta), warn: (msg, meta) => log('warn', msg, meta), info: (msg, meta) => log('info', msg, meta), debug: (msg, meta) => log('debug', msg, meta), /* Convenience: log an Error object */ exception: (msg, err, meta) => log('error', msg, { err: err?.message, stack: !isProd ? err?.stack : undefined, ...meta, }), }; module.exports = logger;