Skip to content

Node系列 · Express:log4js 日志记录

Express 项目接 log4js 的最小可用配置——HTTP 请求自动日志、按环境差异化(开发控制台 / 生产文件)、错误单独归档。

一、安装与最简配置

bash
npm install log4js
javascript
const log4js = require('log4js');

log4js.configure({
  appenders: { console: { type: 'console' } },
  categories: { default: { appenders: ['console'], level: 'debug' } },
});

const logger = log4js.getLogger();

logger.info('Express 服务启动');

二、HTTP 请求日志:connectLogger

javascript
const log4js = require('log4js');

log4js.configure({
  appenders: {
    httpAccess: {
      type: 'console',
      layout: { type: 'colored' },
    },
  },
  categories: {
    http: { appenders: ['httpAccess'], level: 'info' },
    default: { appenders: ['httpAccess'], level: 'info' },
  },
});

app.use(log4js.connectLogger(log4js.getLogger('http'), {
  level: 'auto',                    // 5xx → error;4xx → warn;其他 → info
}));

输出示例:

[INFO] http - 127.0.0.1 - GET /api/users 200 42 - 12 ms
[ERROR] http - 127.0.0.1 - POST /api/users 500 89 - 234 ms
level行为
auto自动按 HTTP 状态码分级
info全部 info
warn仅 warn / error

三、生产环境完整配置

javascript
const path = require('path');
const log4js = require('log4js');

const isDev = process.env.NODE_ENV !== 'production';
const LOG_DIR = path.resolve(__dirname, '../logs');

log4js.configure({
  appenders: {
    // 开发:控制台彩色
    devConsole: {
      type: 'console',
      layout: { type: 'colored' },
    },

    // 生产:按日期切分全量日志
    prodAll: {
      type: 'dateFile',
      filename: path.join(LOG_DIR, 'app.log'),
      pattern: 'yyyy-MM-dd',
      compress: true,
      keepFileExt: true,
    },

    // 生产:错误单独归档(按大小轮转)
    prodError: {
      type: 'logLevelFilter',
      level: 'error',
      appender: {
        type: 'file',
        filename: path.join(LOG_DIR, 'error.log'),
        maxLogSize: 10 * 1024 * 1024,   // 10MB
        backups: 5,                       // 保留 5 个备份
      },
    },
  },
  categories: {
    default: {
      appenders: isDev ? ['devConsole'] : ['prodAll', 'prodError'],
      level: isDev ? 'debug' : 'info',
    },
    http: {
      appenders: isDev ? ['devConsole'] : ['prodAll'],
      level: 'info',
    },
    db: {
      appenders: isDev ? ['devConsole'] : ['prodAll'],
      level: 'warn',
    },
    auth: {
      appenders: isDev ? ['devConsole'] : ['prodAll', 'prodError'],
      level: 'info',
    },
  },
});

module.exports = log4js;

四、按模块用不同 logger

javascript
const log4js = require('./config/log4js');

const httpLogger = log4js.getLogger('http');
const dbLogger = log4js.getLogger('db');
const authLogger = log4js.getLogger('auth');

httpLogger.info('GET /api/users 200');
dbLogger.warn('慢查询:耗时 1200ms');
authLogger.info('用户登录', { userId: 1, ip: req.ip });

不同 category 写到不同文件 / 不同级别,方便按模块排查问题。

五、错误日志单独归档

生产环境建议错误单独写一个文件,方便快速排查:

javascript
// appenders.prodError 的 level: 'error' 表示只接收 error 及以上级别
// 普通 info / warn 不会写到 error.log,只在 app.log
logger.error('数据库连接失败', err);   // 写到 app.log + error.log
logger.info('用户登录成功');           // 只写到 app.log

六、应用退出时优雅关闭

javascript
// 应用退出前刷写所有缓冲日志
process.on('SIGTERM', () => {
  log4js.shutdown(() => {
    console.log('日志系统已关闭');
    process.exit(0);
  });
});

不调 shutdown() 可能丢失最后几条日志——尤其用了 fileSync / 异步 appender 时。

七、自定义日志格式

javascript
log4js.configure({
  appenders: {
    custom: {
      type: 'console',
      layout: {
        type: 'pattern',
        pattern: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%p] %c - %m%n',
      },
    },
  },
  categories: { default: { appenders: ['custom'], level: 'info' } },
});
占位符含义
%d时间戳
%p级别(INFO / ERROR)
%c分类
%m消息
%n换行
%x自定义上下文(如请求 ID)

八、最佳实践

场景推荐
开发控制台彩色 + level: 'debug'
生产文件按日期切分 + 错误单独 + level: 'info'
HTTP 请求connectLogger + level auto
多模块按 category 拆分(http / db / auth
敏感字段永远不记密码、token、身份证号
应用退出log4js.shutdown() 刷写缓冲
日志归档dateFile + compress: true 自动管理

九、小结

  • Express 接 log4js:configure() + app.use(connectLogger())
  • 按环境分:开发控制台 + debug;生产文件 + info
  • 错误单独归档:logLevelFilter + level: 'error'
  • 按模块 category 拆分日志(http / db / auth
  • 应用退出 log4js.shutdown() 刷写缓冲
  • 敏感字段(密码 / token)永远不记日志