同一次请求,哪些字段应跨越三条信号?

一次订单请求带有路由模板 /orders/{id}、方法 GET,也可能接触认证头和 SQL 绑定值。关联日志与指标需要的是 Trace ID 和 Span ID,不必让令牌或原始参数进入属性,更不该把每个订单 ID 当成 histogram 维度。本篇把范围限定为一个受控线程、一个请求 Scope、一个 Logback appender 和进程内三种数据读取;测试既证明安全的记录方式,也构造一个 View 过滤后仍泄漏 exemplar 的反例。

位置 允许字段 控制措施 验证对象
Span route=/orders/{id}、http.request.method=GET 业务侧只赋允许键;SDK 上限设为 2 内存 SpanData
Logs 固定消息、logback.mdc.route Logback 桥接只捕获 route;SDK 上限设为 1 内存 LogRecordData
Metrics histogram 聚合 route 模板 测量输入只传允许字段,View 再保留 route reader 的点及 exemplar

源码里字段和关联 ID 走不同通道

Java SDK 固定 1.31.0,完整 SHA c25c0a0ee0da01ab2f74ba83052d1c249ed57020;Logback instrumentation 固定完整 SHA 97d87f3f3b2bc61aa396d71a98387a1e857618af。以下为静态源码/测试阅读,实验执行结果见下一节。

SpanLimitsBuilder.setMaxNumberOfAttributes 只能限制数量;上游 SdkSpanTest.droppingAttributes 断言超过限额被丢弃,并未按字段名脱敏。LogLimits 也限制数量/字符串长度;上游 LogLimitsTest 检查默认上限为 128 和显式配置。先写入的敏感字段未必被数量限制拦下;属性限制不是内容过滤器。

Logback appender 的 setCaptureMdcAttributes 接受键名单。实际 LoggingEventMapper.captureMdcAttributes 在名单为 * 时遍历所有 MDC 项;白名单则只提指定键,上游 LoggingEventMapperTest.testSome 核对键前缀 logback.mdc.。日志的 SpanContext 来自 mapper 设置的当前 Context,不靠复制 MDC 的字符串。SDK 还提供 LogRecordProcessor.onEmit 扩展点;本实验直接使用 Simple processor,没有声称它能自动检测凭据,也没有实现后置脱敏器。

Metrics ViewBuilder.setAttributeFilter 能过滤点的属性,但 ReservoirCell.filtered 从原始测量属性减去聚合点的键,构造 exemplar 的 filteredAttributes。上游 FilteredExemplarReservoirTest 覆盖 sample filter,而不是承诺 View 清理 exemplar。因此必须在调用 record() 前控制输入。

一个 Scope,三个数据对象

Lab20Test 在 sampled Span Scope 中调用 logger.info("order accepted") 和同步 histogram record(30, route)。Span 使用 SimpleSpanProcessor 与内存 exporter,Logs 使用 Logback 桥接、Simple processor 和内存 exporter,Metrics 使用进程内 reader。记录的 Span 与 LogRecord 的 Trace ID、Span ID 相等,histogram 唯一 exemplar 的关联 ID 也与该 Span 相等;断言 route 模板保留,模拟 authorization、db.bind 不进入三类观测对象。17 篇没有导出 Span,本篇增加的是进程内 Span exporter,不是网络 Trace 接收。

在 examples/opentelemetry-java/ 运行(完整 JDK 21):

1
JAVA_HOME=/tmp/otel-20260930/jdk-extract/usr/lib/jvm/java-21-openjdk-amd64 ./mvnw -q -pl sdk-labs -Dtest=Lab20Test test

退出码 0,原始摘要:

1
2
LAB20 span=1,log=1,metric=count:1,exemplar=1,idsMatch=true,allowedRoute=/orders/{id},sensitiveAbsent=true
LAB20 counterexample=pointFiltered,exemplarStillHasSyntheticSecret

第二个测试故意将合成 secret 放入测量属性:聚合点 authorization 消失,但 exemplar 的 filteredAttributes 仍有该值。它没有网络输出,却足以否定“View 白名单就是脱敏”的判断。第一个测试的合成敏感值曾存在该线程 MDC;这里只验证 OTel appender 的导出对象,不验证别的日志 appender、异步转发、进程转储或整个服务不会泄漏。生产系统应在请求边界避免把敏感数据放入 MDC、消息正文、Span 或指标测量,统一审核所有 sink。

易错判断与练习

“设置属性上限即可避免泄漏”忽略字段内容和写入顺序;“指标点干净就表示 exemplar 干净”被上面的反例直接推翻。route 只能用模板,不能用包含具体订单编号的原始路径,否则每个编号可能创建新的指标时间序列。只在内存里看到关联 ID 仍不足以说明 Collector 接收、后端可查。

  1. 修改 Lab20Test 的反例,在 record() 前从输入属性移除 authorization,同时断言点和 exemplar 都不包含它;解释为何仅调整 View 的 filter 不够。
  2. 为实验设计一个允许字段表,分别列出 Span、日志正文/MDC、Metric 测量与 exemplar;指出如果将 setCaptureMdcAttributes("*") 引入第三方日志输出,本篇哪一条断言无法覆盖它。

参考资料:SDK v1.31.0 trace/logs/metrics、instrumentation Logback;版本、命令、输出和审校见 examples/opentelemetry-java/evidence/20/RUN.md。

导航:18 Logs SDK · 19 Logback 与 MDC · 当前篇:20 字段治理。