OpenRouter 安全与可观测

OpenRouter 安全与可观测

Google BigQuery

4 分钟阅读

Google BigQuery

将追踪发送到 Google BigQuery

Google BigQuery 是无服务器云数据仓库。OpenRouter 可以将追踪直接流式写入 BigQuery 表,用于自定义分析、长期存储和商业智能。每条追踪恰好对应一行,因此无需按 trace_id 分组或去重即可直接查询该表。

第 1 步:选择项目并启用 BigQuery API

  1. 打开 Google Cloud Console,创建或选择一个项目。
  2. 复制项目 ID(不是项目名称)。它是 IAM 和管理 > 设置(IAM & Admin > Settings) 中显示的小写标识符。
  3. API 和服务 > 库(APIs & Services > Library) 中搜索 BigQuery API,然后点击 启用(Enable)

第 2 步:创建数据集

  1. 打开 BigQuery > 资源管理器(Explorer),选择该项目,然后选择 创建数据集(Create dataset)
  2. 选择数据集 ID,例如 openrouter
  3. 谨慎选择数据集位置。数据集区域在创建后无法更改,请选择符合数据驻留要求的位置。
BigQuery 数据集

第 3 步:创建追踪表

在数据集中创建 openrouter_traces 表。配置该目标端时,可在 OpenRouter 控制台中找到确切的 SQL——点击 查看设置说明(View Setup Instructions)。将 DDL 中的 my-gcp-project 替换为你的项目 ID(若选择了不同的数据集或表 ID 也请一并替换),然后在 BigQuery SQL 工作区中运行:

BigQuery 表设置

创建完成后,该表会出现在你的数据集中:

BigQuery 数据集中的表

第 4 步:创建服务账号

  1. 在该项目中打开 IAM 和管理 > 服务账号(IAM & Admin > Service Accounts),点击 创建服务账号(Create service account)(例如 openrouter-broadcast)。
  2. 向其授予追踪数据集上的 BigQuery Data Editor 角色(不要在组织或项目级别授予):在 BigQuery 中打开该数据集的菜单,选择 共享 > 权限(Share > Permissions),添加服务账号邮箱,然后选择 BigQuery Data Editor。不要授予 BigQuery Job User——该目标端使用流式插入,不会创建查询作业。
  3. 打开服务账号的 密钥(Keys) 选项卡,选择 添加密钥 > 创建新密钥(Add key > Create new key),选择 JSON,然后下载密钥。

请妥善保管下载的密钥。其中包含私钥,不应提交到源代码控制。

第 5 步:在 OpenRouter 中启用广播

前往 设置 > 可观测性(Settings > Observability),打开 启用广播(Enable Broadcast) 开关。

启用广播

第 6 步:配置 BigQuery

点击 Google BigQuery 旁的编辑图标,并填写:

BigQuery 配置
  • Google Cloud project ID:包含该数据集的项目 ID。
  • Service-account key JSON:已下载 JSON 密钥文件的完整内容。
  • BigQuery dataset:上文创建的数据集 ID(默认:openrouter)。
  • BigQuery table:上文创建的表 ID(默认:openrouter_traces)。

第 7 步:测试并保存

点击 测试连接(Test Connection) 验证配置。连接测试会读取表的元数据以验证项目、数据集、表和凭据,然后检查凭据是否具有行插入权限,因此只读权限会在测试阶段失败,而不是等到后续每条追踪才失败。仅当测试通过时才会保存配置。

第 8 步:发送测试追踪

点击 发送追踪(Send Trace),或通过 OpenRouter 发出一次 API 请求,然后查询 BigQuery 表以确认已收到该追踪:

SELECT
  trace_id,
  span_id,
  timestamp,
  model,
  status,
  total_tokens,
  total_cost
FROM `my-gcp-project.openrouter.openrouter_traces`
ORDER BY timestamp DESC
LIMIT 20;
BigQuery 表预览

查询示例

按模型分析费用

SELECT
  DATE(timestamp) as day,
  model,
  SUM(total_cost) as total_cost,
  SUM(total_tokens) as total_tokens,
  COUNT(*) as request_count
FROM `my-gcp-project.openrouter.openrouter_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
  AND status = 'ok'
GROUP BY day, model
ORDER BY day DESC, total_cost DESC;

用户活动分析

SELECT
  user_id,
  COUNT(DISTINCT trace_id) as trace_count,
  COUNT(DISTINCT session_id) as session_count,
  SUM(total_tokens) as total_tokens,
  SUM(total_cost) as total_cost,
  AVG(duration_ms) as avg_duration_ms
FROM `my-gcp-project.openrouter.openrouter_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
GROUP BY user_id
ORDER BY total_cost DESC;

错误分析

SELECT
  trace_id,
  timestamp,
  model,
  level,
  finish_reason,
  metadata,
  input,
  output
FROM `my-gcp-project.openrouter.openrouter_traces`
WHERE status = 'error'
  AND timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR)
ORDER BY timestamp DESC;

模型服务提供商性能对比

SELECT
  provider_name,
  model,
  AVG(duration_ms) as avg_duration_ms,
  APPROX_QUANTILES(duration_ms, 100)[OFFSET(50)] as p50_duration_ms,
  APPROX_QUANTILES(duration_ms, 100)[OFFSET(95)] as p95_duration_ms,
  COUNT(*) as request_count
FROM `my-gcp-project.openrouter.openrouter_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
  AND status = 'ok'
GROUP BY provider_name, model
HAVING request_count >= 10
ORDER BY avg_duration_ms;

按 API 密钥统计用量

SELECT
  api_key_name,
  COUNT(DISTINCT trace_id) as trace_count,
  SUM(total_cost) as total_cost,
  SUM(prompt_tokens) as prompt_tokens,
  SUM(completion_tokens) as completion_tokens
FROM `my-gcp-project.openrouter.openrouter_traces`
WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY api_key_name
ORDER BY total_cost DESC;

访问 JSON 列

attributesinputoutputmetadatamodel_parametersresource_attributes 列为 JSON 类型。使用 BigQuery 的 JSON 函数查询嵌套字段:

SELECT
  trace_id,
  JSON_VALUE(metadata, '$.custom_field') as custom_value,
  JSON_VALUE(attributes, '$."gen_ai.request.model"') as requested_model
FROM `my-gcp-project.openrouter.openrouter_traces`
WHERE JSON_VALUE(metadata, '$.custom_field') IS NOT NULL;

要解析输入消息:

SELECT
  trace_id,
  JSON_VALUE(input, '$.messages[0].role') as first_message_role,
  JSON_VALUE(input, '$.messages[0].content') as first_message_content
FROM `my-gcp-project.openrouter.openrouter_traces`
LIMIT 10;

表结构设计

类型化列

该表结构将常用查询字段提取为类型化列,以便高效筛选和聚合:

  • 标识符trace_iduser_idsession_id
  • 时间戳:用于时间序列分析的 TIMESTAMP
  • 模型信息:用于费用和性能分析
  • 指标:用于计费的 Token 与费用

JSON 列

访问频率较低、结构可变的数据存储在 JSON 列中:

  • attributes:完整的 OTEL 属性集
  • input/output:可变的消息结构
  • metadata:用户定义的键值
  • model_parameters:模型特定配置

tags 列是重复的 STRING 列(ARRAY<STRING>)。使用 BigQuery 的 JSON_VALUEJSON_QUERY 函数查询 JSON 字段。

自定义元数据

trace 字段中的自定义元数据存储在 metadata JSON 列中。你可以使用 BigQuery 的 JSON 函数进行查询。

支持的元数据键

BigQuery 映射说明
trace_idtrace_id column / metadata JSON用于归组的自定义追踪标识符
trace_namemetadata JSON追踪的自定义名称
span_namemetadata JSON中间 Span 的名称
generation_namemetadata JSONLLM 生成的名称

示例

{
  "model": "openai/gpt-4o",
  "messages": [{ "role": "user", "content": "Forecast next quarter revenue..." }],
  "user": "user_12345",
  "session_id": "session_abc",
  "trace": {
    "trace_name": "Revenue Forecasting",
    "generation_name": "Generate Forecast",
    "department": "finance",
    "quarter": "Q2-2026",
    "model_version": "v3"
  }
}

查询自定义元数据

SELECT
  trace_id,
  JSON_VALUE(metadata, '$.department') as department,
  JSON_VALUE(metadata, '$.quarter') as quarter,
  JSON_VALUE(metadata, '$.model_version') as model_version,
  total_cost,
  total_tokens
FROM `my-gcp-project.openrouter.openrouter_traces`
WHERE JSON_VALUE(metadata, '$.department') IS NOT NULL
ORDER BY timestamp DESC;

补充说明

  • user 字段会映射到类型化列 user_id
  • session_id 字段会映射到类型化列 session_id
  • trace 中的所有自定义元数据键都存储在 metadata JSON 列中,便于灵活查询

故障排除

  • 找不到项目或权限被拒绝:确认所配置的项目 ID 是包含该数据集的项目,且服务账号属于预期项目。
  • 找不到表:确认数据集和表 ID,以及该表是在已配置的数据集位置中创建的。
  • 403 permission denied:向服务账号授予该数据集上的 BigQuery Data Editor。项目级访问可能受组织策略限制,请直接核对该数据集权限。
  • 400 invalid or schema mismatch:将表结构与设置说明中的 DDL 进行比对。尤其注意时间戳必须是 TIMESTAMP,嵌套追踪字段必须是 JSONtags 必须是 ARRAY<STRING>

更多资源

隐私模式

当该目标端启用 隐私模式 时,追踪中会排除提示词和补全内容。其余追踪数据——Token 用量、费用、时间信息、模型信息以及自定义元数据——仍会正常发送。详情见 隐私模式