Control request log data
The Request logs setting controls how much request detail leaves the data-plane cluster for the Tetrate management plane, from full prompt and response bodies down to nothing at all. Those bodies routinely carry the most sensitive data an organization handles (customer records, source code, regulated personal data), and residency and privacy commitments often require that content to stay inside the customer's own infrastructure while usage and cost remain centrally visible.
The setting governs the request_log records that reach the management plane, and accepts three values:
| Mode | What reaches the management plane | What is dropped |
|---|---|---|
| Full (default) | Identifiers, counters, costs, headers without credentials, llm_parameters, request body, response body. | Credential headers. |
| Metadata only | A fixed field list: identifiers, counters, costs, timing, three product routing headers, and the data plane's budget, fallback, rate-limit and attribute routing decisions. | Bodies, request headers, other response headers, request parameters, the caller's attribute values, client_name, and the key prefix, hash, and address. |
| Off | Nothing. | The entire request_log record. |
Each mode sends a subset of the mode above it. Full gives the Admin Console's per-request views their full detail. Metadata only keeps usage analytics and the spend each budget measures updating while prompt and response content, headers, and caller-supplied values remain inside the cluster (see What the metadata-only record contains). Off suppresses the request_log record at the management plane altogether, which also stops new usage and spend from being recorded; it is a last resort.
The fixed Metadata only field list and the credential removal in every mode require a 0.9.0 or later data plane. An older data plane under Metadata only still sends every field except the two bodies.
The setting is system-wide for the deployment. Switching takes effect across the data plane in under a minute: no restart, no reinstall.
Persona: Platform operator working in the Admin Console, often in coordination with security or compliance stakeholders.
Estimated time: 5 to 10 minutes to choose, set, and verify a mode; longer if an external collector is also being wired up.
When this guide applies
Typical cases:
| Situation | Suggested mode |
|---|---|
| Default evaluation or development, where full per-request detail in the Admin Console is wanted | Full |
| Prompt and completion content must stay inside the cluster, but usage and cost visibility is still required centrally | Metadata only |
No request_log record may leave the cluster at all, for the strictest data-residency posture | Off |
| Full content is required for observability, but in a self-managed stack rather than the Admin Console | Metadata only or Off, paired with an external OTEL destination |
Outcomes
By the end of this guide:
- The three modes are understood, including every field each one forwards and what it drops.
- A mode has been set from the Admin Console and confirmed active with a smoke test.
- The audit trail for the change has been located.
- Where required, the full record has been confirmed arriving at a self-managed OTEL collector while content is withheld from the management plane.
What each mode affects
The mode affects only the records stored at the management plane. Its effect on each Admin Console surface and downstream path is shown below:
| Surface | Full | Metadata only | Off |
|---|---|---|---|
| Admin Console Request logs view (per-request rows) | rows with bodies and headers | rows without bodies, headers, or request parameters; budget fallback and attribute routing badges still show, but the attribute routing details omit the caller's attribute values | no rows |
| Admin Console Usage view and budget spend meters (tokens, costs over time) | populated | populated | no new data; history kept |
| Admin Console Audit logs view | populated | populated | populated |
| Billing / transactions table | populated | populated | populated |
| Customer-attached OTEL destination on the data plane | full record | full record (mode applies only to the management plane) | full record |
Usage analytics and the spend a budget measures are computed from request_log records. Under Off they stop growing: existing history is kept, but new requests are not counted, so a budget can let requests through past its limit.
Two things are never affected by this setting:
- Billing. Transactions are written through a separate path. Every request still produces a transaction regardless of mode, so monthly billing remains accurate.
- Customer-attached destinations. A self-managed OTEL receiver (see the External destination section below) receives the full record under every mode; the toggle scopes only what crosses the cluster boundary toward Tetrate.
What the metadata-only record contains
The request_log record is one JSON document per request. Under Metadata only, a 0.9.0 or later data plane builds the record from a fixed field list and sends nothing else; a field added to the record in a later release is not sent under Metadata only unless it is added to that list. The table below lists every field and which mode sends it.
| Field | Full | Metadata only | Notes |
|---|---|---|---|
request_id | yes | yes | Gateway-assigned request identifier. |
timestamp | yes | yes | Time the record was built. |
user_id | yes | yes | Identity of the calling user as resolved from the credential. From 0.9.0, on a 0.9.0 data plane, a caller admitted with a project OIDC token is recorded by a stable value derived from the token's sub, never the sub itself: e_ followed by the first 24 hex characters of the SHA-256 of a sub that is an email address, or o_ followed by the unpadded base64url encoding of any other sub. See Find one person's requests. |
api_key_id, api_key_name | yes | yes | Identifier and display name of the API key. Key names are set by administrators in the Admin Console. |
api_key_prefix, api_key_hash | yes | no | Prefix and hash of a legacy key. The key itself is never sent (see below). |
api_key_address | yes | no | Internal key address, equal to the key identifier on this data plane. |
model_name | yes | yes | The resolved model, or the requested model when no resolution happened. |
endpoint, hostname | yes | yes | The API path called and the Host the caller sent. |
client_name | yes | no | The calling application's name, derived from the X-Title header, falling back to User-Agent. Not the registered client. Usage grouped by this name shows these requests as unknown. |
status, status_code | yes | yes | success, error, or rejected, plus the HTTP status. |
input_tokens, output_tokens, cached_input_tokens, cache_creation_input_tokens | yes | yes | Token counts as reported by the provider. |
*_cost, *_cost_before_fee, additional_cost, additional_cost_before_fee | yes | yes | Per-component cost decimals. |
usage_source | yes | yes | Whether the token counts are actual, partial, or estimated. |
duration_ms, upstream_duration_ms | yes | yes | End-to-end and upstream latency. |
llm_parameters | yes | decisions only | A JSON object of request parameters (temperature, max_tokens, top_p, frequency_penalty, presence_penalty), the names of body fields the gateway dropped when adapting the request for the provider (normalized_dropped), budget and rate-limit decisions, the reject and fallback reason, and the attribute-routing decision, including the X-Tars-Metadata values the decision matched on. No prompt text. Under Metadata only the data plane keeps only its own decisions: budget_action, budget_fallback_model, fallback_reason, reject_reason, rate_limit, and the attribute_routing_* policy, scope, mode, outcome, rule, model, provider, from_model, out-of-set attribute names and skipped rules. It drops the request parameters, normalized_dropped, attribute_routing_attributes (the caller's values), and the customer and project IDs in the routing record. |
request_headers | yes | no | Every inbound request header as a flat map, after the strip described below, plus the product-added X-Tars-Id (the gateway identifier). Includes customer-added headers. |
response_headers | yes | X-Model-Requested, X-Model-Effective, X-Ai-Eg-Upstream-Backend-Name only | Under Full, upstream response headers after the strip described below, plus X-Model-Effective, X-Model-Requested, and X-Model-Provider. The three headers kept under Metadata only drive the routing badges and the managed, BYOK, or passthrough usage classification. |
request_body | yes | no | The full request payload. |
response_body | yes | no | The full response payload. |
Three points answer the questions most often asked about this record.
- Client IP address: only under Full. The record has no dedicated address field, but it keeps
X-Forwarded-Forinrequest_headers, and Envoy appends the connecting peer's address to that header on every request. The address of the client (or of the load balancer in front of the gateway) therefore reaches the management plane under Full. Metadata only sends no request headers. - Customer-added headers: only under Full. No allow-list exists. Under Full the data plane removes only headers whose name starts with
Authorization,X-API-Key,X-Tars,X-Envoy,X-Internal, orX-Project-Idfrom the request side, andX-RateLimit,OpenAI,Anthropic,X-Groq,X-Envoy,Server, orSet-Cookiefrom the response side, plus the credential headers below. Any other header the application attached is sent, and theX-Tars-Metadatavalues that attribute-based routing matched on are copied intollm_parametersas well. (In passthrough mode theAnthropic-*response headers are kept on purpose, so the caller's own organization ID and rate-limit budget appear in the record.) Under Metadata only none of these are sent. - Credentials: no, in any mode.
Authorization,Proxy-Authorization,Cookie,Set-Cookie,X-API-Key,api-key,x-goog-api-key,X-Tars-Api-Key, and any IdP token header are removed before the record is sent, so neither the Agent Router key nor a passthrough provider token reaches the management plane. For a legacy key under Full,api_key_hash(an HMAC of the key) andapi_key_prefixare sent instead; the management plane matches the key on the hash. This covers authentication headers only: a secret pasted into a prompt, a custom header, or a provider error body is not detected, and under Full it is sent.
The mode scopes the request_log record alone. Billing transactions (user, key, model, token counts, amount) are always sent. Envoy access-log entries are always streamed to the management plane for Model Context Protocol (MCP) request logging: every entry carries the peer address and X-Forwarded-For, the management plane stores a client IP for MCP calls, and it discards the entries for other traffic on arrival. AI tracing spans reach the management plane unless routed elsewhere, under their own content mode, and with trace capture set to metadata-only a failed request's full error body is still recorded in the span's exception event (an open product defect). Guardrail trigger records are governed by Settings → Guardrails. See Export telemetry to an observability stack.
How to change the mode
The mode is changed from the Admin Console alone.
- Sign in to the Admin Console with an admin account.
- Go to Settings → Request logs.
- Pick a mode from the dropdown. The card shows a short description of what each mode stores.
- Click Save.
The save action:
- Persists the new mode to system settings.
- Pushes the change to the data plane via the existing self-heal channel.
- Writes an audit log entry recording who changed the mode and to what value.
The change reaches the data plane in under a minute, usually a few seconds. Confirm it by sending a prompt through the gateway and checking the Request logs view (see the next section).
How to verify the mode is active
The active mode is confirmed by sending a known prompt through the gateway and observing how it appears in the Admin Console. This smoke test uses the Admin Console alone; no kubectl is required.
Setup once
In the Admin Console, go to API Keys → Create key. Copy the sk-... value. Note an address that serves the key's project: the Base URL the Developer Console prints for the project, or an address on the project's Data Planes tab in the Admin Console.
Smoke test per mode
-
Set the mode in Settings → Request logs.
-
Send a prompt with a distinctive word:
curl -X POST https://<your-gateway>/v1/chat/completions \-H "Authorization: Bearer sk-<your-key>" \-H "Content-Type: application/json" \-d '{"model":"claude-haiku-4-5","messages":[{"role":"user","content":"Say MANGO once and stop."}],"max_tokens":50}'Alternatively, send a prompt from the Playground.
-
Wait ~10 seconds, then check the Admin Console.
Expected result by mode:
| Mode | Request logs view | Usage view |
|---|---|---|
| Full | New row for MANGO. Detail panel shows prompt + response bodies. | MANGO's tokens + cost reflected. |
| Metadata only | New row for MANGO with tokens, cost, and timing. Body panels say "No request body available" / "No response body available", and no request headers are shown. | MANGO's tokens + cost reflected. |
| Off | No row for MANGO. | MANGO's tokens + cost not reflected. |
Repeat for each mode under test, picking a different keyword each time (MANGO / PAPAYA / DURIAN / KIWI, etc.) so the rows are easy to spot.
Audit log check
After any change, Audit logs in the Admin Console shows a new row:
- Resource type:
system_settings - Resource ID:
request_logs.mp_mode - Action:
UPDATE - Body:
{"mode":"<the saved value>"} - Actor: the acting user account
- Source IP, User agent, Correlation ID populated
External destination: sending the full record to a self-managed backend
To keep prompt and response content inside a self-managed observability stack while still letting Tetrate see usage and costs, configure Metadata only mode on the management-plane path and route the full record to an operator-run backend. Off also keeps content inside the cluster, but stops usage and budget spend meters at the management plane.
The data plane always emits the full record to any local destination, regardless of mode. The typical operator pattern is:
- Mode =
metadata_only(oroffas a last resort, which stops usage and budget spend meters at the management plane) - Local destination = a backend subscribed to the
requestlogsignal, as below
There are two ways to attach the local destination. The collector-routed path (subscribing a custom backend to the requestlog signal in Helm values) is the supported primary path. The older direct-attach method (hand-setting OTLP environment variables on the egress container) still works and is documented below as legacy, with a migration path.
Primary path: subscribe a custom backend to the requestlog signal
In the data-plane Helm values (the file that carries global.observability), add a backends.custom[] entry subscribed to requestlog:
global:
observability:
enabled: true
signals:
requestlog:
enabled: true # chart default
backends:
custom:
- name: request-log-sink
endpoint: otel-collector.observability.svc.cluster.local:4317
signals: [requestlog] # explicit subscription: an omitted list never includes requestlog
tls: { insecure: true } # in-cluster plaintext hop; omit for TLS
sendingQueue: { queueSize: 64 }
Apply with helm upgrade (or the normal chart-management flow). The subscription is the only switch; from it, the chart wires the whole path automatically:
- The data plane's local OpenTelemetry collector renders a dedicated request-log lane: an OTLP/gRPC receiver on port 3501 (with a raised message-size cap) plus an OTLP/HTTP receiver on 3502, and a pipeline whose destinations are exactly the subscribed backends.
- The gateway's egress container is injected with the emission configuration (
OTEL_LOGS_EXPORTER=otlp,OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=http://otel-gateway.<system-namespace>:3501,OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=grpc) so the module pushes request logs to the collector over OTLP/gRPC (the default transport on this path). These variables are chart-managed: do not set them by hand alongside a subscription (the render fails, see the migration section).
Emission and the collector lane appear and disappear together: remove the subscription and, on the next upgrade, the receiver, the pipeline, and the injected variables are all gone.
The endpoint shape, protocol: otlphttp for backends behind HTTP load balancers or with records past a gRPC size cap, per-backend TLS, header auth from the otel-gateway-backend-credentials Secret, and queue sizing (the sendingQueue: { queueSize: 64 } above is the recommended request-log sizing) are covered field-by-field in Custom observability backends, including how to fan the same records out to several destinations by adding more entries.
Legacy path: direct-attach via container environment variables
This is the pre-fan-out mechanism: it supports exactly one destination, speaks OTLP/HTTP, and bypasses the data plane's local collector. Existing setups keep working unchanged, but new destinations should use the collector-routed path above.
Two environment variables on the egress proxy container:
| Variable | Value |
|---|---|
OTEL_LOGS_EXPORTER | otlp |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | the full URL of the collector's OTLP/HTTP logs endpoint, including the /v1/logs path |
For example, a collector listening at https://collector.observability.svc.cluster.local:4318 requires the endpoint https://collector.observability.svc.cluster.local:4318/v1/logs.
The /v1/logs suffix is mandatory on this variable. Per the OTLP exporter specification, the signal-specific OTEL_EXPORTER_OTLP_LOGS_ENDPOINT is used verbatim, and most collectors only respond on that path. Omitting it is the single most common misconfiguration. (Alternatively, set the base variable OTEL_EXPORTER_OTLP_ENDPOINT to the collector URL without a path; the exporter appends /v1/logs itself. On data planes released before July 2026, only the signal-specific variable works; the base form was posted verbatim and returned 404.)
When OTEL_EXPORTER_OTLP_LOGS_PROTOCOL is unset, the transport stays OTLP/HTTP (http/protobuf). This has always been the direct-attach behavior, and upgrades preserve it. An explicit OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=grpc is honored on current data planes; with gRPC the endpoint carries no /v1/logs path and the URL scheme selects TLS (http:// plaintext, https:// TLS). Since 0.6.0 the gRPC exporter also honors OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE, OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE, and OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY, for request logs and guardrail events alike, so a pinned CA or mutual TLS set through those variables is in effect. On a data plane before 0.6.0 an https:// collector was verified against the system roots only and no client certificate was sent, whatever those variables held.
In the Helm values for the egress chart, the variables go on the egress container's env block:
envoyProxy:
provider:
kubernetes:
envoyDeployment:
container:
env:
- name: OTEL_LOGS_EXPORTER
value: otlp
- name: OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
value: https://<your-collector>/v1/logs
Apply with helm upgrade. The egress pod restarts and the new endpoint becomes the local destination.
If the collector requires an auth header, add it via OTEL_EXPORTER_OTLP_HEADERS ("Authorization=Bearer <token>"); for sensitive headers, bind from a Kubernetes Secret with valueFrom.secretKeyRef instead of inlining.
Migrating from direct-attach to the collector-routed path
The collector-routed path replaces hand-managed env with a declared backend: it fans out to any number of destinations, moves header credentials from env strings into a Kubernetes Secret, and adds collector-side queueing and byte-aware batching in front of the backend. Migration is a single values change:
- Add the backend entry (as in the primary-path example above) pointing at the same collector. For OTLP/gRPC (
protocol: otlp, the default) the endpoint is barehost:port, no scheme, no/v1/logs. To keep talking OTLP/HTTP to a collector that only listens on 4318, setprotocol: otlphttpand use the fullhttp(s)://URL without the/v1/logssuffix (the collector exporter appends signal paths itself). - In the same change, remove the operator-set variables from
envoyProxy.provider.kubernetes.envoyDeployment.container.env:OTEL_LOGS_EXPORTER,OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, andOTEL_EXPORTER_OTLP_LOGS_PROTOCOLif present. Move anyOTEL_EXPORTER_OTLP_HEADERSauth to the backend'sauth.headerswith theotel-gateway-backend-credentialsSecret (see Custom observability backends). - Apply, and re-run the verification below.
The render fails on collision: by design. As soon as any backend subscribes to requestlog, those three variables become chart-managed, and the chart refuses to render a values file that also sets any of them on the container env. helm template / helm upgrade aborts before anything rolls out, with an error naming the colliding key:
egress-helm: OTEL_LOGS_EXPORTER is chart-managed while the requestlog fan-out lane is active
(global.observability.signals.requestlog.enabled plus a subscribed
observability.backends.custom[] entry), but it is also set via
envoyProxy.provider.kubernetes.envoyDeployment.container.env (legacy direct-attach).
Remove the operator-supplied key and configure the destination as a custom backend instead —
see the request-logs migration guide: https://docs.tetrate.ai/guides/ops/cost-and-quota/configuring-request-logs
The fix is exactly what the message says: delete the named key (and its siblings) from the container env, keeping the destination only as a backends.custom[] entry. There is no silent precedence: the chart never lets the two mechanisms configure the same variables at once. Conversely, while no backend subscribes to requestlog, operator-set variables pass through untouched, which is why existing direct-attach setups keep working until they opt in. (The same rule protects the guardrail-trigger lane's TARS_GUARDRAILS_OTEL_EXPORTER_OTLP_LOGS_ENDPOINT, and refuses the deprecated AIDISCOVERY_OTEL_EXPORTER_OTLP_LOGS_ENDPOINT name the same way; see Guardrails filter exporter settings.)
What changes in behavior after migrating:
| Direct-attach (legacy) | Collector-routed | |
|---|---|---|
| Destinations | Exactly one | Any number of subscribed backends |
| Transport from the module | OTLP/HTTP (unset protocol) | OTLP/gRPC to the local collector; per-backend otlp or otlphttp onward |
| Auth | Header string in container env | Per-backend headers from the otel-gateway-backend-credentials Secret |
| Buffering | SDK exporter only | Plus a sized in-memory queue and byte-aware batching per backend in the collector |
| Lifecycle | Hand-managed env | Rendered and removed with the subscription |
Verifying the external destination
The external destination is verified by setting the management-plane path to drop everything, then confirming the full record still arrives at the local collector.
-
Stand up (or reuse) an OTEL collector reachable from the data-plane cluster on the URL above.
-
Apply the Helm values change. Wait for the egress pod to roll.
-
Set the mode to Off in the Admin Console (the strongest case: Tetrate gets nothing).
-
Send a test prompt via
curlas above. -
Within ~10 seconds, the collector logs a record with:
- Body: the full request-log record as a plain JSON document (
request_id, model, tokens, costs, and the request and response bodies inline). No decompression or decoding is needed; any OTLP-compatible backend can display and index it directly. - Attribute
event.type:request_log, plusgen_ai.*andtars.*dimensions (tars.request.idjoins these records with guardrail-trigger records shipped to the same stack). - Resource attribute
log.type:request_log
Request logs are the only records on this stream. Internal management-plane records (billing transactions, access-log forwards, metrics) never reach the local destination.
- Body: the full request-log record as a plain JSON document (
Data planes released before July 2026 sent the local destination the management-plane transport envelope instead: records with body Request log or ALS access log data export and an opaque gzip-compressed payload attribute marked json+gzip / protobuf+gzip. If the collector shows those, upgrade the data plane; the record content was unreadable by standard OTLP backends.
- The Admin Console's Request logs view shows no new row for this prompt, and the Usage view does not count it (off mode at the management plane). The transaction is still recorded for billing.
If the collector receives nothing, on the collector-routed path:
-
Confirm the emission variables were injected and the collector lane rendered:
kubectl get envoyproxy -n <egress-namespace> -o yaml | grep -A1 OTEL_kubectl get cm otel-gateway-config -n <system-namespace> -o yaml | grep -A5 requestlogIf both are absent, one leg of the gate is off:
global.observability.enabled,signals.requestlog.enabled, or the backend's explicitsignals: [requestlog]subscription (an omittedsignalslist never includesrequestlog). -
Check the
otel-gatewaycollector pod's logs for export errors toward the backend, and see the delivery-semantics section of Custom observability backends for queue-overflow and oversized-record failure modes. -
Confirm the backend is reachable from the data-plane cluster's pod network. If it is outside the cluster (for example, behind a tunnel or external load balancer), test reachability with
curlfrom a debug pod first.
On the legacy direct-attach path:
- Confirm
OTEL_EXPORTER_OTLP_LOGS_ENDPOINTends in/v1/logs. - Confirm
OTEL_LOGS_EXPORTER=otlpis set; without it the local destination is disabled. - Check the egress pod's container logs for
OTEL SDK errorlines; they include the failing URL and error code.
Frequently asked questions
Does switching to off break monthly billing?
No. Transactions are written through a separate code path and arrive regardless of mode, so billing stays accurate. The Usage view and budget spend meters, however, stop counting new requests, because they are computed from request_log records.
Does switching to off lose audit information about who ran which prompt?
The request_log row is the per-request record at the management plane. Under off it is not stored there. If that detail is required, run metadata_only (identifiers, tokens, costs, and timing reach the management plane; bodies and headers do not) or wire an external destination as above.
Does switching to metadata_only retroactively strip bodies from rows already in the database?
No. The setting affects only records produced from the moment it takes effect onward. Existing rows are unchanged.
Can different projects have different modes?
Not in this version. The setting is system-wide for the deployment.
What happens when an unknown mode value is saved via a direct database write?
The data plane treats anything it does not recognize as full, so a misconfigured value can never silently drop data. The Admin Console only ever writes one of the three known values.
Why is the body panel empty in the Admin Console?
Either the request genuinely had no body, the response was empty (for example, an error before a generation finished), or the mode when the request was processed was metadata_only. The Admin Console does not currently distinguish these cases in the UI.
Find one person's requests
On a 0.9.0 or later data plane, a caller admitted with a project OIDC token never appears under its sub in Request Logs, metrics, or traces, so a search for an email address finds nothing. The value to search for is computed from the sub exactly as the token carries it. The address is hashed as written, with no case folding, so [email protected] and [email protected] give different values.
For a sub that is an email address, the value is e_ followed by the first 24 hex characters of its SHA-256:
For any other sub, the value is o_ followed by its unpadded base64url encoding:
printf 'o_%s\n' "$(printf '%s' 'svc-batch-42' | base64 | tr '+/' '-_' | tr -d '=')"
Where it is possible, filtering on the API key or the project is simpler and does not depend on knowing how the identity provider spells the sub. Callers that authenticate with an API key are recorded as before.
Can an application's own x-tars- headers be kept in the request log?
From 0.9.0, on a 0.9.0 data plane, yes. The data plane removes every X-Tars request header from the record, except the names listed in the REQUEST_LOG_EXPORT_HEADERS environment variable of the gateway proxy container: a comma-separated list such as x-tars-team,x-tars-cost-center. The variable goes under egressResources.envoyProxy.provider.kubernetes.envoyDeployment.container.env in the data plane's values:
egressResources:
envoyProxy:
provider:
kubernetes:
envoyDeployment:
container:
env:
REQUEST_LOG_EXPORT_HEADERS: x-tars-team,x-tars-cost-center
The list is read once when the gateway starts, so a change takes effect only after the gateway pods restart. A listed header is kept in the record's request_headers, and reaches every destination that receives that field. Names that the data plane uses itself are refused and logged at warning level: credentials, gateway-internal names, headers removed on arrival, and names such as x-tars-project and x-tars-metadata. A listed name that a gateway auth policy uses as its token header is removed from the record, as is every listed name while the management plane sends the data plane no gateway authentication configuration.
The values are whatever the caller sent. The gateway does not check them against the credential, so they serve for attribution and reporting, never for granting or refusing access.
Does the request log mode change what traces carry?
No. AI tracing spans have their own content settings in the data plane's Helm values, and a trace destination set to metadata only keeps model, token, and attribution attributes while dropping prompt and response content. See Configure data plane request tracing from Helm values.
Where to go next
Custom observability backends
The full field reference for backends.custom[], more signals to subscribe, and the delivery guarantees behind the collector-routed path.
Manage log retention and purge
Govern how long the records each mode produces are kept and how they are removed.
Configure data residency and no-retention
Keep regulated content in-region and unretained at the provider.