零度AI
高级15 分钟阅读

Claude Code 日志管理

使用 Claude Code 实现统一日志收集、分析与追踪,ELK Stack 实战

Claude Code日志ELKLogstash监控

日志管理概述

日志是排查问题和监控系统运行状态的关键。Claude Code 可以帮你快速搭建完整的日志系统。

日志级别

常见的日志级别:DEBUG(调试)、INFO(信息)、WARN(警告)、ERROR(错误)、FATAL(致命)。

日志工具封装

Winston 日志库

bash
帮我创建一个统一日志模块,使用 Winston,包含:
- 日志分级输出
- 文件轮转
- 控制台彩色输出
- 自定义格式
javascript
// lib/logger/index.js
import winston from "winston";
import path from "path";
import { fileURLToPath } from "url";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

// 日志格式
const logFormat = winston.format.combine(
  winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
  winston.format.errors({ stack: true }),
  winston.format.printf(({ level, message, timestamp, stack, ...meta }) => {
    let msg = `${timestamp} [${level.toUpperCase()}]: ${message}`;

    if (Object.keys(meta).length > 0) {
      msg += ` ${JSON.stringify(meta)}`;
    }

    if (stack) {
      msg += `\n${stack}`;
    }

    return msg;
  })
);

// 控制台格式(带颜色)
const consoleFormat = winston.format.combine(
  winston.format.colorize(),
  winston.format.timestamp({ format: "HH:mm:ss" }),
  winston.format.printf(({ level, message, timestamp }) => {
    return `${timestamp} ${level}: ${message}`;
  })
);

// 创建 logger 实例
const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || "info",
  format: logFormat,
  transports: [
    // 控制台输出
    new winston.transports.Console({
      format: consoleFormat,
    }),

    // 错误日志文件
    new winston.transports.File({
      filename: path.join(__dirname, "../../logs/error.log"),
      level: "error",
      maxsize: 10 * 1024 * 1024, // 10MB
      maxFiles: 5,
    }),

    // 所有日志文件
    new winston.transports.File({
      filename: path.join(__dirname, "../../logs/combined.log"),
      maxsize: 10 * 1024 * 1024, // 10MB
      maxFiles: 5,
    }),

    // JSON 格式日志(供 ELK 使用)
    new winston.transports.File({
      filename: path.join(__dirname, "../../logs/json.log"),
      format: winston.format.combine(
        winston.format.timestamp(),
        winston.format.json()
      ),
      maxsize: 10 * 1024 * 1024,
      maxFiles: 5,
    }),
  ],
});

// 添加入参方法
logger.logWithMeta = (level, message, meta = {}) => {
  logger.log(level, message, { meta });
};

export default logger;

日志中间件

javascript
// lib/middleware/logger.js
import logger from "../logger/index.js";

export function requestLogger(req, res, next) {
  const start = Date.now();

  // 请求完成后的日志
  res.on("finish", () => {
    const duration = Date.now() - start;
    const logData = {
      method: req.method,
      url: req.originalUrl,
      status: res.statusCode,
      duration: `${duration}ms`,
      ip: req.ip,
      userAgent: req.get("user-agent"),
    };

    if (res.statusCode >= 400) {
      logger.warn("Request completed with error", logData);
    } else {
      logger.info("Request completed", logData);
    }
  });

  next();
}

// 错误日志中间件
export function errorLogger(err, req, res, next) {
  logger.error("Unhandled error", {
    message: err.message,
    stack: err.stack,
    url: req.originalUrl,
    method: req.method,
    body: req.body,
  });

  next(err);
}

日志收集系统

ELK Stack 集成

bash
帮我创建一个 Logstash 配置文件,用于收集应用日志。
conf
# logstash/app.conf
input {
  # 从文件读取日志
  file {
    path => "/var/log/myapp/*.log"
    start_position => "beginning"
    sincedb_path => "/dev/null"
    codec => json
  }

  # 从 TCP 端口接收日志
  tcp {
    port => 5000
    codec => json_lines
  }
}

filter {
  # 解析 JSON 日志
  if [message] =~ /^\{/ {
    json {
      source => "message"
      target => "parsed"
    }
  }

  # 添加时间戳
  date {
    match => ["[timestamp]", "ISO8601"]
    target => "@timestamp"
  }

  # 添加来源标记
  mutate {
    add_field => {
      "application" => "myapp"
      "environment" => "production"
    }
  }

  # 过滤敏感信息
  mutate {
    gsub => [
      "[password]", ".", "*",
      "[token]", ".", "*",
      "[api_key]", ".", "*"
    ]
  }
}

output {
  # 输出到 Elasticsearch
  elasticsearch {
    hosts => ["http://elasticsearch:9200"]
    index => "app-logs-%{+YYYY.MM.dd}"
  }

  # 调试输出
  stdout {
    codec => rubydebug
  }
}

Docker 日志收集

yaml
# docker-compose.yml (部分)
services:
  app:
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  logstash:
    image: logstash:8.12.0
    volumes:
      - ./logstash/app.conf:/usr/share/logstash/pipeline/app.conf
      - app_logs:/var/log/myapp

  elasticsearch:
    image: elasticsearch:8.12.0
    environment:
      - discovery.type=single-node
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    volumes:
      - es_data:/usr/share/elasticsearch/data

  kibana:
    image: kibana:8.12.0
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200

volumes:
  app_logs:
  es_data:

日志分析

Kibana 查询示例

bash
# 错误日志统计
# 统计每分钟的错误数量
GET app-logs-*/_search
{
  "size": 0,
  "query": {
    "bool": {
      "must": [
        { "match": { "level": "ERROR" } }
      ]
    }
  },
  "aggs": {
    "errors_over_time": {
      "date_histogram": {
        "field": "@timestamp",
        "fixed_interval": "1m"
      }
    }
  }
}
bash
# 慢请求分析
GET app-logs-*/_search
{
  "size": 10,
  "sort": [{ "duration": "desc" }],
  "query": {
    "range": {
      "duration": { "gt": 1000 }
    }
  }
}

结构化日志

日志追踪 ID

javascript
// lib/logger/traced.js
import logger from "./index.js";
import { randomUUID } from "crypto";

// 为每个请求生成唯一追踪 ID
export function generateTraceId() {
  return randomUUID();
}

// 添加追踪上下文的日志函数
export function tracedLog(traceId) {
  return {
    info: (message, meta = {}) => {
      logger.info(message, { traceId, ...meta });
    },
    warn: (message, meta = {}) => {
      logger.warn(message, { traceId, ...meta });
    },
    error: (message, meta = {}) => {
      logger.error(message, { traceId, ...meta });
    },
    debug: (message, meta = {}) => {
      logger.debug(message, { traceId, ...meta });
    },
  };
}

// Express 中间件
export function traceMiddleware(req, res, next) {
  const traceId = req.headers["x-trace-id"] || generateTraceId();
  req.traceId = traceId;
  res.setHeader("X-Trace-ID", traceId);

  // 将 traceId 传递到子任务
  req.log = tracedLog(traceId);

  next();
}

使用示例

javascript
// routes/user.js
import express from "express";
const router = express.Router();

router.post("/users", async (req, res) => {
  req.log.info("Creating new user", { email: req.body.email });

  try {
    const user = await userService.create(req.body);
    req.log.info("User created successfully", { userId: user.id });
    res.json(user);
  } catch (error) {
    req.log.error("Failed to create user", { error: error.message });
    res.status(500).json({ error: "创建用户失败" });
  }
});

export default router;

日志告警

错误率告警

bash
# Kibana 告警规则
# 当 5 分钟内错误数超过 10 时触发告警
PUT _watcher/watch/error_rate_alert
{
  "trigger": {
    "schedule": { "interval": "5m" }
  },
  "input": {
    "search": {
      "request": {
        "indices": ["app-logs-*"],
        "body": {
          "query": {
            "bool": {
              "must": [
                { "match": { "level": "ERROR" } },
                { "range": { "@timestamp": { "gte": "now-5m" } } }
              ]
            }
          }
        }
      }
    }
  },
  "condition": {
    "compare": {
      "ctx.payload.hits.total.value": {
        "gt": 10
      }
    }
  },
  "actions": {
    "notify_slack": {
      "slack": {
        "message": "错误率告警: 过去5分钟有 {{ctx.payload.hits.total.value}} 个错误"
      }
    }
  }
}

最佳实践

1. 日志内容规范

javascript
// 好的日志示例
logger.info("用户登录成功", {
  userId: user.id,
  email: user.email,
  ip: req.ip,
  userAgent: req.get("user-agent"),
  loginTime: new Date().toISOString(),
});

// 避免的日志
logger.info("用户登录"); // 信息不足
logger.info("用户登录成功", { password: user.password }); // 敏感信息

2. 性能注意事项

javascript
// 使用异步日志避免阻塞主线程
// Winston 默认是异步的

// 避免在日志中记录大对象
logger.info("请求处理完成", { bigArray: JSON.stringify(largeArray) }); // 不好

// 改用引用或摘要
logger.info("请求处理完成", {
  arrayLength: largeArray.length,
  totalSize: calculateSize(largeArray),
});

3. 日志轮转配置

javascript
// 合理的日志轮转策略
const logConfig = {
  errorLog: {
    maxSize: "10m",
    maxFiles: 30, // 保留 30 个文件,约 10 天
  },
  accessLog: {
    maxSize: "50m",
    maxFiles: 14, // 保留 14 个文件,约 2 周
  },
  jsonLog: {
    maxSize: "100m",
    maxFiles: 7, // 保留 7 个文件,约一周
  },
};

日志安全

确保日志中不包含密码、Token、身份证号等敏感信息。使用环境变量或脱敏工具处理。

总结

使用 Claude Code 实现完整的日志系统:

  • 搭建统一的日志框架(Winston/Pino)
  • 实现请求追踪和错误日志中间件
  • 配置 ELK Stack 进行日志收集和分析
  • 设置日志告警规则
  • 遵循日志最佳实践

合理的日志管理是排查生产环境问题的关键。