> For the complete documentation index, see [llms.txt](https://docs.flowcp.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flowcp.ai/guides/stream-logs.md).

# Stream logs to external sinks

Log streaming forwards your MCP execution logs to an external observability system — **Axiom**, **ClickHouse**, **OpenTelemetry**, **Datadog**, **Grafana Loki**, **Splunk** (via the HTTP Event Collector), or any **generic HTTP** endpoint — so MCP activity sits alongside the rest of your telemetry.

Destinations are configured **per workspace**, not per app: one destination covers every app in the workspace, including apps you connect later. You can still narrow a destination to a single app when you want its logs to land somewhere separate. A workspace can have as many destinations as you like.

Log streaming is available on the **Team**, **Business** and **Enterprise** plans. When your plan unlocks it, a **Log streaming** entry appears under **Monitor** in the sidebar, next to **Logs**. The entry sits in an app's menu for convenience, but the destinations it opens are workspace-level — the same list whichever app is selected.

Because a destination forwards execution logs to an external endpoint with a credential you provide, **only workspace owners can add, edit, or remove destinations**. Members can view the current configuration but not change it.

## Adding a destination

1. Open **Monitor → Log streaming** in the sidebar.
2. Click **Add destination** and pick where the logs should go.
3. Fill in the form:
   * **Name** — a label so you can tell destinations apart (e.g. "Axiom prod").
   * **Destination** — pre-filled from your pick; change it here if you chose the wrong one.
   * **Apps** — **All apps in this workspace** (the default) or a single app.
   * The destination's own fields (see the table below).
   * **API key / token** — the credential for the destination.
   * **Only forward errors** / **Minimum status code** — optionally restrict which calls are forwarded (e.g. errors only, or status ≥ 500).
4. Leave **Enabled** on, then click **Create destination**.

### What each destination asks for

| Destination       | Fields                                  | Credential      | Where to get it                                        |
| ----------------- | --------------------------------------- | --------------- | ------------------------------------------------------ |
| **Axiom**         | Region, Dataset                         | Axiom API token | Axiom → Settings → API tokens (needs ingest rights)    |
| **ClickHouse**    | Endpoint URL, Table, User               | Password        | Your ClickHouse service credentials                    |
| **OpenTelemetry** | Endpoint URL, Auth header name *(opt.)* | Token           | Whatever your collector expects                        |
| **Datadog**       | DataDog site                            | DataDog API key | DataDog → Organization Settings → API Keys             |
| **Grafana Loki**  | Endpoint URL, User id *(opt.)*          | API token       | Grafana Cloud → Access policies, or your Loki tenant   |
| **Splunk (HEC)**  | Endpoint URL                            | HEC token       | Splunk → Settings → Data inputs → HTTP Event Collector |
| **Generic HTTP**  | Endpoint URL, Auth header name *(opt.)* | Bearer token    | Whatever token your collector expects                  |

Notes on the addressing fields:

* **Region / DataDog site** — Axiom and Datadog are addressed by an allowlisted intake host rather than a free-form URL. Pick the one matching your account (it is shown in the service's own URL).
* **Endpoint URL** — always `https://`. Splunk takes the HEC base URL (`/services/collector` is appended), Loki the base URL (`/loki/api/v1/push` is appended), and OpenTelemetry either the collector base URL or the full signal URL (`/v1/logs` is appended when missing).
* **Table** *(ClickHouse)* — an existing table, optionally database-qualified (`logs.flowcp_mcp`). Rows are inserted as `JSONEachRow`, so its columns must match the forwarded fields:

  ```sql
  CREATE TABLE logs.flowcp_mcp (
      id                  String,
      toolName            String,
      statusCode          UInt16,
      latencyMs           UInt32,
      errorCode           Nullable(String),
      mcpClientName       String,
      mcpClientVersion    Nullable(String),
      llmModel            Nullable(String),
      endUserConnectionId Nullable(String),
      createdAt           DateTime64(3)
  ) ENGINE = MergeTree ORDER BY (createdAt, id);
  ```
* **User id** *(Grafana Loki)* — Grafana Cloud authenticates the push endpoint with basic auth, where the user is your numeric stack id. Leave it blank to send the token as `Authorization: Bearer` instead.
* **Auth header name** *(OpenTelemetry / Generic HTTP, optional)* — leave blank to send `Authorization: Bearer <token>`, or set a custom header (e.g. `X-API-Key`) to send the raw token instead.

## What gets sent

Only the privacy-safe fields from the [Execution logs](/reference/execution-logs.md) are forwarded — never tokens, request/response bodies, or authorization headers. Each record includes the tool name, status code, latency, error code, the MCP client name/version, the LLM model (when reported), a hashed end-user key, and the timestamp.

Only **published** servers are streamed — draft and disabled servers do not generate forwarded logs.

## How delivery works

FlowCP polls for new execution logs in the background and delivers them in batches roughly once a minute, so expect up to about a minute of latency. Each destination tracks its own position, so a destination added today only forwards logs created from that point on — it does not backfill history. Failed batches are retried automatically; if delivery keeps failing, the destination shows a **Delivery error** with the latest message.

For your security, the API key/token is encrypted at rest and never shown again once saved. When editing a destination, the secret field shows **Configured** — paste a new value to replace it, or click **Remove** to clear it.

Endpoint URLs are validated to block private and reserved network ranges, so a destination can only ever reach a public host. Intake regions, dataset and table names are checked against a strict allowlist or charset for the same reason.

## Editing and removing

Click **Edit** on a destination to change its settings, its app scope or its filters, or toggle **Enabled** to pause streaming without deleting it. Click the trash icon to remove a destination entirely.
