本文档介绍了如何使用 Cloud Logging 和 Cloud Monitoring 监控 Knowledge Catalog(以前称为 Dataplex Universal Catalog)数据洞见扫描的 token 消耗情况。通过跟踪每次扫描和每个作业的 token 用量,您可以了解模型用量趋势并估算整个项目的结算费用。
令牌使用情况日志记录的工作原理
当数据洞见扫描作业 (jsonPayload.type="DATA_DOCUMENTATION") 以 SUCCEEDED 状态完成时,Knowledge Catalog 会向 dataplex.googleapis.com/data_scan 日志写入一个日志条目。此日志条目在 jsonPayload.dataDocumentation 下包含一个 billableTokenUsage 块,用于记录作业的 token 细分。作业的其他日志条目(例如 CREATED、STARTED、FAILED 或 CANCELLED 事件)不包含 billableTokenUsage 块。
在 billableTokenUsage 代码块中,您可以跟踪三个主要 token 类别来估算结算费用:
| 指标 | 说明 | 公式(使用 billableTokenUsage 字段) |
|---|---|---|
| 未缓存的输入 token | 并非由上下文缓存提供的输入 token,包括工具使用提示 token。 | max(promptTokenCount - cachedContentTokenCount, 0) + toolUsePromptTokenCount |
| 缓存的输入 token | 从上下文缓存中提供的输入 token。缓存的 token 会单独跟踪,因为它们不按标准非缓存输入费率计费。 | cachedContentTokenCount |
| 输出 token 数 | 生成的回答 token 加上推理(思考)token。思考 token 按输出 token 计费。 | candidatesTokenCount + thoughtsTokenCount |
如需了解 token 价格详情,请参阅 Data Cloud Agent 价格。
日志条目中的令牌字段
每个 dataplex.googleapis.com/data_scan 日志条目都包含标准资源和作业标识符,例如 resource.labels.datascan_id、resource.labels.location、jsonPayload.jobId 和 jsonPayload.state。对于成功的数据洞见扫描,jsonPayload.dataDocumentation.billableTokenUsage 对象包含以下字段:
| 字段 | 说明 |
|---|---|
promptTokenCount |
发送给模型的提示 token 总数,包括缓存的 token。 |
cachedContentTokenCount |
由上下文缓存提供的提示 token 所占的比例。 |
toolUsePromptTokenCount |
由工具调用(例如架构、配置文件或查询历史记录上下文)添加的提示词元。 |
candidatesTokenCount |
生成的回答 Token,不包括思考 Token。 |
thoughtsTokenCount |
模型生成的推理(思考)token。 |
以下示例展示了成功的数据洞见扫描作业的 dataplex.googleapis.com/data_scan 日志条目:
{
"resource": {
"type": "dataplex.googleapis.com/DataScan",
"labels": {
"datascan_id": "my-insights-scan",
"location": "us-central1",
"resource_container": "projects/1234567890"
}
},
"jsonPayload": {
"type": "DATA_DOCUMENTATION",
"jobId": "8f2b3c4d-1234-5678-9abc-def012345678",
"state": "SUCCEEDED",
"dataDocumentation": {
"billableTokenUsage": {
"promptTokenCount": "20412",
"candidatesTokenCount": "7053"
}
}
}
}
在此示例中,作业未使用上下文缓存、思考 token 或工具调用。因此,非缓存输入词元等于 20,412,缓存输入词元等于 0,输出词元等于 7,053。
准备工作
在查询数据洞见令牌日志或创建监控信息中心之前,请先完成以下前提条件。
所需的角色
如需获得监控数据洞见 token 使用情况所需的权限,请让管理员向您授予项目的以下 IAM 角色:
-
查看数据扫描日志并运行 Observability Analytics 查询:日志查看器 (
roles/logging.viewer) -
在 Monitoring 中创建自定义信息中心:Monitoring Editor (
roles/monitoring.editor) -
在 Monitoring 中查看自定义信息中心:Monitoring Viewer (
roles/monitoring.viewer)
如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限。
启用 API
启用 Dataplex、Logging、Monitoring API(如果尚未启用)。
启用 API 所需的角色
如需启用 API,您需要拥有 serviceusage.services.enable 权限。如果您创建了项目,则可能已经通过 Owner 角色 (roles/owner) 拥有此权限。否则,您可以通过 Service Usage Admin 角色 (roles/serviceusage.serviceUsageAdmin) 获得此权限。了解如何授予角色。
验证日志路由配置
数据洞见扫描会以日志名称 dataplex.googleapis.com/data_scan 写入日志。本文档中的信息中心配置会从项目的 _Default 日志存储桶中读取这些日志。验证项目的日志路由是否满足以下条件:
_Default接收器已启用,会将日志路由到_Default日志存储桶。_Default接收器没有丢弃dataplex.googleapis.com/data_scan或DATA_DOCUMENTATION日志的排除过滤器。
控制台
在 Google Cloud 控制台中,前往日志记录 > 日志路由器页面。
找到
_Default接收器,并验证其状态是否为已启用,其目标是否为_Default存储桶。点击接收器的菜单 (),然后选择查看接收器详情。验证没有任何排除过滤器与
dataplex.googleapis.com/data_scan或DATA_DOCUMENTATION匹配。
gcloud
描述 _Default 接收器,以检查其状态、目标位置和排除过滤器:
gcloud logging sinks describe _Default --project=PROJECT_ID
将 PROJECT_ID 替换为您的 Google Cloud项目 ID。
验证 disabled 未设置为 true,destination 以 /buckets/_Default 结尾,并且 exclusions 下没有与 data_scan 匹配的条目。
在 _Default 存储桶上启用 Observability Analytics
如需对数据洞见扫描日志运行 SQL 查询并为自定义 Observability Analytics 信息中心提供支持,请将项目的 _Default 日志存储桶升级为使用 Observability Analytics。
在 _Default 日志存储桶上启用 Observability Analytics 之前,请查看以下注意事项:
- 将存储桶升级到 Observability Analytics 后,无法撤消,并且不会产生额外费用。
- 您无需创建关联的 BigQuery 数据集。
Observability Analytics SQL 查询仅返回升级完成后到达的日志。较早的日志仍可在 Logs Explorer 中查看,但不会显示在 SQL 查询结果中。升级存储桶后,请至少运行一次数据洞见扫描,以便信息中心有数据可显示。
控制台
在 Google Cloud 控制台中,前往日志记录 > 日志存储页面。
找到
_Default存储桶所在的行,然后查看可使用 Observability Analytics 列:- 如果该列显示打开,则表示 Observability Analytics 已启用。
- 如果该列显示升级,请点击升级并确认升级。
等待几分钟,直到列状态更改为打开。
gcloud
升级
_Default日志存储桶以启用 Observability Analytics:gcloud logging buckets update _Default \ --location=global --enable-analytics --project=PROJECT_ID请将 PROJECT_ID 替换为您的 Google Cloud 项目 ID。
几分钟后,验证升级是否已完成:
gcloud logging buckets describe _Default \ --location=global --project=PROJECT_ID确认输出中包含
analyticsEnabled: true。
在 Logs Explorer 中查看每个作业的 token 用量
如需检查单个数据洞见扫描作业的 token 消耗情况,请使用 Logs Explorer 或 Google Cloud CLI。如需在整个项目中按小时或按天汇总数据,您可以使用 Observability Analytics 创建信息中心。
控制台
在 Google Cloud 控制台中,前往 Logging > Logs Explorer 页面。
如果查询编辑器未显示,请开启显示查询。
在查询编辑器中输入以下查询:
log_id("dataplex.googleapis.com/data_scan") jsonPayload.type="DATA_DOCUMENTATION" jsonPayload.dataDocumentation.billableTokenUsage.promptTokenCount:*可选:如需过滤特定扫描或作业执行,请将以下行之一附加到查询:
resource.labels.datascan_id="DATA_SCAN_ID" jsonPayload.jobId="JOB_ID"替换以下内容:
- DATA_SCAN_ID:数据洞见的 ID。
- JOB_ID:特定扫描作业运行的 ID。
选择时间范围(例如过去 24 小时),然后点击运行查询。
展开日志条目,然后依次打开
jsonPayload>dataDocumentation>billableTokenUsage,以检查该作业的 token 数量。
gcloud
读取最近的数据洞见扫描日志条目,并将令牌字段设置为表格格式:
gcloud logging read \
'log_id("dataplex.googleapis.com/data_scan") AND jsonPayload.type="DATA_DOCUMENTATION" AND jsonPayload.dataDocumentation.billableTokenUsage.promptTokenCount:*' \
--project=PROJECT_ID --freshness=1d --limit=20 \
--format='table(timestamp, resource.labels.datascan_id:label=SCAN,
jsonPayload.jobId:label=JOB,
jsonPayload.dataDocumentation.billableTokenUsage.promptTokenCount:label=PROMPT,
jsonPayload.dataDocumentation.billableTokenUsage.cachedContentTokenCount:label=CACHED,
jsonPayload.dataDocumentation.billableTokenUsage.toolUsePromptTokenCount:label=TOOL_USE,
jsonPayload.dataDocumentation.billableTokenUsage.candidatesTokenCount:label=CANDIDATES,
jsonPayload.dataDocumentation.billableTokenUsage.thoughtsTokenCount:label=THOUGHTS)'
将 PROJECT_ID 替换为您的 Google Cloud项目 ID。
输出表中的空列表示该字段有 0 个 token。
如需将这些字段转换为指标,请使用词元用量日志记录的工作原理中的公式。
使用 Observability Analytics 查询 token 使用情况
您也可以直接在Google Cloud 控制台中的“日志分析”页面上运行或自定义 widget SQL 查询。
每日和每小时图表共享同一个图表查询,该查询会为每个扫描作业返回一行。每个图表 widget 都会按天 (timeBinSize: 86400s) 或按小时 (timeBinSize: 3600s) 对行进行分箱和求和。如果您修改图表查询并将其粘贴回信息中心 widget,请保留列别名 timestamp、total_non_cached_input_tokens、total_cached_input_tokens 和 total_output_tokens。
如需将查询过滤为单次扫描,请将以下条件附加到 WHERE 子句:
AND JSON_VALUE(resource.labels, '$.datascan_id') = 'DATA_SCAN_ID'
将 DATA_SCAN_ID 替换为数据洞见扫描的 ID。
示例图表查询(每日和每小时图表)
SELECT
timestamp,
GREATEST(
IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.promptTokenCount') AS INT64), 0)
- IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.cachedContentTokenCount') AS INT64), 0),
0)
+ IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.toolUsePromptTokenCount') AS INT64), 0)
AS total_non_cached_input_tokens,
IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.cachedContentTokenCount') AS INT64), 0)
AS total_cached_input_tokens,
IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.candidatesTokenCount') AS INT64), 0)
+ IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.thoughtsTokenCount') AS INT64), 0)
AS total_output_tokens
FROM `PROJECT_ID.global._Default._Default`
WHERE
log_id = 'dataplex.googleapis.com/data_scan'
AND JSON_VALUE(json_payload, '$.type') = 'DATA_DOCUMENTATION'
AND JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.promptTokenCount') IS NOT NULL
将 PROJECT_ID 替换为您的 Google Cloud项目 ID。
每个扫描作业的查询示例
SELECT
timestamp,
JSON_VALUE(resource.labels, '$.datascan_id') AS data_scan_id,
JSON_VALUE(resource.labels, '$.location') AS location,
JSON_VALUE(json_payload, '$.jobId') AS job_id,
JSON_VALUE(json_payload, '$.state') AS state,
GREATEST(
IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.promptTokenCount') AS INT64), 0)
- IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.cachedContentTokenCount') AS INT64), 0),
0)
+ IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.toolUsePromptTokenCount') AS INT64), 0)
AS non_cached_input_tokens,
IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.cachedContentTokenCount') AS INT64), 0)
AS cached_input_tokens,
IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.candidatesTokenCount') AS INT64), 0)
+ IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.thoughtsTokenCount') AS INT64), 0)
AS output_tokens
FROM `PROJECT_ID.global._Default._Default`
WHERE
log_id = 'dataplex.googleapis.com/data_scan'
AND JSON_VALUE(json_payload, '$.type') = 'DATA_DOCUMENTATION'
AND JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.promptTokenCount') IS NOT NULL
ORDER BY timestamp DESC
LIMIT 500
将 PROJECT_ID 替换为您的 Google Cloud项目 ID。
使用 Observability Analytics 创建信息中心
如需直观呈现和跟踪模型 token 随时间的使用情况,您可以使用 Observability Analytics 创建自定义 Monitoring 信息中心。
此方法会直接针对 _Default 日志存储桶中的数据洞见扫描日志运行 SQL 查询。它具有以下几项优势:
- 准确性:查询直接从日志条目 JSON 载荷(而非汇总指标估算值)计算确切的 token 数量。
- 精细的作业历史记录:除了汇总图表之外,您还可以在表格视图中检查各个扫描作业。
- 无需额外费用:将
_Default日志存储桶升级到 Observability Analytics 不会产生额外费用。
此选项会创建一个 Monitoring 信息中心,其中包含三个对日志运行 SQL 查询的 widget:
- 每日 token 用量(堆叠条形图)
- 每小时 token 用量(折线图)
- 每个扫描作业的 token 使用情况(显示最近 500 个作业的表格)
以下信息中心 JSON 配置使用内置 @__project_id 变量 (@__project_id.global._Default._Default),该变量会自动查询您在其中打开信息中心的项目。您无需在导入 JSON 之前修改其中的任何项目占位符。
控制台
在 Google Cloud 控制台中,前往 Monitoring 的“信息中心”页面。
点击创建信息中心。
在工具栏中,点击 设置,选择 JSON,然后选择 JSON 编辑器。
将编辑器的现有内容替换为下一部分中提供的信息中心 JSON 定义。
点击应用更改。启用自动保存后,Monitoring 会将信息中心保存为
Data Insights - Token Usage。首次打开信息中心时,每个 widget 都会提示您确认在项目的 BigQuery 引擎上运行 SQL 查询。验证提示是否列出了您的项目,然后点击每个 widget 上的继续执行。
在信息中心工具栏中选择一个时间范围(例如,过去 7 天)。
gcloud
- 将信息中心 JSON 定义保存到名为
dashboard.json的本地文件中。 在 Monitoring 中创建信息中心:
gcloud monitoring dashboards create \ --project=PROJECT_ID \ --config-from-file=dashboard.json将 PROJECT_ID 替换为您的Google Cloud 项目 ID。
在 Google Cloud 控制台中打开“信息中心”页面,选择 Insights Scan - Token Usage,然后点击每个 widget 上的继续执行,以授权执行 SQL 查询。
信息中心 JSON 定义
如需创建信息中心,请展开以下部分并复制 JSON 定义:
JSON 定义
{
"displayName": "Data Insights - Token Usage",
"mosaicLayout": {
"columns": 48,
"tiles": [
{
"xPos": 0,
"yPos": 0,
"width": 24,
"height": 16,
"widget": {
"title": "Daily token usage",
"xyChart": {
"chartOptions": {
"mode": "COLOR",
"displayHorizontal": false
},
"dataSets": [
{
"plotType": "STACKED_BAR",
"targetAxis": "Y1",
"breakdowns": [],
"dimensions": [
{
"column": "timestamp",
"columnType": "TIMESTAMP",
"sortColumn": "timestamp",
"sortOrder": "SORT_ORDER_ASCENDING",
"timeBinSize": "86400s"
}
],
"measures": [
{
"column": "total_non_cached_input_tokens",
"aggregationFunction": {
"type": "sum",
"parameters": []
}
},
{
"column": "total_cached_input_tokens",
"aggregationFunction": {
"type": "sum",
"parameters": []
}
},
{
"column": "total_output_tokens",
"aggregationFunction": {
"type": "sum",
"parameters": []
}
}
],
"timeSeriesQuery": {
"opsAnalyticsQuery": {
"sql": "SELECT\n timestamp,\n GREATEST(\n IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.promptTokenCount') AS INT64), 0)\n - IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.cachedContentTokenCount') AS INT64), 0),\n 0)\n + IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.toolUsePromptTokenCount') AS INT64), 0)\n AS total_non_cached_input_tokens,\n IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.cachedContentTokenCount') AS INT64), 0)\n AS total_cached_input_tokens,\n IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.candidatesTokenCount') AS INT64), 0)\n + IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.thoughtsTokenCount') AS INT64), 0)\n AS total_output_tokens\nFROM `@__project_id.global._Default._Default`\nWHERE\n log_id = 'dataplex.googleapis.com/data_scan'\n AND JSON_VALUE(json_payload, '$.type') = 'DATA_DOCUMENTATION'\n AND JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.promptTokenCount') IS NOT NULL"
}
}
}
],
"yAxis": {
"label": "tokens",
"scale": "LINEAR"
}
}
}
},
{
"xPos": 24,
"yPos": 0,
"width": 24,
"height": 16,
"widget": {
"title": "Hourly token usage",
"xyChart": {
"chartOptions": {
"mode": "COLOR",
"displayHorizontal": false
},
"dataSets": [
{
"plotType": "LINE",
"targetAxis": "Y1",
"breakdowns": [],
"dimensions": [
{
"column": "timestamp",
"columnType": "TIMESTAMP",
"sortColumn": "timestamp",
"sortOrder": "SORT_ORDER_ASCENDING",
"timeBinSize": "3600s"
}
],
"measures": [
{
"column": "total_non_cached_input_tokens",
"aggregationFunction": {
"type": "sum",
"parameters": []
}
},
{
"column": "total_cached_input_tokens",
"aggregationFunction": {
"type": "sum",
"parameters": []
}
},
{
"column": "total_output_tokens",
"aggregationFunction": {
"type": "sum",
"parameters": []
}
}
],
"timeSeriesQuery": {
"opsAnalyticsQuery": {
"sql": "SELECT\n timestamp,\n GREATEST(\n IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.promptTokenCount') AS INT64), 0)\n - IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.cachedContentTokenCount') AS INT64), 0),\n 0)\n + IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.toolUsePromptTokenCount') AS INT64), 0)\n AS total_non_cached_input_tokens,\n IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.cachedContentTokenCount') AS INT64), 0)\n AS total_cached_input_tokens,\n IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.candidatesTokenCount') AS INT64), 0)\n + IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.thoughtsTokenCount') AS INT64), 0)\n AS total_output_tokens\nFROM `@__project_id.global._Default._Default`\nWHERE\n log_id = 'dataplex.googleapis.com/data_scan'\n AND JSON_VALUE(json_payload, '$.type') = 'DATA_DOCUMENTATION'\n AND JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.promptTokenCount') IS NOT NULL"
}
}
}
],
"yAxis": {
"label": "tokens",
"scale": "LINEAR"
}
}
}
},
{
"xPos": 0,
"yPos": 16,
"width": 48,
"height": 20,
"widget": {
"title": "Token usage per scan job (latest 500)",
"timeSeriesTable": {
"columnSettings": [],
"dataSets": [
{
"tableTemplate": "",
"timeSeriesQuery": {
"opsAnalyticsQuery": {
"sql": "SELECT\n timestamp,\n JSON_VALUE(resource.labels, '$.datascan_id') AS data_scan_id,\n JSON_VALUE(resource.labels, '$.location') AS location,\n JSON_VALUE(json_payload, '$.jobId') AS job_id,\n JSON_VALUE(json_payload, '$.state') AS state,\n GREATEST(\n IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.promptTokenCount') AS INT64), 0)\n - IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.cachedContentTokenCount') AS INT64), 0),\n 0)\n + IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.toolUsePromptTokenCount') AS INT64), 0)\n AS non_cached_input_tokens,\n IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.cachedContentTokenCount') AS INT64), 0)\n AS cached_input_tokens,\n IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.candidatesTokenCount') AS INT64), 0)\n + IFNULL(SAFE_CAST(JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.thoughtsTokenCount') AS INT64), 0)\n AS output_tokens\nFROM `@__project_id.global._Default._Default`\nWHERE\n log_id = 'dataplex.googleapis.com/data_scan'\n AND JSON_VALUE(json_payload, '$.type') = 'DATA_DOCUMENTATION'\n AND JSON_VALUE(json_payload, '$.dataDocumentation.billableTokenUsage.promptTokenCount') IS NOT NULL\nORDER BY timestamp DESC\nLIMIT 500"
}
}
}
],
"metricVisualization": "NUMBER"
}
}
}
]
}
}
限制
- 仅当
dataplex.googleapis.com/data_scan日志未被排除或未从_Default日志存储桶中路由出去时,令牌指标才是完整的。 - 系统只会统计包含 token 元数据的成功作业。不含
billableTokenUsage块的日志条目会从查询和指标中排除。 - 使用这些信息中心可以监控用量趋势和估算费用,但不能用于核对官方 Cloud Billing 账单。
- 每个 Google Cloud 项目都需要有自己的信息中心设置。
- SQL 查询不会返回在
_Default存储桶上启用 Observability Analytics 之前写入的日志条目。 - 每个作业的表格 widget 最多显示所选时间范围内最近的 500 个作业。如需检查较旧的作业,请查询 Logs Explorer。
后续步骤
- 详细了解结构化数据的数据洞见。
- 了解如何将数据洞见用于结构化数据。
- 了解如何监控 Knowledge Catalog 日志。
- 了解如何监控 Knowledge Catalog 指标。