> ## Documentation Index
> Fetch the complete documentation index at: https://braintrust.dev/docs/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](/docs/integrations/sdk-integrations/opentelemetry/link-spans).

## 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)                   |
| Mixed Braintrust and OpenTelemetry instrumentation  | [Link spans](/docs/integrations/sdk-integrations/opentelemetry/link-spans) |

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

<span id="otlp-configuration" />

## 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
convert LLM calls into Braintrust `LLM` spans, which
can be saved as [prompts](/docs/deploy/prompts)
and evaluated in the [playground](/docs/evaluate/playgrounds).

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>

<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](/docs/admin/organizations#data-plane-region).
</Note>

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](/docs/instrument/advanced-tracing#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.
</Note>

<View title="TypeScript" icon="/images/sdk-icons/typescript.svg">
  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);
  ```
</View>

### Braintrust span processor

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

<View title="TypeScript" icon="/images/sdk-icons/typescript.svg">
  To send spans from your TypeScript application, configure a Braintrust span processor.

  <span id="typescript-sdk-configuration" />

  Install the integration and attach it to your OpenTelemetry provider.

  <Steps>
    <Step title="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](/docs/sdks/typescript/migrations/v0-to-v1) for migration instructions.
      </Note>

      Install the [Braintrust TypeScript SDK](/docs/sdks/typescript/quickstart) with the following OpenTelemetry dependencies:

      <CodeGroup>
        ```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
        ```
      </CodeGroup>
    </Step>

    <Step title="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",
            }),
          ],
        }),
      );
      ```
    </Step>
  </Steps>

  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.
</View>

<View title="Python" icon="/images/sdk-icons/python.svg">
  To send spans from your Python application, configure a Braintrust span processor.

  <span id="python-sdk-configuration" />

  Install the integration and attach it to your OpenTelemetry provider.

  <Steps>
    <Step title="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
      ```
    </Step>

    <Step title="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())
      ```
    </Step>
  </Steps>

  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.
</View>

### 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 [**<Icon icon="activity" /> 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](/docs/integrations/sdk-integrations/opentelemetry/attributes) 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](/docs/data-plane-changelog) for deployment requirements and [Upgrade your data plane](/docs/admin/self-hosting/upgrade/routine) if your deployment predates these versions.

To find your organization's API URL, go to **<Icon icon="settings-2" /> Settings** > [**<Icon icon="lock" /> Data plane**](https://www.braintrust.dev/app/~/configuration/org/api-url).

### 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](/docs/sdks/csharp/api-reference) 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](/docs/integrations/sdk-integrations/opentelemetry/attributes#log-records).

### Verify log ingestion

Send a log with a distinctive message and inspect its stored row in your project using the [SQL sandbox](/docs/reference/sql). Check that `output` contains the message and `span_attributes.type` is `log`. To test correlation, send a span and a log with matching trace and span IDs, then verify that their `span_id` and `root_span_id` match while their row `id` values differ.

## Troubleshooting

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

<span id="why-are-my-traces-not-showing-up" />

<AccordionGroup>
  <Accordion title="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](/docs/admin/organizations#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>

  <Accordion title="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.
  </Accordion>
</AccordionGroup>

## Resources

<span id="otel-compatibility" />

<span id="id-and-export-format" />

<span id="compatibility-mode" />

<span id="distributed-tracing" />

<span id="create-opentelemetry-spans-as-children-of-braintrust-spans" />

<span id="create-braintrust-spans-as-children-of-opentelemetry-spans" />

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

<span id="manual-tracing" />

<span id="genai-attributes" />

<span id="braintrust-attributes" />

<span id="genai-events" />

<span id="why-are-some-attributes-missing-from-metadata" />

* [Attributes and events](/docs/integrations/sdk-integrations/opentelemetry/attributes) for field mappings, log storage, and metadata troubleshooting.

<span id="vercel-ai-sdk" />

<span id="nextjs" />

<span id="nodejs" />

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