UNPKG

@flavoai/fastfold

Version:

Flavo frontend package

168 lines 5.79 kB
import fs from 'fs'; import path from 'path'; /** * Request Logger for Fastfold * Logs all incoming/outgoing HTTP requests to a file */ export class RequestLogger { logFilePath; enabled; logRequests; logResponses; logRequestBody; logResponseBody; excludePaths; constructor(config) { this.enabled = config.enabled; this.logFilePath = config.logFilePath || path.resolve(process.cwd(), 'request-logs.txt'); this.logRequests = config.logRequests ?? true; this.logResponses = config.logResponses ?? true; this.logRequestBody = config.logRequestBody ?? true; this.logResponseBody = config.logResponseBody ?? false; this.excludePaths = config.excludePaths || ['/health', '/docs']; // Ensure log directory exists const logDir = path.dirname(this.logFilePath); if (!fs.existsSync(logDir)) { fs.mkdirSync(logDir, { recursive: true }); } // Always start fresh on server boot so logs reflect the current session only const header = `========================================\nFASTFOLD REQUEST LOGS\nStarted: ${new Date().toISOString()}\n========================================\n\n`; fs.writeFileSync(this.logFilePath, header); } /** * Check if a path should be excluded from logging */ shouldExclude(path) { return this.excludePaths.some(excluded => path.startsWith(excluded)); } /** * Append log entry to file */ appendLog(entry) { if (!this.enabled) return; try { fs.appendFileSync(this.logFilePath, entry + '\n'); } catch (error) { console.error('Failed to write to log file:', error); } } /** * Format log entry */ formatLogEntry(data) { const lines = []; lines.push(`[${data.timestamp}] ${data.source.toUpperCase()} ${data.direction.toUpperCase()} ${data.method} ${data.url}`); if (data.headers && Object.keys(data.headers).length > 0) { lines.push(` Headers: ${JSON.stringify(data.headers, null, 2).replace(/\n/g, '\n ')}`); } if (data.body && this.logRequestBody) { const bodyStr = typeof data.body === 'string' ? data.body : JSON.stringify(data.body, null, 2); lines.push(` Body: ${bodyStr.replace(/\n/g, '\n ')}`); } if (data.status !== undefined) { lines.push(` Status: ${data.status}`); } if (data.duration !== undefined) { lines.push(` Duration: ${data.duration}ms`); } if (data.error) { lines.push(` Error: ${data.error}`); } lines.push(''); // Empty line for separation return lines.join('\n'); } /** * Log backend incoming request */ logBackendRequest(req, startTime) { if (!this.enabled || !this.logRequests) return; if (this.shouldExclude(req.path)) return; const entry = this.formatLogEntry({ timestamp: new Date().toISOString(), source: 'backend', direction: 'incoming', method: req.method, url: req.originalUrl || req.url, headers: { 'content-type': req.headers['content-type'], 'authorization': req.headers.authorization ? 'Bearer ***' : undefined, 'user-agent': req.headers['user-agent'] }, body: req.body }); this.appendLog(entry); } /** * Log backend response */ logBackendResponse(req, res, startTime, responseBody) { if (!this.enabled || !this.logResponses) return; if (this.shouldExclude(req.path)) return; const duration = Date.now() - startTime; const entry = this.formatLogEntry({ timestamp: new Date().toISOString(), source: 'backend', direction: 'incoming', method: req.method, url: req.originalUrl || req.url, status: res.statusCode, duration, body: this.logResponseBody ? responseBody : undefined }); this.appendLog(entry); } /** * Log frontend outgoing request (from /internal-logs endpoint) */ logFrontendRequest(data) { if (!this.enabled) return; const entry = this.formatLogEntry({ timestamp: data.timestamp || new Date().toISOString(), source: 'frontend', direction: 'outgoing', method: data.method, url: data.url, headers: data.headers, body: data.body }); this.appendLog(entry); } /** * Get Express middleware for logging */ getMiddleware() { return (req, res, next) => { if (!this.enabled) { return next(); } const startTime = Date.now(); // Log request this.logBackendRequest(req, startTime); // Capture response const originalSend = res.send; const originalJson = res.json; res.send = function (body) { // Don't log response body by default (can be large) res.send = originalSend; return res.send(body); }; res.json = function (body) { res.json = originalJson; return res.json(body); }; // Log response when finished res.on('finish', () => { this.logBackendResponse(req, res, startTime); }); next(); }; } } //# sourceMappingURL=logger.js.map