import { appendFileSync, mkdirSync } from 'fs'; import { dirname, resolve } from 'path'; import { fileURLToPath } from 'url'; import { prettyJson } from '../util/prettyJson.js'; import { createLogger } from '../util/logger.js'; const log = createLogger('error-log'); const __dirname = dirname(fileURLToPath(import.meta.url)); const LOG_DIR = process.env.ERROR_LOG_DIR || resolve(__dirname, '../../logs/errors'); const LOG_PREFIX = 'error-'; let dirEnsured = false; function ensureLogDir() { if (dirEnsured) return; mkdirSync(LOG_DIR, { recursive: true }); dirEnsured = true; } /** * Writes a plain-text error log file (no ANSI colors) to the error log * directory and returns the file path. Never throws. * @param {string} label Short description, e.g. "POST /api/delivery 500" * @param {Record} sections Named sections (request, body, response, error…) * @returns {string | null} Path of the log file, or null on failure */ export function writeErrorLog(label, sections) { try { ensureLogDir(); const stamp = new Date().toISOString().replace(/[:.]/g, '-'); const safeLabel = label .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, '') .slice(0, 40); const filePath = resolve(LOG_DIR, `${LOG_PREFIX}${stamp}-${safeLabel}.log`); const divider = '='.repeat(80); const parts = [ `Timestamp: ${new Date().toISOString()}`, `Label: ${label}`, divider, ]; for (const [name, value] of Object.entries(sections)) { parts.push(`--- ${name} ${'-'.repeat(Math.max(0, 74 - name.length))}`); parts.push(prettyJson(value, { colors: false })); } appendFileSync(filePath, `${parts.join('\n')}\n`); log.info(`Wrote ${filePath}`); return filePath; } catch (err) { log.error('Failed to write error log:', err.message); return null; } }