深入 OpenTelemetry 18:Logs SDK 从 emit 到批量导出
调用 emit 后,日志已经写出去了吗?
logRecordBuilder().emit() 只把一条记录交给本地 processor;它既不保证已被导出,也不负责把 Logback 的普通日志自动接进来。为了分开观察“产生记录”和“处理队列”,实验设置了极长的批处理调度间隔,发出一条日志,再显式 flush。固定的事件时间是 epoch 90 秒,观察时间由测试时钟提供,为 100 秒。
| 阶段 | 本篇对象 | 可以判定的事实 | 不代表 |
|---|---|---|---|
| API 调用 | Logger / LogRecordBuilder |
body、severity、事件时间被设置 | 已导出 |
| processor | 队列、工作线程 | 已排队,可能因队列满被丢弃 | 已接收 |
| exporter | InMemoryLogRecordExporter |
内存列表里可读到 LogRecordData |
Collector 或后端已入库 |
从 SDK 源码看对象何时定型
以下为 opentelemetry-java v1.31.0 固定 SHA c25c0a0ee0da01ab2f74ba83052d1c249ed57020 的静态源码阅读;运行结论另见下节。服务创建 SdkLoggerProvider,合成 processors 与共享状态。若一个 processor 也不注册,loggerBuilder 走 noop 路径;“有 provider”并不自动得到导出链。
SdkLogger.logRecordBuilder 为每条日志生成 builder,携带 instrumentation scope。SdkLogRecordBuilder.emit 先检查是否 shutdown,然后以显式 Context 或调用线程当前 Context 构造记录;没有显式 observed timestamp 时使用 provider 时钟,未设置的事件 timestamp 保留为 0。记录里的 SpanContext 来自选定 Context,不会凭日志文本推导。上游 SdkLogRecordBuilderTest.emit_AllFields 与 emit_NoFields 分别核对显式与默认字段。
BatchLogRecordProcessor.onEmit 交给有界队列;满时 offer 失败并计入 dropped,不阻塞请求线程。其 worker 在异步线程将 ReadWriteLogRecord 转成 LogRecordData;SdkReadWriteLogRecord.toLogRecordData 复制属性快照。默认队列 2048、批量 512、间隔 1 秒、exporter 超时 30 秒,来自 BatchLogRecordProcessorBuilder,不是本次实验的设置。上游 BatchLogRecordProcessorTest 还覆盖批量、队列溢出、导出异常与超时;这些失败条件不由本篇的一条记录模拟。
用同步屏障验证处理结果
Lab18Test 固定 resource service.name=checkout,记录 body order rejected、WARN、事件时间 90s、观察时间 100s,并将 schedule delay 改为一天。调用 forceFlush().join(5, SECONDS) 之后读取内存 exporter,逐字段断言数据,且检查恰有一条、scope 为 checkout.logs、SpanContext 无效。在 examples/opentelemetry-java/ 运行:
1 | |
退出码 0,原始摘要:
1 | |
这里 forceFlush() 请求 worker 排空队列,flush 执行 export 后完成结果;但 exportCurrentBatch 即使 exporter 失败也会清空 batch,故 flush 返回成功不能泛化为远端可靠持久化。关闭 provider 会关闭 exporter,内存 exporter 会清空列表;测试在关闭前检查数据,关闭后仅检查新的 emit 不会再被处理。本例没触发超时、满队列或网络失败。
易错判断与练习
“配置了 SDK 就自动接管 Logback”是错误的:本篇手动通过 OTel Logs API emit;普通日志框架尚未桥接。“时间戳一样就说明两个时钟同步”也不成立:这里明确区分事件时间与 SDK 观察时间,二者由测试人为固定,生产环境可能有延迟或偏差。
- 修改
Lab18Test,移除setTimestamp(90, SECONDS),断言事件时间变成 0,观察时间仍为 100s;再解释这两个字段在上游默认测试中的含义。 - 若批处理队列满或 exporter 抛异常,
emit()与forceFlush()各能保证什么?从BatchLogRecordProcessor的addLog与exportCurrentBatch找出反例,不要把本篇的单条内存实验当成网络丢失率测试。
参考资料:Java SDK v1.31.0 Logs 源码与测试;实验、版本与审校记录见 examples/opentelemetry-java/evidence/18/RUN.md。
导航:17 Exemplar · 当前篇:18 Logs SDK。
