分析事件建模、归约与发送器
这一阶段是分析上报系统的核心,属于幕后支撑部分。它不直接干主任务,而是把系统里发生的事记清楚,再安全地发出去。facts.rs 和 events.rs 先规定“账单”格式:会话、报错、工具调用、权限请求都按统一样子记录。accepted_lines.rs 专门看代码改动,统计用户接受了多少行,同时用匿名指纹避免上传真实代码。reducer.rs 像记账员,把零散动作拼成完整事件。client.rs 负责排队和发送,尽量不拖慢程序。lib.rs 提供统一入口和小工具。app-server 与 goal 的适配文件,则把登录、配置和目标变化接到这套上报流程里。
分析领域模型
这些文件定义 analytics crate 表面,以及子系统其余部分所依赖的内部事实和序列化事件架构。
analytics/src/facts.rs源码 ↗
这份文件本身不负责把数据发出去,而是先规定“数据该怎么装”。项目运行时会发生很多事:用户发请求、模型回答、工具被调用、回合失败、上下文被压缩、插件状态变化等。如果每个地方都随便拼数据,后面做统计就会乱。这里用一批结构体和枚举把这些事情标准化。结构体像表格的一行,保存具体信息;枚举像下拉选项,限制状态只能是几种明确值,比如成功、失败、被拒绝。比较重要的是错误转换:底层的 CodexErr 很复杂,这里会把它归成适合分析的 CodexErrKind,并保留可能的 HTTP 状态码(网络请求返回的数字状态)。这样分析系统不用懂所有内部细节,也能知道失败大概是哪类问题。
build_track_events_context46–56 ↗
fn build_track_events_context(
model_slug: String,
thread_id: String,
turn_id: String,
) -> TrackEventsContext
作用:这个函数把模型名、会话线程编号、当前回合编号打包成一个追踪上下文。后面记录技能、插件、应用调用等事件时,可以带上这三个关键身份信息,知道“这件事发生在哪次对话的哪一轮”。
数据流:进去的是 model_slug、thread_id、turn_id 三个字符串 → 函数不做额外计算,只把它们放进 TrackEventsContext 这个小包里 → 出来的是一个可复制的上下文对象,原始输入被转移进去,没有改动别的状态。
调用关系:它是给其他分析事件准备“身份证”的小工具。谁需要记录某个事件属于哪个模型、哪个线程、哪个回合,就可以先调用它,再把得到的 TrackEventsContext 塞进对应的事件输入里。
TurnCodexErrorFact::from_codex_err129–135 ↗
fn from_codex_err(thread_id: String, turn_id: String, error: &CodexErr) -> Self
作用:这个函数把一次回合里的内部错误,整理成可以上报分析系统的错误事实。它会同时带上线程编号和回合编号,避免只知道“错了”,却不知道是哪一次对话出的问题。
数据流:进去的是 thread_id、turn_id,以及一个内部错误 CodexErr 的引用 → 函数保留这两个编号,并把复杂错误交给 TurnCodexError::from_codex_err 简化成分析用的错误信息 → 出来的是 TurnCodexErrorFact,里面包含位置和错误摘要,不会修改原来的错误对象。
调用关系:当回合失败或需要专门追踪 Codex 错误时会用到它。调用图里它会被 turn_lifecycle_emits_failed_turn_event 和 track_turn_codex_error 使用;它自己再把真正的错误归类工作交给 TurnCodexError::from_codex_err。
调用图:调用 1 个内部函数(from_codex_err);被 2 处调用(turn_lifecycle_emits_failed_turn_event, track_turn_codex_error)。
TurnCodexError::from_codex_err186–191 ↗
fn from_codex_err(error: &CodexErr) -> Self
作用:这个函数把底层错误压缩成两样最适合统计的东西:错误类别和 HTTP 状态码。HTTP 状态码就是网络服务常见的 404、500 这类数字,用来说明请求失败的大概原因。
数据流:进去的是一个 CodexErr 错误引用 → 函数用 into 把错误转换成 CodexErrKind 这种标准分类,并调用 http_status_code_value 取出可能存在的网络状态码 → 出来的是 TurnCodexError,只保留分析需要的摘要信息。
调用关系:它处在“内部错误”和“分析事件”之间,像翻译员。TurnCodexErrorFact::from_codex_err 会调用它;它再依赖 CodexErrKind::from 的分类规则,以及错误对象自己的 http_status_code_value 方法来补充状态码。
调用图:调用 1 个内部函数(http_status_code_value);被 1 处调用(from_codex_err);外部调用 1 个(into)。
CodexErrKind::from195–238 ↗
fn from(error: &CodexErr) -> Self
作用:这个函数负责给各种 CodexErr 错误贴上统一标签。比如超时、配额用完、服务器过载、沙盒失败、JSON 解析失败等,都会被归到固定的分析分类里。
数据流:进去的是一个具体的 CodexErr → 函数逐个匹配它属于哪种错误变体,只保留“是哪一类”,丢掉不适合直接统计的复杂细节 → 出来的是 CodexErrKind,也就是分析系统能稳定识别的错误种类。
调用关系:它是错误统计的分类表。TurnCodexError::from_codex_err 通过 into 间接使用它,把复杂错误变成可上报、可聚合的类别;如果将来新增底层错误,通常也要在这里补上对应分类。
TurnSteerRejectionReason::from309–314 ↗
fn from(error: InputError) -> Self
作用:这个函数把“给当前回合追加指令失败”的具体请求错误,转换成分析系统使用的拒绝原因。这样后面可以统计用户的追加输入为什么没被接受。
数据流:进去的是一个 TurnSteerRequestError,比如没有正在进行的回合、期望的回合不匹配、当前处于不可追加指令的审核或压缩阶段 → 函数按情况映射到同名或对应的 TurnSteerRejectionReason → 出来的是标准化的拒绝原因,不改动其他数据。
调用关系:它连接用户追加指令的请求层和分析记录层。当前端或协议层发现追加指令不能执行时,可以把请求错误交给它,得到适合写入 CodexTurnSteerEvent 的拒绝原因。
analytics/src/events.rs源码 ↗
可以把这个文件想成一套“事件表格模板”。Codex 运行时会发生很多事:线程启动了、用户用了某个应用、插件被安装了、命令执行了、上下文被压缩了、安全守卫审核了一次高风险操作。分析系统不能直接看程序内部杂乱的数据,所以这里定义了很多可序列化的结构体。序列化就是把程序里的数据变成 JSON 这类可传输格式。文件里大部分内容是在规定每种事件要带哪些字段,比如线程编号、模型名、耗时、错误类型、token 用量等。少量函数负责把内部事实转换成这些事件字段,比如把插件状态转成事件名,把 hook 来源转成固定字符串,或者补上当前系统版本和 CPU 架构。这样做的好处是:上报口径集中在一个地方,不同模块不会各写各的,后续统计也更稳定。
TrackEventRequest::should_send_in_isolated_request89–91 ↗
fn should_send_in_isolated_request(&self) -> bool
作用:判断某个事件是不是需要单独发一次请求,而不是和其他事件打包一起发。现在主要是“已接受代码行指纹”这类事件需要隔离发送。
数据流:进去的是一个待上报事件 → 它检查这个事件的具体种类 → 出来一个 true 或 false,告诉发送端要不要把它单独装车发送。
调用关系:它位于事件发送前的分流环节。发送逻辑拿到 TrackEventRequest 后会用这个判断来决定打包方式;函数内部只是做类型匹配,不再把工作交给其他项目函数。
调用图:外部调用 1 个(matches!)。
GuardianReviewTrackContext::new309–329 ↗
fn new(
thread_id: String,
turn_id: String,
review_id: String,
target_item_id: Option<String>,
approval_request_source: GuardianApprovalRequestSource,
r
作用:创建一次 Guardian 安全审核的跟踪上下文。它会记住这次审核是谁发起的、审核什么动作、什么时候开始,方便结束时算耗时并生成上报事件。
数据流:进去的是线程编号、回合编号、审核编号、被审核动作、超时时间等基础信息 → 它把这些信息存起来,并读取当前 Unix 毫秒时间和一个本地计时器起点 → 出来一个 GuardianReviewTrackContext,后面用它生成完整统计数据。
调用关系:它在 run_guardian_review 开始审核时被调用,相当于先按下秒表。之后 track_guardian_review 会拿这个上下文继续补全结束结果。
调用图:被 1 处调用(run_guardian_review);外部调用 2 个(now, now_unix_millis)。
GuardianReviewTrackContext::event_params331–380 ↗
fn event_params(
&self,
result: GuardianReviewAnalyticsResult,
completed_at_ms: u64,
) -> GuardianReviewEventParams
作用:把一次 Guardian 审核的开始信息和结束结果合并成可上报的事件参数。它负责把“审核做了什么、结果如何、花了多久、用了多少 token”整理成一张完整表。
数据流:进去的是之前保存的审核上下文、审核结果和完成时间 → 它复制线程、回合、动作等基础字段,填入决定、失败原因、模型信息、token 用量,并用本地计时器算出总耗时 → 出来 GuardianReviewEventParams,可放进最终事件请求里发送。
调用关系:它在 track_guardian_review 记录审核结果时被调用。它不直接发送事件,只负责把事件内容拼好,像填完报销单再交给发送模块。
调用图:被 1 处调用(track_guardian_review);外部调用 2 个(elapsed, clone)。
GuardianReviewAnalyticsResult::without_session408–431 ↗
fn without_session() -> Self
作用:生成一个“没有真正启动 Guardian 审核会话”的默认结果。通常表示系统为了安全选择保守拒绝,也就是宁可不放行。
数据流:进去没有参数 → 它创建一份默认审核结果:决定为拒绝,最终状态为失败关闭,尝试次数为 1,模型、token、风险等级等会话信息都为空 → 出来一个 GuardianReviewAnalyticsResult。
调用关系:它被 run_guardian_review、run_review、run_ephemeral_review 以及多处测试和统计代码使用。其他流程如果拿不到 Guardian 会话,也能用这份默认结果继续完成统计,不至于缺事件。
调用图:被 11 处调用(guardian_review_metrics_record_counts_durations_and_token_usage, run_guardian_review, run_guardian_review_session_before_deadline, run_ephemeral_review, run_review, wait_for_guardian_review_cancel_drains_expected_turn_after_stale_terminal_event, wait_for_guardian_review_ignores_prior_turn_aborts, wait_for_guardian_review_ignores_prior_turn_completion, wait_for_guardian_review_ignores_prior_turn_errors, wait_for_guardian_review_preserves_structured_session_error (+1 more))。
GuardianReviewAnalyticsResult::from_session433–449 ↗
fn from_session(params: GuardianReviewSessionAnalyticsParams) -> Self
作用:根据已经启动的 Guardian 审核会话信息,创建一份统计结果的初始版本。它先用安全默认值打底,再补上会话里的模型、线程和配置情况。
数据流:进去的是 GuardianReviewSessionAnalyticsParams,里面有 Guardian 线程编号、模型、提供方、是否复用上下文等信息 → 它先调用 without_session 得到默认结果,再覆盖会话相关字段 → 出来一个带有会话背景的 GuardianReviewAnalyticsResult。
调用关系:它在 run_review_on_session 中使用。流程是:真正拿到 Guardian 会话后,先用它做统计底稿,后续审核执行完再继续更新决定、token、耗时等结果。
调用图:被 1 处调用(run_review_on_session);外部调用 1 个(without_session)。
plugin_state_event_type963–970 ↗
fn plugin_state_event_type(state: PluginState) -> &'static str
作用:把插件状态转换成分析系统认识的事件名。比如插件安装、卸载、启用、禁用,各自要有固定名字。
数据流:进去的是 PluginState,也就是插件当前发生了哪种状态变化 → 它按状态做一一对应 → 出来一个固定字符串,比如 codex_plugin_installed。
调用关系:它在 ingest_plugin_state_changed 处理插件状态变化时被调用。状态变化先被系统发现,再由这个函数换成标准事件名,最后用于构造上报事件。
调用图:被 1 处调用(ingest_plugin_state_changed)。
codex_app_metadata972–985 ↗
fn codex_app_metadata(
tracking: &TrackEventsContext,
app: AppInvocation,
) -> CodexAppMetadata
作用:把一次应用调用整理成分析事件里需要的应用信息。它会补上线程、回合、产品客户端和模型名,让这次调用能被追溯到具体上下文。
数据流:进去的是当前跟踪上下文和 AppInvocation 应用调用事实 → 它读取线程编号、回合编号、模型名,并调用 originator 取得产品客户端标识 → 出来 CodexAppMetadata,里面包含应用名、连接器编号、调用类型等字段。
调用关系:它被 ingest_app_used 使用,也被应用提及和应用使用事件的序列化测试使用。它像一个小翻译,把内部的应用调用记录翻译成可上报字段。
调用图:调用 1 个内部函数(originator);被 3 处调用(app_mentioned_event_serializes_expected_shape, app_used_event_serializes_expected_shape, ingest_app_used)。
codex_plugin_metadata987–1013 ↗
fn codex_plugin_metadata(plugin: PluginTelemetryMetadata) -> CodexPluginMetadata
作用:把插件的遥测信息整理成插件事件通用元数据。安装、卸载、启用、禁用、使用插件时都需要这些基础信息。
数据流:进去的是 PluginTelemetryMetadata,包括插件身份、远程插件编号和能力摘要 → 它优先使用远程插件编号,没有就用本地插件键;再提取插件名、市场名、是否有技能、MCP 服务数量、连接器编号,并补上产品客户端标识 → 出来 CodexPluginMetadata。
调用关系:它被插件状态变化上报、插件使用元数据构造以及相关测试调用。codex_plugin_used_metadata 会把它当作基础零件,再额外加上使用时的线程和模型信息。
调用图:调用 1 个内部函数(originator);被 4 处调用(plugin_management_event_can_use_remote_plugin_id_override, plugin_management_event_serializes_expected_shape, codex_plugin_used_metadata, ingest_plugin_state_changed)。
codex_compaction_event_params1015–1050 ↗
fn codex_compaction_event_params(
input: CodexCompactionEvent,
session_id: String,
app_server_client: CodexAppServerClientMetadata,
runtime: CodexRuntimeMetadata,
thread_source: Op
作用:把一次上下文压缩事件整理成上报参数。上下文压缩就是把对话历史变短,避免模型上下文太长,这个函数记录压缩前后发生了什么。
数据流:进去的是 CodexCompactionEvent 以及会话、客户端、运行环境、线程来源等外层信息 → 它把触发原因、压缩阶段、策略、状态、错误、压缩前后 token 数、耗时等字段搬进事件参数 → 出来 CodexCompactionEventParams。
调用关系:它被 ingest_compaction 调用,也被序列化测试使用。压缩模块产生事实后,这个函数负责把事实包装成分析系统可读的事件内容。
调用图:被 2 处调用(compaction_event_serializes_expected_shape, ingest_compaction)。
codex_goal_event_params1052–1077 ↗
fn codex_goal_event_params(
input: CodexGoalEvent,
session_id: String,
app_server_client: CodexAppServerClientMetadata,
runtime: CodexRuntimeMetadata,
thread_source: Option<ThreadS
作用:把目标相关事件整理成上报参数。目标可以理解为线程里正在追踪的一项任务或计划,这里记录它的状态变化和资源使用情况。
数据流:进去的是 CodexGoalEvent 以及会话、客户端、运行环境、线程来源等信息 → 它填入目标编号、事件类型、目标状态、是否有 token 预算、累计 token 和累计时间 → 出来 CodexGoalEventParams。
调用关系:它在 ingest_goal 中被调用。目标系统产生日志事实后,这个函数把它变成统一事件参数,再交给后续上报流程。
调用图:被 1 处调用(ingest_goal)。
codex_plugin_used_metadata1079–1094 ↗
fn codex_plugin_used_metadata(
tracking: &TrackEventsContext,
plugin: PluginTelemetryMetadata,
) -> CodexPluginUsedMetadata
作用:生成“插件被使用了”这个事件的详细信息。它不仅包含插件是谁,还包含这次使用发生在哪个线程、哪个回合、用的哪个模型。
数据流:进去的是当前跟踪上下文和插件遥测信息 → 它先取出插件能力里的 MCP 服务名,再调用 codex_plugin_metadata 生成插件基础信息,最后补上线程编号、回合编号和模型名 → 出来 CodexPluginUsedMetadata。
调用关系:它被 ingest_plugin_used 调用,也被插件使用事件测试调用。它复用 codex_plugin_metadata,避免插件基础字段在不同事件里写出两套口径。
调用图:调用 1 个内部函数(codex_plugin_metadata);被 2 处调用(plugin_used_event_serializes_expected_shape, ingest_plugin_used)。
codex_hook_run_metadata1096–1108 ↗
fn codex_hook_run_metadata(
tracking: &TrackEventsContext,
hook: HookRunFact,
) -> CodexHookRunMetadata
作用:把一次 hook 运行记录整理成上报信息。hook 是在某些关键时刻自动触发的小脚本或扩展点,比如工具使用前后、会话开始时。
数据流:进去的是当前跟踪上下文和 HookRunFact → 它补上线程、回合、模型名,并把 hook 事件名、来源、状态分别转换成分析系统使用的固定表示 → 出来 CodexHookRunMetadata。
调用关系:它被 ingest_hook_run 调用,也被多项 hook 序列化和映射测试使用。它把具体转换工作交给 analytics_hook_event_name、analytics_hook_source 和 analytics_hook_status。
调用图:调用 3 个内部函数(analytics_hook_event_name, analytics_hook_source, analytics_hook_status);被 4 处调用(hook_run_event_serializes_expected_shape, hook_run_metadata_maps_sources_and_statuses, hook_run_metadata_maps_stopped_status, ingest_hook_run)。
analytics_hook_event_name1110–1123 ↗
fn analytics_hook_event_name(event_name: HookEventName) -> &'static str
作用:把 hook 事件枚举转换成固定字符串。这样分析数据里看到的是稳定名字,而不是程序内部的枚举格式。
数据流:进去的是 HookEventName,例如 PreToolUse 或 SessionStart → 它按每种事件返回对应字符串 → 出来一个 &'static str,也就是程序内置的固定文本。
调用关系:它只被 codex_hook_run_metadata 调用。hook 运行事件需要填事件名时,会先经过这个函数统一命名。
调用图:被 1 处调用(codex_hook_run_metadata)。
analytics_hook_source1125–1139 ↗
fn analytics_hook_source(source: HookSource) -> &'static str
作用:把 hook 的来源转换成分析系统用的固定小写字符串。来源可能是系统、用户、项目配置、插件、云端管理配置等。
数据流:进去的是 HookSource → 它把每种来源映射成一个稳定字符串,比如 system、user、plugin、unknown → 出来这个字符串供事件参数使用。
调用关系:它只被 codex_hook_run_metadata 调用。这样 hook 来源字段在所有上报事件里保持同一种写法。
调用图:被 1 处调用(codex_hook_run_metadata)。
current_runtime_metadata1141–1149 ↗
fn current_runtime_metadata() -> CodexRuntimeMetadata
作用:读取当前 Codex 运行环境的信息。它告诉分析系统:这次运行用的是哪个 Codex 版本、什么操作系统、系统版本和 CPU 架构。
数据流:进去没有业务参数 → 它读取编译时的包版本、Rust 标准库提供的操作系统和架构常量,并通过 os_info 获取系统版本 → 出来 CodexRuntimeMetadata。
调用关系:它在 track_initialize 和 subagent_thread_started_event_request 中被调用。线程初始化或子代理线程启动时,需要把运行环境一起放进事件。
调用图:被 2 处调用(track_initialize, subagent_thread_started_event_request);外部调用 2 个(env!, get)。
subagent_thread_started_event_request1151–1178 ↗
fn subagent_thread_started_event_request(
input: SubAgentThreadStartedInput,
) -> ThreadInitializedEvent
作用:生成“子代理线程已启动”的初始化事件。子代理可以理解为主任务派出去的助手线程,这个事件记录它是谁、从哪里来、父线程是谁。
数据流:进去的是 SubAgentThreadStartedInput,包含子线程编号、会话编号、客户端信息、模型、父线程、来源和创建时间 → 它构造客户端元数据,调用 current_runtime_metadata 获取运行环境,调用 subagent_source_name 转换来源名称 → 出来 ThreadInitializedEvent,事件名是 codex_thread_initialized。
调用关系:它被 ingest_subagent_thread_started 调用,也被多个子代理启动场景的测试调用。子代理刚启动时,系统用它把“新线程出生证明”填好并准备上报。
调用图:调用 2 个内部函数(current_runtime_metadata, subagent_source_name);被 6 处调用(subagent_thread_started_memory_consolidation_serializes_expected_shape, subagent_thread_started_other_serializes_expected_shape, subagent_thread_started_other_serializes_explicit_parent_thread_id, subagent_thread_started_review_serializes_expected_shape, subagent_thread_started_thread_spawn_serializes_thread_lineage, ingest_subagent_thread_started)。
subagent_source_name1180–1182 ↗
fn subagent_source_name(subagent_source: &SubAgentSource) -> String
作用:把子代理来源转换成一个可记录的名称。它让分析事件能区分子代理是因为审查、线程派生、内存整理等哪类原因启动的。
数据流:进去的是 SubAgentSource 的引用 → 它读取这个来源的 kind,并转成字符串 → 出来一个 String,作为事件里的 subagent_source 字段。
调用关系:它被 subagent_thread_started_event_request 调用,也被 from_thread_metadata 使用。凡是需要把子代理来源写进元数据的地方,都会通过它统一命名。
调用图:调用 1 个内部函数(kind);被 2 处调用(subagent_thread_started_event_request, from_thread_metadata)。
analytics_hook_status1184–1190 ↗
fn analytics_hook_status(status: HookRunStatus) -> HookRunStatus
作用:规范化 hook 的运行状态。特别是如果意外拿到 Running,也就是“还在运行中”,这里会保守地当成 Failed,避免上报一个不该出现的中间状态。
数据流:进去的是 HookRunStatus → 如果状态是 Running,就改成 Failed;其他状态原样返回 → 出来一个适合上报的 HookRunStatus。
调用关系:它被 codex_hook_run_metadata 调用。hook 运行事件生成时,状态会先经过这里清洗一下,再写入事件参数。
调用图:被 1 处调用(codex_hook_run_metadata)。
analytics/src/lib.rs源码 ↗
这个文件像一家店的前台。真正的东西分散在 accepted_lines、client、events、facts、reducer 等模块里,但外部使用者不需要知道每个柜台在哪,因为这里把常用类型和函数统一重新导出。这样别的代码只要引用 analytics 这个库,就能拿到事件客户端、各种分析事件参数、错误类型、用量记录等。文件底部还有几个安全小工具:取当前 Unix 时间(Unix 时间就是从 1970 年 1 月 1 日开始算的秒数或毫秒数)、把枚举转成字符串、把 usize 或 i64 转成 u64。这里特别注意“失败时不要炸掉程序”:比如系统时间异常时给默认值,数字太大转不了时给最大值或直接返回空值。这让埋点和统计代码更稳,不会因为一个边角数据把主流程弄崩。
now_unix_seconds61–66 ↗
fn now_unix_seconds() -> u64
作用:返回当前时间的 Unix 秒数,也就是从 1970 年 1 月 1 日到现在过了多少秒。统计事件通常需要时间戳,用它就能给事件打上“什么时候发生”的标签。
数据流:进去时不需要参数 → 它读取系统当前时间,计算这个时间距离 Unix 起点过了多少秒;如果系统时间有问题,就用默认的 0 时长兜底 → 出来一个 u64 数字,表示当前 Unix 秒级时间戳,不修改任何外部数据。
调用关系:它是 analytics 库里的通用时间工具。需要给事件、事实或日志补时间的代码会调用它;它把真正取时间的工作交给标准库的 SystemTime::now,也就是调用图里的 now(ext)。
调用图:外部调用 1 个(now)。
now_unix_millis68–76 ↗
fn now_unix_millis() -> u64
作用:返回当前时间的 Unix 毫秒数,比秒更精细。适合记录更精确的事件发生时间,比如一次请求或一次交互的时间点。
数据流:进去时不需要参数 → 它读取系统当前时间,算出距离 Unix 起点的毫秒数,再把这个值安全转成 u64;如果时间异常就当作 0,如果数字大到转不了就用 u64 的最大值兜底 → 出来一个 u64 毫秒时间戳,不改动外部状态。
调用关系:它和 now_unix_seconds 类似,都是给统计数据打时间标签的工具。它调用标准库取当前时间的 now(ext),再调用 try_from(ext) 做安全数字转换,避免因为类型装不下而让程序崩掉。
serialize_enum_as_string78–82 ↗
fn serialize_enum_as_string(value: &T) -> Option<String>
作用:把一个可序列化的枚举值尽量变成字符串。简单说,就是把代码里的“选项名”转成适合写进 JSON 或统计事件里的文字。
数据流:进去一个实现了 serde::Serialize 的值,通常是枚举 → 它先用 serde_json::to_value 把它转成 JSON 值,再看看这个 JSON 值是不是字符串;是的话取出字符串,不是或转换失败就返回 None → 出来的是 Option<String>,有值表示转成功,没值表示不能安全地当字符串用。
调用关系:它是事件和事实序列化时的辅助工具。调用方想把枚举按文字记录下来时会用它;它把具体的 JSON 转换交给外部库函数 to_value(ext),自己只负责判断结果是不是字符串。
调用图:外部调用 1 个(to_value)。
usize_to_u6484–86 ↗
fn usize_to_u64(value: usize) -> u64
作用:把 usize 转成 u64,并且用安全方式兜底。usize 是跟机器位数有关的大小数字,u64 是固定 64 位的无符号数字,统计数据里更常用后者。
数据流:进去一个 usize 数字 → 它尝试把这个数字转换成 u64;如果能装下就返回原值,如果装不下就返回 u64::MAX 这个最大值 → 出来一个 u64,不修改任何外部数据。
调用关系:它是埋点数据整理时的小转换器。调用方在记录长度、数量、计数时可能会用到它;真正的安全转换由 try_from(ext) 完成,这个函数负责给失败情况一个稳定结果。
调用图:外部调用 1 个(try_from)。
option_i64_to_u6488–90 ↗
fn option_i64_to_u64(value: Option<i64>) -> Option<u64>
作用:把“可能有、也可能没有”的 i64 数字安全转成“可能有、也可能没有”的 u64 数字。它会自动丢掉负数或装不下的值,避免把不合理的数据写进统计结果。
数据流:进去一个 Option<i64>:可能是 Some(数字),也可能是 None → 如果是 None,就直接保持 None;如果有数字,就尝试转成 u64,只有非负且能装下时才成功 → 出来一个 Option<u64>,成功时有值,原本没值或转换失败时没值。
调用关系:它用于清洗统计字段里的可选数字。调用方拿到某些来源不完全可靠的 i64 值时,可以先交给它过滤;它不再调用本文件里的其他函数,只在本地完成可选值展开和安全转换。
事件归约流程
这些文件实现从原始观察和 accepted-line 解析到最终 analytics 事件记录的核心转换。
analytics/src/accepted_lines.rs源码 ↗
当用户接受 AI 给出的代码修改时,系统想知道“接受了多少代码”,但又不能把真实文件名和代码行直接发出去。这个文件就像一个给代码改动打码的统计员:先读统一 diff(一种常见的补丁文本格式),找出每个文件里真正新增和删除的行;再把文件路径和有效代码行变成哈希值(不可直接还原的短字符串),用于本地测试和未来归因。它还会尝试从当前目录的 Git 远程地址算出仓库指纹,避免直接暴露仓库 URL。一个重要细节是:虽然本地会算行指纹,但真正组装分析事件时,目前不会上传这些路径和行的哈希,只上传数量等汇总信息。
accepted_line_fingerprints_from_unified_diff30–83 ↗
fn accepted_line_fingerprints_from_unified_diff(
unified_diff: &str,
) -> AcceptedLineFingerprintSummary
作用:读取一整段统一 diff,算出接受的新增行数、删除行数,并给比较像真实代码的新增行生成匿名指纹。别人会用它来把“代码补丁文本”变成适合统计的摘要。
数据流:输入是一段 diff 字符串。它逐行扫描:遇到文件头就记住当前文件路径,遇到代码块标记就开始看真正的增删内容;以加号开头的行算新增,以减号开头的行算删除;新增行会先清理空白和过滤太短、没内容的行,再用 fingerprint_hash 分别给路径和代码行打码。输出是 AcceptedLineFingerprintSummary,里面有新增数量、删除数量和本地算出的指纹列表。
调用关系:它是这套统计的核心解析器。accepted_line_event_input 会在准备分析事件前调用它;测试函数也直接调用它来确认各种 diff 写法都能被正确理解。它把路径清理交给 normalize_diff_path,把代码行清理交给 normalize_effective_line,把打码交给 fingerprint_hash。
调用图:调用 3 个内部函数(fingerprint_hash, normalize_diff_path, normalize_effective_line);被 4 处调用(parses_counts_and_effective_added_fingerprints, parses_hunk_lines_that_look_like_file_headers, skips_added_file_metadata_headers, accepted_line_event_input);外部调用 1 个(new)。
fingerprint_hash85–92 ↗
fn fingerprint_hash(domain: &str, value: &str) -> String
作用:把仓库地址、文件路径或代码行变成稳定的匿名哈希。这样系统可以比较“是不是同一个东西”,但不用保存原文。
数据流:输入是一个 domain(说明这是 repo、path 还是 line)和一个 value(真实内容)。它把固定版本前缀、domain 和 value 一起喂给 SHA-1 哈希算法(一种把文本变成固定长度摘要的算法),最后输出十六进制字符串。它不改动外部状态。
调用关系:accepted_line_fingerprints_from_unified_diff 用它给路径和代码行打码;accepted_line_repo_hash_for_cwd 也用它给仓库远程地址打码。domain 的存在像给不同箱子贴标签,避免同一段文字在不同用途下混在一起。
调用图:被 1 处调用(accepted_line_fingerprints_from_unified_diff);外部调用 2 个(format!, new)。
accepted_line_fingerprint_event_requests94–129 ↗
fn accepted_line_fingerprint_event_requests(
input: AcceptedLineFingerprintEventInput,
) -> Vec<TrackEventRequest>
作用:把已经整理好的接受代码统计信息包装成要发送的分析事件请求。它是从内部数据变成“可上报事件”的转换步骤。
数据流:输入是 AcceptedLineFingerprintEventInput,里面有事件类型、会话标识、模型名、完成时间、仓库指纹、新增删除行数和本地行指纹。它把这些字段放进 CodexAcceptedLineFingerprintsEventRequest,再包成 TrackEventRequest 列表输出。需要注意:输入里的 line_fingerprints 会被丢弃,事件里上传的是空列表,只保留汇总数量等信息。
调用关系:maybe_emit_turn_event 会在一次交互结束、准备发分析数据时调用它。它不再解析 diff,也不查 Git,只负责把前面算好的结果装进事件外壳,交给后续分析发送流程。
调用图:被 1 处调用(maybe_emit_turn_event);外部调用 1 个(vec!)。
accepted_line_repo_hash_for_cwd131–141 ↗
async fn accepted_line_repo_hash_for_cwd(cwd: &Path) -> Option<String>
作用:根据当前工作目录所在的 Git 仓库,算出一个匿名仓库指纹。这样分析里能区分仓库,但不用上传真实仓库地址。
数据流:输入是当前目录路径 cwd。它异步读取这个 Git 仓库的远程地址列表,优先拿 origin,如果没有就拿任意一个远程地址;然后尽量把地址规范化,比如不同写法统一成同一种形式;最后用 fingerprint_hash 生成 repo 哈希。读不到远程地址时输出 None。
调用关系:maybe_emit_turn_event 会在准备接受代码统计事件时调用它,用来补充 repo_hash。它依赖外部的 get_git_remote_urls_assume_git_repo 读取 Git 信息,也依赖 canonicalize_git_remote_url 做地址规范化。
调用图:被 1 处调用(maybe_emit_turn_event);外部调用 1 个(get_git_remote_urls_assume_git_repo)。
normalize_diff_path143–155 ↗
fn normalize_diff_path(path: &str) -> Option<String>
作用:把 diff 里的文件路径整理成干净的项目内路径。它会去掉 diff 常见的 a/、b/ 前缀,并排除 /dev/null 这种代表“没有旧文件或新文件”的占位符。
数据流:输入是 diff 文件头里的路径文本。它先去掉两边空白;如果路径是 /dev/null,就输出 None;否则去掉开头的 b/ 或 a/,输出整理后的字符串。它不访问文件系统,只处理文本。
调用关系:accepted_line_fingerprints_from_unified_diff 在看到 +++ 文件头时调用它,用它确定后面新增行属于哪个文件。没有这个清理,路径指纹可能会因为 a/、b/ 这种补丁格式前缀而不稳定。
调用图:被 1 处调用(accepted_line_fingerprints_from_unified_diff)。
normalize_effective_line157–169 ↗
fn normalize_effective_line(line: &str) -> Option<String>
作用:判断一行新增内容值不值得做指纹,并把它整理成稳定形式。它会跳过太短或只有符号的行,避免把括号、空行这类噪声算进去。
数据流:输入是一行新增代码,不含开头的加号。它把连续空白压成单个空格;如果整理后长度不超过 3,或者没有字母、数字、下划线,就输出 None;否则输出整理后的代码行字符串。
调用关系:accepted_line_fingerprints_from_unified_diff 每遇到新增行都会调用它。只有它认为“这行有实际内容”时,后续才会调用 fingerprint_hash 生成行指纹。
调用图:被 1 处调用(accepted_line_fingerprints_from_unified_diff)。
tests::parses_counts_and_effective_added_fingerprints176–209 ↗
fn parses_counts_and_effective_added_fingerprints()
作用:这是一个测试,确认普通 diff 能正确数新增、删除,并且只给有实际内容的新增行做指纹。它保护核心解析逻辑不被改坏。
数据流:输入是一段手写 diff,里面有一行删除、三行新增,其中有一行只是右花括号。测试调用 accepted_line_fingerprints_from_unified_diff 后,把结果和预期摘要比较:新增 3、删除 1,但只留下两条有效行指纹。
调用关系:它直接验证 accepted_line_fingerprints_from_unified_diff 的主流程,也间接验证 normalize_diff_path、normalize_effective_line 和 fingerprint_hash 配合得对。
调用图:调用 1 个内部函数(accepted_line_fingerprints_from_unified_diff);外部调用 1 个(assert_eq!)。
tests::skips_added_file_metadata_headers212–228 ↗
fn skips_added_file_metadata_headers()
作用:这是一个测试,确认新增文件时 diff 里的元信息不会被误当成代码行。它防止统计把文件头格式算进真实新增代码。
数据流:输入是一段“新增文件”的 diff,其中包含 --- /dev/null、+++ b/new.py 这些头信息和一行真正的 print 代码。测试解析后检查新增行是 1、删除行是 0、指纹数量是 1。
调用关系:它调用 accepted_line_fingerprints_from_unified_diff,重点覆盖 normalize_diff_path 对 /dev/null 和 b/ 前缀的处理,以及解析器区分文件头和代码块内容的能力。
调用图:调用 1 个内部函数(accepted_line_fingerprints_from_unified_diff);外部调用 1 个(assert_eq!)。
tests::parses_hunk_lines_that_look_like_file_headers231–255 ↗
fn parses_hunk_lines_that_look_like_file_headers()
作用:这是一个测试,确认代码块里面即使出现像 --- 或 +++ 这样的文本,也会按真实增删行处理,而不是误判成文件头。它防止边界情况造成统计错误。
数据流:输入的 diff 在真正的代码块里有一行以 --- 开头、一行以 +++ 开头。测试调用解析器后,期望得到删除 1、新增 1,并为新增内容生成对应指纹。
调用关系:它直接测试 accepted_line_fingerprints_from_unified_diff 对 in_hunk 状态的处理。这个状态像一个开关:进入代码块后,类似文件头的行也要按代码内容看待。
调用图:调用 1 个内部函数(accepted_line_fingerprints_from_unified_diff);外部调用 1 个(assert_eq!)。
analytics/src/reducer.rs源码 ↗
系统运行时会冒出很多原始事实:客户端连上来了、线程创建了、一次对话回合开始或结束了、命令执行了、文件改了、权限审批通过或拒绝了。这个文件的核心是 AnalyticsReducer,它把这些事实先暂存起来,等信息凑齐后再生成 TrackEventRequest,也就是最终要发出去的分析事件。它会记住连接信息、线程信息、回合状态、工具开始时间、待审批请求等。这样做很重要,因为很多事件不是一次就完整出现的,比如工具完成时才知道结果,但开始时间早就来了;回合结束时还要合并模型配置、耗时、token 用量和工具次数。没有这个文件,统计数据会丢上下文、时间不准,或者把同一次操作拆成互不相干的碎片。
AnalyticsDropSite::guardian168–176 ↗
fn guardian(input: &'a GuardianReviewEventParams) -> Self
作用:为“守护者审核”事件准备一张错误定位小卡片。以后如果缺少必要上下文,日志能说清楚是哪条线程、哪个回合、哪个审核出问题。
数据流:输入一个守护者审核参数 → 取出线程号、回合号、审核号 → 输出 AnalyticsDropSite,专门标记这是 guardian 事件。
调用关系:ingest_guardian_review 在准备上报守护者审核前调用它;如果后面 thread_context_or_warn 发现上下文不全,就用这张卡片写警告日志。
调用图:被 1 处调用(ingest_guardian_review)。
AnalyticsDropSite::review178–186 ↗
fn review(input: &'a PendingReviewState) -> Self
作用:为普通审批事件准备错误定位信息。它帮助日志把“哪次用户或守护者审批丢了统计事件”讲清楚。
数据流:输入一个待处理审批状态 → 拿到线程、回合、审核、工具项编号 → 输出带有 review 标签的定位对象。
调用关系:emit_review_event 在真正生成审批统计事件前调用它;如果缺连接或线程资料,就交给 thread_context_or_warn 报警。
调用图:被 1 处调用(emit_review_event)。
AnalyticsDropSite::compaction188–196 ↗
fn compaction(input: &'a CodexCompactionEvent) -> Self
作用:为上下文压缩事件准备定位信息。上下文压缩可以理解为把长对话整理变短,它也需要带着线程和回合身份上报。
数据流:输入压缩事件 → 提取线程号和回合号 → 输出标记为 compaction 的定位对象。
调用关系:ingest_compaction 用它检查上下文;缺资料时 warn_missing_analytics_context 会根据它写出明确日志。
调用图:被 1 处调用(ingest_compaction)。
AnalyticsDropSite::goal198–206 ↗
fn goal(input: &'a CodexGoalEvent) -> Self
作用:为目标事件准备定位信息。目标事件可能有回合号,也可能没有,所以这里会按实际情况记录。
数据流:输入目标事件 → 提取线程号和可选的回合号 → 输出标记为 goal 的定位对象。
调用关系:ingest_goal 在生成目标统计事件前使用它;它主要服务于上下文检查和丢弃事件时的日志说明。
调用图:被 1 处调用(ingest_goal)。
AnalyticsDropSite::tool_item208–219 ↗
fn tool_item(
notification: &'a codex_app_server_protocol::ItemCompletedNotification,
item_id: &'a str,
) -> Self
作用:为工具项事件准备定位信息。工具项就是命令、文件修改、网页搜索这类一次具体动作。
数据流:输入工具完成通知和工具项编号 → 取线程号、回合号、工具项号 → 输出标记为 tool item 的定位对象。
调用关系:ingest_notification 在工具完成时调用它;如果找不到线程或连接资料,就能准确记录是哪一个工具项没法上报。
调用图:被 1 处调用(ingest_notification)。
AnalyticsDropSite::turn_steer221–229 ↗
fn turn_steer(thread_id: &'a str) -> Self
作用:为“调整正在进行的回合”事件准备定位信息。turn steer 指用户中途追加或改变输入。
数据流:输入线程号 → 输出一个没有回合号的定位对象,因为这类请求可能还没被接受到某个具体回合里。
调用关系:emit_turn_steer_event 用它查线程元数据;查不到时会用它写警告。
调用图:被 1 处调用(emit_turn_steer_event)。
AnalyticsDropSite::turn231–239 ↗
fn turn(thread_id: &'a str, turn_id: &'a str) -> Self
作用:为一次完整对话回合事件准备定位信息。它把线程号和回合号绑在一起,方便排查统计事件为什么没发出去。
数据流:输入线程号和回合号 → 输出标记为 turn 的定位对象。
调用关系:maybe_emit_turn_event 在尝试发回合统计时调用它;上下文缺失时用它告诉日志具体丢的是哪个回合。
调用图:被 1 处调用(maybe_emit_turn_event)。
ThreadMetadataState::from_thread_metadata282–306 ↗
fn from_thread_metadata(
session_id: String,
session_source: &SessionSource,
thread_source: Option<ThreadSource>,
parent_thread_id: Option<String>,
initializati
作用:把线程创建时带来的原始信息,整理成后续所有统计事件都能复用的线程元数据。
数据流:输入会话号、会话来源、线程来源、父线程号和初始化方式 → 判断是不是子代理线程,并生成子代理来源名 → 输出 ThreadMetadataState。
调用关系:emit_thread_initialized 创建线程初始化事件时调用它;它还借助 subagent_source_name 把子代理来源转换成统计系统认识的名字。
调用图:调用 1 个内部函数(subagent_source_name);被 1 处调用(emit_thread_initialized)。
TurnToolCounts::record370–393 ↗
fn record(&mut self, item: &ThreadItem)
作用:给一个回合里的工具调用分类计数。比如命令算一类,文件修改算一类,网页搜索算一类。
数据流:输入一个线程项目 → 判断它是不是要统计的工具 → 对对应类别加一,同时总数加一;普通消息、推理、计划等不会计数。
调用关系:工具完成通知到来时,AnalyticsReducer::ingest_notification 会更新回合里的 TurnToolCounts;最后 codex_turn_event_params 会把这些数字放进回合统计事件。
AnalyticsReducer::ingest397–516 ↗
async fn ingest(&mut self, input: AnalyticsFact, out: &mut Vec<TrackEventRequest>)
作用:这是这个文件的总入口。所有原始分析事实都会先进到这里,再被分派给更具体的处理函数。
数据流:输入一个 AnalyticsFact 和输出列表 → 按事实类型拆开,比如初始化、请求、响应、通知、自定义事件 → 更新内部状态,并可能往输出列表追加统计事件。
调用关系:外部测试和上层收集器会调用它;它再把活交给 ingest_initialize、ingest_response、ingest_notification、ingest_compaction 等一系列专门函数。
调用图:调用 23 个内部函数(ingest_app_mentioned, ingest_app_used, ingest_compaction, ingest_effective_permissions_approval_response, ingest_error_response, ingest_goal, ingest_guardian_review, ingest_hook_run, ingest_initialize, ingest_notification (+13 more));被 6 处调用(ingest_complete_child_turn, ingest_completed_command_execution_item, ingest_initialize, ingest_rejected_turn_steer, ingest_review_prerequisites, ingest_turn_prerequisites)。
AnalyticsReducer::ingest_initialize518–541 ↗
fn ingest_initialize(
&mut self,
connection_id: u64,
params: InitializeParams,
product_client_id: String,
runtime: CodexRuntimeMetadata,
rpc_transport:
作用:记录一条客户端连接的基本资料。后续任何事件都需要知道它来自哪个客户端、什么运行环境。
数据流:输入连接号、初始化参数、客户端产品号、运行时信息和通信方式 → 组装成 ConnectionState → 存进 connections 表。
调用关系:AnalyticsReducer::ingest 收到 Initialize 事实时调用它;后续线程、回合、工具、审批事件都会通过连接号取这里保存的信息。
调用图:被 1 处调用(ingest)。
AnalyticsReducer::ingest_subagent_thread_started543–569 ↗
fn ingest_subagent_thread_started(
&mut self,
input: SubAgentThreadStartedInput,
out: &mut Vec<TrackEventRequest>,
)
作用:处理子代理线程启动。子代理可以理解为主对话派出去的一个小助手线程。
数据流:输入子代理线程信息 → 尝试继承父线程的连接号 → 保存新线程元数据 → 立刻输出一个线程初始化统计事件。
调用关系:AnalyticsReducer::ingest 收到 SubAgentThreadStarted 时调用它;它会使用 subagent_thread_started_event_request 生成标准事件。
调用图:调用 1 个内部函数(subagent_thread_started_event_request);被 1 处调用(ingest);外部调用 1 个(ThreadInitialized)。
AnalyticsReducer::ingest_guardian_review571–592 ↗
fn ingest_guardian_review(
&mut self,
input: GuardianReviewEventParams,
out: &mut Vec<TrackEventRequest>,
)
作用:把守护者审核事实变成可上报事件。守护者审核是系统自动安全检查的一种结果。
数据流:输入守护者审核参数 → 查线程和连接上下文 → 如果齐全就补上会话、客户端、运行时信息 → 输出 GuardianReview 事件。
调用关系:由 AnalyticsReducer::ingest 分派调用;它先用 AnalyticsDropSite::guardian 和 thread_context_or_warn 做上下文检查,再构造事件。
调用图:调用 2 个内部函数(guardian, thread_context_or_warn);被 1 处调用(ingest);外部调用 2 个(new, GuardianReview)。
AnalyticsReducer::ingest_request594–623 ↗
fn ingest_request(
&mut self,
connection_id: u64,
request_id: RequestId,
request: ClientRequest,
)
作用:记录客户端发来的请求中那些需要等待响应才能完整统计的部分。比如回合开始请求和回合调整请求。
数据流:输入连接号、请求号和客户端请求 → 如果是 TurnStart 或 TurnSteer,就保存线程号、图片输入数量等临时信息 → 暂时不输出事件。
调用关系:AnalyticsReducer::ingest 收到 ClientRequest 时调用它;后续 ingest_response 或 ingest_error_response 会拿这些暂存信息继续处理。
调用图:调用 1 个内部函数(num_input_images);被 1 处调用(ingest);外部调用 3 个(TurnStart, TurnSteer, now_unix_seconds)。
AnalyticsReducer::ingest_turn_resolved_config625–638 ↗
async fn ingest_turn_resolved_config(
&mut self,
input: TurnResolvedConfigFact,
out: &mut Vec<TrackEventRequest>,
)
作用:记录某个回合最终采用的配置。配置包括模型、权限、沙箱、审批策略等,是回合统计的重要组成。
数据流:输入回合配置事实 → 放进对应 TurnState → 同时记录线程号和图片数量 → 尝试调用 maybe_emit_turn_event 看信息是否已经凑齐。
调用关系:由 AnalyticsReducer::ingest 处理 TurnResolvedConfig 时调用;它不会一定立刻发事件,而是和回合完成、性能数据等一起等待。
调用图:调用 1 个内部函数(maybe_emit_turn_event);被 1 处调用(ingest)。
AnalyticsReducer::ingest_turn_token_usage640–650 ↗
async fn ingest_turn_token_usage(
&mut self,
input: TurnTokenUsageFact,
out: &mut Vec<TrackEventRequest>,
)
作用:记录一个回合用了多少 token。token 可以简单理解为模型处理文字的计量单位。
数据流:输入 token 用量事实 → 找到或创建对应回合状态 → 保存线程号和用量 → 尝试发完整回合事件。
调用关系:由 AnalyticsReducer::ingest 调用;maybe_emit_turn_event 会在配置、性能、完成状态都齐了之后真正输出。
调用图:调用 1 个内部函数(maybe_emit_turn_event);被 1 处调用(ingest)。
AnalyticsReducer::ingest_turn_profile652–661 ↗
async fn ingest_turn_profile(
&mut self,
input: TurnProfileFact,
out: &mut Vec<TrackEventRequest>,
)
作用:记录一个回合的耗时画像。它描述模型采样、工具等待等阶段各花了多久。
数据流:输入回合性能事实 → 保存到对应 TurnState → 再尝试生成完整回合统计事件。
调用关系:由 AnalyticsReducer::ingest 调用;它是 maybe_emit_turn_event 判断“回合统计是否完整”的必要材料之一。
调用图:调用 1 个内部函数(maybe_emit_turn_event);被 1 处调用(ingest)。
AnalyticsReducer::ingest_turn_codex_error663–672 ↗
fn ingest_turn_codex_error(&mut self, input: TurnCodexErrorFact)
作用:记录回合里 Codex 自己遇到的错误。这个错误会在最终回合事件里体现。
数据流:输入回合错误事实 → 找到或创建 TurnState → 如果还没有线程号就补上 → 保存错误信息;不直接输出事件。
调用关系:由 AnalyticsReducer::ingest 调用;后续 codex_turn_event_params 会把这里保存的错误种类和 HTTP 状态码放进回合统计。
调用图:被 1 处调用(ingest)。
AnalyticsReducer::ingest_skill_invoked674–722 ↗
async fn ingest_skill_invoked(
&mut self,
input: SkillInvokedInput,
out: &mut Vec<TrackEventRequest>,
)
作用:上报技能被调用的事件。技能可以理解为项目或用户提供的一段可复用能力。
数据流:输入技能调用列表和跟踪信息 → 对每个技能判断作用范围、找 git 仓库和仓库地址 → 生成稳定的技能 ID → 输出 SkillInvocation 事件。
调用关系:由 AnalyticsReducer::ingest 调用;它会调用 get_git_repo_root、collect_git_info、skill_id_for_local_skill,并把结果包装成 SkillInvocation。
调用图:调用 2 个内部函数(skill_id_for_local_skill, originator);被 1 处调用(ingest);外部调用 3 个(SkillInvocation, collect_git_info, get_git_repo_root)。
AnalyticsReducer::ingest_app_mentioned724–733 ↗
fn ingest_app_mentioned(&mut self, input: AppMentionedInput, out: &mut Vec<TrackEventRequest>)
作用:上报某个应用被提到的情况。这里的应用是 Codex 能识别或集成的外部应用。
数据流:输入提及列表和跟踪信息 → 为每个提及生成应用元数据 → 往输出列表追加 AppMentioned 事件。
调用关系:由 AnalyticsReducer::ingest 调用;它主要把多条 mention 展开成多条统计事件。
调用图:被 1 处调用(ingest)。
AnalyticsReducer::ingest_app_used735–742 ↗
fn ingest_app_used(&mut self, input: AppUsedInput, out: &mut Vec<TrackEventRequest>)
作用:上报某个应用被实际使用。提到和使用是两回事,这里记录的是使用。
数据流:输入应用使用信息 → 生成应用元数据 → 输出 AppUsed 事件。
调用关系:由 AnalyticsReducer::ingest 调用;它使用 codex_app_metadata 统一整理事件参数。
调用图:调用 1 个内部函数(codex_app_metadata);被 1 处调用(ingest);外部调用 1 个(AppUsed)。
AnalyticsReducer::ingest_hook_run744–750 ↗
fn ingest_hook_run(&mut self, input: HookRunInput, out: &mut Vec<TrackEventRequest>)
作用:上报 hook 被运行。hook 可以理解为某些时机自动触发的小脚本或小动作。
数据流:输入 hook 运行信息 → 生成 hook 元数据 → 输出 HookRun 事件。
调用关系:由 AnalyticsReducer::ingest 调用;它把具体字段交给 codex_hook_run_metadata 整理。
调用图:调用 1 个内部函数(codex_hook_run_metadata);被 1 处调用(ingest);外部调用 1 个(HookRun)。
AnalyticsReducer::ingest_plugin_used752–758 ↗
fn ingest_plugin_used(&mut self, input: PluginUsedInput, out: &mut Vec<TrackEventRequest>)
作用:上报插件被使用。插件是给系统加功能的外部扩展。
数据流:输入插件使用信息 → 生成插件使用元数据 → 输出 PluginUsed 事件。
调用关系:由 AnalyticsReducer::ingest 调用;它用 codex_plugin_used_metadata 生成统计字段。
调用图:调用 1 个内部函数(codex_plugin_used_metadata);被 1 处调用(ingest);外部调用 1 个(PluginUsed)。
AnalyticsReducer::ingest_plugin_state_changed760–776 ↗
fn ingest_plugin_state_changed(
&mut self,
input: PluginStateChangedInput,
out: &mut Vec<TrackEventRequest>,
)
作用:上报插件状态变化,比如安装、卸载、启用、禁用。
数据流:输入插件和新状态 → 根据状态选择事件类型 → 输出对应的 PluginInstalled、PluginUninstalled、PluginEnabled 或 PluginDisabled 事件。
调用关系:由 AnalyticsReducer::ingest 调用;它依赖 plugin_state_event_type 和 codex_plugin_metadata 统一事件内容。
调用图:调用 2 个内部函数(codex_plugin_metadata, plugin_state_event_type);被 1 处调用(ingest);外部调用 4 个(PluginDisabled, PluginEnabled, PluginInstalled, PluginUninstalled)。
AnalyticsReducer::ingest_response778–836 ↗
async fn ingest_response(
&mut self,
connection_id: u64,
response: ClientResponse,
out: &mut Vec<TrackEventRequest>,
)
作用:处理客户端请求的成功响应。很多统计事件要等响应回来后才知道真实 ID 或结果。
数据流:输入连接号、客户端响应和输出列表 → 线程响应会发线程初始化事件;回合开始响应会补回合号;回合调整响应会进入专门处理 → 可能输出事件或更新状态。
调用关系:由 AnalyticsReducer::ingest 调用;它会调用 emit_thread_initialized、ingest_turn_steer_response 和 maybe_emit_turn_event。
调用图:调用 3 个内部函数(emit_thread_initialized, ingest_turn_steer_response, maybe_emit_turn_event);被 1 处调用(ingest)。
AnalyticsReducer::ingest_server_request838–952 ↗
fn ingest_server_request(&mut self, _connection_id: u64, request: ServerRequest)
作用:记录服务器向用户发起的审批请求。比如命令要不要执行、文件要不要改、权限要不要放开。
数据流:输入服务器请求 → 判断审批对象、触发原因、是否要额外权限或网络权限 → 保存为 pending_reviews,等待用户回应。
调用关系:由 AnalyticsReducer::ingest 调用;后续 ingest_server_response、ingest_effective_permissions_approval_response 或 ingest_server_request_aborted 会取出这份待审批状态。
调用图:调用 1 个内部函数(user_review_id);被 1 处调用(ingest);外部调用 1 个(option_i64_to_u64)。
AnalyticsReducer::ingest_server_response954–997 ↗
fn ingest_server_response(
&mut self,
completed_at_ms: u64,
response: ServerResponse,
out: &mut Vec<TrackEventRequest>,
)
作用:处理用户对服务器审批请求的回应。它把“同意、拒绝、取消”翻译成统计系统的状态。
数据流:输入完成时间和服务器响应 → 找到对应 pending_review → 转换审批结果 → 调用 emit_review_event 输出审批事件。
调用关系:由 AnalyticsReducer::ingest 调用;它通过 command_execution_review_result 或 file_change_review_result 得到统一的审核状态。
调用图:调用 3 个内部函数(emit_review_event, command_execution_review_result, file_change_review_result);被 1 处调用(ingest)。
AnalyticsReducer::ingest_effective_permissions_approval_response999–1018 ↗
fn ingest_effective_permissions_approval_response(
&mut self,
completed_at_ms: u64,
request_id: RequestId,
response: CoreRequestPermissionsResponse,
out: &mut V
作用:处理权限申请的审批回应。它专门用于有效权限请求这类审批。
数据流:输入完成时间、请求号和权限回应 → 取出待审批状态 → 判断是否批准以及是本回合还是本会话生效 → 输出审批事件。
调用关系:由 AnalyticsReducer::ingest 调用;它用 effective_permissions_review_result 翻译结果,再交给 emit_review_event。
调用图:调用 2 个内部函数(emit_review_event, effective_permissions_review_result);被 1 处调用(ingest)。
AnalyticsReducer::ingest_server_request_aborted1020–1037 ↗
fn ingest_server_request_aborted(
&mut self,
completed_at_ms: u64,
request_id: RequestId,
out: &mut Vec<TrackEventRequest>,
)
作用:处理审批请求被中止的情况。比如请求还没等到用户答复就取消了。
数据流:输入完成时间和请求号 → 找到待审批状态 → 标记为 Aborted 和无解决方案 → 输出审批事件。
调用关系:由 AnalyticsReducer::ingest 调用;它复用 emit_review_event 生成标准审批统计。
调用图:调用 1 个内部函数(emit_review_event);被 1 处调用(ingest)。
AnalyticsReducer::ingest_error_response1039–1050 ↗
fn ingest_error_response(
&mut self,
connection_id: u64,
request_id: RequestId,
error_type: Option<AnalyticsJsonRpcError>,
out: &mut Vec<TrackEventRequest>,
作用:处理客户端请求收到错误响应的情况。尤其是回合调整被拒绝时,需要上报拒绝原因。
数据流:输入连接号、请求号和错误类型 → 从 requests 里取出之前暂存的请求 → 交给 ingest_request_error_response 继续分类处理。
调用关系:由 AnalyticsReducer::ingest 调用;它本身只负责找回那条请求的上下文。
调用图:调用 1 个内部函数(ingest_request_error_response);被 1 处调用(ingest)。
AnalyticsReducer::ingest_request_error_response1052–1070 ↗
fn ingest_request_error_response(
&mut self,
connection_id: u64,
request: RequestState,
error_type: Option<AnalyticsJsonRpcError>,
out: &mut Vec<TrackEventReque
作用:把不同请求的错误响应分开处理。当前主要关心回合调整请求的失败。
数据流:输入连接号、请求状态和错误类型 → 如果是 TurnStart 暂不处理;如果是 TurnSteer 就转给 ingest_turn_steer_error_response。
调用关系:只由 ingest_error_response 调用;它是错误响应处理的小分流器。
调用图:调用 1 个内部函数(ingest_turn_steer_error_response);被 1 处调用(ingest_error_response)。
AnalyticsReducer::ingest_turn_steer_error_response1072–1087 ↗
fn ingest_turn_steer_error_response(
&mut self,
connection_id: u64,
pending_request: PendingTurnSteerState,
error_type: Option<AnalyticsJsonRpcError>,
out: &mut
作用:上报回合调整被拒绝。它会尽量把拒绝原因也带上。
数据流:输入连接号、待处理的回合调整请求和错误类型 → 把错误翻译成拒绝原因 → 输出一个结果为 Rejected 的 turn steer 事件。
调用关系:由 ingest_request_error_response 调用;它使用 rejection_reason_from_error_type,再交给 emit_turn_steer_event 发事件。
调用图:调用 2 个内部函数(emit_turn_steer_event, rejection_reason_from_error_type);被 1 处调用(ingest_request_error_response)。
AnalyticsReducer::ingest_notification1089–1216 ↗
async fn ingest_notification(
&mut self,
notification: ServerNotification,
out: &mut Vec<TrackEventRequest>,
)
作用:处理服务器主动发来的通知。这是工具开始、工具完成、回合开始、回合完成、diff 更新等实时信息的入口。
数据流:输入通知 → 按通知类型更新工具开始时间、工具计数、回合状态、最新 diff,或生成工具/审核/回合事件 → 把可上报事件追加到输出列表。
调用关系:由 AnalyticsReducer::ingest 调用;它会调用 tracked_tool_item_id、tool_item_event、ingest_guardian_review_completed、maybe_emit_turn_event 等函数。
调用图:调用 7 个内部函数(tool_item, ingest_guardian_review_completed, maybe_emit_turn_event, thread_context_or_warn, analytics_turn_status, tool_item_event, tracked_tool_item_id);被 1 处调用(ingest);外部调用 3 个(option_i64_to_u64, matches!, warn!)。
AnalyticsReducer::emit_thread_initialized1218–1267 ↗
fn emit_thread_initialized(
&mut self,
connection_id: u64,
thread: codex_app_server_protocol::Thread,
model: String,
initialization_mode: ThreadInitializationMo
作用:生成线程初始化事件,并保存线程元数据。线程是后续回合和工具事件的“大房间”。
数据流:输入连接号、线程对象、模型和初始化方式 → 查连接信息 → 整理线程元数据并保存 → 输出 ThreadInitialized 事件。
调用关系:ingest_response 在收到线程创建、恢复、分叉响应时调用它;它使用 ThreadMetadataState::from_thread_metadata。
调用图:调用 1 个内部函数(from_thread_metadata);被 1 处调用(ingest_response);外部调用 2 个(ThreadInitialized, try_from)。
AnalyticsReducer::ingest_compaction1269–1289 ↗
fn ingest_compaction(&mut self, input: CodexCompactionEvent, out: &mut Vec<TrackEventRequest>)
作用:上报上下文压缩事件。压缩用于把过长的对话内容整理成更短上下文。
数据流:输入压缩事实 → 查线程和连接上下文 → 补齐会话、客户端、运行时等信息 → 输出 Compaction 事件。
调用关系:由 AnalyticsReducer::ingest 调用;它用 AnalyticsDropSite::compaction 和 thread_context_or_warn 确保上下文完整。
调用图:调用 3 个内部函数(codex_compaction_event_params, compaction, thread_context_or_warn);被 1 处调用(ingest);外部调用 2 个(new, Compaction)。
AnalyticsReducer::ingest_goal1291–1309 ↗
fn ingest_goal(&mut self, input: CodexGoalEvent, out: &mut Vec<TrackEventRequest>)
作用:上报目标事件。目标描述用户或系统正在追踪的任务方向。
数据流:输入目标事实 → 查上下文 → 用线程元数据和连接信息补齐事件参数 → 输出 Goal 事件。
调用关系:由 AnalyticsReducer::ingest 调用;它依赖 codex_goal_event_params 组装最终字段。
调用图:调用 3 个内部函数(codex_goal_event_params, goal, thread_context_or_warn);被 1 处调用(ingest);外部调用 2 个(new, Goal)。
AnalyticsReducer::ingest_guardian_review_completed1311–1351 ↗
fn ingest_guardian_review_completed(
&mut self,
notification: codex_app_server_protocol::ItemGuardianApprovalReviewCompletedNotification,
out: &mut Vec<TrackEventRequest>,
作用:处理守护者审核完成通知。它把系统自动审核的结果转换成统一的审核事件。
数据流:输入守护者审核完成通知 → 翻译状态、审核对象、触发原因和权限需求 → 生成 PendingReviewState → 调用 emit_review_event 输出事件。
调用关系:由 ingest_notification 在收到 ItemGuardianApprovalReviewCompleted 时调用;它使用 guardian_review_result、guardian_review_subject_metadata 等辅助函数。
调用图:调用 5 个内部函数(emit_review_event, guardian_review_requested_additional_permissions, guardian_review_requested_network_access, guardian_review_result, guardian_review_subject_metadata);被 1 处调用(ingest_notification);外部调用 1 个(option_i64_to_u64)。
AnalyticsReducer::ingest_turn_steer_response1353–1376 ↗
fn ingest_turn_steer_response(
&mut self,
connection_id: u64,
request_id: RequestId,
response: TurnSteerResponse,
out: &mut Vec<TrackEventRequest>,
)
作用:处理回合调整成功。它表示用户追加或改变输入被系统接受了。
数据流:输入连接号、请求号和调整响应 → 找到之前暂存的调整请求 → 给对应回合的 steer_count 加一 → 输出 Accepted 的 turn steer 事件。
调用关系:由 ingest_response 调用;最后统一交给 emit_turn_steer_event 生成事件。
调用图:调用 1 个内部函数(emit_turn_steer_event);被 1 处调用(ingest_response)。
AnalyticsReducer::emit_turn_steer_event1378–1417 ↗
fn emit_turn_steer_event(
&mut self,
connection_id: u64,
pending_request: PendingTurnSteerState,
accepted_turn_id: Option<String>,
result: TurnSteerResult,
作用:真正生成回合调整事件。无论调整成功还是失败,都通过它输出统一格式。
数据流:输入连接、待处理请求、被接受的回合号、结果和拒绝原因 → 查连接和线程元数据 → 生成 TurnSteer 事件;缺资料则写警告并放弃。
调用关系:ingest_turn_steer_response 和 ingest_turn_steer_error_response 都调用它;它用 AnalyticsDropSite::turn_steer 帮助错误日志定位。
调用图:调用 2 个内部函数(turn_steer, warn_missing_analytics_context);被 2 处调用(ingest_turn_steer_error_response, ingest_turn_steer_response);外部调用 1 个(TurnSteer)。
AnalyticsReducer::emit_review_event1419–1465 ↗
fn emit_review_event(
&mut self,
pending_review: PendingReviewState,
reviewer: Reviewer,
status: ReviewStatus,
resolution: ReviewResolution,
completed_a
作用:真正生成审批统计事件。它统一处理用户审批和守护者审批。
数据流:输入待审批状态、审核者、状态、解决方式和完成时间 → 先更新工具项审批汇总 → 查线程上下文 → 输出 ReviewEvent。
调用关系:ingest_server_response、ingest_effective_permissions_approval_response、ingest_server_request_aborted 和 ingest_guardian_review_completed 都会调用它。
调用图:调用 5 个内部函数(review, record_item_review_summary, thread_context_or_warn, item_review_summary_key, observed_duration_ms);被 4 处调用(ingest_effective_permissions_approval_response, ingest_guardian_review_completed, ingest_server_request_aborted, ingest_server_response);外部调用 1 个(ReviewEvent)。
AnalyticsReducer::record_item_review_summary1467–1484 ↗
fn record_item_review_summary(
&mut self,
item_key: ToolItemKey,
reviewer: Reviewer,
status: ReviewStatus,
resolution: ReviewResolution,
pending_review:
作用:把某个工具项经历过的审批记录成汇总。这样工具完成事件里也能带上审批次数和最终结果。
数据流:输入工具项键、审核者、状态、解决方式和待审批详情 → 累加总审批数、用户/守护者审批数 → 更新最终批准结果和权限请求标记。
调用关系:emit_review_event 在发现审批属于某个工具项时调用它;后续 tool_item_event 会读取这份汇总。
调用图:调用 1 个内部函数(final_approval_outcome);被 1 处调用(emit_review_event)。
AnalyticsReducer::maybe_emit_turn_event1486–1544 ↗
async fn maybe_emit_turn_event(&mut self, turn_id: &str, out: &mut Vec<TrackEventRequest>)
作用:在信息凑齐时发出完整回合事件。它像一道闸门,只有配置、性能、完成状态等都到齐才开门。
数据流:输入回合号 → 检查 TurnState 是否有线程号、图片数、配置、性能、完成信息 → 查连接和线程元数据 → 输出 TurnEvent,并可能输出已接受代码行指纹事件 → 删除该回合状态。
调用关系:ingest_response、ingest_notification、ingest_turn_resolved_config、ingest_turn_token_usage、ingest_turn_profile 都会调用它;它使用 codex_turn_event_params 和 accepted_line_event_input。
调用图:调用 6 个内部函数(accepted_line_fingerprint_event_requests, accepted_line_repo_hash_for_cwd, turn, accepted_line_event_input, codex_turn_event_params, warn_missing_analytics_context);被 5 处调用(ingest_notification, ingest_response, ingest_turn_profile, ingest_turn_resolved_config, ingest_turn_token_usage);外部调用 2 个(new, TurnEvent)。
AnalyticsReducer::thread_connection_or_warn1546–1566 ↗
fn thread_connection_or_warn(
&self,
drop_site: AnalyticsDropSite<'_>,
) -> Option<&ConnectionState>
作用:按线程找到对应连接信息,找不到就写清楚警告。很多事件都必须知道来自哪个客户端连接。
数据流:输入定位对象 → 用线程号查线程状态,再查连接号,再查连接状态 → 返回连接状态;任一步失败都返回空并写日志。
调用关系:thread_context_or_warn 调用它;它通过 warn_missing_analytics_context 统一记录缺什么。
调用图:调用 1 个内部函数(warn_missing_analytics_context);被 1 处调用(thread_context_or_warn)。
AnalyticsReducer::thread_context_or_warn1568–1582 ↗
fn thread_context_or_warn(
&self,
drop_site: AnalyticsDropSite<'_>,
) -> Option<(&ConnectionState, &ThreadMetadataState)>
作用:一次性取到线程事件常用的完整上下文:连接信息和线程元数据。
数据流:输入定位对象 → 先取连接状态 → 再取线程元数据 → 成功返回二者;缺少任何一块就写警告并返回空。
调用关系:工具、审批、目标、压缩、守护者审核等事件生成前都会调用它,避免发出缺关键字段的统计事件。
调用图:调用 2 个内部函数(thread_connection_or_warn, warn_missing_analytics_context);被 5 处调用(emit_review_event, ingest_compaction, ingest_goal, ingest_guardian_review, ingest_notification)。
warn_missing_analytics_context1585–1606 ↗
fn warn_missing_analytics_context(
drop_site: &AnalyticsDropSite<'_>,
missing: MissingAnalyticsContext,
)
作用:统一打印“统计事件因为缺上下文被丢弃”的警告日志。它让排查问题时不用猜是哪类资料缺了。
数据流:输入事件定位对象和缺失类型 → 生成缺失上下文字段名和可选连接号 → 写一条 tracing 警告日志。
调用关系:emit_turn_steer_event、maybe_emit_turn_event、thread_connection_or_warn、thread_context_or_warn 在放弃事件时都会调用它。
调用图:被 4 处调用(emit_turn_steer_event, maybe_emit_turn_event, thread_connection_or_warn, thread_context_or_warn);外部调用 1 个(warn!)。
tracked_tool_item_id1608–1629 ↗
fn tracked_tool_item_id(item: &ThreadItem) -> Option<&str>
作用:判断一个线程项目是不是需要单独统计的工具项,并取出它的编号。
数据流:输入 ThreadItem → 如果是命令、文件修改、MCP 工具、动态工具、协作代理、网页搜索或图片生成,就返回 id;普通消息等返回空。
调用关系:ingest_notification 在工具开始和完成通知里调用它;没有 id 的项目不会进入工具项事件流程。
调用图:被 1 处调用(ingest_notification)。
item_review_summary_key1631–1642 ↗
fn item_review_summary_key(pending_review: &PendingReviewState) -> Option<ToolItemKey>
作用:判断一条审批是否应该归到某个工具项下面。不是所有审批都属于具体工具。
数据流:输入待审批状态 → 如果审核对象是命令、文件修改或 MCP 工具,就用线程号、回合号、工具项号组成键;权限类和网络访问类返回空。
调用关系:emit_review_event 调用它;返回键后 record_item_review_summary 才能把审批结果累积到工具事件里。
调用图:被 1 处调用(emit_review_event)。
tool_item_event1655–1970 ↗
fn tool_item_event(input: ToolItemEventInput<'_>) -> Option<TrackEventRequest>
作用:把一个完成的工具项转换成对应的统计事件。不同工具有不同字段,比如命令有退出码,文件修改有新增删除数量。
数据流:输入工具项、开始/完成时间、连接和线程上下文、审批汇总 → 按工具类型计算结果、失败原因和附加计数 → 输出对应的 TrackEventRequest;未完成或不支持的项目返回空。
调用关系:ingest_notification 在收到工具完成通知后调用它;它会进一步使用 tool_item_base、command_execution_outcome、file_change_counts、web_search_query_count 等辅助函数。
调用图:调用 12 个内部函数(collab_agent_tool_name, collab_tool_call_outcome, command_action_counts, command_execution_outcome, command_execution_tool_name, dynamic_tool_call_outcome, file_change_counts, image_generation_outcome, mcp_tool_call_outcome, patch_apply_outcome (+2 more));被 1 处调用(ingest_notification);外部调用 9 个(CollabAgentToolCall, CommandExecution, DynamicToolCall, FileChange, ImageGeneration, McpToolCall, WebSearch, option_i64_to_u64, usize_to_u64)。
command_action_counts1987–2001 ↗
fn command_action_counts(command_actions: &[CommandAction]) -> CommandActionCounts
作用:统计一条命令里包含的动作类型。比如读取文件、列文件、搜索等。
数据流:输入命令动作列表 → 遍历每个动作 → 输出总数、读取数、列文件数、搜索数、未知数。
调用关系:tool_item_event 在生成命令执行事件时调用它,把动作统计写进 CodexCommandExecutionEventParams。
调用图:被 1 处调用(tool_item_event);外部调用 3 个(default, len, usize_to_u64)。
tool_item_base2012–2050 ↗
fn tool_item_base(
thread_id: &str,
turn_id: &str,
item_id: String,
tool_name: String,
outcome: ToolItemOutcome,
context: ToolItemContext<'_>,
) -> CodexToolItemEventBase
作用:生成所有工具事件共有的基础字段。它避免每种工具重复填写线程、时间、审批和结果信息。
数据流:输入线程号、回合号、工具项号、工具名、执行结果和上下文 → 合并连接、运行时、线程来源、审批汇总、耗时等 → 输出 CodexToolItemEventBase。
调用关系:tool_item_event 处理每种工具时都会调用它;它内部用 observed_duration_ms 计算从开始到完成的观察耗时。
调用图:调用 1 个内部函数(observed_duration_ms);被 1 处调用(tool_item_event)。
observed_duration_ms2052–2054 ↗
fn observed_duration_ms(started_at_ms: u64, completed_at_ms: u64) -> Option<u64>
作用:计算开始时间到完成时间之间经过了多少毫秒。若时间倒挂,就不给出结果。
数据流:输入开始毫秒和完成毫秒 → 用安全减法计算差值 → 输出可选耗时。
调用关系:emit_review_event 用它算审批耗时;tool_item_base 用它算工具项生命周期耗时。
调用图:被 2 处调用(emit_review_event, tool_item_base)。
user_review_id2056–2058 ↗
fn user_review_id(request_id: &RequestId) -> String
作用:给用户审批生成一个统一格式的审核编号。它在原请求号前加上 user 前缀。
数据流:输入 RequestId → 格式化成 user:<request_id> → 输出字符串。
调用关系:ingest_server_request 在保存待用户审批状态时调用它,确保用户审批编号和守护者审核编号能区分开。
调用图:被 1 处调用(ingest_server_request);外部调用 1 个(format!)。
command_execution_review_result2060–2089 ↗
fn command_execution_review_result(
decision: CommandExecutionApprovalDecision,
) -> (ReviewStatus, ReviewResolution)
作用:把命令执行审批决定翻译成统计系统的审核状态和解决方式。
数据流:输入命令审批决定 → 区分接受、会话内接受、策略修改、网络规则修改、拒绝、取消 → 输出 ReviewStatus 和 ReviewResolution。
调用关系:ingest_server_response 在用户回应命令审批时调用它,然后把结果交给 emit_review_event。
调用图:被 1 处调用(ingest_server_response)。
file_change_review_result2091–2102 ↗
fn file_change_review_result(
decision: FileChangeApprovalDecision,
) -> (ReviewStatus, ReviewResolution)
作用:把文件修改审批决定翻译成统一审核结果。
数据流:输入文件修改审批决定 → 判断是批准、会话内批准、拒绝还是取消 → 输出状态和解决方式。
调用关系:ingest_server_response 在用户回应文件修改审批时调用它。
调用图:被 1 处调用(ingest_server_response)。
effective_permissions_review_result2104–2117 ↗
fn effective_permissions_review_result(
response: &CoreRequestPermissionsResponse,
) -> (ReviewStatus, ReviewResolution)
作用:把权限申请回应翻译成审核结果。没有授予任何权限就算拒绝。
数据流:输入权限回应 → 如果权限列表为空,输出 Denied;否则按授权范围是回合还是会话输出 Approved 和对应解决方式。
调用关系:ingest_effective_permissions_approval_response 调用它,再交给 emit_review_event 上报。
调用图:被 1 处调用(ingest_effective_permissions_approval_response)。
guardian_review_result2119–2137 ↗
fn guardian_review_result(
status: GuardianApprovalReviewStatus,
) -> Option<(ReviewStatus, ReviewResolution)>
作用:把守护者审核状态翻译成统一审核结果。进行中的状态不会上报完成事件。
数据流:输入守护者审核状态 → InProgress 返回空;批准、拒绝、超时、中止分别映射成对应 ReviewStatus → 输出可选结果。
调用关系:ingest_guardian_review_completed 调用它;如果返回空,就说明审核还没到终态,不生成事件。
调用图:被 1 处调用(ingest_guardian_review_completed)。
guardian_review_subject_metadata2139–2188 ↗
fn guardian_review_subject_metadata(
action: &GuardianApprovalReviewAction,
) -> (ReviewSubjectKind, String, ReviewTrigger)
作用:判断守护者审核的对象是什么,以及为什么触发审核。
数据流:输入守护者动作 → 区分命令、execve 拦截、补丁、网络访问、权限请求、MCP 工具 → 输出审核对象类型、对象名称和触发原因。
调用关系:ingest_guardian_review_completed 调用它来填充 PendingReviewState。
调用图:被 1 处调用(ingest_guardian_review_completed)。
guardian_review_requested_additional_permissions2190–2202 ↗
fn guardian_review_requested_additional_permissions(action: &GuardianApprovalReviewAction) -> bool
作用:判断守护者审核是否涉及额外权限申请。比如写文件、开网络、请求文件系统权限。
数据流:输入守护者动作 → 对不同动作检查权限字段 → 输出 true 或 false。
调用关系:ingest_guardian_review_completed 调用它;权限请求里的网络判断会交给 guardian_review_request_permissions_network_enabled。
调用图:调用 1 个内部函数(guardian_review_request_permissions_network_enabled);被 1 处调用(ingest_guardian_review_completed)。
guardian_review_requested_network_access2204–2215 ↗
fn guardian_review_requested_network_access(action: &GuardianApprovalReviewAction) -> bool
作用:判断守护者审核是否涉及网络访问。这个信息会写进审批和工具汇总。
数据流:输入守护者动作 → 网络访问动作直接返回 true;权限请求则检查 network.enabled;其他返回 false。
调用关系:ingest_guardian_review_completed 调用它;它复用 guardian_review_request_permissions_network_enabled。
调用图:调用 1 个内部函数(guardian_review_request_permissions_network_enabled);被 1 处调用(ingest_guardian_review_completed)。
guardian_review_request_permissions_network_enabled2217–2225 ↗
fn guardian_review_request_permissions_network_enabled(
permissions: &RequestPermissionProfile,
) -> bool
作用:检查权限配置里是否明确启用了网络。它是一个小型取值辅助函数。
数据流:输入请求权限配置 → 查看 network 字段和 enabled 值 → 没写就当 false,写了就返回对应布尔值。
调用关系:guardian_review_requested_additional_permissions 和 guardian_review_requested_network_access 都调用它。
调用图:被 2 处调用(guardian_review_requested_additional_permissions, guardian_review_requested_network_access)。
final_approval_outcome2227–2243 ↗
fn final_approval_outcome(
reviewer: Reviewer,
status: ReviewStatus,
resolution: ReviewResolution,
) -> FinalApprovalOutcome
作用:把最后一次审批结果变成更直观的最终批准结论。比如用户批准、用户拒绝、守护者拒绝。
数据流:输入审核者、审核状态和解决方式 → 按组合映射成 FinalApprovalOutcome → 输出最终结论。
调用关系:record_item_review_summary 调用它,把结果保存到工具项审批汇总里。
调用图:被 1 处调用(record_item_review_summary)。
command_execution_tool_name2245–2252 ↗
fn command_execution_tool_name(source: CommandExecutionSource) -> &'static str
作用:把命令来源翻译成统计里的工具名。不同来源的命令会归到不同名字。
数据流:输入命令执行来源 → 统一执行环境返回 unified_exec,用户 shell 返回 user_shell,代理命令返回 shell。
调用关系:tool_item_event 在生成命令执行工具事件时调用它。
调用图:被 1 处调用(tool_item_event)。
command_execution_outcome2254–2269 ↗
fn command_execution_outcome(
status: &CommandExecutionStatus,
) -> Option<(ToolItemTerminalStatus, Option<ToolItemFailureKind>)>
作用:把命令执行状态翻译成工具最终状态和失败原因。
数据流:输入命令状态 → 进行中返回空;完成返回 Completed;失败返回 ToolError;被拒绝返回 ApprovalDenied。
调用关系:tool_item_event 处理命令执行时调用它;返回空表示现在还不能发完成事件。
调用图:被 1 处调用(tool_item_event)。
patch_apply_outcome2271–2286 ↗
fn patch_apply_outcome(
status: &PatchApplyStatus,
) -> Option<(ToolItemTerminalStatus, Option<ToolItemFailureKind>)>
作用:把补丁应用状态翻译成工具最终状态和失败原因。补丁应用就是把文件改动真正写进去。
数据流:输入补丁状态 → 进行中返回空;完成、失败、被拒绝分别映射成统一工具状态。
调用关系:tool_item_event 处理文件修改事件时调用它。
调用图:被 1 处调用(tool_item_event)。
mcp_tool_call_outcome2288–2299 ↗
fn mcp_tool_call_outcome(
status: &McpToolCallStatus,
) -> Option<(ToolItemTerminalStatus, Option<ToolItemFailureKind>)>
作用:把 MCP 工具调用状态翻译成统一工具结果。MCP 是一种让外部工具接入的协议。
数据流:输入 MCP 工具状态 → 进行中返回空;完成返回 Completed;失败返回 Failed 和 ToolError。
调用关系:tool_item_event 处理 MCP 工具调用时调用它。
调用图:被 1 处调用(tool_item_event)。
dynamic_tool_call_outcome2301–2312 ↗
fn dynamic_tool_call_outcome(
status: &DynamicToolCallStatus,
) -> Option<(ToolItemTerminalStatus, Option<ToolItemFailureKind>)>
作用:把动态工具调用状态翻译成统一工具结果。动态工具是运行时才确定的一类工具。
数据流:输入动态工具状态 → 进行中返回空;完成或失败映射成统一终态和失败原因。
调用关系:tool_item_event 处理 DynamicToolCall 时调用它。
调用图:被 1 处调用(tool_item_event)。
collab_tool_call_outcome2314–2325 ↗
fn collab_tool_call_outcome(
status: &CollabAgentToolCallStatus,
) -> Option<(ToolItemTerminalStatus, Option<ToolItemFailureKind>)>
作用:把协作代理工具调用状态翻译成统一工具结果。协作代理工具用于启动、等待或控制其他代理。
数据流:输入协作代理工具状态 → 进行中返回空;完成返回 Completed;失败返回 Failed 和 ToolError。
调用关系:tool_item_event 处理 CollabAgentToolCall 时调用它。
调用图:被 1 处调用(tool_item_event)。
image_generation_outcome2327–2335 ↗
fn image_generation_outcome(status: &str) -> (ToolItemTerminalStatus, Option<ToolItemFailureKind>)
作用:把图片生成状态翻译成统一工具结果。这里用字符串判断失败或成功。
数据流:输入状态字符串 → failed 或 error 算失败并标记 ToolError;其他状态都算完成 → 输出终态和可选失败原因。
调用关系:tool_item_event 生成图片生成事件时调用它。
调用图:被 1 处调用(tool_item_event)。
collab_agent_tool_name2337–2345 ↗
fn collab_agent_tool_name(tool: &CollabAgentTool) -> &'static str
作用:把协作代理工具枚举翻译成统计里的工具名。比如 spawn_agent、send_input。
数据流:输入协作代理工具类型 → 按类型返回固定字符串名称。
调用关系:tool_item_event 处理协作代理工具事件时调用它。
调用图:被 1 处调用(tool_item_event)。
file_change_counts2355–2366 ↗
fn file_change_counts(changes: &[codex_app_server_protocol::FileUpdateChange]) -> FileChangeCounts
作用:统计一次文件修改里新增、更新、删除、移动各有多少个文件。
数据流:输入文件变更列表 → 遍历每个变更类型 → 输出 FileChangeCounts。
调用关系:tool_item_event 在生成文件修改统计事件时调用它。
调用图:被 1 处调用(tool_item_event);外部调用 1 个(default)。
dynamic_content_counts2375–2389 ↗
fn dynamic_content_counts(items: &[DynamicToolCallOutputContentItem]) -> DynamicContentCounts
作用:统计动态工具输出里文本和图片各有多少项。
数据流:输入输出内容项列表 → 遍历区分文字和图片 → 输出总数、文本数、图片数。
调用关系:tool_item_event 处理动态工具调用时使用它,把输出内容规模写进事件。
调用图:外部调用 2 个(len, usize_to_u64)。
web_search_action_kind2391–2398 ↗
fn web_search_action_kind(action: &WebSearchAction) -> WebSearchActionKind
作用:把网页搜索动作翻译成统计系统的动作类型。比如搜索、打开页面、页内查找。
数据流:输入 WebSearchAction → 按具体动作返回 Search、OpenPage、FindInPage 或 Other。
调用关系:tool_item_event 生成网页搜索事件时用它记录这次网页工具到底做了什么。
web_search_query_count2400–2411 ↗
fn web_search_query_count(query: &str, action: Option<&WebSearchAction>) -> Option<u64>
作用:估算一次网页搜索里实际有多少个查询词。不是所有网页动作都有查询词。
数据流:输入旧版 query 字符串和可选动作对象 → 搜索动作优先看 queries 列表,其次看单个 query;打开页面和页内查找返回空;没有动作时按旧字段判断。
调用关系:tool_item_event 在生成网页搜索事件时调用它。
调用图:被 1 处调用(tool_item_event)。
accepted_line_event_input2413–2441 ↗
fn accepted_line_event_input(
turn_id: &str,
turn_state: &TurnState,
) -> Option<(AcceptedLineFingerprintEventInput, PathBuf)>
作用:从回合最新 diff 里提取被接受的代码行指纹事件输入。diff 可以理解为文件改动清单。
数据流:输入回合号和回合状态 → 读取 latest_diff 并分析新增/删除行指纹 → 如果没有接受的行就返回空;否则补线程、模型、完成时间等 → 输出事件输入和工作目录。
调用关系:maybe_emit_turn_event 在发回合事件后调用它;随后会计算仓库哈希并生成 accepted line 指纹事件。
调用图:调用 1 个内部函数(accepted_line_fingerprints_from_unified_diff);被 1 处调用(maybe_emit_turn_event);外部调用 1 个(now_unix_seconds)。
codex_turn_event_params2443–2570 ↗
fn codex_turn_event_params(
app_server_client: CodexAppServerClientMetadata,
runtime: CodexRuntimeMetadata,
turn_id: String,
turn_state: &TurnState,
thread_metadata: &ThreadMetadat
作用:把完整的 TurnState 打包成回合统计事件参数。它是回合事件字段的总装配台。
数据流:输入客户端信息、运行时、回合号、回合状态和线程元数据 → 拆出配置、性能、token、错误、工具计数等 → 输出 CodexTurnEventParams。
调用关系:maybe_emit_turn_event 在确认回合资料完整后调用它;它会用 sandbox_policy_mode、collaboration_mode_mode、reasoning_summary_mode、personality_mode 做字段归一化。
调用图:调用 4 个内部函数(collaboration_mode_mode, personality_mode, reasoning_summary_mode, sandbox_policy_mode);被 1 处调用(maybe_emit_turn_event);外部调用 1 个(unreachable!)。
sandbox_policy_mode2572–2594 ↗
fn sandbox_policy_mode(permission_profile: &PermissionProfile, cwd: &Path) -> &'static str
作用:把复杂的权限配置翻译成简单的沙箱模式名称。沙箱是限制程序能读写哪里、能不能联网的安全笼子。
数据流:输入权限配置和当前目录 → 判断是否无沙箱、外部沙箱、全盘可写、只读、或工作区可写 → 输出固定字符串。
调用关系:codex_turn_event_params 调用它,把权限细节压缩成统计里好理解的 sandbox_policy 字段。
调用图:调用 2 个内部函数(file_system_sandbox_policy, network_sandbox_policy);被 1 处调用(codex_turn_event_params)。
collaboration_mode_mode2596–2601 ↗
fn collaboration_mode_mode(mode: ModeKind) -> &'static str
作用:把协作模式翻译成统计里的简单模式名。计划模式单独记录,其他执行或默认模式归为 default。
数据流:输入 ModeKind → Plan 返回 plan;Default、PairProgramming、Execute 返回 default。
调用关系:codex_turn_event_params 调用它。
调用图:被 1 处调用(codex_turn_event_params)。
reasoning_summary_mode2603–2608 ↗
fn reasoning_summary_mode(summary: Option<ReasoningSummary>) -> Option<String>
作用:整理推理摘要设置。没有摘要或明确为 None 时不写字段。
数据流:输入可选 ReasoningSummary → None 或 Some(None) 返回空;其他值转成字符串返回。
调用关系:codex_turn_event_params 调用它来填 reasoning_summary 字段。
调用图:被 1 处调用(codex_turn_event_params)。
personality_mode2610–2615 ↗
fn personality_mode(personality: Option<Personality>) -> Option<String>
作用:整理人格设置。人格是模型回复风格的一种配置。
数据流:输入可选 Personality → 没有或 None 返回空;其他人格转成字符串返回。
调用关系:codex_turn_event_params 调用它来填 personality 字段。
调用图:被 1 处调用(codex_turn_event_params)。
analytics_turn_status2617–2624 ↗
fn analytics_turn_status(status: codex_app_server_protocol::TurnStatus) -> Option<TurnStatus>
作用:把协议里的回合状态翻译成分析系统里的回合状态。进行中的回合不算最终状态。
数据流:输入 app-server 的 TurnStatus → Completed、Failed、Interrupted 映射为对应分析状态;InProgress 返回空。
调用关系:ingest_notification 在收到 TurnCompleted 通知时调用它。
调用图:被 1 处调用(ingest_notification)。
num_input_images2626–2631 ↗
fn num_input_images(input: &[UserInput]) -> usize
作用:数一次数组里有多少张图片输入。图片可能是上传图片,也可能是本地图片。
数据流:输入用户输入列表 → 遍历并筛选 Image 和 LocalImage → 输出数量。
调用关系:ingest_request 在记录 TurnStart 和 TurnSteer 请求时调用它,后续回合事件会带上这个数量。
调用图:被 1 处调用(ingest_request);外部调用 1 个(iter)。
rejection_reason_from_error_type2633–2640 ↗
fn rejection_reason_from_error_type(
error_type: Option<AnalyticsJsonRpcError>,
) -> Option<TurnSteerRejectionReason>
作用:把错误类型翻译成回合调整被拒绝的原因。没有错误类型就没有原因。
数据流:输入可选 AnalyticsJsonRpcError → 如果是 TurnSteer 或 Input 错误,就转换成 TurnSteerRejectionReason → 输出可选拒绝原因。
调用关系:ingest_turn_steer_error_response 调用它,再把结果交给 emit_turn_steer_event。
调用图:被 1 处调用(ingest_turn_steer_error_response)。
skill_id_for_local_skill2642–2658 ↗
fn skill_id_for_local_skill(
repo_url: Option<&str>,
repo_root: Option<&Path>,
skill_path: &Path,
skill_name: &str,
) -> String
作用:给本地技能生成稳定的匿名 ID。这样统计能识别同一个技能,又不直接暴露原始路径组合。
数据流:输入仓库地址、仓库根目录、技能路径和技能名 → 先规范化路径 → 拼出原始 ID 字符串 → 用 SHA1 哈希生成最终 ID。
调用关系:ingest_skill_invoked 调用它;测试 reducer_ingests_skill_invoked_fact 也会覆盖它的行为。
调用图:调用 1 个内部函数(normalize_path_for_skill_id);被 2 处调用(reducer_ingests_skill_invoked_fact, ingest_skill_invoked);外部调用 3 个(format!, update, new)。
normalize_path_for_skill_id2664–2682 ↗
fn normalize_path_for_skill_id(
repo_url: Option<&str>,
repo_root: Option<&Path>,
skill_path: &Path,
) -> String
作用:把技能路径规范化,保证同一个技能在不同写法下尽量得到同样路径。仓库技能用相对路径,个人或系统技能用绝对路径。
数据流:输入仓库地址、仓库根目录和技能路径 → 尝试转成真实绝对路径 → 如果有仓库根目录则尽量转为相对仓库路径 → 统一把反斜杠换成斜杠 → 输出字符串路径。
调用关系:skill_id_for_local_skill 调用它;多个路径相关测试也直接验证它。
调用图:被 5 处调用(normalize_path_for_skill_id_admin_scoped_uses_absolute_path, normalize_path_for_skill_id_repo_root_not_in_skill_path_uses_absolute_path, normalize_path_for_skill_id_repo_scoped_uses_relative_path, normalize_path_for_skill_id_user_scoped_uses_absolute_path, skill_id_for_local_skill);外部调用 1 个(canonicalize)。
tests::managed_full_disk_with_restricted_network_reports_external_sandbox2692–2703 ↗
fn managed_full_disk_with_restricted_network_reports_external_sandbox()
作用:测试一种特殊权限配置的统计名字是否正确。全盘可写但网络受限时,应报告为 external_sandbox。
数据流:构造一个托管沙箱权限配置 → 调用 sandbox_policy_mode → 断言结果是 external_sandbox。
调用关系:这是测试代码,保护 sandbox_policy_mode 的边界行为不被改错。
调用图:调用 2 个内部函数(from_runtime_permissions_with_enforcement, unrestricted);外部调用 1 个(assert_eq!)。
tests::guardian_review_result_maps_terminal_statuses2706–2712 ↗
fn guardian_review_result_maps_terminal_statuses()
作用:测试守护者审核状态映射。它确认进行中的审核不上报,超时会映射成 TimedOut。
数据流:输入 InProgress 和 TimedOut 两种状态 → 调用 guardian_review_result → 断言返回结果符合预期。
调用关系:这是测试代码,保护 guardian_review_result 的终态判断逻辑。
调用图:外部调用 1 个(assert!)。
客户端交付层
这些文件公开运行时客户端,用于为应用排队、归约、去重并交付 analytics 事件。
analytics/src/client.rs源码 ↗
这个文件像一个后台邮局。业务代码只要调用各种 track_xxx 方法,把“发生了什么”塞进队列就行;真正的整理、合并、发送都在后台异步完成。队列用 mpsc 通道(一种任务之间传消息的管道)接收 AnalyticsFact,也就是一条原始事实;AnalyticsReducer 会把这些事实加工成可发送的事件。发送前会先检查登录信息,只给 Codex 后端用户发;还会把某些必须单独发送的事件拆成独立请求。它也做了去重,比如同一轮里同一个 app 或插件只记一次,避免数据被刷爆。调试版本里还可以把事件写到本地文件,而不是发网络请求,方便开发者检查埋点内容。
AnalyticsEventsDestination::from_base_url75–78 ↗
fn from_base_url(base_url: String) -> Self
作用:根据后台基础地址决定分析事件要发到哪里。它会顺手看看调试环境里有没有要求把事件抓到本地文件。
数据流:进去一个基础网址 → 先读取环境变量里的抓包文件路径 → 再交给 from_base_url_and_capture_file 统一决定是走 HTTP 还是写文件 → 出来一个目的地配置。
调用关系:AnalyticsEventsClient::new 创建客户端时会用它。它自己不发送事件,只负责把“目的地”这件事先定好。
调用图:调用 1 个内部函数(analytics_capture_file_from_env);被 1 处调用(new);外部调用 1 个(from_base_url_and_capture_file)。
AnalyticsEventsDestination::from_base_url_and_capture_file80–103 ↗
fn from_base_url_and_capture_file(base_url: String, capture_file: Option<PathBuf>) -> Self
作用:把“后台网址”和“可选的本地抓包文件”变成最终发送目的地。调试版如果指定了抓包文件,就会关闭网络发送,改为写文件。
数据流:进去基础网址和可选文件路径 → 如果是调试版且有文件路径,就初始化抓包文件并返回 CaptureFile;否则清理网址末尾多余斜杠并拼出 analytics 接口地址 → 出来 Http 或 CaptureFile 目的地。
调用关系:from_base_url 会把环境变量读到的文件路径交给它。测试也会直接调用它,确认有无抓包文件时目的地选择正确。
调用图:调用 1 个内部函数(initialize);被 3 处调用(analytics_destination_ignores_capture_file_in_release, analytics_destination_uses_explicit_capture_file, analytics_destination_uses_http_without_capture_file);外部调用 3 个(format!, error!, warn!)。
analytics_capture_file_from_env106–116 ↗
fn analytics_capture_file_from_env() -> Option<PathBuf>
作用:在调试版本里读取一个环境变量,看看开发者是否想把分析事件保存到本地文件。正式版本永远返回空,避免误开本地抓包。
数据流:进去没有参数 → 调试版读取指定环境变量,并忽略空值 → 出来一个文件路径,或者没有路径。
调用关系:AnalyticsEventsDestination::from_base_url 会调用它。它只是提供配置线索,不决定最终怎么发送。
调用图:被 1 处调用(from_base_url);外部调用 1 个(var_os)。
AnalyticsEventsQueue::new119–134 ↗
fn new(auth_manager: Arc<AuthManager>, destination: AnalyticsEventsDestination) -> Self
作用:创建分析事件队列,并启动一个后台任务负责从队列里取事件、整理事件、发送事件。这样主程序只需要快速投递,不必等网络。
数据流:进去登录管理器和发送目的地 → 创建一个有容量限制的消息队列 → 后台循环接收 AnalyticsFact,用 AnalyticsReducer 转成 TrackEventRequest,再调用 send_track_events 发送 → 出来一个可被客户端持有的队列对象,同时带有去重用的记录表。
调用关系:AnalyticsEventsClient::new 在启用分析时会调用它。它启动的后台任务是整条分析链路的工人,后续 try_send 投进来的事实都会被它消费。
调用图:调用 1 个内部函数(send_track_events);外部调用 7 个(new, new, new, new, default, channel, spawn)。
AnalyticsEventsQueue::try_send136–141 ↗
AnalyticsEventsQueue::should_enqueue_app_used143–159 ↗
fn should_enqueue_app_used(
&self,
tracking: &TrackEventsContext,
app: &AppInvocation,
) -> bool
作用:判断某个 app 使用事件该不该入队,主要用来防止同一轮里同一个连接器重复上报。没有连接器 ID 的 app 会直接允许上报。
数据流:进去一轮追踪信息和一个 app 调用记录 → 如果 app 没有 connector_id 就返回允许;否则把 turn_id 和 connector_id 组成去重钥匙,放进一张带锁的集合 → 如果以前没见过就返回 true,见过就返回 false;集合太大时会清空。
调用关系:AnalyticsEventsClient::track_app_used 会先问它。只有它说“没重复”,事件才会继续交给 record_fact。
AnalyticsEventsQueue::should_enqueue_plugin_used161–174 ↗
fn should_enqueue_plugin_used(
&self,
tracking: &TrackEventsContext,
plugin: &PluginTelemetryMetadata,
) -> bool
作用:判断某个插件使用事件该不该入队,避免同一轮里同一个插件重复上报。它保护分析数据不被重复点击或重复触发污染。
数据流:进去一轮追踪信息和插件元数据 → 用 turn_id 加插件 ID 做去重钥匙 → 放进带锁集合,第一次出现返回 true,重复出现返回 false;集合过大时先清空。
调用关系:AnalyticsEventsClient::track_plugin_used 会调用它。通过检查后才会真正记录 PluginUsed 事件。
AnalyticsEventsClient::new178–188 ↗
fn new(
auth_manager: Arc<AuthManager>,
base_url: String,
analytics_enabled: Option<bool>,
) -> Self
作用:创建一个可用的分析客户端。它会根据配置决定是否真的启用队列和发送功能。
数据流:进去登录管理器、后台基础地址、是否启用分析的配置 → 先用 from_base_url 算出目的地 → 如果配置没有明确关闭,就创建 AnalyticsEventsQueue;如果关闭,就不创建队列 → 出来一个客户端对象。
调用关系:系统初始化分析客户端时会调用它。之后各处业务代码拿着这个客户端调用 track_xxx,上报不同事件。
调用图:调用 1 个内部函数(from_base_url);被 4 处调用(analytics_events_client_from_config, emit_subagent_session_started_includes_fork_lineage_from_session_configuration, make_session_and_context, make_session_and_context_with_auth_config_home_and_rx)。
AnalyticsEventsClient::disabled190–192 ↗
fn disabled() -> Self
作用:创建一个完全不会上报的分析客户端。测试或不想发送埋点的场景会用它。
数据流:进去没有参数 → 构造一个 queue 为 None 的客户端 → 出来的客户端仍然有所有 track_xxx 方法,但调用后不会入队。
调用关系:很多测试会使用它,保证测试不碰网络、不产生真实分析事件。record_fact 看到没有队列就会直接跳过。
调用图:被 33 处调用(command_execution_started_helper_emits_once, complete_command_execution_item_emits_declined_once_for_pending_command, guardian_command_execution_notifications_wrap_review_lifecycle, interrupted_subagent_activity_removes_missing_thread_watch, test_handle_token_count_event_emits_usage_and_rate_limits, test_handle_token_count_event_without_usage_info, test_handle_turn_complete_emits_completed_without_error, test_handle_turn_complete_emits_error_multiple_turns, test_handle_turn_complete_emits_failed_with_error, test_handle_turn_diff_emits_v2_notification (+15 more))。
AnalyticsEventsClient::track_skill_invocations194–208 ↗
fn track_skill_invocations(
&self,
tracking: TrackEventsContext,
invocations: Vec<SkillInvocation>,
)
作用:记录技能被调用的情况。技能列表为空时不会记录,避免产生没有意义的空事件。
数据流:进去追踪上下文和一组技能调用记录 → 如果列表为空就结束;否则包装成 SkillInvoked 自定义分析事实 → 交给 record_fact 入队。
调用关系:build_skill_injections 在构建技能注入时会调用它。它不直接发送,只把事实交给通用入口 record_fact。
调用图:调用 1 个内部函数(record_fact);被 1 处调用(build_skill_injections);外部调用 2 个(Custom, SkillInvoked)。
AnalyticsEventsClient::track_initialize210–224 ↗
fn track_initialize(
&self,
connection_id: u64,
params: InitializeParams,
product_client_id: String,
rpc_transport: AppServerRpcTransport,
)
作用:记录一次客户端初始化连接。它会带上连接 ID、初始化参数、产品客户端 ID、运行环境和 RPC 传输方式。
数据流:进去连接编号、初始化参数、产品客户端 ID 和传输类型 → 调用 current_runtime_metadata 取当前运行环境信息 → 组成 Initialize 分析事实 → 交给 record_fact。
调用关系:initialize 流程会调用它。它帮助后台知道客户端是怎么启动和连接进来的。
调用图:调用 2 个内部函数(record_fact, current_runtime_metadata);被 1 处调用(initialize)。
AnalyticsEventsClient::track_subagent_thread_started226–230 ↗
fn track_subagent_thread_started(&self, input: SubAgentThreadStartedInput)
作用:记录子代理线程开始运行。子代理可以理解为主任务派出去的“小助手”。
数据流:进去一个 SubAgentThreadStartedInput → 包装成 SubAgentThreadStarted 自定义事实 → 交给 record_fact。
调用关系:emit_subagent_session_started 会调用它。它把子代理启动这件事接入统一分析队列。
调用图:调用 1 个内部函数(record_fact);被 1 处调用(emit_subagent_session_started);外部调用 2 个(Custom, SubAgentThreadStarted)。
AnalyticsEventsClient::track_guardian_review232–241 ↗
fn track_guardian_review(
&self,
tracking: &GuardianReviewTrackContext,
result: GuardianReviewAnalyticsResult,
completed_at_ms: u64,
)
作用:记录 Guardian 审核结果。Guardian 可以理解为对某些操作做安全或权限检查的守门人。
数据流:进去审核追踪上下文、审核结果和完成时间 → 让 tracking 生成事件参数 → 包装成 GuardianReview 自定义事实 → 交给 record_fact。
调用关系:它在审核生命周期结束时使用。具体参数由 GuardianReviewTrackContext 生成,发送仍走统一 record_fact。
调用图:调用 2 个内部函数(record_fact, event_params);外部调用 3 个(new, Custom, GuardianReview)。
AnalyticsEventsClient::track_app_mentioned243–250 ↗
fn track_app_mentioned(&self, tracking: TrackEventsContext, mentions: Vec<AppInvocation>)
作用:记录某一轮对话里提到了哪些 app。没有提到任何 app 时不记录。
数据流:进去追踪上下文和 app 提及列表 → 如果列表为空就返回 → 否则包装成 AppMentionedInput → 交给 record_fact。
调用关系:调用方在识别到 app 被提及时使用它。它只负责收集事实,不负责去重或发送。
调用图:调用 1 个内部函数(record_fact);外部调用 2 个(Custom, AppMentioned)。
AnalyticsEventsClient::track_request252–269 ↗
fn track_request(
&self,
connection_id: u64,
request_id: RequestId,
request: &ClientRequest,
)
作用:记录客户端发来的关键请求,但只记录 TurnStart 和 TurnSteer 这类和一轮对话推进有关的请求。其他请求会被忽略,减少噪音。
数据流:进去连接 ID、请求 ID 和客户端请求 → 先检查请求类型是否值得记录 → 如果值得,就克隆请求并包装成 ClientRequest 事实 → 交给 record_fact。
调用关系:track_initialized_request 会调用它。它是请求进入系统时的埋点过滤器,只让重要请求进入分析管道。
调用图:调用 1 个内部函数(record_fact);被 1 处调用(track_initialized_request);外部调用 3 个(new, clone, matches!)。
AnalyticsEventsClient::track_app_used271–281 ↗
fn track_app_used(&self, tracking: TrackEventsContext, app: AppInvocation)
作用:记录某个 app 被实际使用。它会先做同一轮去重,避免同一个 app 被重复算多次。
数据流:进去追踪上下文和 app 调用记录 → 如果客户端没有队列就结束 → 调 should_enqueue_app_used 检查是否重复 → 通过后包装成 AppUsedInput → 交给 record_fact。
调用关系:业务代码在 app 真正被用到时调用它。它依赖队列里的去重表,再通过 record_fact 进入后台处理。
调用图:调用 1 个内部函数(record_fact);外部调用 2 个(Custom, AppUsed)。
AnalyticsEventsClient::track_hook_run283–287 ↗
fn track_hook_run(&self, tracking: TrackEventsContext, hook: HookRunFact)
作用:记录 hook 的运行情况。hook 可以理解为在某些节点自动触发的小脚本或小动作。
数据流:进去追踪上下文和 hook 运行事实 → 包装成 HookRunInput → 交给 record_fact。
调用关系:hook 执行后可调用它。它把 hook 结果转成统一的自定义分析事实。
调用图:调用 1 个内部函数(record_fact);外部调用 2 个(Custom, HookRun)。
AnalyticsEventsClient::track_plugin_used289–299 ↗
fn track_plugin_used(&self, tracking: TrackEventsContext, plugin: PluginTelemetryMetadata)
作用:记录插件被使用。它和 app 使用类似,会先做同一轮去重。
数据流:进去追踪上下文和插件元数据 → 如果客户端没有队列就结束 → 调 should_enqueue_plugin_used 检查是否重复 → 通过后包装成 PluginUsedInput → 交给 record_fact。
调用关系:插件功能被触发时使用它。它先走队列的去重判断,再走统一入队入口。
调用图:调用 1 个内部函数(record_fact);外部调用 2 个(Custom, PluginUsed)。
AnalyticsEventsClient::track_compaction301–305 ↗
fn track_compaction(&self, event: crate::facts::CodexCompactionEvent)
作用:记录上下文压缩事件。压缩通常是为了把很长的历史内容整理得更短,方便继续对话。
数据流:进去一个 CodexCompactionEvent → 装箱以减少大对象搬运成本 → 包装成 Compaction 自定义事实 → 交给 record_fact。
调用关系:压缩流程完成或发生时会调用它。它不处理压缩本身,只记录这件事。
调用图:调用 1 个内部函数(record_fact);外部调用 3 个(new, Custom, Compaction)。
AnalyticsEventsClient::track_goal_event307–311 ↗
fn track_goal_event(&self, event: CodexGoalEvent)
作用:记录目标相关事件,比如某个目标开始、推进或完成。它让分析后台能看见任务层面的进展。
数据流:进去一个 CodexGoalEvent → 装箱并包装成 Goal 自定义事实 → 交给 record_fact。
调用关系:track 会调用它,把目标事件接入分析系统。后续由 reducer 和发送函数处理。
调用图:调用 1 个内部函数(record_fact);被 1 处调用(track);外部调用 3 个(new, Custom, Goal)。
AnalyticsEventsClient::track_turn_resolved_config313–317 ↗
fn track_turn_resolved_config(&self, fact: TurnResolvedConfigFact)
作用:记录某一轮对话最终解析出来的配置。这样可以知道实际运行时用了哪些设置,而不只是用户写了什么配置。
数据流:进去 TurnResolvedConfigFact → 装箱并包装成 TurnResolvedConfig 自定义事实 → 交给 record_fact。
调用关系:配置解析完成后适合调用它。它把配置结果变成分析事实,交给通用队列。
调用图:调用 1 个内部函数(record_fact);外部调用 3 个(new, Custom, TurnResolvedConfig)。
AnalyticsEventsClient::track_turn_token_usage319–323 ↗
fn track_turn_token_usage(&self, fact: TurnTokenUsageFact)
作用:记录某一轮使用了多少 token。token 可以理解为模型读写文本时的计量单位,常用于统计成本和用量。
数据流:进去 TurnTokenUsageFact → 装箱并包装成 TurnTokenUsage 自定义事实 → 交给 record_fact。
调用关系:模型调用或一轮结束后可调用它。它只记录用量,真正发送由后台队列完成。
调用图:调用 1 个内部函数(record_fact);外部调用 3 个(new, Custom, TurnTokenUsage)。
AnalyticsEventsClient::track_turn_profile325–329 ↗
fn track_turn_profile(&self, fact: TurnProfileFact)
作用:记录一轮对话的性能画像,比如耗时分布之类的信息。它帮助发现哪里慢。
数据流:进去 TurnProfileFact → 装箱并包装成 TurnProfile 自定义事实 → 交给 record_fact。
调用关系:性能统计完成后使用它。它把画像数据放进统一分析流水线。
调用图:调用 1 个内部函数(record_fact);外部调用 3 个(new, Custom, TurnProfile)。
AnalyticsEventsClient::track_turn_codex_error331–335 ↗
fn track_turn_codex_error(&self, fact: TurnCodexErrorFact)
作用:记录某一轮里发生的 Codex 错误。这样后台能统计错误类型和频率。
数据流:进去 TurnCodexErrorFact → 装箱并包装成 TurnCodexError 自定义事实 → 交给 record_fact。
调用关系:错误被整理成事实后调用它。它不处理错误恢复,只负责上报线索。
调用图:调用 1 个内部函数(record_fact);外部调用 3 个(new, Custom, TurnCodexError)。
AnalyticsEventsClient::track_plugin_installed337–344 ↗
fn track_plugin_installed(&self, plugin: PluginTelemetryMetadata)
作用:记录插件被安装。它把插件状态标成 Installed。
数据流:进去插件元数据 → 组成 PluginStateChangedInput,状态设为 Installed → 包装成自定义事实 → 交给 record_fact。
调用关系:remote_plugin_install_response 会调用它。它让插件安装事件进入分析后台。
调用图:调用 1 个内部函数(record_fact);被 1 处调用(remote_plugin_install_response);外部调用 2 个(Custom, PluginStateChanged)。
AnalyticsEventsClient::track_plugin_uninstalled346–353 ↗
fn track_plugin_uninstalled(&self, plugin: PluginTelemetryMetadata)
作用:记录插件被卸载。它把插件状态标成 Uninstalled。
数据流:进去插件元数据 → 组成 PluginStateChangedInput,状态设为 Uninstalled → 包装成自定义事实 → 交给 record_fact。
调用关系:插件卸载流程可调用它。它和安装、启用、禁用共用同一种状态变化事件。
调用图:调用 1 个内部函数(record_fact);外部调用 2 个(Custom, PluginStateChanged)。
AnalyticsEventsClient::track_plugin_enabled355–362 ↗
fn track_plugin_enabled(&self, plugin: PluginTelemetryMetadata)
作用:记录插件被启用。它用于知道用户打开了哪些插件。
数据流:进去插件元数据 → 组成 PluginStateChangedInput,状态设为 Enabled → 包装成自定义事实 → 交给 record_fact。
调用关系:emit_plugin_toggle_events 会调用它。它记录插件开关变化中的“打开”动作。
调用图:调用 1 个内部函数(record_fact);被 1 处调用(emit_plugin_toggle_events);外部调用 2 个(Custom, PluginStateChanged)。
AnalyticsEventsClient::track_plugin_disabled364–371 ↗
fn track_plugin_disabled(&self, plugin: PluginTelemetryMetadata)
作用:记录插件被禁用。它用于知道用户关闭了哪些插件。
数据流:进去插件元数据 → 组成 PluginStateChangedInput,状态设为 Disabled → 包装成自定义事实 → 交给 record_fact。
调用关系:emit_plugin_toggle_events 会调用它。它记录插件开关变化中的“关闭”动作。
调用图:调用 1 个内部函数(record_fact);被 1 处调用(emit_plugin_toggle_events);外部调用 2 个(Custom, PluginStateChanged)。
AnalyticsEventsClient::record_fact373–377 ↗
fn record_fact(&self, input: AnalyticsFact)
作用:这是所有分析事实进入队列的统一入口。如果客户端被禁用,它会安静地什么都不做。
数据流:进去一条 AnalyticsFact → 检查客户端有没有队列 → 有队列就调用 queue.try_send 投递;没有队列就直接丢弃 → 不返回业务结果。
调用关系:几乎所有 track_xxx 方法最终都会调用它。它把上层各种事件统一收口到 AnalyticsEventsQueue。
调用图:被 26 处调用(track_app_mentioned, track_app_used, track_compaction, track_effective_permissions_approval_response, track_error_response, track_goal_event, track_guardian_review, track_hook_run, track_initialize, track_notification (+15 more))。
AnalyticsEventsClient::track_response379–400 ↗
fn track_response(
&self,
connection_id: u64,
request_id: RequestId,
response: ClientResponsePayload,
)
作用:记录客户端请求对应的关键响应,但只记录线程开始、恢复、分叉和一轮对话开始或调整这类重要响应。
数据流:进去连接 ID、请求 ID 和响应内容 → 先用类型匹配过滤掉不重要响应 → 重要响应会被装箱成 ClientResponse 事实 → 交给 record_fact。
调用关系:响应返回给客户端时可调用它。它像一道筛子,只把关键响应送进分析队列。
调用图:调用 1 个内部函数(record_fact);外部调用 2 个(new, matches!)。
AnalyticsEventsClient::track_error_response402–415 ↗
fn track_error_response(
&self,
connection_id: u64,
request_id: RequestId,
error: JSONRPCErrorError,
error_type: Option<AnalyticsJsonRpcError>,
)
作用:记录一次错误响应。它既保存 JSON-RPC 错误,也可以带上分析系统理解的错误分类。
数据流:进去连接 ID、请求 ID、错误对象和可选错误类型 → 包装成 ErrorResponse 事实 → 交给 record_fact。
调用关系:同名的上层错误跟踪流程会调用它。它让失败请求也能被纳入分析。
调用图:调用 1 个内部函数(record_fact);被 1 处调用(track_error_response)。
AnalyticsEventsClient::track_server_request417–422 ↗
fn track_server_request(&self, connection_id: u64, request: ServerRequest)
作用:记录服务器主动发给客户端的请求。这里的服务器请求是 app server 协议里的消息。
数据流:进去连接 ID 和 ServerRequest → 把请求装箱,组成 ServerRequest 分析事实 → 交给 record_fact。
调用关系:send_request_to_connections 在向连接发送请求时会调用它。它记录“服务器向外问了什么”。
调用图:调用 1 个内部函数(record_fact);被 1 处调用(send_request_to_connections);外部调用 1 个(new)。
AnalyticsEventsClient::track_server_response424–429 ↗
fn track_server_response(&self, completed_at_ms: u64, response: ServerResponse)
作用:记录服务器收到或完成的响应。它带上完成时间,方便分析耗时和生命周期。
数据流:进去完成时间和 ServerResponse → 把响应装箱,组成 ServerResponse 分析事实 → 交给 record_fact。
调用关系:notify_client_response 会调用它。它记录服务器请求链路的结果。
调用图:调用 1 个内部函数(record_fact);被 1 处调用(notify_client_response);外部调用 1 个(new)。
AnalyticsEventsClient::track_effective_permissions_approval_response431–442 ↗
fn track_effective_permissions_approval_response(
&self,
completed_at_ms: u64,
request_id: RequestId,
response: RequestPermissionsResponse,
)
作用:记录权限审批的响应。权限审批就是系统询问“这个操作可不可以做”后得到的答复。
数据流:进去完成时间、请求 ID 和权限响应 → 把响应装箱,组成 EffectivePermissionsApprovalResponse 事实 → 交给 record_fact。
调用关系:权限审批流程完成时会用它。它把审批结果接入同一套分析发送机制。
调用图:调用 1 个内部函数(record_fact);外部调用 1 个(new)。
AnalyticsEventsClient::track_server_request_aborted444–449 ↗
fn track_server_request_aborted(&self, completed_at_ms: u64, request_id: RequestId)
作用:记录服务器请求被中止。它说明某个正在等结果的请求没有正常完成。
数据流:进去完成时间和请求 ID → 组成 ServerRequestAborted 事实 → 交给 record_fact。
调用关系:cancel_all_requests、cancel_request、cancel_requests_for_thread 和 notify_client_error 会调用它。它专门记录取消或异常中断。
调用图:调用 1 个内部函数(record_fact);被 4 处调用(cancel_all_requests, cancel_request, cancel_requests_for_thread, notify_client_error)。
AnalyticsEventsClient::track_notification451–465 ↗
fn track_notification(&self, notification: ServerNotification)
作用:记录服务器通知里的关键事件,比如一轮开始、完成、差异更新、条目开始完成、Guardian 审核开始完成。其他通知会被忽略。
数据流:进去一个 ServerNotification → 先检查是否属于关心的几类通知 → 如果是,就装箱成 Notification 事实并交给 record_fact;否则直接返回。
调用关系:通知发出或收到时可调用它。它过滤通知噪音,只留下对分析有用的生命周期节点。
调用图:调用 1 个内部函数(record_fact);外部调用 3 个(new, Notification, matches!)。
send_track_events468–487 ↗
async fn send_track_events(
auth_manager: &AuthManager,
destination: &AnalyticsEventsDestination,
events: Vec<TrackEventRequest>,
)
作用:把 reducer 产出的事件真正送出去前做总控检查。没有事件、没有登录、或不是 Codex 后端账号时都不会发送。
数据流:进去登录管理器、目的地和一组事件 → 空列表直接返回 → 异步读取 auth 登录信息 → 确认使用 Codex 后端 → 用 track_event_request_batches 拆批 → 每批调用 send_track_events_request。
调用关系:AnalyticsEventsQueue::new 启动的后台任务会调用它。它是“整理完成”和“发网络请求”之间的关卡。
调用图:调用 3 个内部函数(send_track_events_request, track_event_request_batches, auth);被 1 处调用(new)。
track_event_request_batches489–510 ↗
fn track_event_request_batches(events: Vec<TrackEventRequest>) -> Vec<Vec<TrackEventRequest>>
作用:把一组事件拆成若干批。某些事件要求单独发送,它会把这些事件独立成一批,避免和别的事件混在一起。
数据流:进去一串 TrackEventRequest → 从前到后扫描 → 普通事件放进当前批;遇到必须单独发送的事件,就先收尾当前批,再给它单独建一批 → 出来多个事件批次。
调用关系:send_track_events 在发送前调用它。它决定 send_track_events_request 会被调用几次、每次带哪些事件。
调用图:被 1 处调用(send_track_events);外部调用 2 个(new, vec!)。
send_track_events_request512–553 ↗
async fn send_track_events_request(
auth: &CodexAuth,
destination: &AnalyticsEventsDestination,
events: Vec<TrackEventRequest>,
)
作用:发送一批分析事件请求。调试抓包模式下写文件;正常模式下发 HTTP POST 到分析后台。
数据流:进去认证信息、目的地和一批事件 → 空批次直接返回 → 包成 TrackEventsRequest → 调试版先尝试 capture_track_events_request,若成功就不走网络 → HTTP 模式下创建客户端,带认证头和 JSON 内容发请求 → 成功静默,失败或异常写警告日志。
调用关系:send_track_events 会按批调用它。它是这个文件里真正碰网络或抓包文件的发送点。
调用图:调用 2 个内部函数(capture_track_events_request, create_client);被 1 处调用(send_track_events);外部调用 2 个(auth_provider_from_auth, warn!)。
capture_track_events_request556–571 ↗
fn capture_track_events_request(
destination: &AnalyticsEventsDestination,
payload: &TrackEventsRequest,
) -> bool
作用:调试版本专用:把分析事件请求追加写入本地文件。它用于开发时检查将要上报的内容,而不是发到服务器。
数据流:进去目的地和请求载荷 → 如果目的地不是 CaptureFile,就返回 false → 如果是文件目的地,就调用 append_payload 写入;失败则记错误日志 → 返回 true,表示已经处理过,不该再走网络。
调用关系:send_track_events_request 在调试版发送前会先调用它。它让本地抓包模式覆盖网络发送。
调用图:调用 1 个内部函数(append_payload);被 1 处调用(send_track_events_request);外部调用 1 个(error!)。
app-server/src/analytics_utils.rs源码 ↗
这个文件解决的是“怎么安全、统一地创建统计上报工具”的问题。统计上报就是把一些使用事件发给后台,用来了解功能有没有被使用、是否出错等。这里的核心函数会拿到两个东西:一个是 AuthManager,也就是登录和认证信息的管理器;另一个是 Config,也就是应用配置。它从配置里取出 ChatGPT 服务的基础地址,并把末尾多余的斜杠去掉,避免拼接网址时出现双斜杠这类小问题。它还读取 analytics_enabled 这个开关,决定分析上报是否启用。最后,它把这些信息交给 AnalyticsEventsClient::new,造出真正负责发送分析事件的客户端。这个文件很小,但很重要,因为它把“按配置创建分析客户端”的规则集中在一处,避免不同入口各写一套、写出不一致的行为。
analytics_events_client_from_config7–16 ↗
fn analytics_events_client_from_config(
auth_manager: Arc<AuthManager>,
config: &Config,
) -> AnalyticsEventsClient
作用:这个函数根据当前登录管理器和配置,创建一个可以发送分析事件的客户端。别人用它,是为了不用关心分析服务地址怎么整理、分析开关从哪里读。
数据流:进去的是一个 AuthManager 的共享引用,以及一份 Config 配置。函数会读取配置里的 chatgpt_base_url,把末尾的“/”去掉,再读取 analytics_enabled 分析开关;然后把认证管理器、整理后的服务地址、开关状态一起交给 AnalyticsEventsClient::new。出来的是一个 AnalyticsEventsClient,供后续代码用来按需上报分析事件;它本身不修改配置,也不立刻发送事件。
调用关系:它通常在服务器启动或测试处理器构建时被调用,比如 start_uninitialized、build_test_processor 和 run_main_with_transport_options 会用它准备分析事件客户端。它自己不做复杂工作,而是把整理好的材料交给 AnalyticsEventsClient::new,由后者创建真正的客户端对象。
调用图:调用 1 个内部函数(new);被 3 处调用(start_uninitialized, build_test_processor, run_main_with_transport_options)。
目标事件适配器
此文件添加目标专用帮助器,通过共享客户端将目标生命周期变化转换为 analytics 载荷。
ext/goal/src/analytics.rs源码 ↗
这个文件像一个专门填报表的小助手。项目里的“目标”会经历创建、使用资源、状态变化、清除等事情,但分析系统不直接懂这些业务对象,所以这里的 GoalAnalytics 负责把目标里的信息整理成 CodexGoalEvent,再交给 AnalyticsEventsClient 发送出去。它还会避免乱报:比如状态没真的变,就不会记录“状态变化”事件;只有在统计用量时,才会带上累计用掉的 token(模型处理文字的计量单位)和时间。GoalEventAttribution 用来说明这次事件是不是由某一轮对话触发的;如果没有具体对话,就明确记成没有来源。
GoalAnalytics::new16–18 ↗
fn new(client: AnalyticsEventsClient) -> Self
作用:创建一个 GoalAnalytics,让后续代码可以用它来上报目标相关的分析事件。简单说,就是把真正发送事件的客户端包起来,变成一个更懂“目标”的记录器。
数据流:进去的是一个 AnalyticsEventsClient,也就是负责把事件送到分析系统的客户端;函数把它保存到 GoalAnalytics 里面;出来的是一个可以被其他地方复用的 GoalAnalytics 实例。
调用关系:它会在 new_with_host_capabilities 搭建系统能力时被调用,相当于启动阶段把“目标事件记录器”装配好。之后创建、用量结算、状态更新等流程都会拿这个记录器来用。
调用图:被 1 处调用(new_with_host_capabilities)。
GoalAnalytics::created20–26 ↗
fn created(
&self,
goal: &codex_state::ThreadGoal,
attribution: GoalEventAttribution<'_>,
)
作用:记录“目标被创建了”这件事。有人新建目标时调用它,方便之后统计目标创建情况。
数据流:进去的是目标本身,以及这次创建是否来自某一轮对话;函数把事件类型定为 Created;然后交给 track 统一整理并发送,自己不直接碰底层分析客户端。
调用关系:它由 handle_create 在处理创建目标时调用。它的角色是把“创建”这个业务动作翻译成标准分析事件,再把后续细节交给 GoalAnalytics::track。
调用图:调用 1 个内部函数(track);被 1 处调用(handle_create)。
GoalAnalytics::usage_accounted28–34 ↗
fn usage_accounted(
&self,
goal: &codex_state::ThreadGoal,
attribution: GoalEventAttribution<'_>,
)
作用:记录“目标的资源用量已经被结算”这件事。这里的资源主要包括 token 用量和耗时,用来统计一个目标实际花了多少成本。
数据流:进去的是目标和来源信息;函数把事件类型定为 UsageAccounted;track 会从目标里取出累计 token 和累计时间,一起放进分析事件里发出去。
调用关系:它由 account_active_goal_progress 在给当前目标结算进度和用量时调用。它不自己拼完整事件,而是把事件类别交给 GoalAnalytics::track 统一处理。
调用图:调用 1 个内部函数(track);被 1 处调用(account_active_goal_progress)。
GoalAnalytics::status_changed36–45 ↗
fn status_changed(
&self,
goal: &codex_state::ThreadGoal,
previous_status: Option<codex_state::ThreadGoalStatus>,
attribution: GoalEventAttribution<'_>,
)
作用:在目标状态真的发生变化时,记录一条“状态变了”的事件。它不会因为重复设置同一个状态就乱发统计。
数据流:进去的是当前目标、旧状态以及来源信息;函数先比较旧状态和目标现在的状态;如果旧状态存在并且不一样,就让 track 发送 StatusChanged 事件;如果没变,就什么也不发。
调用关系:它会被 account_active_goal_progress 和 handle_update 调用,分别对应结算进度时可能改变状态、以及外部更新目标时可能改变状态。它先做一道“是否真的变化”的检查,再把真正上报交给 GoalAnalytics::track。
调用图:调用 1 个内部函数(track);被 2 处调用(account_active_goal_progress, handle_update)。
GoalAnalytics::cleared47–49 ↗
fn cleared(&self, goal: &codex_state::ThreadGoal)
作用:记录“目标被清除”这件事。清除通常不是某一轮具体对话触发的,所以这里会明确标成没有对话来源。
数据流:进去的是被清除的目标;函数固定使用 NoTurn 表示没有具体 turn_id;然后把事件类型定为 Cleared,交给 track 生成并发送分析事件。
调用关系:它是清理目标流程里的记录入口。和 created、usage_accounted、status_changed 一样,它只说明发生了什么事,具体如何包装和发送统一交给 GoalAnalytics::track。
调用图:调用 1 个内部函数(track)。
GoalAnalytics::track51–76 ↗
fn track(
&self,
goal: &codex_state::ThreadGoal,
attribution: GoalEventAttribution<'_>,
event_kind: GoalEventKind,
)
作用:这是本文件真正把目标信息打包成分析事件的地方。其他公开方法只告诉它“发生了哪类事”,它负责把事件填完整并交给分析客户端发送。
数据流:进去的是目标、来源信息和事件类型;它从目标里取线程编号、目标编号、当前状态、是否有 token 预算等信息;如果是用量结算事件,还会带上累计 token 和累计耗时;最后生成 CodexGoalEvent,并调用 track_goal_event 送出去。
调用关系:它被 cleared、created、status_changed、usage_accounted 这些更具体的方法调用,是统一出口。它再把整理好的事件交给 AnalyticsEventsClient 的 track_goal_event,因此把业务代码和底层分析发送方式隔开了。
调用图:调用 1 个内部函数(track_goal_event);被 4 处调用(cleared, created, status_changed, usage_accounted)。