可观测性导出器
可观测性导出器会将网关用量事件发送到用于链路追踪、日志记录、存储或记账的目标。本指南通过 AISIX Enterprise 和 AISIX Cloud 的控制面路径,或 AISIX OSS 的资源文件路径配置 OTLP/HTTP 导出器,再说明目标选择、内容采集和投递行为。
准备工作
请先准备以下内容:
- 以下任一配置路径:
- AISIX Enterprise 或 AISIX Cloud,其中包含一个环境、已关联的网关和具备写入作用域的 Admin Token。对于 AISIX Enterprise,请按照 AISIX Enterprise 入门指南操作。如需申请 AISIX Cloud 访问权限,请联系 API7。
- 加载声明式
resources.yaml文件的开源 AISIX 网关。
- 计划使用的导出器对应的遥测目标。
- 用于验证投递的可用模型别名和调用方 API Key。
导出验证所用的网关和请求参数:
# AISIX_PROXY 不含尾部斜杠或端点路径
# 本地快速入门使用 http://127.0.0.1:3000
export AISIX_PROXY="YOUR_AISIX_GATEWAY_URL"
export AISIX_API_KEY="YOUR_CALLER_API_KEY"
export MODEL_ALIAS="YOUR_MODEL_ALIAS"
对于控制面示例,请导出 Admin API 基础 URL、Admin Token 和环境 ID:
# AISIX_CP 包含 /api,且不含尾部斜杠
# 本地 AISIX Enterprise 快速入门使用 http://localhost:8080/api
export AISIX_CP="YOUR_AISIX_CONTROL_PLANE_API_URL"
export AISIX_TOKEN="YOUR_ADMIN_TOKEN"
export ENV_ID="YOUR_ENVIRONMENT_ID"
其他部署方式请使用可访问的 AISIX Cloud Admin API URL。
选择导出器类型
两种配置路径都支持所有导出器类型。AISIX Cloud Admin API 请求和资源文件条目通过 kind 值选择类型,控制台则显示同一底层类型对应的标签:
kind 值 | 控制台标签 | 适用场景 |
|---|---|---|
otlp_http | OTLP/HTTP | 已经通过 OTLP/HTTP 收集器或厂商端点收集链路(Trace)。 |
object_store | Object storage | 希望将批量 NDJSON 请求事件写入 Amazon S3、S3 兼容存储、Google Cloud Storage 或 Azure Blob。 |
datadog | Datadog | 使用 Datadog Logs HTTP 接收端。 |
aliyun_sls | Alibaba Cloud SLS | 使用阿里云日志服务作为日志目标。 |
网关会将遥测直接发送到所选目标。
配置 OTLP 导出器
设置示例使用的收集器端点和授权请求头:
# 请替换为实际值
export OTLP_ENDPOINT="https://collector.example.com/v1/traces"
export OTLP_AUTH_HEADER="Bearer YOUR_COLLECTOR_TOKEN"
网关进程必须能够访问该端点。端点可以是任意主机的 https:// 或 http:// URL,例如集群内的采集器 http://otel-collector-dev:4318/v1/traces。如果接收端与网关进程位于同一主机,可以使用 http://localhost:4318/v1/traces;容器化网关则需要使用接收端在容器网络或宿主机上的地址。如果接收端不要求认证,请从导出器中移除 headers 块。
使用 http:// 时,静态请求头和导出的遥测数据以明文在网络中传输。请求头可能携带认证 Token,遥测数据可能包含提示词和响应内容,因此请在可信网络中使用 http://。
1.7.0 及更早版本的网关仅在运维人员配置的 http:// 端点主机名为 localhost、127.0.0.1 或 otel-collector 时接受该端点。环境中注册了此类网关时,AISIX Cloud Admin API 会以 422 DP_INCOMPATIBLE 拒绝保存其他主机上的 http:// 端点。将该环境的网关升级后即可保存。
AISIX Enterprise 或 AISIX Cloud
创建导出器并获取其 ID:
EXPORTER_ID=$(curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/observability_exporters" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "prod-otlp",
"kind": "otlp_http",
"endpoint": "'"${OTLP_ENDPOINT}"'",
"headers": {
"Authorization": "'"${OTLP_AUTH_HEADER}"'"
},
"sample_rate": 1
}' | jq -r '.observability_exporter.id')
❶ 只有当 OTLP 目标要求时才设置静态请求头。请求头值会加密存储,并且读取操作绝不会返回这些值。
❷ sample_rate: 1 可以让投递检查得到确定的结果。它等同于省略该字段,即导出每个请求链路。验证完成后,如需减少跨度(Span)数量,可以降低该值。
获取导 出器,确认存储的配置:
curl -sS "$AISIX_CP/environments/$ENV_ID/observability_exporters/$EXPORTER_ID" \
-H "Authorization: Bearer $AISIX_TOKEN"
你应该会看到类似下面的响应。读取操作通过 header_keys 暴露已配置的请求头名称,并通过 headers_set 确认已存储请求头值;但绝不会返回请求头值本身。
{
"observability_exporter": {
"id": "b46a9f5d-6a4d-4bb1-ae2c-4ab1b22f5e80",
"env_id": "0f2f6a1e-9d33-4a8f-9a6e-2a7b6a9c1d2e",
"name": "prod-otlp",
"enabled": true,
"kind": "otlp_http",
"endpoint": "https://collector.example.com/v1/traces",
"header_keys": ["Authorization"],
"headers_set": true,
"sample_rate": 1,
"created_at": "2026-07-22T08:30:00Z",
"updated_at": "2026-07-22T08:30:00Z"
}
}
保存 $EXPORTER_ID,以便后续更新或删除导出器。导出器默认启用。设置 enabled: false 可在不发送遥测的情况下保存资源。停用或删除导出器后,每个网关收到该配置变更时都会停止其投递流水线,并从投递健康报告中移除该导出器。流水线会按下文的重试规则和关闭期限尝试排空已排队的事件,但不保证全部送达。重新启用会启动一条计数器清零的新流水线。
AISIX OSS
将以下导出器添加到完整资源文件的 observability_exporters 中。使用环境变量插值可以避免将收集器 Token 写入文件:
observability_exporters:
- name: prod-otlp
kind: otlp_http
endpoint: ${OTLP_ENDPOINT}
headers:
Authorization: ${OTLP_AUTH_HEADER}
sample_rate: 1
验证组装后的完整文件:
aisix validate --resources resources.yaml
在进程环境中设置 OTLP_ENDPOINT 和 OTLP_AUTH_HEADER,然后启动或重启网关。只有当这些变量已经可用于运行中的进程时,才重新加载网关。导出器默认启用;添加 enabled: false 可保留条目但不发送遥测。
验证 OTLP 投递
保存导出器只能确认 AISIX 接受了配置。要验证上面配置的 OTLP 导出器,请获取 AISIX 返回的请求 ID,在目标端查找该 ID,并检查网关的投递信号。如果正在检查的现有 OTLP 导出器的 sample_rate 小于 1,请暂时将采样率设为 1,避免该请求被采样丢弃。
通过一个可用的模型别名发送成功请求,并获取 AISIX 返回的 ID。无论 AISIX 接受调用方提供的 ID,还是自行生成 ID,这种方式都能取得准确结果:
export EXPORTER_NAME="prod-otlp"
if TEST_REQUEST_ID=$(
set -o pipefail
curl -fsS -D - -o /dev/null \
-X POST "$AISIX_PROXY/v1/chat/completions" \
-H "Authorization: Bearer $AISIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "'"${MODEL_ALIAS}"'",
"messages": [{"role": "user", "content": "Reply with: exporter check"}]
}' \
| awk 'tolower($1) == "x-aisix-request-id:" { id = $2; sub(/\r$/, "", id) } END { print id }'
) && test -n "$TEST_REQUEST_ID"; then
export TEST_REQUEST_ID
printf 'request ID: %s\n' "$TEST_REQUEST_ID"
else
unset TEST_REQUEST_ID
printf 'verification request failed or returned no request ID\n' >&2
false
fi
等待导出器发送该批次,然后在 OTLP 后端查找 aisix.request_id 属性等于 $TEST_REQUEST_ID 的跨度。找到该跨度即可确认端到端投递:网关已加载导出器,在需要时完成目标端认证,且目标端已接受该批次。
在 AISIX Enterprise 或 AISIX Cloud 中,环境的 可观测性 视图通过导出器行显示投递是否健康,以及在线网关已发送的批次数。也可以通过 Admin API 查看相同的逐网关心跳数据:
curl -sS "$AISIX_CP/environments/$ENV_ID/dp_nodes" \
-H "Authorization: Bearer $AISIX_TOKEN" \
| jq --arg exporter "$EXPORTER_NAME" \
'[.data[] | select(.status != "offline") | .exporter_health[]? | select(.name == $exporter)]'
下次心跳后,至少一个在线网关应报告大于零的 delivered_batches 和 last_error: null。网关重启或导出器配置变更时,计数器会重置。非空的 last_error 表示当前投递失败。后续成功投递会清除 last_error,但历史字段 failed_batches 和 last_failure_unix 仍会保留数值。
使用开源 AISIX 网关时,请在目标端确认成功。如果记录未到达,请在网关日志中检查 sink delivery failed; retrying 或 sink delivery dropped。
了解投递行为
每个导出器都有自己的投递管道:一个内存队列,后面是一个把记录攒批并发往目标端的后台工作任务。下面这些行为是固定的,没有配置项。
记录会一直缓冲,直到批次达到 100 条记录或经过 1 秒,以先到者为准。
目标端以可恢复的方式拒收一个批次时(连接失败、429 或 5xx),网关按时间预算而不是固定次数重试。延迟从 200 毫秒开始,逐次翻倍,上限为 30 秒;重试会持续到该批次首次尝试之后的 300 秒为止。预算耗尽后,该批次被丢弃,并在 aisix_otlp_fanout_drops_total 中计为 retries_exhausted。429 或 503 响应上的 Retry-After 头会取代计算出的延迟,同样以 30 秒封顶,因此要求更长等待的目标端会被尊重而不是被反复冲击。该预算让批次有机会跨越暂时的接收端故障,但不构成无损保证:即使故障短于 300 秒,队列溢出、永久性投递错误或进程关闭仍可能导致事件丢失。
在一个批次被重试期间,新记录仍在不断到达。每个导出器队列可容纳 8,192 条记录;队列满时被拒收的是最新的那条记录,计为 queue_full,这样已经排队的记录仍有机会送达目标端。
启用了完整内容采集的导出器,其记录由自己持有,包含提示词和响应文本。在 content_max_bytes 取默认值 131,072 的情况下,目标端卡住时装满 8,192 条记录的队列可能占用数百 MB 常驻内存。当你在可能卡住的目标端上启用完整采集时,请结合网关的内存上限来设置 content_max_bytes。请参阅配置内容采集。
解读 OTLP 链路
AISIX 为每个采样请求导出一个相互关联的链路层级。跨度的父子关系、W3C 链路延续、名称、状态、语义约定属性、采集内容和资源字段参见 OTLP 链路参考。
配置其他导出器
当遥测需要发送到对象存储或日志服务,而不是 OTLP 链路后端时,请使用其他导出器类型。使用 AISIX Enterprise 或 AISIX Cloud 时,将以下对象之一作为 POST $AISIX_CP/environments/$ENV_ID/observability_exporters 的请求体。
对象存储
对于对象存储,请选择存储服务提供方、存储桶和对象键前缀。默认认证模式使用由网关解析的凭证引用:
{
"name": "request-events-s3",
"kind": "object_store",
"provider": "s3",
"bucket": "acme-aisix-events",
"prefix": "ai-gateway",
"region": "us-east-1",
"credential_ref": "acme_s3"
}
对象存储支持 Amazon S3、Google Cloud Storage、Azure Blob 和 S3 兼容目标。只有当网关运行时带有可写入存储桶的附加身份时,才对 S3 或 GCS 使用云身份。对于 Azure Blob,以及要求静态凭证的 S3 兼容目标,请使用 credential_ref。
对于 MinIO、Cloudflare R2 或阿里云 OSS 等 S3 兼容目标,请显式设置目标端点。未设置端点时,S3 导出器会使用原生 AWS S3 端点。
endpoint 用于覆盖存储服务商的原生端点,其含义取决于 provider:
provider | endpoint 设置的内容 |
|---|---|
s3 | 上传目标的 S3 兼容主机。 |
gcs | Cloud Storage XML API 基础 URL,例如私有端点。上传走 XML API,因此该端点必须提供 XML API;只接受 JSON API 上传的模拟器(例如原版 fake-gcs-server 镜像)无法接收对象。它优先于服务账号 JSON 中的 gcs_base_url。 |
azure_blob | Blob 服务端点。 |
该值可以是任意主机的 https:// 或 http:// URL,例如集群内的 MinIO http://minio.storage.svc:9000。使用 http:// 时,上传的请求事件以明文在网络中传输,因此请在可信网络中使用。1.7.0 及更早版本的网关仅对 localhost、127.0.0.1 和少数模拟器主机名接受 http://。环境中注册了此类网关时,AISIX Cloud Admin API 会以 422 DP_INCOMPATIBLE 拒绝保存其他主机上的 http:// 端点。将该环境的网关升级后即可保存。
endpoint 不能与云身份同时使用:AISIX Cloud Admin API 会拒绝该组合;开源网关加载该组合后永远不会上传,并报告永久性投递错误。控制台为 S3 兼容目标、Cloud Storage 和 Azure Blob 提供该字段,并在选择云身份时隐藏它。
gcs 端点要求网关为 1.5.0 或更高版本,因为更早的网关会忽略它,转而上传到 Google 的公共 Cloud Storage 端点。AISIX Enterprise 和 AISIX Cloud 支持的所有网关版本(1.6.0 及更高)都支持它。低于 1.6.0 的已连接网关在保存时不做检查,依赖该端点之前请先将其升级。参见窗口期内控制面会做哪些检查。
阿里云 SLS
配置端点主机、项目、日志库和凭证引用:
{
"name": "request-events-sls",
"kind": "aliyun_sls",
"endpoint": "ap-southeast-3.log.aliyuncs.com",
"project": "acme-observability",
"logstore": "ai-gateway",
"credential_ref": "acme_sls"
}
Datadog
配置 Datadog 站点、服务名称、标签和凭证引用:
{
"name": "request-events-datadog",
"kind": "datadog",
"site": "datadoghq.com",
"service": "ai-gateway",
"tags": ["team:platform", "tier:prod"],
"credential_ref": "acme_datadog"
}
对于资源文件路径,请将以下条目添加到 observability_exporters:
observability_exporters:
- name: request-events-s3
kind: object_store
provider: s3
bucket: acme-aisix-events
prefix: ai-gateway
region: us-east-1
credential_ref: acme_s3
- name: request-events-sls
kind: aliyun_sls
endpoint: ap-southeast-3.log.aliyuncs.com
project: acme-observability
logstore: ai-gateway
credential_ref: acme_sls
- name: request-events-datadog
kind: datadog
site: datadoghq.com
service: ai-gateway
tags: ["team:platform", "tier:prod"]
credential_ref: acme_datadog
只保留实际使用的目标,然后按照 OTLP 示例所述验证资源文件并启动或重新加载网关。使用与 OTLP 相同的请求 ID 方法验证这些导出器:在对象存储和 SLS 记录中按 request_id 查找,在 Datadog 日志中按 aisix.request_id 查找。Snowflake 指南介绍了如何直接检查对象存储输出。
SLS、Datadog 和对象存储导出器通过凭证引用或云身份,让目标凭证不进入导出器资源。网关发送遥测时会在本地解析这些凭证。
配置内容采集
导出器默认包含请求状态、Token 计数、模型和服务提供方标识、请求 ID、结束原因和时间信息,但不包含提示词和响应正文。导出记录不包含成本:控制面为每个请求定价,因此请从它的用量数据读取成本。1.9.0 及更早版本的网关还会导出 cost_usd 字段,除 Realtime 会话和 Batch 作业外,其值都是 0。OTLP/HTTP、SLS 和 Datadog 导出器可以选择启用完整内容采集。
失败元数据需要单独考虑。每个失败事件都携带最长 2,048 个字符的 error_message,其中可能包含被拒绝的请求值或上游返回的错误详情。content_mode: metadata_only 不会移除该字段。即使不启用完整内容采集,也请确认目的地可以接收失败详情。
启用完整内容采集
创建导出器时添加 content_mode 和 content_max_bytes。在 AISIX Enterprise 和 AISIX Cloud 中,可以使用相同字段更新现有导出器:
curl -sS -X PATCH "$AISIX_CP/environments/$ENV_ID/observability_exporters/$EXPORTER_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content_mode": "full",
"content_max_bytes": 131072
}'
在控制台中,将导出器表单的 内容模式 设置为 完整(提示词 + 响应),然后调整 每字段最大内容字节数。
对于资源文件路径,请将这些字段添加到导出器条目中,然后验证并重新加载文件:
observability_exporters:
- name: prod-otlp
kind: otlp_http
endpoint: ${OTLP_ENDPOINT}
headers:
Authorization: ${OTLP_AUTH_HEADER}
content_mode: full
content_max_bytes: 131072
只有当目标已获准接收最终用户提示词和响应文本时,才使用完整内容采集。AISIX 会按配置的字节上限分别限制每个采集内容字段。
了解截断记录
有效 JSON 超过 content_max_bytes 时,AISIX 会先按结构缩减,使导出字段仍为有效 JSON。如果缩减后仍超过上限,则回退到 UTF-8 安全的字节截断。采集的提示词是序列化后的请求体,在 AISIX 替换其中的 base64 媒体之后遵循同样规则。
| 内容 | 截断行为 |
|---|---|
| 长字符串 | 保留前缀,并追加 ...[aisix: truncated, N bytes total] 行内标记。 |
| Base64 数据 URI | 用包含原始大小的占 位符替换编码数据。 |
| 长数组 | 保留首尾样本,并插入 {"_aisix_truncated": true, "omitted_items": N} 元素,其中 N 统计全部省略项。 |
| 非 JSON 内容,或结构缩减后仍放不下的 JSON | 在 UTF-8 字符边界处截断。 |
发生截断时,OTLP 记录包含 aisix.content_truncated: true,Datadog 和 SLS 记录包含 content_truncated: true。
采集内容中的 Base64 媒体
在 content_mode: full 下,AISIX 会把采集到的请求中的 base64 媒体替换为占位符 [aisix: base64 data uri omitted, N bytes],不论大小,并且在应用 content_max_bytes 之前完成。这条规则覆盖所有被采集的请求类型,包括向量嵌入、重排序、completions、decisions 和 MCP tools/call 参数,也覆盖所有携带内容的目的地:OTLP、阿里云 SLS 和 Datadog。object_store 类型不携带内容。变化的只是导出的副本,发给上游的请求和返回给客户端的响应都不受影响。
AISIX 按形式或按声明该字段的 part 识别 base64 媒体:
- 任意字段中整个字符串就是 base64 data URI 的值,包括图片、音频和 PDF part。URI 保留开头部分,例如
data:image/png;base64,[aisix: base64 data uri omitted, N bytes],N是整个 URI 的长度。 - base64 类型的
image或document块的source.data,包括位于工具结果内的块。 input_audio.data、input_filepart 或file对象上的file_data,以及回放的image_generation_call的result。- Gemini 请求体的
inlineData.data和 Bedrock Converse 请求体的source.bytes,它们出现在透传路由上。 - MCP
image或audio块中与mimeType同级的data,以及内嵌resource块的resource.blob。
对于上述不带 URI 开头的字段,N 是负载的长度。
结构化字段(OTLP 和 Datadog 上的 gen_ai.input.messages,SLS 上的 input_messages)会保留媒体 part 和能取到的媒体类型,因此记录仍能说明请求带过媒体:
{"type":"blob","modality":"image","mime_type":"image/png","content":"[aisix: base64 data uri omitted, 1368 bytes]"}
http 或 https URL 保持原样。AISIX 不会替换内嵌在其它文本中的 data URI、JSON 字符串形式的工具参数,以及上面未列出字段中的裸 base64。大多数无法解析为 JSON 的内容(例如 A2A 消息文本,或在负载中途被切断的流式帧)没有字段可读,因此 AISIX 会在它出现的第一个媒体标记(例如 ;base64,)处截断,并在那里写入占位符。
响应侧的替换覆盖三种情况。在 /v1/images/generations 和 /v1/images/edits 上,b64_json 和任何 data URI 会换成占位符,url、revised_prompt 等其它字段不变。在透传路由上,同样的识别会作用于上游自身的响应体和原始流式帧,并且还覆盖 partial_image_b64 和 bytesBase64Encoded,但提取出的模型回答文本不会被改写。在 MCP tools/call 上,采集到的 result 同样换成占位符,详见下文。其它被采集的响应(包括向量嵌入)保留 base64,除非截断时将其替换。
在 MCP tools/call 的结果中(包括标记了 isError 的结果),AISIX 会替换 image 和 audio 块的 data、resource.blob,以及任何位置(包括 structuredContent)以 base64 data URI 开头的字符串。块本身、mimeType 和资源的 uri 保留。AISIX 不会替换内嵌在 text 块或 resource.text 中的 base64,也不会替换 structuredContent 中未被识别的键下的裸 base64。一个常见情况是,工具按 MCP 规范把 structuredContent 序列化后同时放进 text 块,此时该 text 块里的载荷仍会被导出。
1.8.0 及更早版本的网关可能按原样导出 base64 媒体,并且会采集图片端点的 b64_json。
在透传路由上,如果响应不是合法的 UTF-8、嵌套超过 128 层,或包含解析器无法读取的数值(例如 1e999),根据路由协议的不同,导出内容可能原样保留 base64,也可能在第一个媒体标记处被截断。这种情况下,没有标记的形状(例如 MCP 的 blob)会原样导出。
Datadog 和 SLS 记录中的采集内容
设置 content_mode: full 时,Datadog 和阿里云 SLS 导出器报告消息交换所用的结构化内容与 OTLP 导出器的采集内容属性相同:相同的消息、相同的 text、reasoning 和 tool_call 输出 part,以及相同的 finish_reason 取值。两个导出器各自使用以下字段名:
| 内容 | 阿里云 SLS | Datadog |
|---|---|---|
| 请求消息 | input_messages | gen_ai.input.messages |
Anthropic system 或 Responses instructions | system_instructions | gen_ai.system_instructions |
| 已声明的工具 | tool_definitions | gen_ai.tool.definitions |
| assistant 输出消息 | output_messages | gen_ai.output.messages |
| 未结构化的提示词侧 | prompt | gen_ai.prompt |
| 未结构化的响应侧 | response | gen_ai.completion |
SLS 把每个结构化值写成紧凑的 JSON 字符串,Datadog 则写成嵌套 JSON。结构化字段只在记录确有对应内容时写入,因此 system_instructions 只会出现在带 Anthropic Messages system 字段或 Responses instructions 字段的请求上。聊天补全中的 system 消息仍保留在 input_messages 中。对于 Responses 请求,已声明的工具还包括其 additional_tools 输入 item 中的工具,排在顶层工具之后,重复声明会被合并,这些 item 也不会出现在请求消息中。已声明的工具还包括客户端执行的 tool search 所加载的工具,见工具参数。这次搜索的 tool_search_call 和 tool_search_output item 在请求消息中分别记为一次工具调用和一次工具响应。
提示词侧和响应侧分别判断,规则与 OTLP 相同。被采集截断、无法转换,或结构化值会超过 content_max_bytes 的一侧,保留在平铺字段中。无法转换的内容包括不属于消息交换的负载,例如向量嵌入输入、图片、音频、任务和 Agent 文本。输出消息的回落方式也与 OTLP 相同:采集到的输出 part 超过上限时,输出消息改由响应文本构建;只有该文本也被截断时,响应侧才保留为平铺字段。因此,一条记录可能同时携带结构化的输入消息和平铺的 response,反之亦然。MCP 工具调用的参数仍写在平铺的提示词字段中,结果写在平铺的响应字段中。与之前一样,content_truncated: true 标记内容被截断的记录。
既没有结构化内容、也没有采集到文本的一侧,仍会写成空的平铺字段。例如,在得到任何响应之前被放弃的请求会把消息记录在 input_messages 中,并写入空的 response。OTLP 则省略这样的一侧。
content_max_bytes 分别限制每个字段,因此一条记录最多可以携带四个受上限约束的内容字段:三个输入字段和输出消息。
1.6.0 及更早版本的网关把所有采集到的提示词写入 SLS prompt 和 Datadog gen_ai.prompt,把所有采集到的响应写入 SLS response 和 Datadog gen_ai.completion。升级后,这些字段只携带未结构化的一侧。
- 把读取
prompt或response的 SLS 索引、保存的查询、仪表盘和告警迁移到结构化字段。对于被截断或无法转换的一侧,仍需读取prompt和response。 - 在 SLS 中为
input_messages、system_instructions、tool_definitions和output_messages创建字段索引。没有字段索引时,这些字段只能通过全文检索查到 。要查询值内部的键,请使用 JSON 类型索引,或为字段开启统计并在 SQL 中用json_extract读取。 - 把基于
gen_ai.prompt或gen_ai.completion的 Datadog facet 和 monitor 迁移到gen_ai.input.messages和gen_ai.output.messages属性。
采集透传内容
对于成功中继的透传路由流量,完整内容采集会将请求体记录为字符串,并遵循导出器的内容上限和 JSON 结构感知截断规则。对于缓冲响应,响应符合受支持的提取结构时会记录提取出的文本,否则将响应体记录为文本。对于流式响应,会记录累积的提取文本;不透明数据负载则保留为文本。这些内容中的 base64 媒体都会被替换,详见采集内容中的 Base64 媒体。
采集失败请求
任一 OTLP/HTTP、SLS 或 Datadog 导出器为 content_mode: full 时,所有端点、所有状态码的每条失败行都带请求体和该行的响应体。失败行指状态码为 400 及以上或 error_class 非空的行,包括上游返回的 401 和 403、禁用模型这类网关拒绝,以及重试或回退链中每次失败的尝试。同一请求在 metadata_only 下导出时,没有 prompt 和 response 字段。
请求体的位置与成功时相同。在 /v1/chat/completions、/v1/messages 和 /v1/responses 上,请求能够转换时记录为结构化输入消息(OTLP 和 Datadog 上为 gen_ai.input.messages,SLS 上为 input_messages),否则保留在平铺的提示词字段中。multipart 请求记录文本字段,并为每个文件加 <name>_sha256 校验和;/v1/files 上传记录文本字段加 file_sha256。不存储文件二进制内容。
响应写在平铺的响应字段:OTLP 上为 aisix.content.response,SLS 上为 response,Datadog 上为 gen_ai.completion。它和请求体一样受 content_max_bytes 限制。AISIX 按以下顺序选择响应内容:
- 流在中途失败或调用方离开之前已经送达的内容。
- 上游为该次尝试发送的正文,最多前 64 KiB。
- 在请求的终态行上,网关发给调用方的错误体。
/v1/messages和/v1/messages/count_tokens上是 Anthropic 形状,MCP 和 A2A 上是 JSON-RPC。 - 其余情况下,AISIX 构造的正文:
{"error":{"message":<error_message>,"type":<error_class>}}。
因此 response 不一定是上游说的话。上游应答没有正文时,终态行的 response 是网关发给调用方的错误,例如 {"error":{"message":"upstream returned 404 Not Found","type":"upstream_error"}}。这时请看 error_message:with no error detail in the response 表示上游确实没有给出详情。参见读懂上游错误文案。
以下失败有所不同:
499行带请求体,不合成响应体,因为调用方已经离开。流已送达的内容仍会记录。- 响应侧安全护栏拒绝记录的是返回给调用方的拒绝内容,而不是被拦截的上游输出。
- 透传路由不解码上游压缩过的错误体。其
error_message为upstream returned HTTP <status> with a compressed error body,response记录 AISIX 根据该消息构造的正文,而不是压缩字节。 - Realtime 上游以空正文或压缩正文拒绝握手时没有错误体可记录,因为调用方收到的是 WebSocket 帧。
response记录 AISIX 构造的正文,例如{"error":{"message":"upstream returned HTTP 500 with no error detail in the response","type":"upstream_status"}}。 - A2A Agent 的错误体只在有完整内容导出器时读取,并且网关在应答前读取。因此响应慢的 Agent 会延迟调用方收到应答,最长到该 Agent 的超时时间。
路由没有请求体时,失败行也不带请求体:没有请求体的任务路由(例如 get、list、delete 和 cancel)、请求体为空的透传请求,以及所有 Realtime 错误事件。multipart 字段读完之前就失败的行同样不带,透传路由在读取请求体之前的拒绝(包括认证和授权)也不带。
有些失败不产生用量事件,因此没有失败行,只有访问日志:无法解析的请求体(例如 JSON 写错)和调用方认证失败。这些情况及透传路由的例外参见用量日志记录了什么。内容只进入导出器,控制台不展示请求体。
1.8.0 及更早版本的网关没有在各失败路径上一致地采集完整的请求和响应。
保护失败行内容
请把 content_mode: full 的目的地视为包含内容的数据源,并相应限制访问。失败行并不总是脱敏的:
- 在安全护栏运行之前失败的行携带原样的未脱敏请求体,例如未知模型的
404、403和 IP 拒绝。 - 在 images、embeddings、rerank、completions、speech 和
count_tokens上,调用方在安全护栏运行途中离开产生的499行携带未脱敏的请求体。 - 被安全护栏拦截的行已应用
mask动作规则,但触发拦截的文本仍留在行中。需要调用远程安全护栏服务的脱敏(例如 AWS Bedrock)不会执行,因为请求已被拒绝。 - 透传路由、任务路由和 A2A 无法改写请求,其失败行携带原样的请求体,安全护栏拦截时也一样。
回退链中每次失败的尝试都会重复携带请求体,因此失败流量会增加完整内容的数据量。
查看各端点采集的内容
| 端点类型 | 采集的内容 |
|---|---|
| 文本生成 | 响应文本。OTLP、Datadog 和阿里云 SLS 都以结构化 part 导出输出,包括推理和工具调用,详见采集内容和 Datadog 和 SLS 记录中的采集 内容。 |
MCP tools/call | 通过流量控制和输入安全护栏检查后的 params.arguments,并应用所有数据脱敏;以及存在时的 JSON-RPC result,其中的 base64 媒体换成占位符,详见采集内容中的 Base64 媒体。输出安全护栏阻断记录参数和 JSON-RPC 拒绝内容,不记录被拦截的结果。其它 MCP 方法不会被采集。 |
| A2A | message/send 和 message/stream 上,请求消息与回复消息里文本部分的内容。文件部分、数据部分和 JSON-RPC 信封不会被采集;但没有消息文本的调用(例如读取任务或取消)的失败行会记录其 JSON-RPC 正文。 |
| 向量嵌入和重排序 | 完整响应 JSON。 |
| 图片生成和图片编辑 | 响应 JSON,其中的 b64_json 换成占位符,详见采集内容中的 Base64 媒体。url、revised_prompt 等其它字段不变。变化的只是导出内容,客户端仍会收到图片。 |
| 音频转录 | 返回的转录文本。上传音频不会被采集;其 SHA-256 校验和会与请求文本字段一起记录。 |
| 文本转语音 | 不采集二进制语音响应。 |
导出遥测中的模型字段
当调用方请求的模型别名与处理某次尝试的模型不同时,用量遥测会同时记录两者。这对路由和合议流量很重要,因为一个面向调用方的别名可能解析为多个目标模型调用。
不同目标会以各自的遥测格式呈现这些值。OTLP 链路使用以下字段:
aisix.requested_model包含调用方请求的别名或模型组名称。gen_ai.request.model包含该次尝试发送给上游的模型名称。gen_ai.response.model包含服务提供方上报的具体响应模型版本。aisix.model_id标识调用上游的模型资源:调用方请求的直连模型,或路由模型、语义路由器为该次尝试选定的目标。在选定路由目标之前写入的记录上为空。参见记录指向哪个模型。aisix.requested_model_id标识调用方请求的模型资源。参见按请求模型 ID 关联。
1.6.0 及更早版本的网关在 gen_ai.request.model 中放置调用方请求的别名。
按请求模型 ID 关联
aisix.requested_model 是名称,因此重命名模型会把它的遥测拆成两个序列。aisix.requested_model_id 携带调用方所请求模型的 ID,重命名不会改变它。按该字段分组或过滤,即可跨重命名追踪同一个路由模型、语义路由器或合议模型。
它的取值取决于调用方所请求模型的类型:
| 请求的模型 | aisix.requested_model_id | aisix.model_id |
|---|---|---|
| 直接模型或向量嵌入模型 | 该模型的 ID。 | 同一个 ID。 |
| 通配符模型 | 该通配符模型的 ID。 | 同一个 ID。 |
| 路由模型或语义路由器 | 它自身的 ID,出现在该请求的每条记录上,包括失败的尝试、最终应答的尝试,以及选定目标之前写入的记录。 | 该次尝试所分发到的目标模型。在选定目标之前写入的记录上为空。 |
| 合议模型 | 该合议模型的 ID,出现在每个合议成员和评审模型的记录上。 | 该记录对应的合议成员或评审模型。合议模型的汇总记录以及合议成员运行之后的失败携带合议模型的 ID。请求在合议成员运行前被拒绝时为空。 |
当请求的名称已解析到模型、但网关没有选定任何目标时,例如调用方在第一次尝试之前取消了请求、安全护栏拦截了请求、限流以 429 拒绝了请求,或响应缓存命中,aisix.requested_model_id 仍携带所请求模型的 ID。对于路由模型或语义路由器,这些记录上的 aisix.model_id 为空,因此在 aisix.model_id 中按模型组的 ID 过滤会漏掉它们,请改用 aisix.requested_model_id 过滤。
当名称未解析到已配置的模型时,该字段为空,导出器不携带它。这包括未知的模型名称、透传路由正文中的 model(网关不会把它当作模型查找),以及不指定模型的 MCP 和 A2A 调用。
1.6.0 及更早版本的网关不导出 aisix.requested_model_id。
其他目标中的模型字段
Datadog 日志使用 aisix.requested_model 表示调用方请求的别名,使用 aisix.requested_model_id 表示所请求模型资源的 ID,使用 gen_ai.response.model 表示具体响应模型版本,并使用 aisix.model_id 表示调用上游的模型资源,取值与 OTLP 属性相同。
对象存储和阿里云 SLS 会保留底层用量事件字段名称,例如 requested_model、requested_model_id、model_id 和 provider_model_version。其中 model_id 的取值与 aisix.model_id 相同。阿里云 SLS 不写入为空的 requested_model_id,对象存储则将其写为空字符串。
导出遥测中的请求类型
每条记录还携带该请求要求网关做的事情——对话补全、图片生成、视频提交、工具调用。取值及其含义参见区分请求类型。
各目的地对它的命名不同:
- 对象存储和阿里云 SLS 保留用量事件字段名
operation。 - Datadog 映射为
aisix.operation。 - OTLP 以
aisix.operation跨度属性携带,并出现在该请求整个跨度层级的每一层上,因此可以在链路的根节点按类型筛选。
OTLP 跨度上同时还有 OpenTelemetry 的 gen_ai.operation.name 属性,两者回答的是不同的问题。后者在 OpenTelemetry 词汇表有对应取值时使用该取值,能区分模型推理与 Agent、工具调用,否则使用 aisix.operation 的值。聊天补全、messages 和 responses 都报告为 chat,因此它无法区分这三者。端点本身重要时,请按 aisix.operation 过滤。
导出遥测中的请求头与响应头
每个导出器都可以记录选定的入站请求头和选定的上游响应头的值。所有导出器类型都接受两个可选的请求头名称列表:
request_headers:记录到该请求每条用量事件上的入站请求头,让共用一个 API Key 的调用方可以按其发送的请求头进行归因。upstream_response_headers:按每次尝试记录的上游响应头,取自该次尝试自己的上游响应,例如服务提供方的请求 ID 或区域。
每个导出器只记录自己列表中的请求头,因此两个导出器可以记录不同的请求头。记录与 content_mode 无关,这些值也永远不会发送到控制面。配置方式、名称规则和取值规则参见按请求头归因事件。
各目的地对这些值的命名不同:
| 导出器 | 请求头 | 上游响应头 |
|---|---|---|
| 对象存储 | 每条记录中嵌套的 request_headers 对象。 | 每条记录中嵌套的 upstream_response_headers 对象。 |
| 阿里云 SLS | 一个 request_headers 字段,其值为该对象的紧凑 JSON 字符串。 | 一个 upstream_response_headers 字段,其值为该对象的紧凑 JSON 字符串。 |
| Datadog | 嵌套 的 aisix.request_headers 属性,单个请求头即 aisix.request_headers.<name>。 | 嵌套的 aisix.upstream_response_headers 属性,单个响应头即 aisix.upstream_response_headers.<name>。 |
| OTLP | SERVER 跨度上每个请求头一个 http.request.header.<name> 属性。1.6.0 及更早版本的网关将其放在操作跨度上。 | 生成式 AI 操作跨度上每个响应头一个 http.response.header.<name> 属性;请求有尝试时,即尝试跨度。 |
两种 OTLP 属性都是只含一个元素的字符串数组,遵循 OpenTelemetry HTTP 语义约定。
导出遥测中的 Token 计数
导出器收到的是用量事件记录下来的 Token 计数,保留的是上游使用的口径,而不是调用方读到的 usage 块。详见记录的 Token 计数。
各目的地的报告方式如下:
- 对象存储和阿里云 SLS 按记录原样保留用量事件字段,包括
reasoning_tokens、total_tokens,以及音频和图像计数prompt_audio_tokens、prompt_image_tokens、completion_audio_tokens和completion_image_tokens。为零的reasoning_tokens,以及未上报的total_tokens或音频、图像计数会被省略。 - Datadog 日志携带
gen_ai.usage.input_tokens和gen_ai.usage.output_tokens,存在时还携带记录的aisix.cache_creation_tokens、aisix.cache_creation_5m_tokens、aisix.cache_creation_1h_tokens、aisix.cache_read_tokens、aisix.reasoning_tokens、aisix.total_tokens、aisix.prompt_audio_tokens、aisix.prompt_image_tokens、aisix.completion_audio_tokens和aisix.completion_image_tokens。 - OTLP 跨度携带
gen_ai.usage.input_tokens、gen_ai.usage.output_tokens、gen_ai.usage.cache_read.input_tokens、gen_ai.usage.cache_creation.input_tokens、gen_ai.usage.reasoning.output_tokens,以及gen_ai.usage.{audio,image}.{input,output}_tokens计数。参见 Token 用量属性。
按照 OpenTelemetry 生成式 AI 约定,输入 Token 包含全部缓存 Token。因此 OTLP 和 Datadog 报告的 gen_ai.usage.input_tokens 为 prompt_tokens + cache_creation_tokens + cache_read_tokens。只有以 Anthropic 形态上报的用量(例如 Anthropic 和 Amazon Bedrock)这两个缓存字段才不为零,这种形态把缓存读取和写入与输入 Token 分开计数。OpenAI 形态的上游已经把缓存 Token 计入 prompt_tokens,因此其导出的输入计数不变。例如,一个 Anthropic 回复有 19 个输入 Token、1,200 个缓存读取 Token 和 300 个缓存创建 Token,导出的 gen_ai.usage.input_tokens 为 1,519;1.6.0 及更早版本的网关对同一回复导出 19。
按照 OpenTelemetry 生成式 AI 约定,输出 Token 包含推理。因此,当事件把推理计在补全之外时,OTLP 和 Datadog 报告的 gen_ai.usage.output_tokens 为 completion_tokens + reasoning_tokens,否则为 completion_tokens。判断条件与控制面计价时相同:total_tokens 和 reasoning_tokens 都大于零,且 total_tokens 等于提示词 + 补全 + 推理 Token 之和、而不等于提示词 + 补全 Token 之和。详见推理 Token 如何计费。
该判断按事件进行,而不是按服务提供方。Vertex AI 适配器上的 Gemini 思考模型通常满足该条件;把推理报告在 completion_tokens 之外、且总数包含推理的 OpenAI 兼容上游同样满足。对 Gemini 而言,导出的输出计数与以前相同;对后一种 OpenAI 兼容上游,导出值为补全 + 推理,而 1.4.0 及更早版本的网关只导出补全。
AISIX Enterprise 或 AISIX Cloud
控制台在目标环境的 可观测性 视图中管理相同的导出器资源。导出器表单会收集目标字段、内容模式和各类型专属选项。
无论导出器通过 API 还是控制台保存,控制面都会把配置投射到关联到该环境的 AISIX 网关。
以下行为适用于投射到 AISIX 网关的导出器:
- AISIX 网关会直接将请求遥测发送到你的目标。控制面不会代理导出的遥测。
- 除非在导出器上启用完整内容采集,否则提示词和响应内容会留在网关上。
- 凭证引用由 AISIX 网关解析。当目标需要运行时凭证时,控制台会展示需要在网关上配置的环境变量。
- 投递健康状态来自网关心跳数据,会显示批次是否正在发送,或网关是否报告了投递错误。
对于 OTLP/HTTP 导出器,控制台 提供 Langfuse、Honeycomb 和 Grafana Cloud Tempo 的预设,也接受自定义 OTLP 端点。
链路界面 URL 模板是可选的。当 日志 视图需要将请求记录链接到外部链路界面时使用它。模板必须包含 {request_id},以便控制面用日志记录中的请求 ID 替换它。
下一步
使用 OTLP 链路参考解析链路层级、父子关系、状态和属性。参见将请求链路导出到 ClickHouse,将 AISIX 连接到 ClickStack,或为现有 ClickHouse 数据库部署 Collector。
如需在 Snowflake 中查询对象存储遥测,请继续阅读将请求遥测加载到 Snowflake。使用指标与用量事件比较导出记录与网关指标,并使用访问日志与请求关联追踪单个请求。