- Create src/utils/http.js - fetchT and HTTP helpers - Create src/utils/logging.js - Structured logging and error logging - Create src/utils/responses.js - Standard API responses - Create src/utils/async-handler.js - Async wrapper with error handling - Create src/utils/index.js - Consolidated exports Removes scattered helper functions from server.js
120 lines
3.5 KiB
JavaScript
120 lines
3.5 KiB
JavaScript
/**
|
|
* Logging utilities - Structured logging and error handling
|
|
*/
|
|
const fsp = require('fs').promises;
|
|
const path = require('path');
|
|
|
|
const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
|
|
/**
|
|
* Create a structured logger
|
|
*/
|
|
function createLogger(LOG_LEVEL) {
|
|
function log(level, context, message, data = {}) {
|
|
if (LOG_LEVELS[level] < LOG_LEVEL) return;
|
|
|
|
const entry = {
|
|
t: new Date().toISOString(),
|
|
level,
|
|
ctx: context,
|
|
msg: message,
|
|
};
|
|
|
|
if (Object.keys(data).length) entry.data = data;
|
|
|
|
const fn = level === 'error' ? console.error : level === 'warn' ? console.warn : console.info;
|
|
fn(JSON.stringify(entry));
|
|
}
|
|
|
|
log.info = (ctx, msg, data) => log('info', ctx, msg, data);
|
|
log.warn = (ctx, msg, data) => log('warn', ctx, msg, data);
|
|
log.error = (ctx, msg, data) => log('error', ctx, msg, data);
|
|
log.debug = (ctx, msg, data) => log('debug', ctx, msg, data);
|
|
|
|
return log;
|
|
}
|
|
|
|
/**
|
|
* Enhanced error logging with context tracking
|
|
*/
|
|
async function logError(ERROR_LOG_FILE, MAX_ERROR_LOG_SIZE, context, error, additionalInfo = {}, log) {
|
|
const timestamp = new Date().toISOString();
|
|
|
|
// Extract request context
|
|
const requestContext = {};
|
|
if (additionalInfo.req) {
|
|
const req = additionalInfo.req;
|
|
const clientIP = req.ip || req.socket?.remoteAddress || '';
|
|
requestContext.requestId = req.id;
|
|
requestContext.ip = clientIP;
|
|
requestContext.userAgent = req.get('user-agent');
|
|
requestContext.method = req.method;
|
|
requestContext.path = req.path;
|
|
delete additionalInfo.req;
|
|
}
|
|
|
|
const logEntry = {
|
|
timestamp,
|
|
context,
|
|
...requestContext,
|
|
error: {
|
|
message: error.message || error,
|
|
stack: error.stack,
|
|
code: error.code
|
|
},
|
|
...additionalInfo
|
|
};
|
|
|
|
const contextInfo = Object.keys(requestContext).length > 0
|
|
? `\nRequest Context: ${JSON.stringify(requestContext, null, 2)}`
|
|
: '';
|
|
const logLine = `[${timestamp}] ${context}: ${error.message || error}\n${error.stack || ''}${contextInfo}\nAdditional Info: ${JSON.stringify(additionalInfo, null, 2)}\n${'='.repeat(80)}\n`;
|
|
|
|
try {
|
|
// Rotate log if it exceeds max size
|
|
try {
|
|
const stats = await fsp.stat(ERROR_LOG_FILE);
|
|
if (stats.size > MAX_ERROR_LOG_SIZE) {
|
|
const rotated = ERROR_LOG_FILE + '.1';
|
|
const exists = await fsp.access(rotated).then(() => true).catch(() => false);
|
|
if (exists) await fsp.unlink(rotated);
|
|
await fsp.rename(ERROR_LOG_FILE, rotated);
|
|
}
|
|
} catch (_) { /* file may not exist yet */ }
|
|
|
|
await fsp.appendFile(ERROR_LOG_FILE, logLine);
|
|
} catch (e) {
|
|
if (log && log.error) {
|
|
log.error('errorlog', 'Failed to write to error log', { error: e.message });
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return a safe error message without leaking internals
|
|
*/
|
|
function safeErrorMessage(error) {
|
|
const msg = error.message || String(error);
|
|
|
|
// Detect port conflict errors
|
|
const portMatch = msg.match(/exposing port TCP [^:]*:(\d+)/);
|
|
if (portMatch || msg.includes('port is already allocated') || msg.includes('ports are not available')) {
|
|
const port = portMatch ? portMatch[1] : 'requested';
|
|
return `[DC-200] Port ${port} is already in use. Try a different port or stop the service using that port first.`;
|
|
}
|
|
|
|
// Only expose short, user-facing messages
|
|
if (msg.length < 200 && !msg.includes('/') && !msg.includes('\\') && !msg.includes(' at ')) {
|
|
return msg;
|
|
}
|
|
|
|
return 'An internal error occurred';
|
|
}
|
|
|
|
module.exports = {
|
|
LOG_LEVELS,
|
|
createLogger,
|
|
logError,
|
|
safeErrorMessage,
|
|
};
|