零侵入 · 防爆炸 · 生产级
适用:Kubernetes 1.20+ / 裸机 / 容器混合场景
目标:业务输出单行 JSON,采集层(Promtail / Vector)负责解析、采样、标签与告警,尽量降低业务代码改动与系统资源消耗。
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 字段区分注意:不要将高基数字段(如 userId、orderId)作为标签。
logback 配置与 common-logging 模块统一下发,业务只需引入一次依赖并使用简单 wrapper(如 AppLogger.info(...))。traceId、uri、startTime、userId,请求结束时写入 duration 并清理。示例: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");
}
}
}
Promtail 配置要点(原则):
level 快速 drop DEBUG/TRACE),避免复杂正则。level、namespace、event、exception_type)。duration 作为 numeric 字段供 unwrap/unpack 使用,不作为标签。(保留原方案“三段减压 + 哈希采样 + 异常/慢调用/审计分支”思路,详见 Log.md。)
| 阶段 | CPU | 内存 | 说明 |
|---|---|---|---|
| 原始全量 JSON 解析 | 150-200% | 400 MB | 无过滤 |
| 加三段减压阀后 | 30-40% | 120 MB | 同集群实测 |
| 换 Vector 解析 | 10-15% | 100 MB | Promtail 仅转发 |
测试提示:在 staging 用真实流量做 A/B 对比(72 小时)量化效果。
标签基数门禁:上线前运行基数扫描脚本,任一计划作为 label 的字段 24h 唯一值 > 5000 则阻断发布。
Loki 整流:
limits_config:
per_stream_rate_limit: 3MB
per_stream_rate_limit_burst: 5MB
ingestion_rate_mb: 10
ingestion_burst_size_mb: 20
超限将被丢弃并暴露 rate_limit_discarded_bytes 指标。
audit 流 365d,其余 7d,持久化到 S3/OSS。基数扫描示例(部署前/CI)
logcli query '{env="prod"}' --since=24h | jq -r '.[].event' | sort | uniq -c | awk '$1>5000{print $2, $1}'
保留原有:异常突增、慢调用 P99、审计事件下降。
补充监控:agent 解析错误与丢弃量
# agent parsing errors
sum(rate(promtail_parsing_errors_total[5m])) > 0
# Loki 丢弃流量
rate(loki_ingester_discarded_bytes_total[5m]) > 0
# Promtail/Vector 本地丢弃量(采样)
rate(promtail_dropped_bytes_total[5m]) > 0
每日
kubectl top pod -l app=promtail 单核 CPU <500m每周
rate(promtail_dropped_bytes_total[5m]) > 0(确认采样生效)每月
per_stream_rate_limit 丢弃量并调整采样策略新增:在 Helm/Chart CI 中加入基数检测脚本,发现高基数阻断发布并在 PR 中给出异常样例。
阶段:
parsing_errors、dropped_bytes、Loki ingress。回滚:通过 Git/Helm release 快速回退到上一个成功版本。
如果需要我可以把以下内容作为独立文件加入仓库:
LoggingFilter.java(完整示例)logback-spring.xml 模板如果希望我现在把示例文件写入仓库,请回复我需要的文件清单(例如:LoggingFilter.java, logback-spring.xml, ci/check_cardinality.sh)。