Skip to main content
Version: Dev

OTLP Trace Reference

AISIX exports request telemetry as connected OTLP spans. The hierarchy preserves the inbound request, the logical upstream operation, and individual attempts so operators can investigate retries, failover, provider latency, and captured content without counting one request several times.

Configure the destination, sampling, and content mode in Observability Exporters. This reference explains the traces and attributes that arrive at an OTLP destination.

Understand OTLP Trace Structure​

AISIX exports one trace hierarchy for each request that produces OTLP telemetry, rather than one unrelated span for each usage event. For a model request that reaches an upstream, the hierarchy is:

SpanOTLP KindScope
Inbound requestSERVERCovers the request from arrival until the response finishes or the caller disconnects. A valid inbound traceparent becomes this span's remote parent.
Logical operationCLIENTCovers the gateway's upstream operation across retries and failover attempts. Child of the SERVER span.
Upstream attemptCLIENTRepresents one provider dispatch. Each attempt is a child of the logical operation and carries aisix.attempt_index.

Retries and failover therefore appear as sibling attempt spans under one logical operation. For each usage event, AISIX puts the complete attributes and captured content on the most specific span present: the attempt, otherwise the logical operation, otherwise the SERVER span. The other spans carry the correlation fields needed to join the hierarchy without repeating the whole event.

Not every request has all three levels:

  • A cache hit, input guardrail block, or other pre-dispatch result has only the SERVER span because AISIX made no upstream call.
  • MCP, A2A, Realtime, job, and passthrough calls that do not use per-attempt tracking have the SERVER span and one CLIENT span for the upstream operation.

Use span kind and parentage rather than the span name alone when counting exported traces. Filter for SERVER spans to count sampled request traces. When sample_rate is below 1, requests that were not selected never appear. Some authentication and malformed-input paths reject requests before a usage event or OTLP span exists, so SERVER spans are not a complete inbound-request count. Use request metrics for gateway request volume. Use CLIENT spans carrying aisix.attempt_index when analyzing individual model attempts.

Continue an Inbound W3C Trace​

When a request contains exactly one valid traceparent, AISIX continues that trace and makes its SERVER span a child of the caller's span. A malformed value or multiple traceparent headers are ignored, and AISIX starts a local trace instead of rejecting the request. An accompanying tracestate that passes the gateway's character and length checks is recorded on the SERVER span.

AISIX does not forward the caller's traceparent or tracestate to model providers, passthrough targets, MCP servers, or A2A agents unless forward_client_headers names one of them exactly; a wildcard pattern never matches them. By default the context establishes the caller-to-gateway relationship only. See Upstream Request Headers for the forwarding boundary.

OTLP Span Attribute Reference​

OTLP spans follow the OpenTelemetry generative-AI and HTTP semantic conventions as released in semantic-conventions v1.41.1. Attributes the conventions do not define use the aisix. prefix.

Changes from 1.6.0 and earlier

Trace queries, dashboards, and alerts written against a gateway at 1.6.0 or earlier need updating. The exporter configuration does not change, except for request header capture.

  • gen_ai.system is removed. Use gen_ai.provider.name.
  • gen_ai.prompt and gen_ai.completion are removed from OTLP spans. Captured content uses the attributes in Captured Content. The Datadog and Alibaba Cloud SLS exporters change their content fields the same way; see Captured Content in Datadog and SLS Records.
  • gen_ai.request.model is the model name sent to the upstream. The alias or Model Group name the caller requested moves to aisix.requested_model.
  • gen_ai.usage.input_tokens includes cached input tokens. For usage reported in the Anthropic shape, which keeps cache reads and cache writes beside the input tokens, the value now adds both to the input tokens. The Datadog exporter's gen_ai.usage.input_tokens changes the same way. See Token Counts in Exported Telemetry.
  • Span names change. chat.completions is gone, the SERVER span is named after the HTTP route, such as POST /v1/chat/completions, and MCP spans are named tools/call <tool> instead of execute_tool <tool>.
  • A successful span leaves its status unset instead of setting OK. A 4xx response no longer marks the SERVER span as an error.
  • aisix.client_source_ip and aisix.client_user_agent are renamed to client.address and user_agent.original. They and the http.request.header.<name> attributes move from the operation span to the SERVER span.
  • The SERVER span and a logical span that groups attempts no longer carry gen_ai.* attributes. Only the GenAI operation span does, so a backend that turns GenAI spans into model calls counts each call once.
  • The resource attribute service.name is the gateway's observability.service_name, which defaults to aisix, instead of the fixed value aisix-dp.
  • Request headers are selected per exporter by its request_headers instead of by the gateway's observability.usage_event.request_headers. See Request and Response Headers in Exported Telemetry.

Span Names and Status​

Each span in the trace hierarchy has one role. The GenAI operation span is the most specific span present: the attempt span, otherwise the logical span, otherwise the SERVER span.

SpanNameError status
SERVER{method} {route}, such as POST /v1/chat/completions. The route is added only when the request path is itself one of the gateway's fixed routes, so a passthrough path or /v1/videos/{id} is named by the method alone. A nonstandard method is named HTTP in place of the method.ERROR only for a 5xx status. A 4xx or a 499 leaves the status unset.
GenAI operation{gen_ai.operation.name} {gen_ai.request.model}, such as chat gpt-4o. MCP spans are tools/call {tool}, A2A spans invoke_agent {agent}, and passthrough spans passthrough {route}. The name is the operation alone when the target is empty or longer than 64 bytes, or when a wildcard model served the request, because the caller chose that model name.ERROR for any status of 400 or higher except 499, because a caller that closed the request cancelled it deliberately.
Logical span that groups attemptsSame as the GenAI operation span.Same as the GenAI operation span.

A span with ERROR status also carries error.type, set to the gateway's error class (aisix.error_class) or, when there is none, the status code. A successful span leaves its status unset.

The SERVER span is kind SERVER. The logical and attempt spans are kind CLIENT. A request with only a SERVER span, such as a cache hit, uses that SERVER span as its GenAI operation span.

GenAI Operation Span Attributes​

The GenAI operation span carries the usage event:

AttributeValue
gen_ai.operation.namechat for /v1/chat/completions, /v1/messages, and /v1/responses; text_completion for /v1/completions; embeddings for /v1/embeddings; invoke_agent for A2A; and execute_tool for MCP. Every other request kind reports its aisix.operation value, such as passthrough.
aisix.operationThe gateway's own request kind, which separates chat, messages, and responses.
gen_ai.provider.nameThe provider of the dispatched upstream. Provider keys with the Amazon Bedrock, Google Vertex AI, or Azure OpenAI adapter report aws.bedrock, gcp.vertex_ai, or azure.ai.openai. Other provider keys report openai, anthropic, gcp.gemini, deepseek, x_ai, mistral_ai, groq, cohere, perplexity, or ibm.watsonx.ai for those providers, and any other provider's ID verbatim in lowercase. A passthrough span reports the provider of the route's provider key, and an A2A span reports a2a. MCP spans omit it.
gen_ai.request.modelThe model name sent to the upstream. For a wildcard model, it is the concrete name substituted into the model's template.
aisix.requested_modelThe model alias or Model Group name the caller requested.
aisix.requested_model_idThe ID of the model that aisix.requested_model names. Omitted when that name did not resolve to a configured model. See Correlate by the Requested Model ID.
server.address and server.portThe host and port of the base URL the request was sent to. Amazon Bedrock, Google Vertex AI, and Azure OpenAI provider keys report it only when they set api_base. A passthrough span reports the route's target_url host and omits it for a preserve_host route. MCP and A2A spans report the MCP server or agent URL. A request that never reached an upstream, such as a cache hit or a refusal, omits it.
gen_ai.response.modelThe concrete model version the provider reported.
gen_ai.response.idThe provider's response ID.
gen_ai.response.finish_reasonsA single-element array with the finish reason.
aisix.service_tierThe service tier the upstream reported serving the attempt at, verbatim. Omitted when it reported none. See Read the Served Service Tier.
gen_ai.response.time_to_first_chunkFor a streamed response, the seconds from this attempt's dispatch to the first upstream frame.
http.response.status_codeThe status the usage event recorded.
error.typeSet on failure, as described above.
http.response.header.<name>The upstream response headers listed in the exporter's upstream_response_headers, read off this attempt's upstream response. See Request and Response Headers in Exported Telemetry.

gen_ai.provider.name, gen_ai.request.model, and server.* are omitted when the gateway cannot name the upstream, such as a request that failed before a routing target was selected.

Protocol-specific attributes are also added:

  • MCP: mcp.method.name set to tools/call, gen_ai.tool.name, and aisix.mcp.server_name.
  • A2A: gen_ai.agent.name, gen_ai.conversation.id (the A2A context), and aisix.a2a.operation, aisix.a2a.method, aisix.a2a.protocol_version, aisix.a2a.task_id, aisix.a2a.task_state, and aisix.a2a.stream_event_count.
  • Passthrough routes: aisix.passthrough.route_name, and aisix.client_identity when the route's identity_header extracted an end-user identity.

The span also keeps the gateway's own correlation and attribution attributes when they have values: aisix.request_id, aisix.exporter_name, aisix.api_key_id, aisix.api_key_name, aisix.user_id, aisix.model_id, aisix.attempt_index, aisix.attempt_kind, aisix.attempt_model, aisix.error_class, aisix.error_message, aisix.upstream_ttft_ms, aisix.downstream_latency_ms, aisix.gateway_overhead_ms, aisix.upstream_tpot_ms, aisix.audio.duration_seconds, aisix.gateway_embedding_calls, aisix.gateway_embedding_calls_dropped, aisix.jwt_subject, aisix.jwt_provider, and aisix.jwt_claim_mapping.

Gateway-Protocol Spans​

An A2A or MCP call is not a model inference, so its span is not encoded as one. Following the OpenTelemetry generative-AI semantic conventions, each protocol has its own span name and operation, and carries the protocol-specific attributes listed above:

  • A2A exports as invoke_agent <agent> with gen_ai.operation.name: invoke_agent.
  • MCP exports as tools/call <tool> with gen_ai.operation.name: execute_tool and mcp.method.name: tools/call. A gateway at 1.6.0 or earlier names it execute_tool <tool>.
  • Passthrough routes export as passthrough <route> with gen_ai.operation.name: passthrough.

Model traffic is named after the operation and the upstream model, such as chat gpt-4o, so a trace query on gen_ai.operation.name: chat matches model traffic only. A gateway at 1.6.0 or earlier names every model span chat.completions.

Request Parameters​

On /v1/chat/completions, /v1/messages, /v1/responses, and /v1/completions, the GenAI operation span records the parameters the caller sent, read from the request body in its own protocol. Every attempt of one request carries the same values.

AttributeSource
gen_ai.request.max_tokensmax_completion_tokens or max_tokens; max_output_tokens on /v1/responses.
gen_ai.request.temperature and gen_ai.request.top_ptemperature and top_p.
gen_ai.request.top_ktop_k on chat completions and messages.
gen_ai.request.stop_sequencesstop on chat completions and completions, or stop_sequences on messages.
gen_ai.request.frequency_penalty, gen_ai.request.presence_penalty, and gen_ai.request.seedThe same-named fields on chat completions and completions.
gen_ai.request.choice.countn on chat completions and completions, only when it is not 1.
gen_ai.request.streamtrue, only when the request streamed.
gen_ai.output.typejson for a json_object or json_schema format and text for a text format, from response_format.type on chat completions or text.format.type on responses. Omitted when the request names no format.
openai.request.service_tierservice_tier, except auto, on spans whose gen_ai.provider.name is openai. This is the tier the request asked for. The tier the upstream served the request at can differ, and is aisix.service_tier.

On /v1/embeddings, the span records gen_ai.request.encoding_formats from encoding_format and gen_ai.embeddings.dimension.count from dimensions.

Token Usage Attributes​

Every GenAI operation span except MCP carries usage:

AttributeValue
gen_ai.usage.input_tokensAll input tokens, including cached ones: prompt tokens plus cache creation and cache read tokens that the upstream reported beside them. Always present.
gen_ai.usage.cache_read.input_tokensInput tokens served from the provider's prompt cache, in either accounting shape. Present when greater than zero.
gen_ai.usage.cache_creation.input_tokensInput tokens written to the provider's prompt cache: Anthropic's cache creation count, or the OpenAI-shape cache_write_tokens. Present when the cache creation count is greater than zero or the upstream reported cache_write_tokens.
aisix.cache_creation_5m_tokens and aisix.cache_creation_1h_tokensThe part of the cache creation count written with a 5-minute and a 1-hour lifetime, when the upstream reported the split. Present together or not at all; see Cache Creation by Lifetime.
gen_ai.usage.output_tokensOutput tokens, including reasoning. Omitted on embeddings spans.
gen_ai.usage.reasoning.output_tokensReasoning tokens. Present when greater than zero.
gen_ai.usage.audio.input_tokens and gen_ai.usage.image.input_tokensAudio and image input tokens, as the upstream reported them. Present only when the upstream reported the count.
gen_ai.usage.audio.output_tokens and gen_ai.usage.image.output_tokensAudio and image output tokens, as the upstream reported them. Present only when the upstream reported the count. Omitted on embeddings spans.

See Token Counts in Exported Telemetry for how these values relate to the recorded usage event, and Audio and Image Tokens for which upstreams report the audio and image counts.

Captured Content​

With content_mode: full, the GenAI operation span carries the captured content. For /v1/chat/completions, /v1/messages, /v1/responses, and /v1/completions, a message exchange becomes the conventions' structured attributes, each a JSON string in the conventions' message schema:

  • gen_ai.input.messages holds the request messages. A completions prompt is converted only when it is a single string.
  • gen_ai.system_instructions holds the Anthropic system field or the Responses instructions field. Chat completions system messages stay in gen_ai.input.messages.
  • gen_ai.tool.definitions holds each declared tool's type, name, description, and parameters. An Anthropic tool's input_schema is reported as parameters.
  • gen_ai.output.messages holds one assistant message whose parts follow the order the caller received them, and its finish_reason.

On /v1/chat/completions, /v1/messages, and /v1/responses, streamed and non-streamed, the output message carries three part types:

Part typeFieldsCaptured From
textcontentChat completions content, Anthropic text blocks, and Responses message output text.
reasoningcontentChat completions reasoning_content or reasoning, or Mistral thinking and Databricks reasoning content parts, Anthropic thinking blocks, and the readable summary and text of a Responses reasoning item.
tool_callid, name, and argumentsChat completions tool_calls, Anthropic tool_use blocks, and Responses function_call items. arguments is the parsed JSON value when it parses, and the raw string otherwise.

The parts are read from the response the caller received, so output data masking is reflected, and a request served natively and one translated from another protocol are captured the same way. A Responses encrypted_content is never exported. Output that has no exact part type in the conventions is left out of the message: Anthropic redacted_thinking blocks and server-tool blocks, chat completions refusal and audio, and Responses refusal content and tool-call items other than function_call. On /v1/completions, the message holds the response text as one text part.

The output message's finish_reason uses the conventions' values, whichever protocol reported it:

finish_reasonRaw Values
tool_calltool_calls, tool_call, function_call, and tool_use.
lengthlength, max_tokens, max_output_tokens, and model_context_window_exceeded.
content_filtercontent_filter, content_filtered, refusal, and guardrail_intervened, plus Gemini's SAFETY, RECITATION, BLOCKLIST, PROHIBITED_CONTENT, SPII, IMAGE_SAFETY, and LANGUAGE.
errorerror, plus Gemini's MALFORMED_FUNCTION_CALL, UNEXPECTED_TOOL_CALL, and TOO_MANY_TOOL_CALLS.
stopEvery other value, including stop, end_turn, stop_sequence, and STOP.

Raw values match case-insensitively. gen_ai.response.finish_reasons keeps the raw value the provider reported. The output message needs a finish reason, so it is omitted when the event has none, such as a /v1/responses request that failed or was cancelled. See Responses Finish Reason.

MCP tool calls use gen_ai.tool.call.arguments and, for a successful call, gen_ai.tool.call.result.

Each structured attribute is held to the exporter's content_max_bytes, and the prompt and response sides are judged separately. Content that is not a message exchange, such as embeddings input, images, audio, jobs, and agent text, stays raw under aisix.content.prompt and aisix.content.response. So does a side that capture truncated, that does not convert, or that would exceed the cap once converted, so a gen_ai.* content attribute is never partial. aisix.content_truncated: true marks a record whose content was truncated. When the captured output parts outgrow content_max_bytes, the output message falls back to the response text as one text part, and when that text was truncated too, it stays raw under aisix.content.response.

The Datadog and Alibaba Cloud SLS exporters carry the same structured content for the same event, under their own field names. See Captured Content in Datadog and SLS Records.

SERVER Span Attributes​

The SERVER span describes the inbound HTTP request:

AttributeValue
http.request.methodThe request method. A nonstandard method reports _OTHER, with the method itself in http.request.method_original.
url.pathThe request path, up to 512 bytes.
url.schemehttps on a TLS listener, otherwise http.
http.routeThe route, only when the path is itself one of the gateway's fixed routes.
url.queryThe query string, up to 1,024 bytes, when the request had one.
http.response.status_codeThe final response status.
client.addressThe resolved caller address.
user_agent.originalThe caller's User-Agent.
http.request.header.<name>The request headers listed in the exporter's request_headers. See Request and Response Headers in Exported Telemetry.
aisix.request_id, aisix.exporter_name, and aisix.operationThe keys that join the request's spans.

url.query replaces the value of a sensitive parameter with REDACTED. The parameter names AWSAccessKeyId, Signature, sig, and X-Goog-Signature match case-sensitively, and key, access_token, token, client_secret, password, api_key, and apikey match case-insensitively. Names are compared after percent-decoding. Every other value is exported verbatim, so do not send other secrets in query strings to a gateway whose OTLP destination should not receive them.

A logical span that groups attempts carries only aisix.request_id, aisix.exporter_name, and aisix.operation.

Resource Attributes​

Each export request carries these resource attributes:

  • service.name: the gateway's observability.service_name from its startup configuration, which defaults to aisix. A gateway at 1.6.0 or earlier always reports aisix-dp.
  • service.version: the gateway build version.

The instrumentation scope is aisix-obs.otlp_http_sink, with the gateway build version.

AISIX does not export attributes whose data the gateway does not retain, including openai.response.system_fingerprint, gen_ai.agent.id, mcp.session.id, and jsonrpc.request.id. The service tier the upstream served the request at is exported as aisix.service_tier rather than openai.response.service_tier.

Next Steps​

Configure sampling, content capture, or another destination in Observability Exporters. Use Metrics and Usage Events to compare spans with gateway metrics and stored usage records.