logger.ts•1.81 kB
import winston from 'winston';
import path from 'path';
import fs from 'fs';
import config from '../config';
// 确保日志目录存在
if (!fs.existsSync(config.logDir)) {
fs.mkdirSync(config.logDir, { recursive: true });
}
/**
* 日志格式
*/
const logFormat = winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.printf(({ timestamp, level, message, stack }) => {
let log = `${timestamp} [${level.toUpperCase()}]: ${message}`;
if (stack) {
log += `\n${stack}`;
}
return log;
})
);
/**
* Logger实例
*/
export const logger = winston.createLogger({
level: config.logLevel,
format: logFormat,
transports: [
// 控制台输出
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
logFormat
),
}),
// 错误日志文件
new winston.transports.File({
filename: path.join(config.logDir, 'error.log'),
level: 'error',
maxsize: 5242880, // 5MB
maxFiles: 5,
}),
// 组合日志文件
new winston.transports.File({
filename: path.join(config.logDir, 'combined.log'),
maxsize: 5242880, // 5MB
maxFiles: 5,
}),
],
});
/**
* HTTP请求日志中间件
*/
export const httpLogger = (req: any, res: any, next: any) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
const log = `${req.method} ${req.originalUrl} ${res.statusCode} ${duration}ms`;
if (res.statusCode >= 500) {
logger.error(log);
} else if (res.statusCode >= 400) {
logger.warn(log);
} else {
logger.info(log);
}
});
next();
};
export default logger;