如何在Express中使用Winston进行日志记录,并配置按日分割文件存储

430 阅读2分钟

以下是 Winston 在 Express 中的详细使用方式,以及如何配置按日分割日志文件的步骤:


1. 安装依赖

npm install winston winston-daily-rotate-file express

2. 基础配置:创建 Winston Logger

在项目中新建 logger.js 文件,配置日志系统和文件分割规则:

const winston = require('winston');
const { combine, timestamp, printf, colorize } = winston.format;

// 自定义日志格式
const logFormat = printf(({ level, message, timestamp }) => {
  return `${timestamp} [${level}]: ${message}`;
});

// 创建 Logger 实例
const logger = winston.createLogger({
  level: 'info', // 设置日志级别(error, warn, info, verbose, debug, silly)
  format: combine(
    timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), // 添加时间戳
    logFormat // 应用自定义格式
  ),
  transports: [
    // 控制台输出(带颜色)
    new winston.transports.Console({
      format: combine(colorize(), logFormat)
    }),
    // 按日分割文件
    new winston.transports.DailyRotateFile({
      dirname: 'logs', // 日志目录
      filename: 'app-%DATE%.log', // 文件名(%DATE% 自动替换为日期)
      datePattern: 'YYYY-MM-DD', // 日期格式
      zippedArchive: true, // 压缩旧日志
      maxSize: '20m', // 单个文件最大大小
      maxFiles: '30d' // 保留最近30天的日志
    })
  ],
  // 异常处理(可选)
  exceptionHandlers: [
    new winston.transports.File({ filename: 'logs/exceptions.log' })
  ],
  // Promise拒绝处理(可选)
  rejectionHandlers: [
    new winston.transports.File({ filename: 'logs/rejections.log' })
  ]
});

module.exports = logger;

3. 在 Express 中集成 Winston

3.1 记录请求日志

app.js 中,添加中间件记录 HTTP 请求:

const express = require('express');
const logger = require('./logger'); // 导入配置好的 Logger

const app = express();

// 自定义请求日志中间件
app.use((req, res, next) => {
  const start = Date.now();

  res.on('finish', () => {
    const duration = Date.now() - start;
    logger.info(
      `${req.method} ${req.originalUrl} - ${res.statusCode} (${duration}ms)`
    );
  });

  next();
});

3.2 记录错误日志

在错误处理中间件中捕获异常:

// 错误处理中间件
app.use((err, req, res, next) => {
  logger.error(`Error: ${err.message}`, { stack: err.stack });
  res.status(500).send('Internal Server Error');
});

4. 使用示例

在路由或服务中直接调用 Logger:

// 示例路由
app.get('/api/data', (req, res) => {
  try {
    logger.debug('Fetching data from database...');
    // 业务逻辑
    res.json({ data: 'success' });
  } catch (err) {
    logger.error('Database error: %s', err.message);
    res.status(500).send('Error');
  }
});

5. 日志分割配置详解

  • dirname: 'logs'
    日志文件存储在项目根目录的 logs 文件夹中。
  • filename: 'app-%DATE%.log'
    每日生成的文件名格式(如 app-2023-10-01.log)。
  • datePattern: 'YYYY-MM-DD'
    按日分割,也支持按小时(YYYY-MM-DD-HH)。
  • zippedArchive: true
    旧日志自动压缩为 .gz 文件以节省空间。
  • maxSize: '20m'
    单个日志文件超过 20MB 时触发分割(即使未到日期)。
  • maxFiles: '30d'
    自动删除 30 天前的日志文件。

6. 日志文件结构

运行后生成的日志文件结构如下:

logs/
  app-2023-10-01.log     # 当日日志
  app-2023-09-30.log.gz  # 压缩的旧日志
  exceptions.log         # 异常日志
  rejections.log         # Promise拒绝日志

7. 高级配置(可选)

7.1 多级别日志分割

为不同级别(如 errorinfo)创建独立文件:

transports: [
  new winston.transports.DailyRotateFile({
    level: 'error', // 仅记录 error 级别
    filename: 'error-%DATE%.log',
    // 其他配置同上
  }),
  new winston.transports.DailyRotateFile({
    level: 'info', // 仅记录 info 级别
    filename: 'info-%DATE%.log',
    // 其他配置同上
  })
]

7.2 自定义日志格式

添加更多上下文信息(如请求 IP):

const logFormat = printf(({ level, message, timestamp, meta }) => {
  return `${timestamp} [${level}] ${meta?.ip || '-'} - ${message}`;
});

// 在中间件中传递元数据
logger.info('Request received', { ip: req.ip });

8. 注意事项

  • 日志级别管理
    通过 logger.level 动态调整日志详细程度(生产环境建议设为 infowarn)。
  • 性能优化
    高频日志(如每条请求)建议使用 winston 的批量写入或异步模式。
  • 敏感信息过滤
    避免在日志中记录密码、Token 等敏感数据。
  • 日志清理策略
    根据磁盘空间调整 maxFilesmaxSize

总结

通过 Winston 和 winston-daily-rotate-file,Express 项目可以实现灵活的日志管理,包括按日分割、多级别存储和自动化压缩清理。结合中间件集成,能够全面监控请求流程和异常状态,是生产环境日志系统的可靠选择。