跳到主要内容

查询指标

Mastra 通过三种界面公开相同的五种 OLAP 查询(getMetricAggregategetMetricBreakdowngetMetricTimeSeriesgetMetricPercentiles 以及 discovery helper):进程内 store accessor、运行时 HTTP API 和 mastra api metric CLI。三者接受相同的 Zod 验证输入结构,因此你可以从一次性的 CLI 调查转向编程式仪表板 Tool,而无需重新学习 API。

何时使用
何时使用的直接链接

  • 在 Studio 之外构建自定义仪表板或 KPI 卡片。
  • 为 token 成本或延迟超过阈值时触发的定时告警提供数据。
  • 为 Agent 提供读取自身性能指标并在聊天中解释这些指标的 Tool。
  • 从终端使用 mastra api metric ... 进行一次性调查。

有关可观测性存储本身的设置,请参阅指标概览。可查询的指标名称列表请参阅自动指标参考

备注

指标查询由 observability domain 提供,它需要支持 OLAP 的存储(本地使用 DuckDB,生产环境使用 ClickHouse)。设置方法请参阅指标概览。如果未配置可观测性存储,getStore('observability') 返回 null

使用界面
使用界面的直接链接

进程内
进程内的直接链接

在 Tool、server 路由或 Workflow 步骤内,从 Mastra storage 获取可观测性存储:

src/mastra/tools/agent-latency-tool.ts
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

export const agentLatencyTool = createTool({
id: 'agentLatency',
description: 'Average agent latency over the last hour.',
inputSchema: z.object({}),
execute: async (_input, context) => {
const observability = await context.mastra!.getStorage()!.getStore('observability')
if (!observability) {
throw new Error('Observability domain is not configured (requires DuckDB or ClickHouse)')
}

const result = await observability.getMetricAggregate({
name: ['mastra_agent_duration_ms'],
aggregation: 'avg',
filters: {
timestamp: { start: new Date(Date.now() - 60 * 60 * 1000) },
},
})

return { averageMs: result.value }
},
})

当配置的后端不支持 OLAP 查询时,getStore('observability') 返回 null

HTTP
HTTP的直接链接

mastra dev server(以及任何已部署的 Mastra 运行时)在 /api/observability/metrics/* 下公开相同查询。Aggregate、breakdown、time series 和 percentile endpoint 使用 POST 接收 JSON body。Discovery endpoint 使用带查询参数的 GET

curl -sS -X POST http://localhost:4111/api/observability/metrics/aggregate \
-H "content-type: application/json" \
-d '{"name":["mastra_agent_duration_ms"],"aggregation":"avg"}'

可用路由:

  • POST /api/observability/metrics/aggregate
  • POST /api/observability/metrics/breakdown
  • POST /api/observability/metrics/timeseries
  • POST /api/observability/metrics/percentiles
  • GET /api/observability/metrics(原始行,分页)
  • GET /api/observability/discovery/metric-names
  • GET /api/observability/discovery/metric-label-keys
  • GET /api/observability/discovery/metric-label-values

@mastra/client-js SDK 将相同路由包装为 mastraClient.getMetricAggregate(...)getMetricBreakdown(...) 等方法。

CLI
CLI的直接链接

mastra api metric ... 使用一个 JSON 参数调用相同 endpoint,因此 Agent 或 shell 脚本无需编写代码即可获取指标:

mastra api metric aggregate \
'{"name":["mastra_agent_duration_ms"],"aggregation":"avg"}' \
--url http://localhost:4111

CLI 默认以托管的 Mastra 可观测性(https://observability.mastra.ai)为目标。传入 --url http://localhost:4111 可查询本地 mastra dev server。完整命令列表请参阅 mastra api metric aggregate 及其附近条目。

查询
查询的直接链接

getMetricAggregate
getmetricaggregate的直接链接

返回单个标量,是 KPI 卡片的构建基础。

输入:

  • name:一个或多个指标名称的数组。
  • aggregation'sum' | 'avg' | 'min' | 'max' | 'count' | 'count_distinct' | 'last' 之一。
  • filters:可选的过滤器对象
  • comparePeriod:可选的 'previous_period' | 'previous_day' | 'previous_week',用于周期对比。

响应:

  • valuepreviousValuechangePercent
  • 对于 token 指标,还包括 estimatedCostcostUnitpreviousEstimatedCostcostChangePercent
src/mastra/tools/token-cost-tool.ts
const observability = await mastra.getStorage()!.getStore('observability')

const cost = await observability!.getMetricAggregate({
name: ['mastra_model_total_input_tokens', 'mastra_model_total_output_tokens'],
aggregation: 'sum',
comparePeriod: 'previous_day',
})

console.log(cost.value, cost.estimatedCost, cost.costUnit, cost.changePercent)

getMetricBreakdown
getmetricbreakdown的直接链接

按一个或多个维度对行分组,并聚合每个组,是 top-N 表格(例如“按 Agent 划分的 token”)的构建基础。

输入:

  • name:指标名称数组。
  • groupBy:用于分组的字段数组(例如 ['entityName'])。
  • aggregation:与上文相同的枚举。
  • limit:server 端 top-K 上限。高基数 groupBy 必须提供。
  • orderDirection'ASC' | 'DESC'(默认为 DESC)。
  • filters:可选。

响应:groups[],每项包含 dimensions(组键到值的记录)、valueestimatedCost

const byAgent = await observability!.getMetricBreakdown({
name: ['mastra_model_total_input_tokens'],
groupBy: ['entityName'],
aggregation: 'sum',
limit: 10,
orderDirection: 'DESC',
})

getMetricTimeSeries
getmetrictimeseries的直接链接

按固定间隔对值分桶,是折线图和柱状图的构建基础。

输入:

  • name:指标名称数组。
  • interval'1m' | '5m' | '15m' | '1h' | '1d' 之一。
  • aggregation:与上文相同的枚举。
  • groupBy:可选。省略时,多个指标名称会合并为一个序列。要将指标分开,请为每个指标分别调用一次。
  • filters:可选。

响应:series[],每项包含 namecostUnit,以及由 { timestamp, value, estimatedCost } 组成的 points[]

const inputTokens = await observability!.getMetricTimeSeries({
name: ['mastra_model_total_input_tokens'],
aggregation: 'sum',
interval: '1h',
filters: {
timestamp: { start: new Date(Date.now() - 24 * 60 * 60 * 1000) },
},
})

getMetricPercentiles
getmetricpercentiles的直接链接

返回按时间分桶的百分位值,是延迟图表的构建基础。

输入:

  • name:单个指标名称(字符串,而不是数组)。
  • percentiles01 之间的数字数组,例如 [0.5, 0.95, 0.99]
  • interval:与 getMetricTimeSeries 相同的枚举。
  • filters:可选。

响应:series[],每项包含 percentile 和由 { timestamp, value } 组成的 points[]

const latency = await observability!.getMetricPercentiles({
name: 'mastra_agent_duration_ms',
percentiles: [0.5, 0.95],
interval: '1h',
})

Discovery
Discovery的直接链接

使用这些 endpoint 填充下拉列表,或为 Agent 提供可用于过滤的值菜单。所有 discovery 路由都使用 GET,位于 /api/observability/discovery/ 下。

指标专用(也作为 mastra api metric 子命令公开):

方法参数路径后缀CLI
getMetricNames{ prefix?, limit? }metric-namesmastra api metric names
getMetricLabelKeys{ metricName }metric-label-keysmastra api metric label-keys
getMetricLabelValues{ metricName, labelKey, prefix?, limit? }metric-label-valuesmastra api metric label-values

与 Trace 和日志共享(仅 HTTP,没有专用 CLI 子命令):

方法参数路径后缀
getEntityTypes{}entity-types
getEntityNames{ entityType? }entity-names
getServiceNames{}service-names
getEnvironments{}environments
getTags{ entityType? }tags

过滤
过滤的直接链接

每个查询都接受相同的 filters 对象。最常用的字段包括:

  • name:限制为特定指标名称。(Aggregate/breakdown/time series 顶层的 name 已经实现此功能。当需要在单个查询中混合多个指标时,请使用 filters.name。)
  • timestamp{ start, end, startExclusive, endExclusive }。两个边界均可选。省略 end 表示“直到现在”。
  • providermodelcostUnit:用于 token 和成本指标。
  • labels:精确匹配指标标签的键值,例如持续时间指标使用 { status: 'error' }
  • 关联字段:entityTypeentityNameparentEntityNamerootEntityNameuserIdorganizationIdresourceIdrunIdsessionIdthreadIdrequestIdexecutionSourceenvironmentserviceNameexperimentIdtags

相同的 filters 结构适用于三种界面:

// In-process
await observability!.getMetricAggregate({
name: ['mastra_tool_duration_ms'],
aggregation: 'avg',
filters: { entityName: 'weatherTool', labels: { status: 'error' } },
})
# CLI
mastra api metric aggregate \
'{"name":["mastra_tool_duration_ms"],"aggregation":"avg","filters":{"entityName":"weatherTool","labels":{"status":"error"}}}' \
--url http://localhost:4111
# HTTP
curl -sS -X POST http://localhost:4111/api/observability/metrics/aggregate \
-H "content-type: application/json" \
-d '{"name":["mastra_tool_duration_ms"],"aggregation":"avg","filters":{"entityName":"weatherTool","labels":{"status":"error"}}}'

始终提供时间范围
始终提供时间范围的直接链接

filters.timestamp 是可选的,但对于针对生产存储运行的任何查询,都应将其视为必需。可观测性表通常按事件时间分区(TimescaleDB 则分块)。提供 timestamp.start(最好也提供 end)后,后端可以裁剪到与范围重叠的分区,通常只有一两个。没有时间范围时,planner 必须扫描每个分区;在保留一年数据的情况下可能有数百个片段,这是基于 Postgres 的存储中 OLAP 查询变慢最常见的原因。

临时查询的安全默认值是过去 24 小时;告警和仪表板应匹配其实际评估窗口:

await observability!.getMetricAggregate({
name: ['mastra_agent_duration_ms'],
aggregation: 'p95',
filters: {
timestamp: { start: new Date(Date.now() - 24 * 60 * 60 * 1000) },
},
})

此建议适用于所有后端(ClickHouse、Postgres v-next、DuckDB),但对 Postgres v-next 尤其重要,因为每缺少一个时间边界都会直接增加一次分区扫描。

示例:构建自定义 KPI 卡片
示例:构建自定义 KPI 卡片的直接链接

以下 Tool 返回过去一小时的输入 token 数量和预估成本。Agent 或仪表板可以将其作为 structuredContent 调用,而无需重新实现查询。

src/mastra/tools/token-kpi-tool.ts
import { createTool } from '@mastra/core/tools'
import { z } from 'zod'

export const tokenKpiTool = createTool({
id: 'tokenKpi',
description: 'Returns input-token volume and estimated cost for the last hour.',
inputSchema: z.object({}),
outputSchema: z.object({
inputTokens: z.number().nullable(),
estimatedCost: z.number().nullable(),
costUnit: z.string().nullable(),
changePercent: z.number().nullable(),
}),
execute: async (_input, context) => {
const observability = await context.mastra!.getStorage()!.getStore('observability')
if (!observability) {
throw new Error('Observability domain is not configured (requires DuckDB or ClickHouse)')
}

const result = await observability.getMetricAggregate({
name: ['mastra_model_total_input_tokens'],
aggregation: 'sum',
filters: {
timestamp: { start: new Date(Date.now() - 60 * 60 * 1000) },
},
comparePeriod: 'previous_period',
})

return {
inputTokens: result.value,
estimatedCost: result.estimatedCost ?? null,
costUnit: result.costUnit ?? null,
changePercent: result.changePercent ?? null,
}
},
})