调用 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
JAVA_HOME=/tmp/otel-20260930/jdk-extract/usr/lib/jvm/java-21-openjdk-amd64 ./mvnw -q -pl sdk-labs -Dtest=Lab18Test test

退出码 0,原始摘要:

1
2
LAB18 batch=1,body=order rejected,severity=WARN,event=90s,observed=100s,resource=checkout,scope=checkout.logs
LAB18 noProcessor=flushSuccess,noExporterConfigured

这里 forceFlush() 请求 worker 排空队列,flush 执行 export 后完成结果;但 exportCurrentBatch 即使 exporter 失败也会清空 batch,故 flush 返回成功不能泛化为远端可靠持久化。关闭 provider 会关闭 exporter,内存 exporter 会清空列表;测试在关闭前检查数据,关闭后仅检查新的 emit 不会再被处理。本例没触发超时、满队列或网络失败。

易错判断与练习

“配置了 SDK 就自动接管 Logback”是错误的:本篇手动通过 OTel Logs API emit;普通日志框架尚未桥接。“时间戳一样就说明两个时钟同步”也不成立:这里明确区分事件时间与 SDK 观察时间,二者由测试人为固定,生产环境可能有延迟或偏差。

  1. 修改 Lab18Test,移除 setTimestamp(90, SECONDS),断言事件时间变成 0,观察时间仍为 100s;再解释这两个字段在上游默认测试中的含义。
  2. 若批处理队列满或 exporter 抛异常,emit() 与 forceFlush() 各能保证什么?从 BatchLogRecordProcessor 的 addLog 与 exportCurrentBatch 找出反例,不要把本篇的单条内存实验当成网络丢失率测试。

参考资料:Java SDK v1.31.0 Logs 源码与测试;实验、版本与审校记录见 examples/opentelemetry-java/evidence/18/RUN.md。

导航:17 Exemplar · 当前篇:18 Logs SDK。