> ## Documentation Index
> Fetch the complete documentation index at: https://docs.xpander.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Log destinations

> Copy every request record and the audit log to your OpenTelemetry collector or your own storage bucket.

Request records always stay in your organization's own database. **Log destinations** also sends a copy to your own systems, never with prompt or response text. Open it from the toolbar in [Logs](https://platform.xpander.ai/governance/logs), and choose **Save changes** when done.

![Diagram](https://docs.xpander.ai/images/diagrams/run-llm-gateway-logs-export-a03c2d91.svg)

| Destination | What arrives | Delay |
| - | - | - |
| **OpenTelemetry** | One span per request over OTLP HTTP (JSON), and audit events as log records | About two and a half minutes |
| **Storage bucket** | Parquet files in Amazon S3, Google Cloud Storage or Azure Blob Storage, and audit events in their own folder | About two minutes |

Each destination starts from the moment you save it. Earlier requests are not sent. If it can't be reached, sending pauses and resumes where it stopped, without losing data.

## OpenTelemetry

![Log destinations panel with Add destination and the OpenTelemetry collector endpoint and headers](https://mintcdn.com/xpanderai-099931d1/65C_XNxucwf3Ltb_/images/llm-gateway/14-log-destinations.png?fit=max\&auto=format\&n=65C_XNxucwf3Ltb_\&q=85\&s=8d5f207ecb447df5db6544ef4289448f)

Log destinations: a storage bucket and an OpenTelemetry collector.

**1. Store the collector's headers**

In [Secrets](https://platform.xpander.ai/agents/secrets), store the headers, such as the API key, as JSON (`{"api-key": "..."}`) or comma-separated `key=value` pairs.

**2. Point at the collector**

Under **OpenTelemetry**, turn on **Send traces to a collector**, enter the **Endpoint** (the HTTP port, usually `4318`; `/v1/traces` is added when the URL has no path), pick the secret under **Headers**, and save.

> [!WARNING]
>
> Only OTLP over HTTP is supported. A gRPC endpoint (usually port `4317`) receives nothing. A collector saved for gRPC shows *This collector was saved for gRPC*: point it at the HTTP port and save again.

Each span follows the OpenTelemetry GenAI conventions and carries the provider and models, tokens (input, output, cache, reasoning), cost in USD, timings, the IDs of the person, group, agent, task and conversation, the key name, client, trigger, license model and status. It never carries prompt or response text, an email address or error text. Calls of one agent run or coding session share a trace ID. The endpoint must be reachable from where agents run: public on xpander cloud, reachable from your cluster when self-hosted.

### Coralogix

Create a **Send-Your-Data** API key in Coralogix, then store this secret in xpander. The application and subsystem names are yours to choose:

```json theme={"dark"}
{
  "Authorization": "Bearer <your Send-Your-Data key>",
  "cx-application-name": "xpander-llm-gateway",
  "cx-subsystem-name": "requests"
}
```

Set **Endpoint** to `https://ingress.<your Coralogix domain>/v1/traces`, such as `https://ingress.eu2.coralogix.com/v1/traces`. Spans arrive with the service name `xpander-llm-gateway`, grouped by your application and subsystem names.

### Other collectors

| Backend | Endpoint | Headers secret |
| - | - | - |
| Grafana Cloud | Your stack's OTLP endpoint, such as `https://otlp-gateway-<region>.grafana.net/otlp` | `{"Authorization": "Basic <base64 of instance ID:token>"}` |
| Honeycomb | `https://api.honeycomb.io` (`https://api.eu1.honeycomb.io` in the EU) | `{"x-honeycomb-team": "<API key>"}` |
| Datadog | An OpenTelemetry Collector or Datadog Agent with OTLP HTTP intake on, at `:4318` | What your collector requires, or none |
| Your own OpenTelemetry Collector | Its OTLP HTTP receiver, such as `https://otel.example.com:4318` | What your collector requires, or none |

## Storage bucket

First, store a write credential in [Secrets](https://platform.xpander.ai/agents/secrets):

| Storage | Credential |
| - | - |
| **Amazon S3** | An access key with `s3:PutObject`, as `{"access_key_id": "...", "secret_access_key": "..."}` (plus `"session_token"` for temporary keys) or `ID:SECRET`. For MinIO and other S3-compatible storage, add `"endpoint_url"`. IAM roles are not supported. |
| **Google Cloud** | A service account's whole JSON key file, with `storage.objects.create` |
| **Azure Blob Storage** | The account connection string (with `AccountKey` or `SharedAccessSignature`), or a SAS URL with write access |

Then, under **Destination**, choose **Add destination**, pick the storage, and fill in **Bucket** (**Container** for Azure), an optional **Prefix** such as `gateway/` (no leading slash), the **Region** (S3 only) and the **Credential**. **Send test** writes `<prefix>/_xpander_test.json` and reports **Test row written**, or **The test failed** with the reason: missing bucket, wrong region (it names the right one), access denied, a rejected key, or a blocked storage address. Save. **Turn off** stops the copy.

Files land under `<prefix>/dt=YYYY-MM-DD/hour=HH/` (UTC hour of the call), one row per request. Changing the bucket or prefix restarts the export from that moment. Retention in [Policies](https://platform.xpander.ai/governance/policies) does not delete exported files: set a lifecycle rule on the bucket.

| Columns | What they hold |
| - | - |
| `request_id`, `trace_id`, `created_at`, `stored_at` | The request, its trace, start and record time (UTC) |
| `organization_id`, `user_id`, `group_id`, `principal_kind`, `key_id`, `key_name`, `app_id` | Who called, with which key |
| `agent_id`, `task_id`, `parent_task_id`, `conversation_id`, `trigger_source` | The agent run and what started it |
| `operation`, `internal_ops`, `billed`, `category` | Work xpander did for you, and whether it was billed |
| `client`, `client_version`, `surface` | The coding tool or SDK |
| `license`, `is_byok` | The license model, and whether your key paid |
| `requested_model`, `model`, `served_model`, `provider`, `route`, `route_reason` | Models and routing |
| `status`, `http_status`, `error_code` | The outcome |
| `input_tokens`, `cached_input_tokens`, `cache_write_tokens`, `output_tokens`, `reasoning_tokens` | Tokens |
| `cost_usd`, `input_cost_usd`, `output_cost_usd`, `cache_read_cost_usd`, `cache_write_cost_usd` | Cost in USD and its parts |
| `ttft_ms`, `latency_ms`, `overhead_ms` | Time to first token, total time, gateway time |

Rows never hold prompt or response text, email addresses, IP addresses or error text.

## The audit log

Every destination also receives the organization's [audit log](https://docs.xpander.ai/use/agents/audit.md#the-audit-log): sign-ins, setting changes, credential use, and a data access row for each call that reads or writes your data. That covers skill calls agents make through xpander (API connectors, custom functions, knowledge bases, file storage) and calls people make through the MCP gateway with their own key. A data access row names who acted, on whose behalf, the skill, the action, read or write, the result, the client and the time, never the request or response.

| Destination | Audit events arrive as |
| - | - |
| Storage bucket | Parquet under `<prefix>/audit/dt=YYYY-MM-DD/hour=HH/`, with full details in a `metadata` JSON column |
| OpenTelemetry | Log records at `/v1/logs`, service name `xpander-audit` |

The audit export runs only when the LLM Gateway is on for the organization. Skills served over MCP that an agent's runtime calls directly are in the task's record, but write no data access row, so they don't reach log destinations.

## Self-hosted installs

On Hybrid and Air-Gapped, both destinations are sent from inside your install and only need to be reachable from your cluster. Air-Gapped can use S3-compatible storage in your network through `endpoint_url`.

## Next steps

> **[Read requests in Logs](https://docs.xpander.ai/run/llm-gateway/requests-and-costs.md)**
>
> Usage and every request.

> **[Store the destination's credential](https://platform.xpander.ai/agents/secrets)**
>
> Add it as a secret.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.