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

# Send OpenTelemetry traces and logs to Braintrust

> Send OpenTelemetry traces and logs to Braintrust using an exporter, Collector, or Braintrust span processor

Send traces and logs from your existing [OpenTelemetry](https://opentelemetry.io/docs/) instrumentation to Braintrust. Use an OTLP trace exporter or Braintrust span processor for traces, and an OTLP logs exporter or OpenTelemetry Collector for logs. For combining Braintrust and OpenTelemetry spans in the same trace, see [Link spans](https://docs.braintrust.dev/integrations/sdk-integrations/opentelemetry/link-spans.md).

## Choose an integration path

Choose the setup that matches how your application produces telemetry.

| Your setup | Start here |
| - | - |
| An existing OpenTelemetry trace exporter | [OTLP trace exporter](#otlp-trace-exporter) |
| An OpenTelemetry SDK configured in application code | [Braintrust span processor](#braintrust-span-processor) |
| An OpenTelemetry logs exporter in your application | [OTLP logs exporter](#otlp-logs-exporter) |
| An OpenTelemetry Collector forwarding your logs | [OpenTelemetry Collector](#opentelemetry-collector) |
| Claude API OpenTelemetry export to an OTLP destination | [Anthropic](https://docs.braintrust.dev/integrations/ai-providers/anthropic.md#export-opentelemetry-traces-from-the-claude-api) |
| Mixed Braintrust and OpenTelemetry instrumentation | [Link spans](https://docs.braintrust.dev/integrations/sdk-integrations/opentelemetry/link-spans.md) |

To control how span attributes map to inputs, outputs, and metadata in Braintrust, see [Attributes and events](https://docs.braintrust.dev/integrations/sdk-integrations/opentelemetry/attributes.md).

## Send traces

Traces capture the operations within a request, including their timing and parent-child relationships. Send OpenTelemetry spans to Braintrust using an OTLP trace exporter or a Braintrust span processor in your application.

### OTLP trace exporter

To send traces through an existing OpenTelemetry pipeline, configure its OTLP exporter with your Braintrust endpoint and credentials.

Once you set up an [OTLP exporter](https://opentelemetry.io/docs/languages/js/exporters/) to send traces to Braintrust, Braintrust automatically
converts LLM calls into Braintrust `LLM` spans, which
can be saved as [prompts](https://docs.braintrust.dev/evaluate/prompts.md)
and evaluated in the [playground](https://docs.braintrust.dev/evaluate/playgrounds.md).

For applications that use the [OpenTelemetry SDK](https://opentelemetry.io/docs/languages/) to export traces, set the
following environment variables:

```
OTEL_EXPORTER_OTLP_ENDPOINT=https://api.braintrust.dev/otel
OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer <Your API Key>, x-bt-parent=project_id:<Your Project ID>"
```

> [!NOTE]
>
> The trace endpoint URL is `https://api.braintrust.dev/otel/v1/traces`. If your exporter
> uses signal-specific environment variables, you'll need to set the full path:
> `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://api.braintrust.dev/otel/v1/traces`

> [!NOTE]
>
> If your organization is on the EU data plane, use `https://api-eu.braintrust.dev/otel` instead.
> If you're self-hosting Braintrust, substitute your stack's Universal API URL. For example:
> `OTEL_EXPORTER_OTLP_ENDPOINT=https://dfwhllz61x709.cloudfront.net/otel`
>
> See [Data plane region](https://docs.braintrust.dev/admin/organizations.md#data-plane-region).

The `x-bt-parent` header sets the trace's parent project or experiment. You can use
a prefix like `project_id:`, `project_name:`, or `experiment_id:` here, or pass in
a [span slug](https://docs.braintrust.dev/instrument/advanced-tracing.md#trace-distributed-systems)
(`span.export()`) to nest the trace under a span within the parent object.

> [!NOTE]
>
> To find your project ID, go to your project's configuration page and find the **Copy Project ID** button at the bottom of the page.

**TypeScript**

For JavaScript/TypeScript applications, you can use the `BraintrustExporter` directly:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { BraintrustExporter } from "@braintrust/otel";

const exporter = new BraintrustExporter({
  apiKey: "your-api-key",
  parent: "project_name:your-project",
  filterAISpans: true,
});

const processor = new BatchSpanProcessor(exporter);
```

### Braintrust span processor

Braintrust span processors send trace spans from your application to Braintrust.

**TypeScript**

To send spans from your TypeScript application, configure a Braintrust span processor.

Install the integration and attach it to your OpenTelemetry provider.

**1. Install and configure credentials**

> [!NOTE]
>
> Starting with v1.0, OpenTelemetry functionality has been moved to the separate `@braintrust/otel` [npm package](https://www.npmjs.com/package/@braintrust/otel). This solves ESM build issues in Next.js (edge), Cloudflare Workers, Bun, and TanStack applications, and adds support for both OpenTelemetry v1 and v2. `BraintrustSpanProcessor` works with `@opentelemetry/sdk-trace-base@1.x` and v2 spans.
>
> If you're upgrading from v0.x, see the [upgrade guide](https://docs.braintrust.dev/sdks/typescript/migrations/v0-to-v1.md) for migration instructions.

Install the [Braintrust TypeScript SDK](https://docs.braintrust.dev/sdks/typescript/quickstart.md) with the following OpenTelemetry dependencies:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
# pnpm
pnpm add braintrust @braintrust/otel @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-trace-base
# npm
npm install braintrust @braintrust/otel @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-trace-base
```

**2. Configure the span processor**

For TypeScript and JavaScript applications, use the `BraintrustSpanProcessor` with NodeSDK:

```typescript title="opentelemetry-braintrust.ts" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
import { NodeSDK } from "@opentelemetry/sdk-node";
import { BraintrustSpanProcessor } from "@braintrust/otel";

const sdk = new NodeSDK({
  serviceName: "my-service",
  spanProcessor: new BraintrustSpanProcessor({
    parent: "project_name:your-project-name",
  }),
});

sdk.start();
```

Or configure it manually with a custom tracer provider:

```typescript title="opentelemetry-braintrust.ts" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
import { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
import { trace } from "@opentelemetry/api";
import { BraintrustSpanProcessor } from "@braintrust/otel";

trace.setGlobalTracerProvider(
  new BasicTracerProvider({
    spanProcessors: [
      new BraintrustSpanProcessor({
        parent: "project_name:your-project-name",
      }),
    ],
  }),
);
```

Configure the processor with these arguments:

* `apiKey`: The API key to use for Braintrust. Defaults to the `BRAINTRUST_API_KEY` environment variable.
* `apiUrl`: The URL of the Braintrust API. Defaults to the `BRAINTRUST_API_URL` environment variable or `https://api.braintrust.dev` if not set.
* `parent`: The parent project or experiment to use for Braintrust. Defaults to the `BRAINTRUST_PARENT` environment variable.
* `filterAISpans`: Defaults to `false`. If `true`, only AI-related spans will be sent to Braintrust.
* `customFilter`: A function that gives you fine-grained control over which spans are sent to Braintrust. It takes a span and returns a boolean. If `true`, the span will be sent to Braintrust. If `false`, the span will be dropped. If `null`, don't influence the sampling decision.

In `@braintrust/otel` v1.1.0 and later, with `braintrust` v3.38.0 or later, [span customizers](https://docs.braintrust.dev/sdks/typescript/redact-sensitive-data.md#opentelemetry-spans) also run on spans that `BraintrustSpanProcessor` and `BraintrustExporter` export, so you can redact or modify them before they leave your process.

**Python**

To send spans from your Python application, configure a Braintrust span processor.

Install the integration and attach it to your OpenTelemetry provider.

**1. Install and configure credentials**

Install the Braintrust Python SDK with OpenTelemetry support:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
pip install "braintrust[otel]"
```

Configure these environment variables:

```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
BRAINTRUST_API_KEY=your-api-key
BRAINTRUST_PARENT=project_name:my-otel-project

# BRAINTRUST_API_URL=https://api.braintrust.dev
# - US data plane: Optional (defaults to https://api.braintrust.dev)
# - EU data plane: https://api-eu.braintrust.dev
# - Self-hosted data plane: Your data plane URL
```

**2. Configure the span processor**

For Python applications, use the `BraintrustSpanProcessor` for simplified configuration:

```python title="opentelemetry-braintrust.py" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
import os

from braintrust.otel import BraintrustSpanProcessor
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider

# Configure the global OTel tracer provider
provider = TracerProvider()
trace.set_tracer_provider(provider)

# Send spans to Braintrust.
provider.add_span_processor(BraintrustSpanProcessor())
```

Configure the processor with these arguments:

* `api_key`: The API key to use for Braintrust. Defaults to the `BRAINTRUST_API_KEY` environment variable.
* `api_url`: The URL of the Braintrust API. Defaults to the `BRAINTRUST_API_URL` environment variable or `https://api.braintrust.dev` if not set.
* `parent`: The parent project or experiment to use for Braintrust. Defaults to the `BRAINTRUST_PARENT` environment variable.
* `filter_ai_spans`: Defaults to `False`. If `True`, only AI-related spans will be sent to Braintrust.
* `custom_filter`: A function that gives you fine-grained control over which spans are sent to Braintrust. It takes a span and returns a boolean. If `True`, the span will be sent to Braintrust. If `False`, the span will be dropped. If `None`, don't influence the sampling decision.

In v0.46.0 and later, [span customizers](https://docs.braintrust.dev/sdks/python/redact-sensitive-data.md#opentelemetry-spans) also run on spans that `BraintrustSpanProcessor` exports, so you can redact or modify them before they leave your process.

### Verify trace ingestion

After configuring either trace export method, confirm that spans reach the destination you selected:

1. Run an instrumented operation with a distinctive span name. Let the operation finish and ensure pending spans are exported before the application exits.
2. Open [** Logs**](https://www.braintrust.dev/app/~/logs) in the destination project and find the trace. If you configured an experiment destination, open that experiment instead.
3. Inspect the trace's span names, timing, and parent-child relationships. Check that any inputs, outputs, and metadata you sent appear in the expected fields, using [Attributes and events](https://docs.braintrust.dev/integrations/sdk-integrations/opentelemetry/attributes.md) as a reference.

If the trace is missing, see [Why are my traces not showing up?](#why-are-my-traces-not-showing-up).

## Send logs

Logs capture individual application events, such as a worker starting or a request retrying. Send these records to Braintrust using an OTLP logs exporter or OpenTelemetry Collector. The logs endpoint accepts OTLP over HTTP with `application/json` or `application/x-protobuf` payloads.

| Deployment | Logs endpoint |
| - | - |
| Braintrust-hosted, US | `https://api.braintrust.dev/otel/v1/logs` |
| Braintrust-hosted, EU | `https://api-eu.braintrust.dev/otel/v1/logs` |
| Self-hosted | `/otel/v1/logs` on your data plane's Universal API URL. |

For self-hosted deployments, the endpoint requires data plane v2.12.0 or later. Use data plane v2.14.0 or later for the log-row format and span-event ingestion described below. Data plane v2.14.0 ships with AWS Terraform v6.8.0 and Helm chart 6.18.0. See [Self-hosting releases](https://docs.braintrust.dev/data-plane-changelog.md) for deployment requirements and [Upgrade your data plane](https://docs.braintrust.dev/admin/self-hosting/upgrade/routine.md) if your deployment predates these versions.

To find your organization's API URL, go to ** Settings** > [** Data plane**](https://www.braintrust.dev/app/~/configuration/data-plane).

### OTLP logs exporter

Set `BRAINTRUST_API_KEY` to a Braintrust API key with permission to write to your project, and `BRAINTRUST_PROJECT_ID` to the project ID. For an exporter that supports the standard [OTLP environment variables](https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/), configure the logs signal:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
export OTEL_EXPORTER_OTLP_LOGS_ENDPOINT="https://api.braintrust.dev/otel/v1/logs"
export OTEL_EXPORTER_OTLP_LOGS_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_LOGS_HEADERS="Authorization=Bearer ${BRAINTRUST_API_KEY},x-bt-parent=project_id:${BRAINTRUST_PROJECT_ID}"
```

Replace the endpoint with the URL for your deployment. These variables configure the exporter. You also need to enable log collection in your application's OpenTelemetry SDK.

The signal-specific `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` includes `/v1/logs`. If you use the shared `OTEL_EXPORTER_OTLP_ENDPOINT` instead, set it to the base URL ending in `/otel` and let the exporter append the signal path.

The [C# SDK](https://docs.braintrust.dev/sdks/csharp/api-reference.md) uses `/otel/v1/logs` as its default logs endpoint path, configured by `BRAINTRUST_LOGS_PATH`.

### OpenTelemetry Collector

To receive OTLP logs from an application on the same host and forward them to Braintrust, use this [Collector configuration](https://opentelemetry.io/docs/collector/configuration/). Set `BRAINTRUST_API_KEY` and `BRAINTRUST_PROJECT_ID` in the Collector's environment.

```yaml theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
receivers:
  otlp:
    protocols:
      http:
        endpoint: localhost:4318

processors:
  batch: {}

exporters:
  otlp_http/braintrust:
    endpoint: https://api.braintrust.dev/otel
    headers:
      Authorization: "Bearer ${env:BRAINTRUST_API_KEY}"
      x-bt-parent: "project_id:${env:BRAINTRUST_PROJECT_ID}"

service:
  pipelines:
    logs:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp_http/braintrust]
```

Point your application's HTTP logs exporter at `http://localhost:4318/v1/logs`. The Collector batches the records and sends them to Braintrust. Replace the Braintrust exporter endpoint with your deployment's base URL ending in `/otel`. Collector versions that use the `otlphttp` exporter name require `otlphttp/braintrust` in both the exporter definition and the pipeline.

For stored fields, trace correlation, and severity semantics, see [Log records](https://docs.braintrust.dev/integrations/sdk-integrations/opentelemetry/attributes.md#log-records).

### Verify log ingestion

After configuring either log export method, confirm that records reach your project:

1. Send a log with a distinctive message. Ensure pending records are exported before the application exits.
2. Go to [** Logs**](https://www.braintrust.dev/app/~/logs) in the destination project, choose the  **Logs** row type, and find the message. See [Inspect logs](https://docs.braintrust.dev/instrument/application-logs.md#inspect-logs). To query the row instead, use the [** Query**](https://www.braintrust.dev/app/~/sql) page and check that `output` contains the message and `span_attributes.type` is `log`.
3. Inspect the message's **Output** and **Details**. Check that the log level and any attributes you sent appear in the expected fields, using [Log records](https://docs.braintrust.dev/integrations/sdk-integrations/opentelemetry/attributes.md#log-records) as a reference.
4. To test correlation, send a span and a log with matching trace and span IDs. Verify that their `span_id` and `root_span_id` match while their row `id` values differ.

If log records are missing or rejected, see [Troubleshooting](#troubleshooting).

## Troubleshooting

Check these common causes when traces are missing or log ingestion fails.

> [accordion] Traces are missing
>
> * Braintrust's logs table only shows traces that have a root span (i.e. `span_parents` is empty). If you only send children
>   spans, they will not appear in the logs table. A common reason for this is only sending spans to Braintrust which have a
>   `traceparent` header. To fix this, make sure to send a root span for every trace you want to appear in the UI.
> * Make sure the `OTEL_EXPORTER_OTLP_ENDPOINT` matches your organization's [data plane region](https://docs.braintrust.dev/admin/organizations.md#data-plane-region). Organizations on the EU data plane should use `https://api-eu.braintrust.dev/otel`. Self-hosted deployments should use their custom API URL, for example `https://dfwhllz61x709.cloudfront.net/otel`.
> * You must explicitly set up OpenTelemetry in your application. If you're using Next.js, then follow the [Next.js OpenTelemetry guide](https://nextjs.org/docs/app/guides/open-telemetry).
>   If you are using Node.js without a framework, then follow [this example](https://github.com/vercel/ai/blob/main/examples/ai-core/src/telemetry/stream-text.ts) to set up a basic exporter.

> [accordion] Log records are rejected or ingestion fails
>
> If ingestion fails, check the response:
>
> * A response with `partialSuccess.rejectedLogRecords` greater than zero means some records were rejected. Check their parent routing attributes and the `x-bt-parent` header.
> * A `403` can indicate that the API key lacks write permission or that the destination cannot be resolved. Verify the API key and project ID.
> * A `404` can indicate an incorrect endpoint or a self-hosted version without logs support. Check the signal-specific path and data plane version.

## Resources

* [Link Braintrust and OpenTelemetry spans](https://docs.braintrust.dev/integrations/sdk-integrations/opentelemetry/link-spans.md) for context sharing, ID formats, and propagation across services.

- [Attributes and events](https://docs.braintrust.dev/integrations/sdk-integrations/opentelemetry/attributes.md) for field mappings, log storage, and metadata troubleshooting.

* [Vercel AI SDK OpenTelemetry setup](https://docs.braintrust.dev/integrations/sdk-integrations/vercel.md#opentelemetry-typescript) for Next.js and Node.js examples.


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