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) { 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) { writeLog('ERROR', message, { ...meta, error: error?.message || String(error), stack: error?.stack?.slice(0, 500) || '' }) } /** 业务日志 */ export function logInfo(message: string, meta?: Record) { writeLog('INFO', message, meta) } export default { requestLogger, logError, logInfo }