把 Cursor 的使用数据导进你的可观测平台:OTel 导出与 wire 格式

2026-08-18

先摆一个很具体的问题:团队里有人反馈这个月 token 用得多,管理员想拉一张「按会话排名的 token 消耗」表,看看是哪几段对话吃掉了大头。Cursor 的 OpenTelemetry Export 里有 cursor.token.usage 这个 metric,看起来直接按会话分组就完事了。

按官方文档的口径,这条路走不通。原因写在 cursor.com/docs/enterprise/opentelemetry-export 的「Joining sessions」一节里:metric 的 datapoint 不带 conversation.idrequest.idusage_event.id,文档自述这是为了让 metric 的基数保持有界(keeps metric cardinality bounded)。要做会话粒度的分析,得走 log。

这篇就沿着文档描述的路径把这条链路走一遍,重点落在导出配置与字段结构上。

先说清楚:这是 beta

cursor.com/docs/enterprise/opentelemetry-export 与配套的 wire 参考页 cursor.com/docs/enterprise/opentelemetry-export/wire 都在开头挂了 Beta 标记,原话是特性与 wire surface 在正式可用前仍可能变动。文档还写明这个 beta 只在 Enterprise 计划上提供,由管理员在官方文档写明的 Team Settings > OpenTelemetry Export 里配置。

另一条容易被忽略的前提:Export 是服务端运行的(Export runs server-side)。文档在前置条件里跟着写明:端点必须能从公网访问,Cursor 从一组固定的源 IP 出站。至于本机装的是 Windows 还是 macOS、Linux,官方文档没有给出任何操作系统层面的差异说明——按文档的步骤,你在 Team Settings 里要做的就是填地址与认证头,collector 本身跑在哪台机器上属于你自己的部署选择。

传输面:只认一种编码

wire 页把传输写得很死:

  • OTLP/HTTP binary protobuf(application/x-protobuf),POST
  • 端点是 <base>/v1/metrics<base>/v1/logs
  • Scope 是 cursor.telemetry / 0.1.0

设置页补了一句更要紧的:gRPC 和 JSON 不支持(gRPC and JSON are not supported)。如果你的可观测平台前面挂了一层只收 JSON 的网关,这条链路就直接不通,得先在中间放一个能收 protobuf 的 collector。

还有一个填错就白配的细节:在 Team Settings 里填的是 HTTPS base URL,不带 /v1 后缀,Cursor 自己会追加 /v1/metrics/v1/logs。文档举的例子是填 https://otel.example.com,而不是 https://otel.example.com:4318/v1

认证方面,文档要求你准备一个 bearer token 或 API key,作为请求头由 Cursor 发出,举例是 Authorization: Bearer <token>。凭据在 Cursor 侧加密存储;要轮换的话,文档明确说是编辑既有 destination 后保存,不要删掉重建——删除或停用会丢掉在途数据。

出口方向,文档写明 Cursor 通过服务端 egress 代理发出,流量来自一组固定的 /32 静态地址,并说这些地址不会在没有提前通知的情况下轮换。文档同时给出了自己的建议:把 TLS 和认证当作主要控制手段,IP allowlist 只在你的网络确有要求时再加。具体地址清单在官网那一页上,会不会调整以官方最新内容为准,这里不抄。

Collector 侧:文档给了什么

文档给的最小 OpenTelemetry Collector 配置是这样的:

receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318

processors:
  batch:

exporters:
  # Swap for your sink (datadog, clickhouse, logging, etc.)
  logging:
    verbosity: basic

service:
  pipelines:
    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [logging]
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [logging]

注意 service.pipelines 下只有 metricslogs 两条——因为 Cursor 这边只发这两种信号,文档在限制一节里明说没有 trace(no traces)。TLS 文档建议在 collector 前面的负载均衡、ingress 或 otelcol 自身的 TLS 设置里终结。

如果你用的是 Datadog Agent 的 OTLP ingest,文档给的是:

logs_enabled: true

otlp_config:
  receiver:
    protocols:
      http:
        endpoint: 0.0.0.0:4318
  logs:
    enabled: true

对应的环境变量写法文档也列了:DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT=0.0.0.0:4318DD_LOGS_ENABLED=trueDD_OTLP_CONFIG_LOGS_ENABLED=true。以上两段配置原样抄自官方文档,我们没有做过实测,以官方文档最新内容与你的 collector 实际行为为准。

启用流程官方文档写明是三步:Create destination(填 base URL 与认证头)、Test connection(校验 URL 与认证)、Enable,文档说导出会在约一分钟内开始。每个信号与遥测族各有独立开关;新出现的族默认打开,除非你关掉 auto_enable_new_families 这个设置。

字段结构:四个族、三个 metric、十个 log

wire 页把可导出的东西按 family 归了四类,family id 与 Team Settings 里的开关一一对应,新 destination 下全部默认开启:

Family id信号覆盖
model_usagemetrics + logstoken.usagecost.usageapi.requestapi.errorapi.correction
tool_callsmetricstool.calls
skills_hooks_pluginslogsskill.activatedhook.execution_completeplugin.installed
cloud_agentslogscloud_agent.pull_requestcloud_agent.setupcloud_agent.artifactcloud_agent.mcp_auth_error

metric 一共三个,全部是单调 delta sumcursor.token.usage(单位 {token},按 cursor.token.typeinput / output / cache_read / cache_creation)、cursor.tool.calls(单位 {call},每次完成的工具调用记 1)、cursor.cost.usage(单位 USD)。

cursor.tool.calls 的属性值得单独看一眼,因为它是唯一能区分内建工具与 MCP 工具的地方:cursor.tool.kindbuiltinmcpcursor.tool.name 是内建 id(文档举例 readshell)或客户侧 MCP 工具名,cursor.tool.statussuccess / failure / aborted,并且文档特意注明 MCP 从不报 abortedcursor.mcp.server.name 只在 MCP 情况下出现。

cursor.model.name 在 metric 与部分 log 上都是 optional,wire 页写明它是「routed-intent collapse 之后的公开模型名」:auto: 归到 Autothinking: 归到 Thinkingpro: 归到 Propremium: 归到 Premium,其余原样透传;bugbot 上或来源本身没有模型时会缺失。

log 事件一共十个,wire 页列出的严重级别取值是 INFO=9、WARN=13、ERROR=17。其中 cursor.hook.execution_completecursor.hook.type 枚举有九种:pre_tool_usepost_tool_usepost_tool_use_failurebefore_submit_promptafter_agent_responseafter_agent_thoughtstopsubagent_startsubagent_stopcursor.hook.outcomesuccess / blocked / failed / timeout,其中 failedtimeout 会以 ERROR 级别发出,还带一个 cursor.hook.duration_ms

cursor.skill.activatedcursor.skill.triggeragent_read / manually_attached / skill_name_in_prompt)和 cursor.skill.sourceunspecified / workspace / user / builtin / plugin / claude),如果这个 skill 来自 plugin,还会带 cursor.plugin.name。想统计「团队里哪些 skills 真的被 agent 自己读进去了、哪些是人手动挂的」,就是靠 trigger 这个字段。

四个 id,各管各的

这一段是整份 wire 文档里最容易搞混的地方,文档自己也专门列了一节:

  • cursor.event.id只是去重键,不是跨事件类型的 join 键。文档说它对重试、worker 重启、Cursor 内部 Kafka replay 都是确定性的,前缀 customer-telemetry:v1:... 稳定但整个字符串应当当作不透明值处理。
  • cursor.conversation.id:会话键。IDE 与 CLI 下是 composer chat UUID,Cloud Agent 下是对客户可见的 bc-... agent id。
  • cursor.usage_event.id:请求粒度键,只出现在 api.request / api.error / api.correction 上,用来跟 Cursor 的用量与账单导出对账。
  • cursor.request.id:可选的单次调用 id,文档写明它永远不会出现在 api.correctioncloud_agent.* 上。

resource 层面则是按 (team, user, surface, entrypoint, surface version) 分组,service.name 恒为 cursorcursor.team.id 总是存在,cursor.surfaceunspecified / desktop / cli / cloud_agent / bugbotcursor.user.id 是可选的,文档特意提醒别把它当成必然存在(cloud agent 上经常缺失)。

回到最初那个问题

文档给的 recipe 是:取 cursor.api.request 这一类 log 行,把 cursor.api.request.input_tokensoutput_tokens(需要的话还有两个 cache 字段)按 cursor.conversation.id 分组求和,得到每会话的 token 总量——文档明说这是 metric 给不了的。排完名之后,再用同一个 conversation.id 左连 cursor.skill.activated 看跑了哪些 skills、连 cursor.hook.execution_complete 看 hooks、连 cursor.cloud_agent.* 看云端那边的 setup、PR、artifact 与 MCP 认证失败。

但是有两处到此为止:cursor.tool.calls 是 metric-only,没有 conversation id,文档原话是每会话的工具归因还没有上到 wire 上(not on the wire yet),只能从 metric 出组织级的工具调用率;cursor.cost.usage 同样是 metric-only。另外 subagent 会有自己独立的 conversation id,父级 rollup 尚未导出

哪几处会咬到你

交付语义是两套,别混着用:

  • metrics 是 at-most-once,失败的 metric 请求不重试也不 replay,delta sum 在故障后会出现短暂缺口。
  • logs 是 at-least-once,瞬时失败会自动恢复,文档写明重试窗口约 7 天(这是文档写明的数值,随版本可能变动),但持续 4xx、坏 payload 这类终态拒绝不会 replay。要 exactly-once 的视图,先按 cursor.event.id 去重再 join。
  • 没有顺序保证。correction 可能晚于它修正的那个请求到达,按记录时间戳排序。
  • OTLP 的 partial success 会被尊重,被拒的条目不会重发。
  • 没有 backfill。启用 destination 之前的数据一概拿不到,导出端上游的源数据保留也是约 7 天(与重试窗口是两回事)。
  • metric 是 delta-only,文档提醒严格的 delta-to-cumulative processor 可能会丢掉 end-time-inverted 的点。

还有两条属于「文档里写了但还别依赖」:cursor.api.error 目前不带原始错误消息,低基数的 kind 与 status 属性文档标注为 planned,明说现在别依赖cursor.cloud_agent.pull_requestcreation_failed 已经在跑,但 opened 在生产者逐步铺开期间可能很稀疏

成本这一条要格外小心:cursor.cost.usage尽力而为的估算,不是账单。文档写明同一条 series 同时覆盖了包含额度的消耗与按需用量,BYOK 情况下它只反映 Cursor Token Rate,不含你在供应商那边的实际支出,要发票口径的数据得走 Admin 与 billing API。

和审计日志不是一回事

最后区分一下两条管线,这两者在 cursor.com/docs/enterprise/compliance-and-monitoring 里被并列提到过,但用途完全不同。审计日志记的是管理与安全事件——登录登出、成员增删与角色变更、API key 创建吊销、团队设置、仓库管理、Cloud Agent 环境、目录组、Privacy Mode 变更、团队规则与团队命令等,以 JSON 交付,文档给的示例里带 metadata.timestampmetadata.event_id,外层是 team_idip_addressuser_email 和一个事件专属的 event 对象。文档明确写着不记录 agent 回复与生成的代码内容,如果要记这些,官方建议用 hooks 自己写。

而 OpenTelemetry Export 走的是用量侧,与审计日志的 SIEM streaming 是两条独立管线。文档同样明说这边不含 prompt 内容。所以想同时满足「谁改了设置」和「token 花在哪」两类需求,得把两条都接上,别指望一条管线覆盖全部。

变更策略上,文档说这个 surface 是增量式的:要求消费方容忍未知的属性、事件与枚举值;新增的 metric 与事件会随覆盖面扩展出现,是否自动开启由 auto_enable_new_families 决定;重命名与移除会有明确通知。换句话说,别在解析层写死枚举,遇到没见过的值让它过去。


本文依据 Cursor 官方文档(cursor.com/docscursor.com/help)于 2026-08-18 的公开内容整理。 该产品闭源,本文只复述官方文档写明的机制,不推断其内部实现我们没有对文中涉及的功能做过实测,因此不涉及界面外观、操作手感与运行速度的任何描述。 该产品迭代频繁,文中涉及的设置项与命令随版本变动,请以官方文档最新内容为准。 本文不涉及订阅价格、额度与模型清单,相关信息请以官方定价与模型说明页为准。

安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

合规与许可条款请以官方原文与你所在组织的要求为准,本文不构成法律意见。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。