Log-operation.md 3.5 KB

Promtail JSON 采集方案 (v1.3)

零侵入 · 防爆炸 · 生产级

适用:Kubernetes 1.20+ / 裸机 / 容器混合场景

目标:业务输出单行 JSON,采集层(Promtail / Vector)负责解析、采样、标签与告警,尽量降低业务代码改动与系统资源消耗。


目录


快速概述

  • 目标:统一单行 JSON 输出,由采集层负责所有解析与降噪。
  • 核心设计:尽早低成本过滤 → 保留低基数标签 → 基于哈希的采样 → 针对异常/慢调用/审计分支保留。
  • 可选优化:在高吞吐场景使用 Vector 替代 Promtail 做解析以节省 CPU/内存。

日志格式

建议业务输出单行 JSON(最小必需字段):

{
  "ts":"2026-01-23T14:23:45.123Z",
  "level":"INFO|WARN|ERROR",
  "logger":"c.x.Foo",
  "msg":"...",
  "traceId":"a1b2c3",
  "uri":"/api/order",
  "duration":120,
  "userId":123456,
  "event":"order_create|login|APP_START|security|slow_sql",
  "error":"NullPointerException: xxx"
}

说明:

  • 无异常:error=""
  • 慢调用:duration>500(ms)
  • 审计/安全/生命周期:通过 event 字段区分

注意:不要将高基数字段(如 userIdorderId)作为标签。


开发接入(低侵入)

  • logback 配置与 common-logging 模块统一下发,业务只需引入一次依赖并使用简单 wrapper(如 AppLogger.info(...))。
  • 在网关/Filter/拦截器中统一设置 MDC 字段:traceIduristartTimeuserId,请求结束时写入 duration 并清理。
  • 推荐接入 OpenTelemetry 自动注入 traceId,减少手动传递。

示例:Async JSON appender(logback)

<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
  <encoder class="net.logstash.logback.encoder.LogstashEncoder">
    <includeContext>true</includeContext>
    <provider class="net.logstash.logback.composite.loggingevent.LoggingEventPatternJsonProvider">
      <pattern>{"ts":"%d{yyyy-MM-dd'T'HH:mm:ss.SSS'Z'}","level":"%level","logger":"%logger","msg":"%msg","traceId":"%X{traceId:-}","uri":"%X{uri:-}","duration":"%X{duration:-0}","userId":"%X{userId:-}","event":"%X{event:-}","error":"%X{error:-}"}</pattern>
    </provider>
  </encoder>
</appender>

<appender name="ASYNC_JSON" class="ch.qos.logback.classic.AsyncAppender">
  <appender-ref ref="JSON" />
</appender>

<root level="INFO"><appender-ref ref="ASYNC_JSON"/></root>

Servlet Filter(伪码)

public class LoggingFilter implements Filter {
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
    String traceId = extractOrGenerate(req);
    MDC.put("traceId", traceId);
    MDC.put("uri", ((HttpServletRequest)req).getRequestURI());
    long start = System.currentTimeMillis();
    try { chain.doFilter(req, res); }
    finally {
      MDC.put("duration", String.valueOf(System.currentTimeMillis() - start));
      MDC.remove("traceId"); MDC.remove("uri"); MDC.remove("duration");
    }
  }
}