167 lines
4.8 KiB
JavaScript
167 lines
4.8 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
class ApiDebugLogger {
|
|
constructor() {
|
|
// Use /tmp directory which has universal write permissions in containers
|
|
this.logFile = '/tmp/api_debug.log';
|
|
this.enabled = process.env.NODE_ENV === 'development' || process.env.API_DEBUG === 'true';
|
|
this.fileLoggingEnabled = false;
|
|
|
|
// Debug logging setup
|
|
if (this.enabled) {
|
|
console.log(`🐛 ApiDebugLogger: Enabled (NODE_ENV=${process.env.NODE_ENV}, API_DEBUG=${process.env.API_DEBUG})`);
|
|
console.log(`🐛 ApiDebugLogger: Log file path: ${this.logFile}`);
|
|
|
|
// Try to enable file logging
|
|
try {
|
|
fs.writeFileSync(this.logFile, `# API Debug Log Started at ${new Date().toISOString()}\n`);
|
|
this.fileLoggingEnabled = true;
|
|
console.log(`🐛 ApiDebugLogger: File logging enabled at ${this.logFile}`);
|
|
} catch (error) {
|
|
console.warn(`⚠️ ApiDebugLogger: File logging disabled - using console only. Error: ${error.message}`);
|
|
this.fileLoggingEnabled = false;
|
|
}
|
|
} else {
|
|
console.log(`🐛 ApiDebugLogger: Disabled (NODE_ENV=${process.env.NODE_ENV}, API_DEBUG=${process.env.API_DEBUG})`);
|
|
}
|
|
}
|
|
|
|
log(method, url, statusCode, requestBody = {}, responseBody = {}, headers = {}) {
|
|
if (!this.enabled) return;
|
|
|
|
const timestamp = new Date().toISOString();
|
|
const sanitizedRequest = this.sanitizeData(requestBody);
|
|
const sanitizedResponse = this.sanitizeData(responseBody);
|
|
const sanitizedHeaders = this.sanitizeHeaders(headers);
|
|
|
|
const logEntry = [
|
|
`[${timestamp}]`,
|
|
method.toUpperCase(),
|
|
url,
|
|
`-`,
|
|
`STATUS:${statusCode}`,
|
|
`-`,
|
|
`REQ:${JSON.stringify(sanitizedRequest)}`,
|
|
`-`,
|
|
`RES:${JSON.stringify(sanitizedResponse)}`,
|
|
`-`,
|
|
`HEADERS:${JSON.stringify(sanitizedHeaders)}`
|
|
].join(' ');
|
|
|
|
// Always log to console
|
|
console.log(`🐛 API Log: ${method.toUpperCase()} ${url} - ${statusCode}`);
|
|
|
|
// Try to log to file if enabled
|
|
if (this.fileLoggingEnabled) {
|
|
try {
|
|
fs.appendFileSync(this.logFile, logEntry + '\n');
|
|
} catch (error) {
|
|
console.warn(`⚠️ File logging failed, disabling: ${error.message}`);
|
|
this.fileLoggingEnabled = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
sanitizeData(data) {
|
|
if (!data || typeof data !== 'object') return data;
|
|
|
|
const sanitized = { ...data };
|
|
|
|
// Remove sensitive fields
|
|
const sensitiveFields = ['password', 'token', 'secret', 'key', 'auth', 'authorization'];
|
|
|
|
sensitiveFields.forEach(field => {
|
|
if (sanitized[field]) {
|
|
sanitized[field] = '***REDACTED***';
|
|
}
|
|
});
|
|
|
|
return sanitized;
|
|
}
|
|
|
|
sanitizeHeaders(headers) {
|
|
if (!headers || typeof headers !== 'object') return {};
|
|
|
|
const sanitized = { ...headers };
|
|
|
|
// Remove sensitive headers
|
|
const sensitiveHeaders = ['authorization', 'cookie', 'x-api-key', 'x-auth-token'];
|
|
|
|
sensitiveHeaders.forEach(header => {
|
|
if (sanitized[header]) {
|
|
sanitized[header] = '***REDACTED***';
|
|
}
|
|
});
|
|
|
|
return sanitized;
|
|
}
|
|
|
|
logRequest(req) {
|
|
if (!this.enabled) return;
|
|
|
|
const timestamp = new Date().toISOString();
|
|
const logEntry = `[${timestamp}] INCOMING ${req.method} ${req.url} - BODY:${JSON.stringify(this.sanitizeData(req.body))} - HEADERS:${JSON.stringify(this.sanitizeHeaders(req.headers))}`;
|
|
|
|
// Always log to console
|
|
console.log(`🐛 API Request: ${req.method} ${req.url}`);
|
|
|
|
// Try to log to file if enabled
|
|
if (this.fileLoggingEnabled) {
|
|
try {
|
|
fs.appendFileSync(this.logFile, logEntry + '\n');
|
|
} catch (error) {
|
|
console.warn(`⚠️ File logging failed, disabling: ${error.message}`);
|
|
this.fileLoggingEnabled = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
logResponse(req, res, responseBody) {
|
|
if (!this.enabled) return;
|
|
|
|
this.log(
|
|
req.method,
|
|
req.url,
|
|
res.statusCode,
|
|
req.body,
|
|
responseBody,
|
|
{
|
|
'content-type': res.getHeader('content-type'),
|
|
'user-agent': req.headers['user-agent']
|
|
}
|
|
);
|
|
}
|
|
|
|
clear() {
|
|
try {
|
|
fs.writeFileSync(this.logFile, '# API Debug Log Cleared at ' + new Date().toISOString() + '\n');
|
|
console.log('API debug log cleared');
|
|
} catch (error) {
|
|
console.error('Failed to clear API debug log:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Middleware function for Express
|
|
function apiDebugMiddleware(req, res, next) {
|
|
const logger = new ApiDebugLogger();
|
|
|
|
// Log incoming request
|
|
logger.logRequest(req);
|
|
|
|
// Override res.json to capture response
|
|
const originalJson = res.json;
|
|
res.json = function(data) {
|
|
logger.logResponse(req, res, data);
|
|
return originalJson.call(this, data);
|
|
};
|
|
|
|
next();
|
|
}
|
|
|
|
module.exports = {
|
|
ApiDebugLogger,
|
|
apiDebugMiddleware
|
|
};
|