阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 日志系统的集成与配置

日志系统的集成与配置

作者:陈川 阅读数:25494人阅读 分类: Node.js

日志系统是后端开发中不可或缺的组件,尤其在Express框架中,合理的日志配置能帮助开发者快速定位问题、分析请求行为或监控系统状态。从基础的控制台输出到文件存储,再到第三方服务的集成,日志系统的灵活性和扩展性直接影响开发效率。

日志中间件的选择与基础配置

Express生态中有多种日志中间件可供选择,最常用的是morganwinston的组合。morgan专注于HTTP请求日志,而winston提供了更通用的日志功能。安装基础依赖:

npm install morgan winston

基础配置示例:

const express = require('express');
const morgan = require('morgan');
const winston = require('winston');

const app = express();

// Morgan配置
app.use(morgan('combined'));  // 使用预定义格式

// Winston基础配置
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.json(),
  transports: [
    new winston.transports.Console(),
    new winston.transports.File({ filename: 'error.log', level: 'error' })
  ]
});

app.get('/', (req, res) => {
  logger.info('Homepage accessed');
  res.send('Hello World');
});

日志格式自定义

实际项目中往往需要定制日志格式。morgan支持自定义token和格式字符串:

// 自定义morgan token
morgan.token('request-id', req => req.headers['x-request-id'] || 'none');

app.use(morgan(':request-id :method :url :status :response-time ms'));

对于winston,可以通过组合多种format实现复杂格式:

const { combine, timestamp, label, printf } = winston.format;

const myFormat = printf(({ level, message, label, timestamp }) => {
  return `${timestamp} [${label}] ${level}: ${message}`;
});

logger.add(new winston.transports.Console({
  format: combine(
    label({ label: 'API' }),
    timestamp(),
    myFormat
  )
}));

多环境日志策略

开发环境和生产环境通常需要不同的日志策略。通过环境变量实现配置切换:

const isProduction = process.env.NODE_ENV === 'production';

if (isProduction) {
  logger.add(new winston.transports.File({ 
    filename: 'combined.log',
    format: winston.format.combine(
      winston.format.timestamp(),
      winston.format.json()
    )
  }));
} else {
  logger.add(new winston.transports.Console({
    format: winston.format.combine(
      winston.format.colorize(),
      winston.format.simple()
    )
  }));
}

错误日志的特殊处理

Express中的错误需要特殊捕获和记录。创建错误处理中间件:

app.use((err, req, res, next) => {
  logger.error({
    message: err.message,
    stack: err.stack,
    path: req.path,
    method: req.method
  });
  
  res.status(500).json({ error: 'Internal Server Error' });
});

对于未捕获的异常和Promise rejection,需要全局处理:

process.on('uncaughtException', (error) => {
  logger.error('Uncaught Exception:', error);
  process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
  logger.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

日志文件轮转与压缩

生产环境中日志文件需要自动轮转。使用winston-daily-rotate-file

const DailyRotateFile = require('winston-daily-rotate-file');

logger.add(new DailyRotateFile({
  filename: 'application-%DATE%.log',
  datePattern: 'YYYY-MM-DD',
  zippedArchive: true,
  maxSize: '20m',
  maxFiles: '14d'
}));

结构化日志与云服务集成

现代日志系统通常需要结构化数据以便分析。配置JSON格式并集成ELK等系统:

const { ElasticsearchTransport } = require('winston-elasticsearch');

const esTransport = new ElasticsearchTransport({
  level: 'info',
  clientOpts: { node: 'http://localhost:9200' }
});

logger.add(esTransport);

// 结构化日志示例
logger.info('User login', {
  userId: 12345,
  ip: '192.168.1.1',
  userAgent: req.headers['user-agent']
});

性能优化与日志采样

高流量场景下需要控制日志量,可采用采样策略:

app.use(morgan('combined', {
  skip: (req, res) => res.statusCode < 400,  // 只记录错误请求
  stream: {
    write: (message) => {
      if (Math.random() < 0.1) {  // 10%采样率
        logger.info(message.trim());
      }
    }
  }
}));

敏感信息过滤

日志中需要过滤密码等敏感信息。创建morgan的skip函数:

const sensitiveFields = ['password', 'creditCard'];

app.use(morgan((tokens, req, res) => {
  let logData = {
    method: tokens.method(req, res),
    url: maskSensitiveData(tokens.url(req, res)),
    status: tokens.status(req, res)
  };
  return JSON.stringify(logData);
}));

function maskSensitiveData(url) {
  const urlObj = new URL(url, 'http://dummy.com');
  sensitiveFields.forEach(field => {
    if (urlObj.searchParams.has(field)) {
      urlObj.searchParams.set(field, '******');
    }
  });
  return urlObj.pathname + urlObj.search;
}

请求/响应日志的详细记录

有时需要记录完整的请求和响应体,需注意内存消耗:

const expressWinston = require('express-winston');

app.use(expressWinston.logger({
  winstonInstance: logger,
  requestWhitelist: ['url', 'method', 'headers', 'body'],
  responseWhitelist: ['body', 'statusCode'],
  bodyBlacklist: ['password']
}));

日志与监控系统联动

将日志与APM系统如New Relic集成:

const newrelic = require('newrelic');

logger.add(new winston.transports.Console({
  format: winston.format.printf((info) => {
    newrelic.recordCustomEvent('NodeLog', {
      level: info.level,
      message: info.message
    });
    return info.message;
  })
}));

微服务场景下的分布式追踪

在微服务架构中,需要关联多个服务的日志。集成OpenTelemetry:

const { DiagConsoleLogger, DiagLogLevel, diag } = require('@opentelemetry/api');
const { WinstonInstrumentation } = require('@opentelemetry/instrumentation-winston');

diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);

const winstonInstrumentation = new WinstonInstrumentation({
  enabled: true,
  logHook: (span, record) => {
    record['traceId'] = span.spanContext().traceId;
  }
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌