You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

84 lines
2.2 KiB

/**
* 响应时间统计中间件
* 记录请求响应时间并进行日志记录
*/
import LoggerProvider from '../../../app/providers/LoggerProvider.js'
const logger = LoggerProvider.getLogger('request')
// 静态资源扩展名列表
const staticExts = [".css", ".js", ".png", ".jpg", ".jpeg", ".gif", ".ico", ".svg", ".map", ".woff", ".woff2", ".ttf", ".eot"]
/**
* 判断是否为静态资源
*/
function isStaticResource(path) {
return staticExts.some(ext => path.endsWith(ext))
}
/**
* 格式化请求日志
*/
function formatRequestLog(ctx, ms) {
const user = ctx.state?.user || null
const ip = ctx.ip || ctx.request.ip || ctx.headers["x-forwarded-for"] || ctx.req.connection.remoteAddress
return {
timestamp: new Date().toISOString(),
method: ctx.method,
path: ctx.path,
url: ctx.url,
userAgent: ctx.headers['user-agent'],
user: user ? { id: user.id, username: user.username } : null,
ip,
params: {
query: ctx.query,
body: ctx.request.body
},
status: ctx.status,
responseTime: `${ms}ms`,
contentLength: ctx.length || 0
}
}
/**
* 响应时间记录中间件
*/
export default async function responseTime(ctx, next) {
// 跳过静态资源
if (isStaticResource(ctx.path)) {
await next()
return
}
const start = Date.now()
try {
await next()
} finally {
const ms = Date.now() - start
// 设置响应头
ctx.set("X-Response-Time", `${ms}ms`)
// 页面请求简单记录
if (!ctx.path.includes("/api")) {
if (ms > 500) {
logger.warn(`Slow page request: ${ctx.path} | ${ms}ms`)
}
return
}
// API 请求详细记录
const logLevel = ms > 1000 ? 'warn' : ms > 500 ? 'info' : 'debug'
const slowFlag = ms > 500 ? '🐌' : '⚡'
logger[logLevel](`${slowFlag} API Request:`, formatRequestLog(ctx, ms))
// 如果是慢请求,额外记录
if (ms > 1000) {
logger.error(`Very slow API request detected: ${ctx.method} ${ctx.path} took ${ms}ms`)
}
}
}