Skip to content

追踪系统 ​

可观测性对任何 LLM 网关都至关重要。GodeX 的追踪系统捕获整个请求生命周期中的每个请求、token 使用量事件、流式事件和错误,将它们持久化到 SQLite 用于离线分析。系统专为生产吞吐量设计:AsyncTraceRecorder 在队列中批量处理事件,并定期或在批量大小阈值达到时刷新,使热路径免于磁盘 I/O。当追踪被禁用时,NoopTraceRecorder 以零开销替代真实的记录器。

追踪系统通过 TraceRecordingContext 附加到 ResponsesContext,因此任何有权访问上下文的代码都可以发出追踪记录,而无需了解存储后端。

概览 ​

组件文件用途
TraceRecorderrecorder.ts:5-8核心接口(record、close)
AsyncTraceRecorderrecorder.ts:30-110基于队列的批量记录器
NoopTraceRecorderrecorder.ts:25-28追踪禁用时的零操作记录器
SQLiteTraceStoresqlite.ts:69-297具有四张表的 SQLite 存储
TraceRecordEventtypes.ts:72-76所有记录类型的联合类型
mapTraceRecordToRowrow-mapper.ts:16-98将事件转换为存储行
summarizePayloadpayload.ts:10-35SHA-256 哈希、字节数、可选 JSON 捕获
TraceRecordingContextcontext.ts:4-12附加到每个请求的上下文
createTraceServicestrace-services.ts:15-34基于配置的工厂函数

架构概览 ​

TraceRecorder 接口 ​

TraceRecorder 接口(recorder.ts:5-8)设计上保持最小化:

方法描述
record(event)将追踪事件入队等待持久化
close()刷新剩余事件并释放资源

AsyncTraceRecorder ​

生产环境记录器(recorder.ts:30-110)使用内存队列,具有两个刷新触发器:

配置选项 ​

选项类型描述
maxQueueSizenumber队列中的最大事件数;溢出时丢弃
batchSizenumber每次刷新的事件数量
flushIntervalMsnumber自动刷新的定时器间隔
storeTraceStoreWriter存储后端(通常是 SQLiteTraceStore)
loggerTraceRecorderLogger用于记录丢弃和错误警告
capturePayloadboolean是否存储完整的 JSON 负载
payloadMaxBytesnumber存储负载的字节限制

SQLiteTraceStore ​

SQLite 存储(sqlite.ts:69-297)在构造时自动迁移四张表:

数据库 Schema ​

批量插入包装在事务中(sqlite.ts:90-95)以确保原子性。在 request_id、response_id、event_name 和 code 上创建索引以优化常见查询模式。

追踪记录类型 ​

TraceRecordEvent 联合类型(types.ts:72-76)有四种变体:

类型接口关键字段
requestTraceRequestRecordEventstream、requested_prompt_cache_key、payload
usageTraceUsageRecordEventusage(input_tokens、output_tokens、total_tokens、cached_tokens、reasoning_tokens、cache_hit_ratio)
eventTraceEventRecordEventevent_name、sequence、payload
errorTraceErrorRecordEventevent_name、error_type、domain、code、message、status、payload

所有变体共享 TraceRecordBase(types.ts:19-25),包含 request_id、response_id、provider、model 和 created_at。

事件名称 ​

TraceEventRecordEvent 将 event_name 限制为(types.ts:50-56):

事件名称记录时机
provider.request.prepared最终 provider 请求已经完成 patch,即将进入 provider client;该事件不存储请求体
provider.response.body从上游接收的同步响应体
upstream.stream.event.raw来自上游的原始 SSE 数据块
upstream.stream.event.transformed桥接层转换后的事件
web_search.request托管网络搜索请求负载
web_search.response托管网络搜索响应负载

Provider 请求 payload 存储在 trace_requests,不会在 trace_events 中重复保存。把 request 行和 provider.request.prepared 事件关联起来,就能同时看到最终 patched 请求摘要以及进入 provider client 前的生命周期点。prepared 事件在 provider patchRequest hook 执行之后、request() 或 stream() 调用 provider client 的 HTTP 操作之前记录;它不表示网络发送一定已经成功。

负载捕获 ​

summarizePayload(payload.ts:10-35)控制存储多少数据:

模式capturePayloadpayload_jsonpayload_hash
仅摘要falsenull完整 JSON 的 SHA-256 十六进制值
完整捕获true完整 JSON 字符串(不超过 payloadMaxBytes)SHA-256 十六进制值
截断捕获true截断的 JSON 字符串SHA-256 十六进制值

payload_bytes 字段始终记录原始字节长度,无论是否截断(payload.ts:27)。哈希使用 Bun.CryptoHasher("sha256") 计算(payload.ts:6-8)。

行映射 ​

mapTraceRecordToRow(row-mapper.ts:16-98)根据 event.kind 分派:

类型目标表负载处理
requesttrace_requests通过 summarizePayload 摘要
usagetrace_usage直接从 TraceUsageSnapshot 提取字段
eventtrace_events通过 summarizePayload 摘要
errortrace_errors通过 summarizePayload 摘要

如果任何事件的序列化失败,映射器返回 null 并记录警告,而不是导致刷新崩溃(row-mapper.ts:91-97)。

记录辅助函数 ​

四个辅助函数附加到 TraceRecordingContext,提供便捷的记录方式:

recordTraceRequest ​

request-recorder.ts:4-22 在 provider hooks 执行之后记录最终 patched provider 请求,包括是否为流式请求、可选的 prompt_cache_key,以及 provider 请求体的可选 payload 摘要。配套的 provider.request.prepared trace event 只标记生命周期点,不再次携带请求体。

recordTraceUsage ​

usage-recorder.ts:6-22 通过 traceUsageFromResponseUsage(usage.ts:4-23)将 ResponseUsage 转换为 TraceUsageSnapshot,同时在两者都可用时计算 cache_hit_ratio 为 cached_tokens / input_tokens。

recordTraceEvent ​

event-recorder.ts:7-25 记录一个命名事件,带有可选的负载和用于流内排序的序列号。

recordTraceError ​

error-recorder.ts:5-27 从 GodeXError 或通用错误中提取错误元数据(类型、领域、错误码、消息、状态码),并以完整的错误上下文作为负载进行记录。

服务装配 ​

createTraceServices(trace-services.ts:15-34)读取 TraceConfig 并创建由 SQLiteTraceStore 支持的 AsyncTraceRecorder(当 config.enabled 为 true 时)或 NoopTraceRecorder(当为 false 时):

交叉引用 ​

  • 会话存储 -- 会话存储系统使用类似的 SQLite 持久化模式
  • ProviderSpec 契约 -- 追踪记录中的 provider 和 model 字段来自解析后的 spec

参考文献 ​