Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | 1x 1x 18x 15x 15x 23x 8x 15x 15x 15x 1x 14x 14x 14x 14x 14x 14x 14x 11x 11x 11x 11x 11x 10x 11x 11x 11x 11x 14x 1x | /**
* Request/response logging middleware with trace ID propagation.
*
* Usage:
* const logger = require('../utils/loggerInstance');
* const { createRequestLogger } = require('./requestLogger');
* app.use(createRequestLogger(logger));
*/
const crypto = require('node:crypto');
const SENSITIVE_BODY_KEYS = new Set([
'password',
'token',
'idtoken',
'accesstoken',
'refreshtoken',
'secret',
'credential',
'pin',
'code',
]);
/**
* Strip sensitive fields from request body (shallow clone, one level deep).
*/
function sanitizeBody(body) {
if (!body || typeof body !== 'object') return body;
const clean = {};
for (const [key, value] of Object.entries(body)) {
if (SENSITIVE_BODY_KEYS.has(key.toLowerCase())) continue;
clean[key] = value;
}
return clean;
}
/**
* Create Express middleware that logs every request/response.
*
* @param {object} logger - Logger instance with a `log(entry)` method.
* @returns {Function} Express middleware
*/
function createRequestLogger(logger) {
return function requestLoggerMiddleware(req, res, next) {
// Skip logging for health checks — they add ~1,440 writes/day for no value
if (req.path === '/api/health') {
return next();
}
const startTime = Date.now();
// Generate request trace ID
const requestTraceId = crypto.randomBytes(16).toString('hex');
const sessionTraceId = req.headers['x-session-trace-id'] || null;
// Attach to request for downstream use
req.requestTraceId = requestTraceId;
req.sessionTraceId = sessionTraceId;
res.setHeader('x-request-trace-id', requestTraceId);
// Log after response completes
res.on('finish', () => {
try {
const durationMs = Date.now() - startTime;
const statusCode = res.statusCode;
let level = 'INFO';
if (statusCode >= 500) level = 'ERROR';
else if (statusCode >= 400) level = 'WARN';
const method = req.method;
const path = req.originalUrl || req.url;
const message = `${method} ${path} ${statusCode} ${durationMs}ms`;
logger.log({
level,
source: 'http',
message,
requestTraceId,
sessionTraceId,
userId: req.auth?.uid || null,
context: {
method,
path,
statusCode,
durationMs,
requestBody:
req.body !== null && req.body !== undefined ? sanitizeBody(req.body) : null,
userAgent: req.headers['user-agent'] || null,
},
});
} catch {
// Intentionally swallowed — request logging must never throw to avoid disrupting HTTP responses
}
});
// Never block the request
next();
};
}
module.exports = { createRequestLogger, sanitizeBody };
|