Files
yunrui_asset/backend/middleware/requestLogger.js
T

80 lines
2.0 KiB
JavaScript
Raw Normal View History

/**
* HTTP 请求日志中间件
* 支持:
* - 唯一请求ID生成(UUID格式,避免碰撞)
* - 全链路请求追踪
* - 请求/响应信息结构化记录
*/
const logger = require('../utils/logger');
const crypto = require('crypto');
/**
* 生成唯一请求ID
* 使用UUID格式确保全局唯一性
* @returns {string} 请求ID
*/
const generateRequestId = () => {
if (crypto.randomUUID) {
return crypto.randomUUID();
}
return `${Date.now().toString(36)}-${crypto.randomBytes(16).toString('hex')}`;
};
/**
* 请求日志中间件
* 为每个请求生成唯一ID,记录请求/响应信息
* 支持全链路追踪:requestId贯穿请求生命周期
*/
const requestLogger = (req, res, next) => {
const requestId = generateRequestId();
const startTime = Date.now();
req.requestId = requestId;
const reqLogger = logger.withRequestId(requestId, 'HTTP');
reqLogger.debug('请求开始', {
method: req.method,
url: req.originalUrl,
ip: req.ip || req.connection.remoteAddress,
userAgent: req.get('User-Agent'),
userId: req.user?.userId || null,
});
res.on('finish', () => {
const duration = Date.now() - startTime;
const level = res.statusCode >= 500 ? 'error'
: res.statusCode >= 400 ? 'warn' : 'debug';
const logData = {
method: req.method,
url: req.originalUrl,
statusCode: res.statusCode,
duration: `${duration}ms`,
ip: req.ip || req.connection.remoteAddress,
userAgent: req.get('User-Agent'),
userId: req.user?.userId || null,
contentLength: res.get('Content-Length') || 0,
};
if (res.statusCode >= 400) {
logData.errorMessage = res.locals.errorMessage || null;
logData.errorStack = res.locals.errorStack || null;
}
reqLogger[level](`${req.method} ${req.originalUrl}`, logData);
});
res.on('error', (error) => {
reqLogger.error('响应错误', {
error: error.message,
stack: error.stack,
});
});
next();
};
module.exports = requestLogger;