- userId 延迟到 finish 事件获取(authMiddleware 已执行) - 日志记录 UTC 时间戳,支持前端按用户时区显示 - 日期筛选和级别筛选 watch 监听修复 - 添加 showDatePicker 变量定义
86 lines
2.8 KiB
TypeScript
86 lines
2.8 KiB
TypeScript
import { Request, Response, NextFunction } from 'express'
|
||
import fs from 'fs'
|
||
import path from 'path'
|
||
|
||
// 日志目录
|
||
const LOG_DIR = path.resolve(process.env.LOG_DIR || './logs')
|
||
if (!fs.existsSync(LOG_DIR)) {
|
||
fs.mkdirSync(LOG_DIR, { recursive: true })
|
||
}
|
||
|
||
/** 获取当前日期字符串 YYYY-MM-DD */
|
||
function getDateStr(): string {
|
||
const d = new Date()
|
||
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`
|
||
}
|
||
|
||
/** 格式化时间 HH:mm:ss.SSS */
|
||
function formatTime(date: Date): string {
|
||
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}.${String(date.getMilliseconds()).padStart(3, '0')}`
|
||
}
|
||
|
||
/** 写入日志文件 */
|
||
function writeLog(level: string, message: string, meta?: Record<string, any>) {
|
||
const dateStr = getDateStr()
|
||
const logFile = path.join(LOG_DIR, `${dateStr}.log`)
|
||
const now = new Date()
|
||
const time = formatTime(now)
|
||
const timestamp = now.toISOString() // 记录 UTC 时间戳,前端可按用户时区显示
|
||
const metaStr = meta ? ` ${JSON.stringify(meta)}` : ''
|
||
const line = `[${time}] [${timestamp}] ${level} ${message}${metaStr}\n`
|
||
|
||
fs.appendFile(logFile, line, (err) => {
|
||
if (err) console.error('[Logger] Write error:', err)
|
||
})
|
||
}
|
||
|
||
/** 请求日志中间件 */
|
||
export function requestLogger(req: Request, res: Response, next: NextFunction) {
|
||
const startTime = Date.now()
|
||
const { method, url, ip } = req
|
||
|
||
// 响应结束时记录日志(此时 authMiddleware 已执行,userId 可用)
|
||
res.on('finish', () => {
|
||
const duration = Date.now() - startTime
|
||
const { statusCode } = res
|
||
// 延迟获取 userId,确保 authMiddleware 已执行
|
||
const userId = (req as any).userId
|
||
|
||
const meta = {
|
||
method,
|
||
url,
|
||
status: statusCode,
|
||
duration: `${duration}ms`,
|
||
ip: ip || req.headers['x-forwarded-for'] || 'unknown',
|
||
userId: userId || null,
|
||
userAgent: req.headers['user-agent']?.slice(0, 100) || ''
|
||
}
|
||
|
||
if (statusCode >= 400) {
|
||
writeLog('ERROR', `${method} ${url} ${statusCode} ${duration}ms`, meta)
|
||
} else if (duration > 1000) {
|
||
writeLog('WARN', `Slow request: ${method} ${url} ${duration}ms`, meta)
|
||
} else {
|
||
writeLog('INFO', `${method} ${url} ${statusCode} ${duration}ms`, meta)
|
||
}
|
||
})
|
||
|
||
next()
|
||
}
|
||
|
||
/** 错误日志 */
|
||
export function logError(message: string, error: Error | any, meta?: Record<string, any>) {
|
||
writeLog('ERROR', message, {
|
||
...meta,
|
||
error: error?.message || String(error),
|
||
stack: error?.stack?.slice(0, 500) || ''
|
||
})
|
||
}
|
||
|
||
/** 业务日志 */
|
||
export function logInfo(message: string, meta?: Record<string, any>) {
|
||
writeLog('INFO', message, meta)
|
||
}
|
||
|
||
export default { requestLogger, logError, logInfo }
|