Skip to main content
Version: 1.8.0

Observability Exporters

Observability exporters send usage events from the gateway to destinations used for tracing, logging, storage, or accounting. This guide configures an OTLP/HTTP exporter through the control-plane path for AISIX Enterprise and AISIX Cloud or the resources-file path for AISIX OSS. It then explains destination choices, content capture, and delivery behavior.

Prerequisites​

Before starting, prepare the following:

  • One of these configuration paths:
  • A telemetry destination for the exporter you plan to use.
  • A working model alias and caller API key for delivery verification.

Export the gateway and request values used for verification:

# AISIX_PROXY has no trailing slash or endpoint path
# The local quickstarts use 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"

For the control-plane examples, export the Admin API base URL, admin token, and environment ID:

# AISIX_CP includes /api and has no trailing slash
# The local AISIX Enterprise quickstart uses 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"

In other deployments, use the reachable AISIX Cloud Admin API URL.

Choose an Exporter Kind​

All exporter kinds are available through both configuration paths. AISIX Cloud Admin API requests and resource-file entries select a kind with the kind value, and the dashboard shows a label for the same underlying kind:

kind ValueDashboard LabelUse When
otlp_httpOTLP/HTTPYou already collect traces through an OTLP/HTTP collector or vendor endpoint.
object_storeObject storageYou want batched NDJSON request events in Amazon S3, S3-compatible storage, Google Cloud Storage, or Azure Blob.
datadogDatadogYou use Datadog Logs HTTP intake.
aliyun_slsAlibaba Cloud SLSYou use Alibaba Cloud Simple Log Service as the log destination.

The gateway sends telemetry directly to the selected destination.

Configure an OTLP Exporter​

Set the collector endpoint and authorization header used by the example:

# Replace with your values
export OTLP_ENDPOINT="https://collector.example.com/v1/traces"
export OTLP_AUTH_HEADER="Bearer YOUR_COLLECTOR_TOKEN"

The endpoint must be reachable from the gateway process. It can be an https:// or http:// URL with any host, such as an in-cluster collector at http://otel-collector-dev:4318/v1/traces. A receiver running beside a gateway process on the same host can use http://localhost:4318/v1/traces; a containerized gateway needs the receiver's container-network or host address instead. Remove the headers block from the exporter when the receiver does not require authentication.

With http://, the static headers and the exported telemetry cross the network in plaintext. The headers can carry an authorization token and the telemetry can include prompt and response content, so use http:// on networks you trust.

Gateways 1.7.0 and earlier accept an operator-configured http:// endpoint only when its host is localhost, 127.0.0.1, or otel-collector. While such a gateway is registered in the environment, the AISIX Cloud Admin API refuses to save an http:// endpoint on any other host with 422 DP_INCOMPATIBLE. Upgrade the environment's gateways to save it.

AISIX Enterprise or AISIX Cloud​

Create the exporter and capture its 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')

❶ Set static headers only when the OTLP destination requires them. Header values are encrypted at rest and never returned by read operations.

❷ sample_rate: 1 keeps the delivery check deterministic. It is equivalent to omitting the field, which exports every request trace. After verification, lower the value when you need to reduce span volume.

Retrieve the exporter to confirm the stored configuration:

curl -sS "$AISIX_CP/environments/$ENV_ID/observability_exporters/$EXPORTER_ID" \
-H "Authorization: Bearer $AISIX_TOKEN"

You should see a response similar to the following. Reads expose the configured header names through header_keys and confirm stored values through headers_set; the header values themselves are never returned.

{
"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"
}
}

Keep $EXPORTER_ID for later updates or deletion. Exporters are enabled by default. Set enabled: false to save the resource without sending telemetry. Disabling or deleting an exporter stops its delivery pipeline on each gateway once the configuration change arrives: events already queued are delivered first, and the exporter drops out of the gateway's delivery health report. Re-enabling it starts a fresh pipeline with its counters reset.

AISIX OSS​

Add this exporter to observability_exporters in the complete resources file. Environment interpolation keeps the collector token out of the file:

resources.yaml (OTLP exporter)
observability_exporters:
- name: prod-otlp
kind: otlp_http
endpoint: ${OTLP_ENDPOINT}
headers:
Authorization: ${OTLP_AUTH_HEADER}
sample_rate: 1

Validate the assembled complete file:

aisix validate --resources resources.yaml

Start or restart the gateway with OTLP_ENDPOINT and OTLP_AUTH_HEADER in its process environment. Reload a running gateway only if those variables are already available to the process. Exporters are enabled by default; add enabled: false to keep the entry without sending telemetry.

Verify OTLP Delivery​

A saved exporter confirms only that AISIX accepted its configuration. Verify the OTLP exporter configured above by capturing the request ID that AISIX returns, finding that ID at the destination, and checking the gateway's delivery signal. If you are checking an existing OTLP exporter with sample_rate below 1, temporarily set the rate to 1 so this request cannot be sampled out.

Send one successful request through a working model alias and capture the ID AISIX returns. This remains accurate whether AISIX accepts caller-supplied IDs or generates its own:

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

Wait for the exporter to flush the batch, then search your OTLP backend for a span whose aisix.request_id attribute equals $TEST_REQUEST_ID. Finding the span confirms end-to-end delivery: the gateway loaded the exporter, authenticated to the destination when required, and the destination accepted the batch.

For AISIX Enterprise or AISIX Cloud, the exporter row in the environment's Observability view reports whether delivery is healthy and how many batches online gateways have shipped. You can inspect the same per-gateway heartbeat data through the 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)]'

After the next heartbeat, at least one online gateway should report delivered_batches greater than zero and last_error: null. The counters reset when a gateway restarts or the exporter configuration changes. A non-null last_error means delivery is currently failing. The historical fields failed_batches and last_failure_unix remain populated after a later success clears last_error.

For an open-source AISIX gateway, confirm success at the destination. If the record does not arrive, check gateway logs for sink delivery failed; retrying or sink delivery dropped.

Understand Delivery Behavior​

Every exporter runs its own delivery pipeline: an in-memory queue in front of a worker that batches records and ships them to the destination. The behavior below is fixed and has no configuration.

Records buffer until the batch reaches 100 records or one second passes, whichever comes first.

A destination that refuses a batch transiently — a connection failure, a 429, or a 5xx — is retried on a time budget rather than a fixed number of attempts. The delay starts at 200 milliseconds and doubles up to a 30-second ceiling, and retries continue until 300 seconds after that batch's first attempt. When the budget is spent, the batch is dropped and counted as retries_exhausted in aisix_otlp_fanout_drops_total. A Retry-After header on a 429 or 503 replaces the computed delay, capped at the same 30 seconds, so a destination asking for a longer pause is honored rather than hammered. A budget rather than an attempt count is what lets an exporter survive a receiver outage of a given length: an outage shorter than the budget loses nothing.

While a batch is being retried, new records keep arriving. Each exporter queue holds 8,192 records; when it is full the newest record is refused and counted as queue_full, so the records already queued still have their chance at the destination.

caution

An exporter with full content capture enabled owns its records, prompt and response text included. With content_max_bytes at its default of 131,072, a full 8,192-record queue behind a stalled destination can hold hundreds of megabytes of resident memory. Size content_max_bytes against the gateway's memory limit when you enable full capture on a destination that can stall. See Configure Content Capture.

Interpret OTLP Traces​

AISIX exports one connected trace hierarchy per sampled request. See OTLP Trace Reference for span parentage, W3C trace continuation, names, status, semantic-convention attributes, captured content, and resource fields.

Configure Other Exporters​

Use another exporter kind when telemetry should go to object storage or a logging service instead of an OTLP trace backend. With AISIX Enterprise or AISIX Cloud, send one of the following objects as the body of POST $AISIX_CP/environments/$ENV_ID/observability_exporters.

Object Storage​

For object storage, choose the storage provider, bucket, and key prefix. The default authentication mode uses a credential reference resolved by the gateway:

{
"name": "request-events-s3",
"kind": "object_store",
"provider": "s3",
"bucket": "acme-aisix-events",
"prefix": "ai-gateway",
"region": "us-east-1",
"credential_ref": "acme_s3"
}

Object storage supports Amazon S3, Google Cloud Storage, Azure Blob, and S3-compatible targets. Cloud identity is supported only for S3 or GCS when the gateway has an attached identity that can write to the bucket. Use credential_ref for Azure Blob and for S3-compatible targets that require static credentials.

For an S3-compatible target such as MinIO, Cloudflare R2, or Alibaba Cloud OSS, set the target endpoint explicitly. Without an endpoint, the exporter uses the native AWS S3 endpoint.

endpoint overrides the storage provider's native endpoint, and its meaning follows provider:

providerWhat endpoint sets
s3The S3-compatible host to upload to.
gcsThe Cloud Storage XML API base URL, such as a private endpoint. Uploads use the XML API, so the endpoint must serve it; an emulator that accepts only JSON API uploads, such as the stock fake-gcs-server image, cannot receive objects. It takes precedence over a gcs_base_url in the service-account JSON.
azure_blobThe Blob service endpoint.

The value can be an https:// or http:// URL with any host, such as an in-cluster MinIO at http://minio.storage.svc:9000. With http://, the uploaded request events cross the network in plaintext, so use it on networks you trust. Gateways 1.7.0 and earlier accept http:// only for localhost, 127.0.0.1, and a few emulator host names. While such a gateway is registered in the environment, the AISIX Cloud Admin API refuses to save an http:// endpoint on any other host with 422 DP_INCOMPATIBLE. Upgrade the environment's gateways to save it.

endpoint cannot be combined with cloud identity: the AISIX Cloud Admin API rejects the combination, and an open-source AISIX gateway that loads it never uploads and reports a permanent delivery error. The dashboard offers the field for S3-compatible targets, Cloud Storage, and Azure Blob, and hides it when cloud identity is selected.

A gcs endpoint needs gateways at 1.5.0 or later, because earlier gateways ignore it and upload to Google's public Cloud Storage endpoint instead. Every gateway release that AISIX Enterprise and AISIX Cloud support, 1.6.0 or later, honors it. A connected gateway older than 1.6.0 is not checked when you save; upgrade it before relying on the endpoint. See Mixed-Version Behavior.

Alibaba Cloud SLS​

Configure the endpoint host, project, log store, and credential reference:

{
"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​

Configure the Datadog site, service name, tags, and credential reference:

{
"name": "request-events-datadog",
"kind": "datadog",
"site": "datadoghq.com",
"service": "ai-gateway",
"tags": ["team:platform", "tier:prod"],
"credential_ref": "acme_datadog"
}

For the resources-file path, add these entries to observability_exporters:

resources.yaml (request 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

Keep only the destinations you use, then validate the resources file and start or reload the gateway as described for the OTLP example. Verify these exporters with the same request-ID approach used for OTLP: search object-storage and SLS records by request_id, or Datadog logs by aisix.request_id. The Snowflake guide shows how to inspect object-storage output directly.

SLS, Datadog, and object-storage exporters keep destination credentials out of the exporter resource by using credential references or cloud identity. The gateway resolves those credentials locally when it sends telemetry.

Configure Content Capture​

Exporters include request status, token counts, model and provider identifiers, request IDs, finish reason, and timing by default. Prompt and response bodies remain excluded unless full content capture is enabled on an OTLP/HTTP, SLS, or Datadog exporter.

Enable Full Content Capture​

Add content_mode and content_max_bytes when creating an exporter. In AISIX Enterprise and AISIX Cloud, patch an existing exporter with the same fields:

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
}'

In the dashboard, set Content mode to Full, then adjust Max content bytes in the exporter form.

For the resources-file path, add the fields to the exporter entry, then validate and reload the file:

observability_exporters:
- name: prod-otlp
kind: otlp_http
endpoint: ${OTLP_ENDPOINT}
headers:
Authorization: ${OTLP_AUTH_HEADER}
content_mode: full
content_max_bytes: 131072

Use full content capture only when the destination is approved to receive end-user prompt and response text. AISIX applies the configured byte cap to each captured content field independently.

Understand Truncated Records​

When valid JSON exceeds content_max_bytes, AISIX first tries to reduce the value structurally so the exported field remains valid JSON. If the reduced value still cannot fit the configured cap, AISIX falls back to a UTF-8-safe byte cut. The captured prompt is the serialized request body and follows the same behavior.

ContentTruncation Behavior
Long stringKeeps a prefix and adds the inline marker ...[aisix: truncated, N bytes total].
Base64 data URIReplaces the encoded data with a size placeholder.
Long arrayKeeps head and tail samples around an {"_aisix_truncated": true, "omitted_items": N} element that accounts for every omitted item.
Non-JSON content, or JSON that cannot fit after structural reductionCuts the value at a UTF-8 character boundary.

When truncation occurs, OTLP records include aisix.content_truncated: true. Datadog and SLS records include content_truncated: true.

Captured Content in Datadog and SLS Records​

With content_mode: full, the Datadog and Alibaba Cloud SLS exporters report a message exchange with the same structured content as the OTLP exporter's Captured Content attributes: the same messages, the same text, reasoning, and tool_call output parts, and the same finish_reason values. Each sink uses its own field names:

ContentAlibaba Cloud SLSDatadog
Request messagesinput_messagesgen_ai.input.messages
Anthropic system or Responses instructionssystem_instructionsgen_ai.system_instructions
Declared toolstool_definitionsgen_ai.tool.definitions
Assistant output messageoutput_messagesgen_ai.output.messages
Prompt side that is not structuredpromptgen_ai.prompt
Response side that is not structuredresponsegen_ai.completion

SLS writes each structured value as a compact JSON string. Datadog writes it as nested JSON. A structured field is written only when the record has that content, so system_instructions appears only for an Anthropic Messages system field or a Responses instructions field. A chat completions system message stays in input_messages.

The prompt and response sides are judged separately, by the same rule as OTLP. A side stays in the flat field when capture truncated it, when it does not convert, or when a structured value would exceed content_max_bytes. Content that does not convert includes payloads that are not a message exchange, such as embeddings input, images, audio, jobs, and agent text. The output message falls back the same way as on OTLP: when the captured output parts outgrow the cap, it is built from the response text, and the response stays flat only when that text was truncated too. One record can therefore carry structured input messages beside a flat response, or the other way round. MCP tool calls keep their arguments in the flat prompt field and their result in the flat response field. content_truncated: true marks a record whose content was truncated, as before.

A side with neither structured content nor captured text is still written as an empty flat field. For example, a failed chat request records its messages in input_messages and an empty response. OTLP omits such a side.

content_max_bytes caps each field separately, so one record can carry up to four capped content fields: the three input fields and the output message.

Changes from 1.6.0 and earlier

A gateway at 1.6.0 or earlier writes every captured prompt to SLS prompt and Datadog gen_ai.prompt, and every captured response to SLS response and Datadog gen_ai.completion. After upgrading, those fields hold only the sides that are not structured.

  • Move SLS indexes, saved queries, dashboards, and alerts that read prompt or response to the structured fields. Keep reading prompt and response for truncated or unconvertible sides.
  • Create SLS field indexes for input_messages, system_instructions, tool_definitions, and output_messages. Without a field index, these fields can be reached only by full-text search. Use a JSON-type index to query keys inside a value, or enable analytics on the field and read it with json_extract in SQL.
  • Move Datadog facets and monitors on gen_ai.prompt or gen_ai.completion to the gen_ai.input.messages and gen_ai.output.messages attributes.

Capture Passthrough Content​

For successfully relayed passthrough route traffic, full content capture records the request body as a string, subject to the exporter's content cap and JSON-aware structural truncation. Buffered responses record extracted text when the response matches a supported extraction shape and otherwise record the body as text. Streamed responses record accumulated extracted text; opaque data payloads are retained as text.

Capture Failed Requests​

Full content capture applies to the supported AI proxy endpoint types and MCP tool calls summarized below. A2A capture records message content rather than JSON-RPC envelopes. Realtime and job and batch telemetry are not captured. Passthrough requests rejected during authentication, authorization, input guardrails, or rate limiting do not include captured content.

On /v1/chat/completions, /v1/messages, and /v1/responses, a failure that produces a usage event after request parsing records the request body as the captured prompt, except for 401 and 403 responses. The body becomes the structured input messages when it converts (gen_ai.input.messages on OTLP and Datadog, input_messages on SLS), and otherwise stays in the flat prompt field. This includes input guardrail blocks (422), upstream failures other than 401 and 403, and validation failures after parsing, such as an empty messages array. When data masking runs, AISIX captures the post-mask request body. Malformed JSON is rejected before a usage event is created and is not captured.

The following boundaries also apply:

  • Caller authentication failures are rejected before a usage event is created.
  • When every routing target fails, the last attempt's record carries the prompt.
  • A response-side guardrail block never captures the blocked output.

These boundaries keep rejected credentials and blocked output out of exported content while retaining the request context needed to investigate other failures.

Review Captured Content by Endpoint​

Endpoint TypeCaptured Content
Text generationResponse text. OTLP, Datadog, and Alibaba Cloud SLS export the output as structured parts, including reasoning and tool calls, as described in Captured Content and Captured Content in Datadog and SLS Records.
MCP tools/callparams.arguments after traffic-control and input-guardrail checks, with any data masking applied; the JSON-RPC result when present. Output-guardrail blocks capture neither field. Other MCP methods are not captured.
A2AText from request and reply message parts on message/send and message/stream. File parts, data parts, and JSON-RPC envelopes are not captured.
Embeddings, rerank, and image generationFull response JSON.
Audio transcriptionReturned transcript. Uploaded audio is not captured; its SHA-256 checksum is recorded alongside the request's text fields.
Text to speechBinary speech response is not captured.

Model Fields in Exported Telemetry​

Usage telemetry records both the model alias requested by the caller and the model that served an attempt when those values differ. This distinction matters for routed and ensemble traffic, where one caller-facing alias can resolve to several target calls.

Destinations render these values in their own telemetry format. OTLP traces use the following fields:

  • aisix.requested_model contains the caller-requested alias or Model Group name.
  • gen_ai.request.model contains the model name sent to the upstream for that attempt.
  • gen_ai.response.model contains the concrete response model version when the provider reports one.
  • aisix.model_id identifies the model resource that calls the upstream: the requested direct model, or the target a routing model or semantic router selected for that attempt. It is empty on a record written before a routing target is selected. See Which Model a Record Names.
  • aisix.requested_model_id identifies the model resource the caller requested. See Correlate by the Requested Model ID.

A gateway at 1.6.0 or earlier puts the caller-requested alias in gen_ai.request.model.

Correlate by the Requested Model ID​

aisix.requested_model is a name, so renaming a model splits its telemetry into two series. aisix.requested_model_id carries the ID of the model the caller requested, which a rename does not change. Group or filter by it to follow a routing model, semantic router, or ensemble model across a rename.

Its value depends on the kind of model the caller requested:

Requested modelaisix.requested_model_idaisix.model_id
Direct or embedding modelThe model's ID.The same ID.
Wildcard modelThe wildcard model's ID.The same ID.
Routing model or semantic routerIts own ID, on every record of the request, including the failed attempts, the one that answered, and the records written before a target is selected.The target model that attempt was dispatched to. Empty on a record written before a target is selected.
Ensemble modelThe ensemble model's ID, on the record of every panel member and of the judge.The panel member or judge model of that record. The ensemble's aggregate record and failures after the panel carry the ensemble model's ID. Empty when the request is refused before the panel runs.

When the requested name resolved to a model but the gateway selected no target, such as a request the caller cancelled before the first attempt, a request a guardrail blocked, a request a rate limit rejected with 429, or a response-cache hit, aisix.requested_model_id still carries the requested model's ID. For a routing model or semantic router, aisix.model_id is empty on those records, so a filter on the group's ID in aisix.model_id misses them; filter on aisix.requested_model_id instead.

When the name does not resolve to a configured model, the field is empty and the exporters omit it. That covers an unknown model name, the body model of a passthrough route, which the gateway does not look up as a model, and MCP and A2A calls, which name no model.

A gateway at 1.6.0 or earlier does not export aisix.requested_model_id.

Model Fields in Other Destinations​

Datadog logs use aisix.requested_model for the caller-requested alias, aisix.requested_model_id for the ID of the requested model resource, gen_ai.response.model for the concrete response model version, and aisix.model_id for the model resource that calls the upstream, with the same values as the OTLP attribute.

Object storage and Alibaba Cloud SLS retain usage-event field names such as requested_model, requested_model_id, model_id, and provider_model_version. Their model_id takes the same values as aisix.model_id. Alibaba Cloud SLS omits an empty requested_model_id, and object storage writes it as an empty string.

Request Kind in Exported Telemetry​

Every record also carries the kind of work the request asked for — a chat completion, an image generation, a video submission, a tool call. See Tell Request Kinds Apart for the values and what they mean.

Destinations name it differently:

  • Object storage and Alibaba Cloud SLS keep the usage-event name, operation.
  • Datadog maps it to aisix.operation.
  • OTLP carries it as the aisix.operation span attribute, on every span of the request's hierarchy so a trace can be filtered by kind at its root.

OTLP spans also carry the OpenTelemetry gen_ai.operation.name attribute, and the two answer different questions. That one uses OpenTelemetry's own vocabulary where it has a value, which distinguishes model inference from agent and tool calls, and the aisix.operation value otherwise. It reports chat for chat completions, messages, and responses alike, so it cannot separate those three. Filter on aisix.operation when the endpoint matters.

Request and Response Headers in Exported Telemetry​

Each exporter can record the values of selected inbound request headers and of selected upstream response headers. Every exporter kind accepts two optional lists of header names:

  • request_headers: inbound request headers recorded on every usage event of the request, so callers that share one API key can be attributed by headers they send.
  • upstream_response_headers: upstream response headers recorded per attempt, each read off that attempt's own upstream response, such as a provider's request ID or region.

Each exporter records only the headers in its own lists, so two exporters can record different headers. Recording does not depend on content_mode, and the values are never sent to the control plane. See Attribute Events with Request Headers for the configuration, the name rules, and the value rules.

Destinations name the values differently:

ExporterRequest HeadersUpstream Response Headers
Object storageA nested request_headers object in each record.A nested upstream_response_headers object in each record.
Alibaba Cloud SLSOne request_headers field holding the object as a compact JSON string.One upstream_response_headers field holding the object as a compact JSON string.
DatadogA nested aisix.request_headers attribute, so each header is aisix.request_headers.<name>.A nested aisix.upstream_response_headers attribute, so each header is aisix.upstream_response_headers.<name>.
OTLPOne http.request.header.<name> attribute per header on the SERVER span. A gateway at 1.6.0 or earlier puts it on the operation span instead.One http.response.header.<name> attribute per header on the GenAI operation span, which is the attempt span when the request has attempts.

Both OTLP attributes are string arrays with one element, following the OpenTelemetry HTTP semantic conventions.

Token Counts in Exported Telemetry​

Exporters receive the token counts the usage event recorded, which keep the accounting the upstream used rather than the usage block the caller read. See Recorded Token Counts.

Destinations report them as follows:

  • Object storage and Alibaba Cloud SLS keep the usage-event fields as recorded, including reasoning_tokens, total_tokens, and the audio and image counts prompt_audio_tokens, prompt_image_tokens, completion_audio_tokens, and completion_image_tokens. A zero reasoning_tokens and an unreported total_tokens or audio or image count are omitted.
  • Datadog logs carry gen_ai.usage.input_tokens and gen_ai.usage.output_tokens, plus the recorded 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, and aisix.completion_image_tokens when present.
  • OTLP spans carry 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, and the gen_ai.usage.{audio,image}.{input,output}_tokens counts. See Token Usage Attributes.

In the OpenTelemetry generative-AI convention, input tokens include every cached token. OTLP and Datadog therefore report gen_ai.usage.input_tokens as prompt_tokens + cache_creation_tokens + cache_read_tokens. The two cache fields are non-zero only for usage reported in the Anthropic shape, such as Anthropic and Amazon Bedrock, which counts cache reads and writes beside the input tokens. An OpenAI-shape upstream already counts its cached tokens inside prompt_tokens, so its exported input count is unchanged. For example, an Anthropic reply with 19 input tokens, 1,200 cache read tokens, and 300 cache creation tokens exports gen_ai.usage.input_tokens as 1,519. A gateway at 1.6.0 or earlier exports 19 for the same reply.

In the OpenTelemetry generative-AI convention, output tokens include reasoning. OTLP and Datadog therefore report gen_ai.usage.output_tokens as completion_tokens + reasoning_tokens when the event counts reasoning beside the completion, and as completion_tokens otherwise. The test is the one the control plane prices by: total_tokens and reasoning_tokens are both greater than zero, and total_tokens equals prompt + completion + reasoning tokens but not prompt + completion tokens. See How Reasoning Tokens Are Billed.

The test applies per event, not per provider. A Gemini thinking model on the Vertex AI adapter normally passes it, and so does an OpenAI-compatible upstream that reports reasoning outside completion_tokens with a total that includes it. For Gemini, the exported output count is the same as before; the OpenAI-compatible case exports completion + reasoning where a gateway at 1.4.0 or earlier exported the completion alone.

AISIX Enterprise or AISIX Cloud​

The dashboard manages the same exporter resources from the target environment's Observability view. The exporter form collects the destination fields, content mode, and any kind-specific options.

Whether an exporter is saved through the API or the dashboard, the control plane projects the configuration to the AISIX gateways attached to that environment.

The following behavior applies to exporters projected to AISIX gateways:

  • The AISIX gateway sends request telemetry directly to your destination. The control plane does not proxy exported telemetry.
  • Prompt and response content stays on the gateway unless you enable full content capture on an exporter.
  • Credential references are resolved by the AISIX gateway. When a destination needs runtime credentials, the dashboard shows the environment variables to configure on the gateway.
  • Delivery health comes from gateway heartbeat data and shows whether batches are being shipped or whether the gateway is reporting a delivery error.

For OTLP/HTTP exporters, the dashboard provides presets for Langfuse, Honeycomb, and Grafana Cloud Tempo, and accepts custom OTLP endpoints.

The trace UI URL template is optional. Use it when the Request Logs view should link a request record to an external trace UI. The template must include {request_id} so the control plane can replace it with the request ID from the log record.

Next Steps​

Use OTLP Trace Reference to interpret trace hierarchy, parentage, status, and attributes. See Export Request Traces to ClickHouse to connect AISIX to ClickStack or deploy a Collector for an existing ClickHouse database.

Continue with Load Request Telemetry into Snowflake to query object-storage telemetry in Snowflake. Use Metrics and Usage Events to compare exported records with gateway metrics, and Access Logs and Request Correlation to trace individual requests.