const logger = require('../utils/logger'); const { v4: uuidv4 } = require('uuid'); /** * Request logging middleware * Logs all HTTP requests with timing and response information */ const requestLogger = (req, res, next) => { // Generate unique request ID req.requestId = uuidv4(); // Record start time const startTime = Date.now(); // Log incoming request logger.info('Incoming Request', { requestId: req.requestId, method: req.method, url: req.originalUrl, ip: req.ip || req.connection.remoteAddress, userAgent: req.get('User-Agent'), contentType: req.get('Content-Type'), contentLength: req.get('Content-Length'), referer: req.get('Referer'), body: req.method !== 'GET' && req.body ? sanitizeBody(req.body) : undefined, query: Object.keys(req.query).length > 0 ? req.query : undefined, params: Object.keys(req.params).length > 0 ? req.params : undefined }); // Override res.json to capture response data const originalJson = res.json; res.json = function(data) { res.responseData = data; return originalJson.call(this, data); }; // Override res.send to capture response data const originalSend = res.send; res.send = function(data) { if (!res.responseData) { res.responseData = data; } return originalSend.call(this, data); }; // Log response when request finishes res.on('finish', () => { const responseTime = Date.now() - startTime; const logData = { requestId: req.requestId, method: req.method, url: req.originalUrl, statusCode: res.statusCode, responseTime: `${responseTime}ms`, contentLength: res.get('Content-Length') || 0, ip: req.ip || req.connection.remoteAddress }; // Add response data for errors or debug mode if (res.statusCode >= 400 || process.env.LOG_LEVEL === 'debug') { logData.responseData = sanitizeResponseData(res.responseData); } // Log based on status code if (res.statusCode >= 500) { logger.error('Request Completed with Server Error', logData); } else if (res.statusCode >= 400) { logger.warn('Request Completed with Client Error', logData); } else { logger.http('Request Completed Successfully', logData); } // Log slow requests if (responseTime > 1000) { logger.warn('Slow Request Detected', { ...logData, threshold: '1000ms' }); } }); // Log request errors res.on('error', (error) => { const responseTime = Date.now() - startTime; logger.error('Request Error', { requestId: req.requestId, method: req.method, url: req.originalUrl, error: error.message, responseTime: `${responseTime}ms`, ip: req.ip || req.connection.remoteAddress }); }); next(); }; /** * Sanitize request body to remove sensitive information */ const sanitizeBody = (body) => { if (!body || typeof body !== 'object') { return body; } const sensitiveFields = ['password', 'token', 'secret', 'key', 'auth']; const sanitized = { ...body }; Object.keys(sanitized).forEach(key => { if (sensitiveFields.some(field => key.toLowerCase().includes(field))) { sanitized[key] = '[REDACTED]'; } }); return sanitized; }; /** * Sanitize response data to remove sensitive information */ const sanitizeResponseData = (data) => { if (!data) return data; try { const parsed = typeof data === 'string' ? JSON.parse(data) : data; if (typeof parsed === 'object' && parsed !== null) { const sanitized = { ...parsed }; // Remove sensitive fields from response const sensitiveFields = ['password', 'token', 'secret', 'key']; const sanitizeObject = (obj) => { if (Array.isArray(obj)) { return obj.map(item => sanitizeObject(item)); } else if (obj && typeof obj === 'object') { const result = {}; Object.keys(obj).forEach(key => { if (sensitiveFields.some(field => key.toLowerCase().includes(field))) { result[key] = '[REDACTED]'; } else { result[key] = sanitizeObject(obj[key]); } }); return result; } return obj; }; return sanitizeObject(sanitized); } return parsed; } catch (error) { return '[UNPARSEABLE_RESPONSE]'; } }; /** * Health check endpoint logger * Reduces noise from health check requests */ const healthCheckLogger = (req, res, next) => { // Skip detailed logging for health checks if (req.originalUrl === '/health' || req.originalUrl === '/api/health') { return next(); } return requestLogger(req, res, next); }; module.exports = { requestLogger, healthCheckLogger, sanitizeBody, sanitizeResponseData };