Skip to main content
Version: 1.2.0

Access Logs and Request Correlation

AISIX AI Gateway writes structured access logs and records identifiers that connect requests to callers, fronting infrastructure, and upstream providers. Together, these records let operators reconstruct what happened and find the corresponding entries in each system.

Collect Access Logs

AISIX writes access logs through the process logger to standard error. Your container runtime, service manager, or host logging pipeline can collect that stream.

Set the process log level in the startup configuration:

config.yaml
observability:
log_level: "info"

When set to a valid filter, the RUST_LOG environment variable overrides the configured log level.

note

The access_log field is reserved and currently has no effect. There is no separate access-log format or sink setting; collect the process's standard error stream.

An access log includes the request method, path, status, latency, provider, model, API key ID, request ID, token counts, and routing outcome when those values are available.

For a failed request, error_kind provides a stable failure category and error provides the underlying reason when available.

Understand Log Timing and Coverage

The request type determines when its access log is written and which details are available:

SignalWrittenWhat it provides
Access log for a non-streamed requestWhen the request endsThe request outcome and all fields the gateway resolved, including token counts and the provider response ID when available.
Access log for a streamed requestWhen the response opensThe initial outcome and routing fields. Token counts and the provider response ID are not yet available; the usage event carries the streamed figures.
Access log for a Realtime sessionAfter the WebSocket session closesThe session outcome and resolved request fields. Token totals remain on the usage event.
provider call completed logOnce for each provider call that returns an IDrequest_id, attempt_index, attempt_kind, and provider_request_id for that upstream attempt.
Usage eventOnce per request attempt on supported proxy pathsAttempt outcome, consumption, latency, and provider response ID when available.

Trace Provider Attempts

provider_request_id is the response object ID returned by the provider, such as an OpenAI chat.completion.id, an Anthropic message id, or a Responses API resp_…. Use it to find the call in the provider's console or support records.

The field is omitted rather than left blank when no ID is available, including for guardrail blocks, cache hits, and normalized embeddings, audio, and image responses. For streamed requests, find each returned provider ID in a provider call completed entry. Join it to the access log by request_id, and use attempt_index to distinguish retries or failovers.

Correlate a Request Across Systems

The x-aisix-request-id response header is the main join key for request records. Other supported response headers report cache outcomes, retry timing, and selected targets; see Headers and Error Codes for their route coverage.

A request can involve three kinds of identifiers, and none substitutes for another:

IdentifierAssigned byUse it to
request_idAISIX, or the caller when AISIX adopts a supplied value. Returned as x-aisix-request-id.Find the request's access log and usage events, including exported records and entries on the dashboard's Logs page.
downstream_request_idFronting infrastructure through the x-request-id request header. Recorded in gateway logs but not returned as a separate downstream ID. If AISIX adopts the value, it is returned as x-aisix-request-id.Find the same HTTP request in an ingress controller, reverse proxy, service mesh, or CDN.
provider_request_idThe provider, one for each upstream attempt whose response includes an ID.Find the attempt in the provider's console or quote its ID to provider support.

To investigate a completed response:

  1. Start with the x-aisix-request-id value returned to the caller.
  2. Find the matching access log and usage events by request_id. Use attempt_index to order retries or failovers.
  3. Read provider_request_id from the relevant attempt when you need to investigate the call with the provider.

For a disconnect before response headers, start with downstream_request_id from the fronting infrastructure when available.

Match Fronting Infrastructure

AISIX records separate values for the network connection, forwarded request, and resolved caller address:

ValueIdentifiesMatch it with
peerThe remote end of the accepted TCP connection, including its source port.A fronting proxy's connection record. This is most useful behind a layer-4 load balancer when AISIX uses the host network.
downstream_request_idThe HTTP request ID received in x-request-id.The fronting proxy's or ingress controller's request log.
Resolved caller addressThe caller IP selected through proxy.real_ip, without a port.Access-control decisions and usage records.

When present, peer and downstream_request_id follow the request through its access log, provider call completed entries, and intermediate diagnostic lines. Missing values are omitted rather than logged as empty fields.

AISIX screens an incoming x-request-id before logging it. Recording the value does not make it the gateway's request_id; adopting it is controlled separately by proxy.request_id.accept_headers.

Diagnose Incomplete or Refused Requests

The access log and related signals distinguish where a request stopped:

ScenarioWhat AISIX recordsAdditional signal
Caller disconnects before response headersAn access log with status 499 and error_kind="client_disconnected". It includes the resolved model and provider when available, but no token fields.aisix_proxy_client_cancelled_requests_total counts the request.
Caller disconnects during a streamed responseThe access log keeps the normal response status because it was already written.The usage event records status 499 and the tokens received before the disconnect.
Oversized request body is fully drainedAn aisix::body_limit entry with drain_outcome="completed", logged at info. The caller can receive 413 Content Too Large.aisix_proxy_request_body_limit_rejections_total counts the rejection.
Oversized body cannot be fully drainedThe diagnostic uses cap_reached, timeout, or client_read_error, and the caller usually sees a closed connection. These warn entries are limited to one per outcome per second.The rejection metric counts every occurrence, including diagnostics suppressed by the log limiter.

Body-limit diagnostic entries also carry request_id, declared_content_length, configured_limit_bytes, and drained_bytes. Join them to the access log by request_id.

If the caller disconnects before model resolution, the access log contains neither a model nor a provider.

Reuse Your Own Request ID

If your service already generates a request ID for the business call, send it to AISIX. The adopted value then appears in the response header, access log, every usage event, AISIX Cloud Request Logs, and the x-aisix-request-id header sent upstream.

# AISIX_PROXY is the gateway origin; omit a trailing slash and 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"

curl -i "$AISIX_PROXY/v1/chat/completions" \
-H "Authorization: Bearer $AISIX_API_KEY" \
-H "Content-Type: application/json" \
-H "x-aisix-request-id: req_abc123-orders-svc" \
-d '{
"model": "'"$MODEL_ALIAS"'",
"messages": [
{
"role": "user",
"content": "Hello"
}
]
}'

The response echoes the same value:

HTTP/1.1 200 OK
x-aisix-request-id: req_abc123-orders-svc

Retries and failovers reuse the adopted ID on every usage event, so filtering by that value returns the complete attempt chain.

Accepted Values

AISIX handles supplied values as follows:

Supplied valueResult
1–256 bytes containing only visible ASCII characters (! through ~)AISIX adopts the value. UUIDs, ULIDs, prefixed IDs such as req_abc123, and the hexadecimal nginx $request_id all qualify.
Empty, oversized, non-ASCII, space-containing, or control-character valueAISIX ignores the value, generates a UUID, and continues the request.
A value reused for separate requestsAISIX accepts it because uniqueness is not enforced, but the requests become indistinguishable in logs and event trails.

Generate a fresh ID for each request.

Choose the Accepted Headers

By default, AISIX adopts a caller-supplied ID only from x-aisix-request-id. The following configuration also accepts an infrastructure-assigned x-request-id, while keeping the default header as a fallback:

config.yaml
proxy:
request_id:
# Default: accept only the AISIX request ID header.
# accept_headers: ["x-aisix-request-id"]

# Headers are checked from left to right; the first acceptable value wins.
accept_headers: ["x-request-id", "x-aisix-request-id"]

# To ignore all caller-supplied IDs and always generate a UUID, use:
# accept_headers: []

Use this option when AISIX is the first hop or when the ID assigned by a reverse proxy or ingress controller should become the request's identity everywhere. AISIX records an acceptable x-request-id as downstream_request_id regardless of this setting; when it also adopts that value, request_id and downstream_request_id are identical.

If you configure AISIX with environment variables, set the priority list as a comma-separated AISIX_PROXY__REQUEST_ID__ACCEPT_HEADERS value.

Use only valid, non-reserved HTTP header names. Startup fails if the list contains a malformed name or a reserved header, including credential-bearing headers, host, cookie, traceparent, and tracestate. AISIX blocks these headers because an adopted value is logged, returned to the caller, and sent upstream.

Next Steps

Use Metrics and Logs to monitor aggregate traffic and compare request-level metrics with per-attempt usage events. Configure Observability Exporters to send usage events to an external destination.