mm_os
Version:
这是超级美眉服务端框架,用于快速构建应用程序。
143 lines (125 loc) • 4.5 kB
JavaScript
/**
* 请求性能监控中间件
* 记录请求响应时间,监控慢请求,提供性能数据收集
*/
module.exports = function(server, config) {
if(config.web && !config.web.performance) {
return server;
}
// 默认配置
const cg = Object.assign({
slowThreshold: 1000, // 慢请求阈值,单位毫秒
enableMetrics: true, // 是否启用性能指标收集
ignorePaths: [] // 忽略监控的路径
}, config);
// 性能监控数据收集器
const performanceData = {
counters: new Map(), // 请求计数器
responseTimes: new Map(), // 响应时间数据
slowRequests: [] // 慢请求记录
};
// 全局性能监控对象
$.performanceMonitor = {
record: function(path, responseTime) {
if (!cg.enableMetrics) return;
// 更新请求计数
if (!performanceData.counters.has(path)) {
performanceData.counters.set(path, 1);
performanceData.responseTimes.set(path, { sum: responseTime, count: 1, min: responseTime, max: responseTime });
} else {
performanceData.counters.set(path, performanceData.counters.get(path) + 1);
const stats = performanceData.responseTimes.get(path);
stats.sum += responseTime;
stats.count += 1;
stats.min = Math.min(stats.min, responseTime);
stats.max = Math.max(stats.max, responseTime);
}
// 记录慢请求
if (responseTime > cg.slowThreshold) {
performanceData.slowRequests.push({
path: path,
time: new Date(),
responseTime: responseTime
});
// 限制慢请求记录数量
if (performanceData.slowRequests.length > 1000) {
performanceData.slowRequests.shift();
}
}
},
getStats: function() {
const result = {};
performanceData.counters.forEach((count, path) => {
const times = performanceData.responseTimes.get(path);
result[path] = {
count: count,
avg: times.sum / times.count,
min: times.min,
max: times.max
};
});
return result;
},
getSlowRequests: function(limit = 100) {
return performanceData.slowRequests.slice(-limit);
},
reset: function() {
performanceData.counters.clear();
performanceData.responseTimes.clear();
performanceData.slowRequests = [];
}
};
// 性能监控中间件
server.use(async (ctx, next) => {
// 检查是否需要忽略此路径
const shouldIgnore = cg.ignorePaths.some(pattern => {
if (typeof pattern === 'string') {
return ctx.path === pattern;
} else if (pattern instanceof RegExp) {
return pattern.test(ctx.path);
}
return false;
});
if (shouldIgnore) {
await next();
return;
}
const start = Date.now();
const startTime = process.hrtime();
try {
await next();
} finally {
const ms = Date.now() - start;
const [seconds, nanoseconds] = process.hrtime(startTime);
const executionTime = seconds * 1000 + nanoseconds / 1000000;
// 设置响应时间头
ctx.set('X-Response-Time', `${ms}ms`);
// 记录性能数据
$.performanceMonitor.record(ctx.path, ms);
// 慢请求报警
if (ms > cg.slowThreshold) {
const clientIP = ctx.headers['x-forwarded-for'] || ctx.ip;
$.log.warn(`【慢请求】 ${ctx.method} ${ctx.path} - ${ms}ms - ${clientIP}`);
// 如果是非常慢的请求(超过阈值的2倍),记录更多信息
if (ms > cg.slowThreshold * 2) {
const userAgent = ctx.headers['user-agent'] || 'unknown';
let requestData = '';
try {
if (ctx.method !== 'GET' && ctx.request.body) {
const bodyStr = JSON.stringify(ctx.request.body);
// 限制记录的请求体大小
requestData = bodyStr.length > 1024 ? bodyStr.substring(0, 1024) + '...' : bodyStr;
}
} catch (e) {
requestData = '[无法序列化请求体]';
}
$.log.warn(`【慢请求详情】路径: ${ctx.path}, 方法: ${ctx.method}, 耗时: ${ms}ms, IP: ${clientIP}, UA: ${userAgent.substring(0, 200)}`);
if (requestData) {
$.log.warn(`【慢请求数据】${requestData}`);
}
}
}
}
});
return server;
};