59 lines
2.0 KiB
JavaScript
59 lines
2.0 KiB
JavaScript
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<string, unknown>} 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;
|
|
}
|
|
}
|