---
title: OpenTelemetry API Support
description: >-
  Use the OpenTelemetry API with Datadog SDKs to send traces, metrics, and logs
  to Datadog while maintaining vendor-neutral instrumentation.
breadcrumbs: >-
  Docs > OpenTelemetry in Datadog > Instrument Your Applications > OpenTelemetry
  API Support > OpenTelemetry API Support
---

# OpenTelemetry API Support

{% section displayed-if="Language is Java" %}
This section only applies to users who meet the following criteria: Language is Java

{% section displayed-if="Signal is Logs" %}
This section only applies to users who meet the following criteria: Signal is Logs

{% alert level="danger" %}
OpenTelemetry API support for logs is not available for this language. Use [Datadog Log Collection](https://docs.datadoghq.com/logs/log_collection.md) instead.
{% /alert %}
{% /section %}
{% /section %}

{% section displayed-if="Language is PHP" %}
This section only applies to users who meet the following criteria: Language is PHP

{% section displayed-if="Signal is Logs" %}
This section only applies to users who meet the following criteria: Signal is Logs

{% alert level="danger" %}
OpenTelemetry API support for logs is not available for PHP. Use [Datadog Log Collection](https://docs.datadoghq.com/logs/log_collection.md) instead.
{% /alert %}
{% /section %}
{% /section %}

{% section displayed-if="Language is Ruby" %}
This section only applies to users who meet the following criteria: Language is Ruby

{% section displayed-if="Signal is Logs" %}
This section only applies to users who meet the following criteria: Signal is Logs

{% alert level="danger" %}
OpenTelemetry API support for logs is not available for Ruby. Use [Datadog Log Collection](https://docs.datadoghq.com/logs/log_collection.md) instead.
{% /alert %}
{% /section %}
{% /section %}

{% section displayed-if="Signal is Traces" %}
This section only applies to users who meet the following criteria: Signal is Traces

## Overview{% #overview %}

Use OpenTelemetry tracing APIs with Datadog SDKs to create custom spans, add tags, record events, and more.

{% section displayed-if="Language is Java" %}
This section only applies to users who meet the following criteria: Language is Java

## Setup{% #setup-otel-java %}

{% alert level="info" %}
OpenTelemetry is supported in Java after version 1.24.0.
{% /alert %}

To configure OpenTelemetry to use the Datadog trace provider:

1. If you have not yet read the instructions for auto-instrumentation and setup, start with the [Java Setup Instructions](https://docs.datadoghq.com/tracing/setup/java.md).

1. Make sure you only depend on the OpenTelemetry API (and not the OpenTelemetry SDK).

1. Set the `dd.trace.otel.enabled` system property or the `DD_TRACE_OTEL_ENABLED` environment variable to `true`.

## Adding span tags{% #adding-span-tags-otel-java %}

### Add custom span tags{% #add-custom-span-tags-otel-java %}

Add custom tags to your spans corresponding to any dynamic value within your application code such as `customer.id`.

```
import io.opentelemetry.api.trace.Span;

public void doSomething() {
  Span span = Span.current();
  span.setAttribute("user-name", "Some User");
}
```

### Adding tags globally to all spans{% #adding-tags-globally-otel-java %}

The `dd.tags` property allows you to set tags across all generated spans for an application. This is useful for grouping stats for your applications, data centers, or any other tags you would like to see in Datadog.

```
java -javaagent:<DD-JAVA-AGENT-PATH>.jar \
    -Ddd.tags=datacenter:njc,<TAG_KEY>:<TAG_VALUE> \
    -jar <YOUR_APPLICATION_PATH>.jar
```

### Setting errors on span{% #setting-errors-on-span-otel-java %}

To set an error on a span, use the `setStatus` method:

```
import static io.opentelemetry.api.trace.StatusCode.ERROR;
import io.opentelemetry.api.trace.Span;

public void doSomething() {
  Span span = Span.current();
  span.setStatus(ERROR, "Some error details...");
}
```

### Setting tags and errors on a root span from a child span{% #setting-tags-errors-root-span-otel-java %}

When you want to set tags or errors on the root span from within a child span, you can use the OpenTelemetry Context API:

```
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.context.Scope;

public class Example {

  private final static ContextKey<Span> CONTEXT_KEY =
    ContextKey.named("opentelemetry-traces-local-root-span");

  public void begin() {
    Tracer tracer = GlobalOpenTelemetry.getTracer("my-scope", "0.1.0");
    Span parentSpan = tracer.spanBuilder("begin").startSpan();
    try (Scope scope = parentSpan.makeCurrent()) {
      createChildSpan();
    } finally {
      parentSpan.end();
    }
  }

  private void createChildSpan() {
    Tracer tracer = GlobalOpenTelemetry.getTracer("my-scope", "0.1.0");
    Span childSpan = tracer.spanBuilder("child-span").startSpan();
    try {
      Span rootSpan = Context.current().get(CONTEXT_KEY);
        if (null != rootSpan) {
          rootSpan.setAttribute("my-attribute", "my-attribute-value");
          rootSpan.setStatus(StatusCode.ERROR, "Some error details...");
        }
    } finally {
      childSpan.end();
    }
  }

}
```

## Adding spans{% #adding-spans-otel-java %}

If you aren't using a [supported framework instrumentation](https://docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/dd_libraries/java.md?tab=wget#compatibility), or you would like additional depth in your application's [traces](https://docs.datadoghq.com/tracing/glossary.md#trace), you may want to add custom instrumentation to your code for complete flame graphs or to measure execution times for pieces of code.

If modifying application code is not possible, use the environment variable `dd.trace.methods` to detail these methods.

If you have existing `@Trace` or similar annotations, or prefer to use annotations to complete any incomplete traces within Datadog, use Trace Annotations.

### Trace annotations{% #trace-annotations-otel-java %}

Add `@WithSpan` to methods to have them be traced when running OpenTelemetry and the `dd-java-agent.jar`. If the Agent is not attached, this annotation has no effect on your application.

OpenTelemetry's `@WithSpan` annotation is provided by the `opentelemetry-instrumentation-annotations` dependency.

```
import io.opentelemetry.instrumentation.annotations.WithSpan;

public class SessionManager {

  @WithSpan
  public static void saveSession() {
    // your method implementation here
  }
}
```

### Manually creating a new span{% #manually-creating-a-new-span-otel-java %}

To manually create new spans within the current trace context:

```
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;

public class Example {

  public void doSomething() {
    Tracer tracer = GlobalOpenTelemetry.getTracer("my-scope", "0.1.0");
    Span span = tracer.spanBuilder("my-resource").startSpan();
    try (Scope scope = span.makeCurrent()) {
      // do some work
    } catch (Throwable t) {
      span.recordException(t);
      throw t;
    } finally {
      span.end();
    }
  }
}
```

## Adding span events{% #adding-span-events-otel-java %}

{% alert level="info" %}
Adding span events requires SDK version 1.40.0 or higher.
{% /alert %}

You can add span events using the `addEvent` API. This method requires a `name` parameter and optionally accepts `attributes` and `timestamp` parameters. The method creates a new span event with the specified properties and associates it with the corresponding span.

- **Name** [*required*]: A string representing the event's name.
- **Attributes** [*optional*]: Key-value pairs where the key is a non-empty string and the value is a primitive type or a homogeneous array of primitive values.
- **Timestamp** [*optional*]: A UNIX timestamp representing the event's occurrence time. Expects an `Instant` object.

```
Attributes eventAttributes = Attributes.builder()
    .put(AttributeKey.longKey("int_val"), 1L)
    .put(AttributeKey.stringKey("string_val"), "two")
    .put(AttributeKey.longArrayKey("int_array"), Arrays.asList(3L, 4L))
    .put(AttributeKey.stringArrayKey("string_array"), Arrays.asList("5", "6"))
    .put(AttributeKey.booleanArrayKey("bool_array"), Arrays.asList(true, false))
    .build();

span.addEvent("Event With No Attributes");
span.addEvent("Event With Some Attributes", eventAttributes);
```

Read the [OpenTelemetry specification for adding events](https://opentelemetry.io/docs/specs/otel/trace/api/#add-events) for more information.

### Recording exceptions{% #recording-exceptions-otel-java %}

To record exceptions, use the `recordException` API:

```
span.recordException(new Exception("Error Message"));
span.recordException(new Exception("Error Message"),
    Attributes.builder().put(AttributeKey.stringKey("status"), "failed").build());
```

Read the [OpenTelemetry specification for recording exceptions](https://opentelemetry.io/docs/specs/otel/trace/api/#record-exception) for more information.

## Trace client and Agent configuration{% #trace-client-agent-config-otel-java %}

Both the tracing client and Datadog Agent offer additional configuration options for context propagation. You can also exclude specific resources from sending traces to Datadog if you don't want those traces to be included in calculated metrics, such as traces related to health checks.

### Propagating context with headers extraction and injection{% #propagating-context-otel-java %}

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read [Trace Context Propagation](https://docs.datadoghq.com/tracing/trace_collection/trace_context_propagation.md) for information.

### Resource filtering{% #resource-filtering-otel-java %}

Traces can be excluded based on their resource name, to remove synthetic traffic such as health checks from reporting traces to Datadog. This and other security and fine-tuning configurations can be found on the [Security](https://docs.datadoghq.com/tracing/security.md) page or in [Ignoring Unwanted Resources](https://docs.datadoghq.com/tracing/guide/ignoring_apm_resources.md).
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

## Setup{% #setup-otel-python %}

To configure OpenTelemetry to use the Datadog trace provider:

1. If you have not yet read the instructions for auto-instrumentation and setup, start with the [Python Setup Instructions](https://docs.datadoghq.com/tracing/setup/python.md).

1. Set `DD_TRACE_OTEL_ENABLED` environment variable to `true`.

### Creating custom spans{% #creating-custom-spans-otel-python %}

To create custom spans within an existing trace context:

```
from opentelemetry import trace

tracer = trace.get_tracer(__name__)

def do_work():
    with tracer.start_as_current_span("operation_name") as span:
        # Perform the work that you want to track with the span
        print("Doing work...")
        # When the 'with' block ends, the span is automatically closed
```

## Accessing active spans{% #accessing-active-spans-otel-python %}

To access the currently active span, use the `get_current_span()` function:

```
from opentelemetry import trace

current_span = trace.get_current_span()
# enrich 'current_span' with information
```

## Adding span tags{% #adding-span-tags-otel-python %}

Add attributes to a span to provide additional context or metadata:

```
from opentelemetry import trace

current_span = trace.get_current_span()

current_span.set_attribute("attribute_key1", 1)
```

## Adding span events{% #adding-span-events-otel-python %}

{% alert level="info" %}
Adding span events requires SDK version 2.9.0 or higher.
{% /alert %}

You can add span events using the `add_event` API. This method requires a `name` parameter and optionally accepts `attributes` and `timestamp` parameters.

```
span.add_event("Event With No Attributes")
span.add_event("Event With Some Attributes", {"int_val": 1, "string_val": "two", "int_array": [3, 4], "string_array": ["5", "6"], "bool_array": [True, False]})
```

Read the [OpenTelemetry specification for adding events](https://opentelemetry.io/docs/specs/otel/trace/api/#add-events) for more information.

### Recording exceptions{% #recording-exceptions-otel-python %}

To record exceptions, use the `record_exception` API:

```
span.record_exception(Exception("Error Message"))
span.record_exception(Exception("Error Message"), {"status": "failed"})
```

Read the [OpenTelemetry specification for recording exceptions](https://opentelemetry.io/docs/specs/otel/trace/api/#record-exception) for more information.
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

## Setup{% #setup-otel-nodejs %}

To instrument your application, initialize the Datadog tracer (`dd-trace`) and explicitly register its `TracerProvider` with the OpenTelemetry API. This ensures all OpenTelemetry calls are routed through Datadog.

1. **Add the dependencies**:

   ```
   npm install dd-trace @opentelemetry/api
   ```

1. **Initialize and register the tracer** in your application's entry file (for example, `index.js`), before any other imports:

### Complete example{% #complete-example-otel-nodejs %}

```
// 1. Import the dd-trace library (do not initialize it yet)
const ddtrace = require('dd-trace');

// 2. Initialize the Datadog tracer. This must be the first operation.
const tracer = ddtrace.init({
  // service: 'my-nodejs-app'
  // ... other Datadog configurations
});

// 3. Create and register Datadog's TracerProvider.
const provider = new tracer.TracerProvider();
provider.register(); // This wires the @opentelemetry/api to Datadog

// 4. Import and use the OpenTelemetry API
const otel = require('@opentelemetry/api');
const otelTracer = otel.trace.getTracer(
  'my-custom-instrumentation' // A name for your specific instrumentation
);

// You can now use 'otelTracer' to create spans throughout your application.
```

Datadog combines these OpenTelemetry spans with other Datadog APM spans into a single trace of your application. It also supports [integration instrumentation](https://docs.datadoghq.com/tracing/trace_collection/dd_libraries/nodejs.md#integration-instrumentation) and [OpenTelemetry automatic instrumentation](https://opentelemetry.io/docs/instrumentation/js/automatic/).

## Adding span tags{% #adding-span-tags-otel-nodejs %}

Add custom attributes to your spans to provide additional context:

```
function processData(i, param1, param2) {
  return otelTracer.startActiveSpan(`processData:${i}`, (span) => {
    const result = someOperation(param1, param2);

    // Add an attribute to the span
    span.setAttribute('app.processedData', result.toString());
    span.end();
    return result;
    });
}
```

## Creating spans{% #creating-spans-otel-nodejs %}

To create a new span and properly close it, use the `startActiveSpan` method:

```
function performTask(iterations, param1, param2) {
  // Create a span. A span must be closed.
  return otelTracer.startActiveSpan('performTask', (span) => {
    const results = [];
    for (let i = 0; i < iterations; i++) {
      results.push(processData(i, param1, param2));
    }
    // Be sure to end the span!
    span.end();
    return results;
  });
}
```

## Adding span events{% #adding-span-events-otel-nodejs %}

{% alert level="info" %}
Adding span events requires SDK version 5.17.0/4.41.0 or higher.
{% /alert %}

You can add span events using the `addEvent` API:

```
span.addEvent('Event With No Attributes')
span.addEvent('Event With Some Attributes', {"int_val": 1, "string_val": "two", "int_array": [3, 4], "string_array": ["5", "6"], "bool_array": [true, false]})
```

Read the [OpenTelemetry specification for adding events](https://opentelemetry.io/docs/specs/otel/trace/api/#add-events) for more information.

### Recording exceptions{% #recording-exceptions-otel-nodejs %}

To record exceptions, use the `recordException` API:

```
span.recordException(new TestError())
```

Read the [OpenTelemetry specification for recording exceptions](https://opentelemetry.io/docs/specs/otel/trace/api/#record-exception) for more information.

## Filtering requests{% #filtering-requests-otel-nodejs %}

In some cases, you may want to exclude certain requests from being instrumented, such as health checks or synthetic traffic. You can use the `blocklist` or `allowlist` option on the `http` plugin to ignore these requests.

```
// at the top of the entry point right after tracer.init()
tracer.use('http', {
  blocklist: ['/health', '/ping']
})
```

You can also split the configuration between client and server if needed:

```
tracer.use('http', {
  server: {
    blocklist: ['/ping']
  }
})
```

Additionally, you can exclude traces based on their resource name to prevent the Agent from sending them to Datadog. For more information on security and fine-tuning Agent configurations, read the [Security](https://docs.datadoghq.com/tracing/security.md) or [Ignoring Unwanted Resources](https://docs.datadoghq.com/tracing/guide/ignoring_apm_resources.md) documentation.
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

## Imports{% #imports-otel-go %}

Import the following packages to setup the Datadog trace provider:

```
import (
	"context"
	"log"
	"os"

   "github.com/DataDog/dd-trace-go/v2/ddtrace/ext"
   "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry"
   "github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
)
```

## Setup{% #setup-otel-go %}

To configure OpenTelemetry to use the Datadog trace provider:

1. Add your desired manual OpenTelemetry instrumentation to your Go code following the [OpenTelemetry Go Manual Instrumentation documentation](https://opentelemetry.io/docs/instrumentation/go/manual/). **Important!** Where those instructions indicate that your code should call the OpenTelemetry SDK, call the Datadog tracing library instead.

1. Install the OpenTelemetry package:

   ```
   go get go.opentelemetry.io/otel
   ```

1. Install the Datadog OpenTelemetry wrapper package:

   ```
   go get github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry
   ```

1. Import packages:

   ```
   import (
      "go.opentelemetry.io/otel"
      ddotel "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry"
   )
   ```

1. Create a TracerProvider and defer the Shutdown method:

   ```
   provider := ddotel.NewTracerProvider()
   defer provider.Shutdown()
   ```

1. Set the global TracerProvider:

   ```
   otel.SetTracerProvider(provider)
   ```

1. Run your application.

## Adding span tags{% #adding-span-tags-otel-go %}

Add custom tags to your spans to attach additional metadata and context:

```
// Start a span.
ctx, span := t.Start(ctx, "read.file")
// Set an attribute, or a tag in Datadog terminology, on a span.
span.SetAttributes(attribute.String(ext.ResourceName, "test.json"))
```

### Adding tags globally to all spans{% #adding-tags-globally-otel-go %}

Add tags to all spans by configuring the tracer with the `WithGlobalTag` option:

```
provider := ddotel.NewTracerProvider(
	ddtracer.WithGlobalTag("datacenter", "us-1"),
	ddtracer.WithGlobalTag("env", "dev"),
)
defer provider.Shutdown()

otel.SetTracerProvider(provider)
t := otel.Tracer("")
```

### Setting errors on a span{% #setting-errors-on-a-span-otel-go %}

To set an error on a span:

```
// Start a span.
ctx, span := t.Start(context.Background(), "spanName")

// Set an error on a span with 'span.SetAttributes'.
span.SetAttributes(attribute.String(ext.ErrorMsg, "errorMsg"))

// Alternatively, set an error via end span options.
EndOptions(span, tracer.WithError(errors.New("myErr")))
span.End()
```

## Adding spans{% #adding-spans-otel-go %}

Unlike other Datadog tracing libraries, when tracing Go applications, Datadog recommends that you explicitly manage and pass the Go context of your spans.

```
ctx, span := t.Start(
	ddotel.ContextWithStartOptions(context.Background(), ddtracer.Measured()), "span_name")

span.End()
```

## Adding span events{% #adding-span-events-otel-go %}

{% alert level="info" %}
Adding span events requires SDK version 1.67.0 or higher.
{% /alert %}

You can add span events using the `AddEvent` API:

```
ctx, span := tracer.StartSpan(context.Background(), "span_name")
span.AddEvent("Event With No Attributes")
span.AddEvent("Event With Some Attributes", oteltrace.WithAttributes(attribute.Int("int_val", 1), attribute.String("string_val", "two")))
span.Finish()
```

Read the [OpenTelemetry specification for adding events](https://opentelemetry.io/docs/specs/otel/trace/api/#add-events) for more information.

## Trace client and Agent configuration{% #trace-client-agent-config-otel-go %}

### Propagating context with headers extraction and injection{% #propagating-context-otel-go %}

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read [Trace Context Propagation](https://docs.datadoghq.com/tracing/trace_collection/trace_context_propagation.md) for information.

### Resource filtering{% #resource-filtering-otel-go %}

Traces can be excluded based on their resource name, to remove synthetic traffic such as health checks from reporting traces to Datadog. This and other security and fine-tuning configurations can be found on the [Security](https://docs.datadoghq.com/tracing/security.md) page.
{% /section %}

{% section displayed-if="Language is Ruby" %}
This section only applies to users who meet the following criteria: Language is Ruby

## Requirements and limitations{% #requirements-and-limitations-otel-ruby %}

- Datadog Ruby tracing library `dd-trace-rb` version 1.9.0 or greater.
- Gem version support 1.1.0 or greater.

The following OpenTelemetry features implemented in the Datadog library as noted:

| Feature                           | Support notes                                                                                           |
| --------------------------------- | ------------------------------------------------------------------------------------------------------- |
| OpenTelemetry Context propagation | Datadog and W3C Trace Context header formats are enabled by default.                                    |
| Span processors                   | Unsupported                                                                                             |
| Span Exporters                    | Unsupported                                                                                             |
| `OpenTelemetry.logger`            | `OpenTelemetry.logger` is set to the same object as `Datadog.logger`. Configure through custom logging. |
| Trace/span ID generators          | ID generation is performed by the tracing library, with support for 128-bit trace IDs.                  |

## Configuring OpenTelemetry to use the Datadog tracing library{% #configuring-otel-ruby %}

1. Add your desired manual OpenTelemetry instrumentation to your Ruby code following the [OpenTelemetry Ruby Manual Instrumentation documentation](https://opentelemetry.io/docs/instrumentation/ruby/manual/). **Important!** Where those instructions indicate that your code should call the OpenTelemetry SDK, call the Datadog tracing library instead.

1. Add the `datadog` gem to your Gemfile:

   ```
   source 'https://rubygems.org'
   gem 'datadog' # For dd-trace-rb v1.x, use the `ddtrace` gem.
   ```

1. Install the gem by running `bundle install`.

1. Add the following lines to your OpenTelemetry configuration file:

   ```
   require 'opentelemetry/sdk'
   require 'datadog/opentelemetry'
   ```

1. Add a configuration block to your application:

   ```
   Datadog.configure do |c|
     ...
   end
   ```

Datadog combines these OpenTelemetry spans with other Datadog APM spans into a single trace of your application. It supports [integration instrumentation](https://docs.datadoghq.com/tracing/trace_collection/dd_libraries/ruby.md#integration-instrumentation) and [OpenTelemetry Automatic instrumentation](https://opentelemetry.io/docs/languages/ruby/libraries/) also.

## Adding span events{% #adding-span-events-otel-ruby %}

{% alert level="info" %}
Adding span events requires SDK version 2.3.0 or higher.
{% /alert %}

You can add span events using the `add_event` API:

```
span.add_event('Event With No Attributes')
span.add_event(
  'Event With All Attributes',
  attributes: { 'int_val' => 1, 'string_val' => 'two', 'int_array' => [3, 4], 'string_array' => ['5', '6'], 'bool_array' => [false, true]}
)
```

Read the [OpenTelemetry specification for adding events](https://opentelemetry.io/docs/specs/otel/trace/api/#add-events) for more information.

### Recording exceptions{% #recording-exceptions-otel-ruby %}

To record exceptions, use the `record_exception` API:

```
span.record_exception(
  StandardError.new('Error Message')
)
span.record_exception(
  StandardError.new('Error Message'),
  attributes: { 'status' => 'failed' }
)
```

Read the [OpenTelemetry specification for recording exceptions](https://opentelemetry.io/docs/specs/otel/trace/api/#record-exception) for more information.
{% /section %}

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

## Setup{% #setup-otel-dotnet %}

To configure OpenTelemetry to use the Datadog trace provider:

1. Add your desired manual OpenTelemetry instrumentation to your .NET code following the [OpenTelemetry .NET Manual Instrumentation documentation](https://opentelemetry.io/docs/instrumentation/net/manual/). **Note**: Where those instructions indicate that your code should call the OpenTelemetry SDK, call the Datadog tracing library instead.

1. Install the Datadog .NET tracing library and enable the tracer for your [.NET Framework service](https://docs.datadoghq.com/tracing/trace_collection/dd_libraries/dotnet-framework.md#installation-and-getting-started) or your [.NET Core (and .NET 5+) service](https://docs.datadoghq.com/tracing/trace_collection/dd_libraries/dotnet-core.md#installation-and-getting-started). You can optionally do this with [Single Step APM Instrumentation](https://docs.datadoghq.com/tracing/trace_collection/single-step-apm.md).

1. Set `DD_TRACE_OTEL_ENABLED` environment variable to `true`.

1. Run your application.

Datadog combines these OpenTelemetry spans with other Datadog APM spans into a single trace of your application. It also supports [OpenTelemetry instrumentation libraries](https://opentelemetry.io/docs/instrumentation/net/libraries/).

## Creating custom spans{% #creating-custom-spans-otel-dotnet %}

To manually create spans that start a new, independent trace:

```
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

// Start a new span
using (Activity? activity = Telemetry.ActivitySource.StartActivity("<RESOURCE NAME>"))
{
  activity?.SetTag("operation.name", "custom-operation");
  // Do something
}
```

## Creating spans{% #creating-spans-otel-dotnet %}

To create custom spans within an existing trace context:

```
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

using (Activity? parentScope = Telemetry.ActivitySource.StartActivity("<RESOURCE NAME>"))
{
   parentScope?.SetTag("operation.name", "manual.sortorders");
   using (Activity? childScope = Telemetry.ActivitySource.StartActivity("<RESOURCE NAME>"))
   {
       childScope?.SetTag("operation.name", "manual.sortorders.child");
       SortOrders();
   }
}
```

## Adding span tags{% #adding-span-tags-otel-dotnet %}

Add custom tags to your spans to provide additional context:

```
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

public class ShoppingCartController : Controller
{
    [HttpGet]
    public IActionResult Index(int customerId)
    {
      Activity? activity = Telemetry.ActivitySource.StartActivity("<RESOURCE NAME>")

      // Add a tag to the span for use in the Datadog web UI
      activity?.SetTag("customer.id", customerId.ToString());

      var cart = _shoppingCartRepository.Get(customerId);
      return View(cart);
    }
}
```

## Setting errors on spans{% #setting-errors-on-spans-otel-dotnet %}

Set error information on a span when an error occurs during its execution:

```
try
{
    // do work that can throw an exception
}
catch(Exception e)
{
    activity?.SetTag("error", 1);
    activity?.SetTag("error.message", exception.Message);
    activity?.SetTag("error.stack", exception.ToString());
    activity?.SetTag("error.type", exception.GetType().ToString());
}
```

## Adding span events{% #adding-span-events-otel-dotnet %}

{% alert level="info" %}
Adding span events requires SDK version 2.53.0 or higher.
{% /alert %}

You can add span events using the `AddEvent` API:

```
var eventTags = new ActivityTagsCollection
{
    { "int_val", 1 },
    { "string_val", "two" },
    { "int_array", new int[] { 3, 4 } },
    { "string_array", new string[] { "5", "6" } },
    { "bool_array", new bool[] { true, false } }
};

activity.AddEvent(new ActivityEvent("Event With No Attributes"));
activity.AddEvent(new ActivityEvent("Event With Some Attributes", DateTimeOffset.Now, eventTags));
```

Read the [OpenTelemetry specification for adding events](https://opentelemetry.io/docs/specs/otel/trace/api/#add-events) for more information.

## Propagating context with headers extraction and injection{% #propagating-context-otel-dotnet %}

You can configure the propagation of context for distributed traces by injecting and extracting headers. Read [Trace Context Propagation](https://docs.datadoghq.com/tracing/trace_collection/trace_context_propagation.md) for information.
{% /section %}

{% section displayed-if="Language is PHP" %}
This section only applies to users who meet the following criteria: Language is PHP

## Setup{% #setup-otel-php %}

To configure OpenTelemetry to use the Datadog trace provider:

1. Install [OpenTelemetry API packages](https://opentelemetry.io/docs/languages/php/instrumentation/#instrumentation-setup):

   ```
   composer require open-telemetry/sdk
   ```

1. Add your desired manual OpenTelemetry instrumentation to your PHP code following the [OpenTelemetry PHP Manual Instrumentation documentation](https://opentelemetry.io/docs/instrumentation/php/manual/).

1. Install the [Datadog PHP tracing library](https://docs.datadoghq.com/tracing/trace_collection/dd_libraries/php.md#getting-started).

1. Set `DD_TRACE_OTEL_ENABLED` to `true`.

Datadog combines these OpenTelemetry spans with other Datadog APM spans into a single trace of your application.

## Adding span tags{% #adding-span-tags-otel-php %}

You can add attributes at the exact moment as you are starting the span:

```
$span = $tracer->spanBuilder('mySpan')
    ->setAttribute('key', 'value')
    ->startSpan();
```

Or while the span is active:

```
$activeSpan = OpenTelemetry\API\Trace\Span::getCurrent();
$activeSpan->setAttribute('key', 'value');
```

## Setting errors on a span{% #setting-errors-on-a-span-otel-php %}

Exception information is captured and attached to a span if one is active when the exception is raised:

```
// Create a span
$span = $tracer->spanBuilder('mySpan')->startSpan();

throw new \Exception('Oops!');

// 'mySpan' will be flagged as erroneous and have
// the stack trace and exception message attached as tags
```

Flagging a trace as erroneous can also be done manually:

```
use OpenTelemetry\API\Trace\Span;
use OpenTelemetry\Context\Context;

try {
    throw new \Exception('Oops!');
} catch (\Exception $e) {
    $rootSpan = Span::fromContext(Context::getRoot());
    $rootSpan->recordException($e);
}
```

## Adding spans{% #adding-spans-otel-php %}

To add a span:

```
// Get a tracer or use an existing one
$tracerProvider = \OpenTelemetry\API\Globals::tracerProvider();
$tracer = $tracerProvider->getTracer('datadog')

// Create a span
$span = $tracer->spanBuilder('mySpan')->startSpan();

// ... do stuff

// Close the span
$span->end();
```

## Adding span events{% #adding-span-events-otel-php %}

{% alert level="info" %}
Adding span events requires SDK version 1.3.0 or higher.
{% /alert %}

You can add span events using the `addEvent` API:

```
$span->addEvent("Event With No Attributes");
$span->addEvent(
    "Event With Some Attributes",
    [
        'int_val' => 1,
        'string_val' => "two",
        'int_array' => [3, 4],
        'string_array' => ["5", "6"],
        'bool_array' => [true, false]
    ]
);
```

Read the [OpenTelemetry specification for adding events](https://opentelemetry.io/docs/specs/otel/trace/api/#add-events) for more information.

### Recording exceptions{% #recording-exceptions-otel-php %}

To record exceptions, use the `recordException` API:

```
$span->recordException(new \Exception("Error Message"));
$span->recordException(new \Exception("Error Message"), [ "status" => "failed" ]);
```

Read the [OpenTelemetry specification for recording exceptions](https://opentelemetry.io/docs/specs/otel/trace/api/#record-exception) for more information.

## Accessing active spans{% #accessing-active-spans-otel-php %}

To access the currently active span:

```
$span = OpenTelemetry\API\Trace\Span::getCurrent();
```
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

{% alert level="info" %}
The Datadog Rust SDK is in Preview.
{% /alert %}

Datadog provides support for custom instrumentation in Rust applications through the [`datadog-opentelemetry` crate](https://crates.io/crates/datadog-opentelemetry). This library is built on the OpenTelemetry (OTel) API and SDK, providing a tracer that includes Datadog-specific features and an exporter.

Because this library is built on OpenTelemetry, you use the standard OpenTelemetry API to create traces and spans.

## Setup{% #setup-otel-rust %}

To configure your Rust application to send OpenTelemetry traces to Datadog:

### 1. Add dependencies{% #add-dependencies-otel-rust %}

Add `datadog-opentelemetry` and the core `opentelemetry` crate to your `Cargo.toml`:

```
cargo add datadog-opentelemetry opentelemetry
```

### 2. Initialize the Tracer{% #initialize-tracer-otel-rust %}

In your application's main function, initialize the Datadog tracer provider:

{% alert level="info" %}
You must shut down the provider before your application exits to ensure all pending traces are flushed.
{% /alert %}

```
use datadog_opentelemetry;
use opentelemetry::{global, trace::Tracer};
use std::time::Duration;

fn main() {
    // This picks up env var configuration (like DD_SERVICE)
    // and initializes the global tracer provider
    let tracer_provider = datadog_opentelemetry::tracing()
        .init();

    // --- Your application code starts here ---
    let tracer = global::tracer("my-component");

    tracer.in_span("my-operation", |_cx| {
        // ... do work ...
    });

    println!("Doing work...");

    // --- Your application code ends here ---

    // Shut down the tracer provider to flush remaining spans
    tracer_provider.shutdown_with_timeout(Duration::from_secs(5)).expect("tracer shutdown error");
}
```

### 3. Ensure Agent is running{% #ensure-agent-running-otel-rust %}

The Datadog exporter sends traces to the Datadog Agent, which must be running and accessible.

## Configuration{% #configuration-otel-rust %}

The Datadog Rust SDK is configured using environment variables. For a complete list of options, see the [Configuration documentation](https://docs.datadoghq.com/tracing/trace_collection/library_config/rust.md).

## Examples{% #examples-otel-rust %}

### Get a Tracer{% #get-a-tracer-otel-rust %}

Get an instance of a `Tracer` from the global provider:

```
use opentelemetry::global;

let tracer = global::tracer("my-component");
```

### Create a span{% #create-a-span-otel-rust %}

Use `tracer.in_span` to create a new span. The span is automatically ended when the closure finishes:

```
use opentelemetry::{global, trace::Tracer};

fn do_work() {
    let tracer = global::tracer("my-component");

    tracer.in_span("operation_name", |_cx| {
        // The span is active within this closure
        println!("Doing work...");
    });
}
```

### Create a child span{% #create-a-child-span-otel-rust %}

To create a child span, nest `in_span` calls:

```
use opentelemetry::{global, trace::Tracer};

fn parent_operation() {
    let tracer = global::tracer("my-component");

    tracer.in_span("parent_operation", |_cx| {
        tracer.in_span("child_operation", |_cx| {
            // This span is automatically parented to "parent_operation"
            println!("Doing child work...");
        });
        println!("Doing parent work...");
    });
}
```

### Add span tags{% #add-span-tags-otel-rust %}

Add attributes to a span using the `set_attribute` method:

```
use opentelemetry::trace::{Tracer, TraceContextExt};
use opentelemetry::KeyValue;

fn add_tags_to_span() {
    let tracer = opentelemetry::global::tracer("my-component");

    tracer.in_span("operation.with.tags", |cx| {
        let span = cx.span();

        span.set_attribute(KeyValue::new("customer.id", "12345"));
        span.set_attribute(KeyValue::new("http.method", "GET"));
    });
}
```

### Add span events{% #add-span-events-otel-rust %}

Add time-stamped log messages to a span using the `add_event` method:

```
use opentelemetry::trace::{Tracer, TraceContextExt};
use opentelemetry::KeyValue;

fn add_events_to_span() {
    let tracer = opentelemetry::global::tracer("my-component");

    tracer.in_span("operation.with.events", |cx| {
        let span = cx.span();

        span.add_event("Data received", vec![]);
        span.add_event(
            "Processing data",
            vec![
                KeyValue::new("data.size_bytes", 1024),
                KeyValue::new("data.format", "json"),
            ],
        );
    });
}
```

## Context propagation{% #context-propagation-otel-rust %}

Because Rust does not have automatic instrumentation, you must manually propagate the trace context when making or receiving remote calls to connect traces across services.

For more information, see [Trace Context Propagation](https://docs.datadoghq.com/tracing/trace_collection/trace_context_propagation.md?tab=rust).
{% /section %}
{% /section %}

{% section displayed-if="Signal is Metrics" %}
This section only applies to users who meet the following criteria: Signal is Metrics

{% section
   displayed-if="The selected value for Language is included in the given list: '.NET, Node.js, Python, Ruby, Go, PHP, Rust, Java'" %}
This section only applies to users who meet the following criteria: The selected value for Language is included in the given list: '.NET, Node.js, Python, Ruby, Go, PHP, Rust, Java'

## Overview{% #overview-2 %}

Use the OpenTelemetry Metrics API with Datadog SDKs to send custom application metrics. This is an alternative to [DogStatsD](https://docs.datadoghq.com/extend/dogstatsd.md).

{% section
   displayed-if="The selected value for Language is included in the given list: '.NET, Node.js, Go, Rust, Java'" %}
This section only applies to users who meet the following criteria: The selected value for Language is included in the given list: '.NET, Node.js, Go, Rust, Java'

The Datadog SDK provides a native implementation of the OpenTelemetry API. This means you can write code against the standard OTel interfaces without needing the official OpenTelemetry SDK.

{% alert level="info" %}
You should not install the official OpenTelemetry SDK or any OTLP Exporter packages. The Datadog SDK provides this functionality. Installing both can lead to runtime conflicts and duplicate data.
{% /alert %}
{% /section %}

{% section
   displayed-if="The selected value for Language is included in the given list: 'Python, Ruby, PHP'" %}
This section only applies to users who meet the following criteria: The selected value for Language is included in the given list: 'Python, Ruby, PHP'

This approach works with the existing OpenTelemetry SDK. When you enable this feature, the Datadog SDK detects the OTel SDK and configures its OTLP exporter to send metrics to the Datadog Agent.
{% /section %}

## Prerequisites{% #prerequisites %}

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

- **.NET Runtime**: Requires .NET 6+ (or `System.Diagnostics.DiagnosticSource` v6.0.0+). See Version and instrument support for a list of supported instruments by version.
- **Datadog SDK**: dd-trace-dotnet version 3.30.0 or later.
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

- **Datadog SDK**: `dd-trace-js` version 5.81.0 or later.
- **OpenTelemetry API**: `@opentelemetry/api` version 1.0.0 to 1.10.0. (The Datadog SDK provides the implementation for this API).
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

- **Datadog SDK**: dd-trace-py version 3.18.0 or later.
{% /section %}

{% section displayed-if="Language is Ruby" %}
This section only applies to users who meet the following criteria: Language is Ruby

{% alert level="info" %}
The OpenTelemetry Metrics SDK for Ruby is currently in [alpha implementation](https://github.com/open-telemetry/opentelemetry-ruby/tree/main/metrics_sdk). Report issues with the SDK at [opentelemetry-ruby/issues](https://github.com/open-telemetry/opentelemetry-ruby/issues).
{% /alert %}

- **Datadog SDK**: `datadog` gem version 2.23.0 or later.
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

- **Datadog SDK**: dd-trace-go version 2.6.0 or later.
{% /section %}

{% section displayed-if="Language is PHP" %}
This section only applies to users who meet the following criteria: Language is PHP

- **Datadog SDK**: dd-trace-php version 1.16.0 or later.
- **OpenTelemetry PHP SDK**: Version 1.0.0 or later (`open-telemetry/sdk`).
- **OpenTelemetry OTLP Exporter**: The OTLP exporter package (`open-telemetry/exporter-otlp`).
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

- **Datadog SDK**: `datadog-opentelemetry` crate version 0.3.0 or later.
- **Rust**: MSRV 1.84 or later.
{% /section %}

{% section displayed-if="Language is Java" %}
This section only applies to users who meet the following criteria: Language is Java

- **Datadog SDK**: dd-trace-java version 1.61.0 or later.
{% /section %}

- **An OTLP-compatible destination**: You must have a destination (Agent or Collector) listening on ports 4317 (gRPC) or 4318 (HTTP) to receive OTel metrics.

{% section
   displayed-if="The selected value for Language is included in the given list: '.NET, Node.js, Python, Ruby, Go, Java'" %}
This section only applies to users who meet the following criteria: The selected value for Language is included in the given list: '.NET, Node.js, Python, Ruby, Go, Java'

- **DogStatsD (Runtime Metrics)**: If you also use Datadog [Runtime Metrics](https://docs.datadoghq.com/tracing/metrics/runtime_metrics.md), ensure the Datadog Agent is listening for DogStatsD traffic on port 8125 (UDP). OTel configuration does not route Runtime Metrics through OTLP.
{% /section %}

## Setup{% #setup %}

Follow these steps to enable OTel Metrics API support in your application.

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

1. Install the Datadog SDK. Follow the installation steps for your runtime:
   - [.NET Framework](https://docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/dd_libraries/dotnet-framework.md#install-the-tracer)
   - [.NET Core](https://docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/dd_libraries/dotnet-core.md#install-the-tracer)
1. Enable OTel metrics by setting the following environment variable:
   ```
   export DD_METRICS_OTEL_ENABLED=true
   ```
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

1. Install the Datadog SDK:
   ```
   npm install dd-trace
   ```
1. Enable OTel metrics by setting the following environment variable:
   ```
   export DD_METRICS_OTEL_ENABLED=true
   ```
1. Instrument your application:
   ```
   // On application start
   require('dd-trace').init();
   ```
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

1. Install the Datadog SDK:
   ```
   pip install ddtrace
   ```
1. Install the OTel SDK and Exporter:
   ```
   pip install opentelemetry-sdk opentelemetry-exporter-otlp
   ```
1. Enable OTel metrics by setting the following environment variable:
   ```
   export DD_METRICS_OTEL_ENABLED=true
   ```
1. Instrument your application:
   ```
   ddtrace-run python my_app.py
   ```
{% /section %}

{% section displayed-if="Language is Ruby" %}
This section only applies to users who meet the following criteria: Language is Ruby

1. Add the Datadog SDK and OTel gems:
   ```
   # Add to your Gemfile
   gem 'datadog', '~> 2.23.0'
   gem 'opentelemetry-metrics-sdk', '~> 0.8'
   gem 'opentelemetry-exporter-otlp-metrics', '~> 0.4'
   ```
1. Install dependencies:
   ```
   bundle install
   ```
1. Enable OTel metrics by setting the following environment variable:
   ```
   export DD_METRICS_OTEL_ENABLED=true
   ```
1. Configure your application:
   ```
   require 'opentelemetry/sdk'
   require 'datadog/opentelemetry'
   
   Datadog.configure do |c|
     # Configure Datadog settings here
   end
   
   # Call after Datadog.configure to initialize metrics
   OpenTelemetry::SDK.configure
   ```
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

1. Install the Datadog SDK:
   ```
   go get github.com/DataDog/dd-trace-go/v2
   ```
1. Enable OTel metrics by setting the following environment variable:
   ```
   export DD_METRICS_OTEL_ENABLED=true
   ```
1. Configure your application:
   ```
   import (
       "context"
       "time"
   
       "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry/metric"
       "go.opentelemetry.io/otel"
   )
   
   // Create MeterProvider with Datadog-specific defaults
   mp, err := metric.NewMeterProvider()
   if err != nil {
       panic(err)
   }
   
   // Set as global MeterProvider
   otel.SetMeterProvider(mp)
   
   // Your application code here...
   
   // Shutdown to flush remaining metrics
   ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   defer cancel()
   metric.Shutdown(ctx, mp)
   ```
{% /section %}

{% section displayed-if="Language is PHP" %}
This section only applies to users who meet the following criteria: Language is PHP

1. Install the Datadog PHP tracer following the [official installation instructions](https://docs.datadoghq.com/tracing/trace_collection/dd_libraries/php.md#install-the-extension).

1. Install the required OpenTelemetry packages:

   ```
   composer require open-telemetry/sdk
   composer require open-telemetry/exporter-otlp
   ```

1. Enable OTel metrics by setting the following environment variable:

   ```
   export DD_METRICS_OTEL_ENABLED=true
   ```

Alternatively, set it in your `php.ini`:

   ```
   datadog.metrics_otel_enabled = true
   ```

1. Configure your application. The Datadog SDK automatically configures the OpenTelemetry MeterProvider when your application loads. No additional code configuration is required.

If your Datadog Agent is running on a non-default location, configure the endpoint:

   ```
   # Option 1: Using the Agent URL
   export DD_TRACE_AGENT_URL=http://your-agent-host:8126
   
   # Option 2: Using the Agent host
   export DD_AGENT_HOST=your-agent-host
   ```

The SDK automatically resolves the appropriate OTLP endpoint (port 4318 for HTTP, port 4317 for gRPC).
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

1. Add the Datadog SDK to your `Cargo.toml`:
   ```
   [dependencies]
   datadog-opentelemetry = { version = "0.3.0" }
   opentelemetry = { version = "0.31", features = ["metrics"] }
   ```
1. Enable OTel metrics by setting the following environment variable:
   ```
   export DD_METRICS_OTEL_ENABLED=true
   ```
1. Configure your application:
   ```
   // Initialize metrics with default configuration
   let meter_provider = datadog_opentelemetry::metrics().init();
   
   // Your application code here...
   
   // Shutdown to flush remaining metrics
   meter_provider.shutdown().unwrap();
   ```
{% /section %}

{% section displayed-if="Language is Java" %}
This section only applies to users who meet the following criteria: Language is Java

1. Add the Datadog SDK (`dd-trace-java`) to your project and [enable its instrumentation](https://docs.datadoghq.com/tracing/trace_collection/dd_libraries/java.md).
1. Make sure you only depend on the OpenTelemetry API (and not the OpenTelemetry SDK).
1. Enable OTel metrics by setting the following environment variable:
   ```
   export DD_METRICS_OTEL_ENABLED=true
   ```
{% /section %}

## Examples{% #examples %}

You can use the standard OpenTelemetry API packages to create custom metrics.

### Create a counter{% #create-a-counter %}

This example uses the OTel Metrics API to create a counter that increments every time an item is processed:

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

```
using System.Diagnostics.Metrics;

// Define a meter
Meter meter = new("MyService", "1.0.0");

// Create a counter instrument
Counter<long> requestsCounter = meter.CreateCounter<long>("http.requests_total");

// Perform work
// ...

// Record measurements
requestsCounter.Add(1, new("method", "GET"), new("status_code", "200"));
```
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

```
const { metrics } = require('@opentelemetry/api');

const meter = metrics.getMeter('my-service', '1.0.0');

// Counter - monotonically increasing values
const requestCounter = meter.createCounter('http.requests', {
  description: 'Total HTTP requests',
  unit: 'requests'
});
requestCounter.add(1, { method: 'GET', status: 200 });
```
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

```
import os
os.environ["DD_METRICS_OTEL_ENABLED"] = "true"
import ddtrace.auto # This must be imported before opentelemetry
from opentelemetry import metrics

# ddtrace automatically configures the MeterProvider
meter = metrics.get_meter(__name__)

# Counter - monotonically increasing values
counter = meter.create_counter("http.requests_total")
counter.add(1, {"method": "GET", "status_code": "200"})
```
{% /section %}

{% section displayed-if="Language is Ruby" %}
This section only applies to users who meet the following criteria: Language is Ruby

```
require 'opentelemetry/api'

# dd-trace-rb automatically configures the MeterProvider
meter = OpenTelemetry.meter_provider.meter('my-service', '1.0.0')

# Counter - monotonically increasing values
counter = meter.create_counter('http.requests_total')
counter.add(1, attributes: { 'method' => 'GET', 'status_code' => '200' })
```
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

```
import (
    "context"

    "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry/metric"
    "go.opentelemetry.io/otel"
    otelmetric "go.opentelemetry.io/otel/metric"
    "go.opentelemetry.io/otel/attribute"
)

// Initialize MeterProvider (typically done once at startup)
mp, _ := metric.NewMeterProvider()
otel.SetMeterProvider(mp)

// Get a meter
meter := otel.Meter("my-service")

// Create a counter
counter, _ := meter.Int64Counter(
    "http.requests_total",
    otelmetric.WithDescription("Total number of HTTP requests"),
)

// Record measurements
counter.Add(context.Background(), 1,
    attribute.String("method", "GET"),
    attribute.String("status_code", "200"),
)
```
{% /section %}

{% section displayed-if="Language is PHP" %}
This section only applies to users who meet the following criteria: Language is PHP

```
use OpenTelemetry\API\Globals;

// dd-trace-php automatically configures the MeterProvider
$meter = Globals::meterProvider()->getMeter('my-service');
$counter = $meter->createCounter('requests', 'requests', 'Total number of requests');
$counter->add(1, ['method' => 'GET', 'route' => '/api/users']);
```
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

```
use opentelemetry::global;
use opentelemetry::metrics::Counter;
use opentelemetry::KeyValue;

// datadog-opentelemetry automatically configures the MeterProvider
let meter = global::meter("my-service");
let counter: Counter<u64> = meter.u64_counter("requests").build();
counter.add(1, &[KeyValue::new("method", "GET")]);
```
{% /section %}

{% section displayed-if="Language is Java" %}
This section only applies to users who meet the following criteria: Language is Java

```
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;

// Define a meter
Meter meter = GlobalOpenTelemetry.get().getMeter("MyService");

// Create a counter instrument
LongCounter counter = meter.counterBuilder("http.requests_total").build();

// Perform work
// ...

// Record measurements
counter.add(1, Attributes.builder().put("method", "GET").put("status_code", "200").build());
```
{% /section %}

### Create a histogram{% #create-a-histogram %}

This example uses the OTel Metrics API to create a histogram to track request durations:

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

```
using System.Diagnostics.Metrics;

// Define a meter
Meter meter = new("MyService", "1.0.0");

// Create a histogram instrument
Histogram<double> responseTimeHistogram = meter.CreateHistogram<double>("http.response.time");

// Perform work
var watch = System.Diagnostics.Stopwatch.StartNew();
await Task.Delay(1_000);
watch.Stop();

// Record measurements
responseTimeHistogram.Record(watch.ElapsedMilliseconds, new("method", "GET"), new("status_code", "200"));
```
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

```
const { metrics } = require('@opentelemetry/api');

const meter = metrics.getMeter('my-service', '1.0.0');

// Histogram - distribution of values
const durationHistogram = meter.createHistogram('http.duration', {
  description: 'HTTP request duration',
  unit: 'ms'
});
durationHistogram.record(145, { route: '/api/users' });
```
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

```
import os
os.environ["DD_METRICS_OTEL_ENABLED"] = "true"
import ddtrace.auto # This must be imported before opentelemetry
from opentelemetry import metrics
import time

# ddtrace automatically configures the MeterProvider
meter = metrics.get_meter(__name__)

# Histogram - distribution of values
histogram = meter.create_histogram(
    name="http.request_duration",
    description="HTTP request duration",
    unit="ms"
)

start_time = time.time()
# ... simulate work ...
time.sleep(0.05)
end_time = time.time()

duration = (end_time - start_time) * 1000 # convert to milliseconds
histogram.record(duration, {"method": "POST", "route": "/api/users"})
```
{% /section %}

{% section displayed-if="Language is Ruby" %}
This section only applies to users who meet the following criteria: Language is Ruby

```
require 'opentelemetry/api'
require 'time'

# dd-trace-rb automatically configures the MeterProvider
meter = OpenTelemetry.meter_provider.meter('my-service', '1.0.0')

# Histogram - distribution of values
histogram = meter.create_histogram('http.request_duration',
  description: 'HTTP request duration',
  unit: 'ms'
)

start_time = Time.now
# ... simulate work ...
sleep(0.05)
end_time = Time.now

duration = (end_time - start_time) * 1000 # convert to milliseconds
histogram.record(duration, attributes: { 'method' => 'POST', 'route' => '/api/users' })
```
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

```
import (
    "context"
    "time"

    "go.opentelemetry.io/otel"
    otelmetric "go.opentelemetry.io/otel/metric"
    "go.opentelemetry.io/otel/attribute"
)

// Get a meter (assuming MeterProvider is already configured)
meter := otel.Meter("my-service")

// Create a histogram
histogram, _ := meter.Float64Histogram(
    "http.request_duration",
    otelmetric.WithDescription("HTTP request duration"),
    otelmetric.WithUnit("ms"),
)

// Measure request duration
start := time.Now()
// ... perform work ...
time.Sleep(50 * time.Millisecond)
duration := float64(time.Since(start).Nanoseconds()) / 1e6

histogram.Record(context.Background(), duration,
    attribute.String("method", "POST"),
    attribute.String("route", "/api/users"),
)
```
{% /section %}

{% section displayed-if="Language is PHP" %}
This section only applies to users who meet the following criteria: Language is PHP

```
use OpenTelemetry\API\Globals;

// dd-trace-php automatically configures the MeterProvider
$meter = Globals::meterProvider()->getMeter('my-service');
$histogram = $meter->createHistogram('http.request_duration', 'ms', 'HTTP request duration');

$start = microtime(true);
// ... perform work ...
usleep(50000);
$duration = (microtime(true) - $start) * 1000;

$histogram->record($duration, ['method' => 'POST', 'route' => '/api/users']);
```
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

```
use opentelemetry::global;
use opentelemetry::KeyValue;
use std::time::Instant;

// Get a meter (assuming MeterProvider is already configured)
let meter = global::meter("my-service");
let histogram = meter.f64_histogram("http.request_duration").build();

// Measure request duration
let start = Instant::now();
// ... perform work ...
std::thread::sleep(std::time::Duration::from_millis(50));
let duration = start.elapsed().as_secs_f64() * 1000.0;

histogram.record(duration, &[
    KeyValue::new("method", "POST"),
    KeyValue::new("route", "/api/users"),
]);
```
{% /section %}

{% section displayed-if="Language is Java" %}
This section only applies to users who meet the following criteria: Language is Java

```
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;

// Define a meter
Meter meter = GlobalOpenTelemetry.get().getMeter("MyService");

// Create a histogram instrument
DoubleHistogram histogram = meter.histogramBuilder("http.response.time").build();

// Perform work
// ...

// Record measurements
histogram.record(duration, Attributes.builder().put("method", "GET").put("status_code", "200").build());
```
{% /section %}

## Supported configuration{% #supported-configuration %}

To enable this feature, you must set `DD_METRICS_OTEL_ENABLED=true`.

All OTLP exporter settings (such as endpoints, protocols, and timeouts), resource attributes, and temporality preferences are configured using a shared set of OpenTelemetry environment variables.

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

### Transport features{% #transport-features %}

The default feature includes gRPC transport. Additional transport options:

- For gRPC transport: `features = ["metrics-grpc"]` (default)
- For HTTP transport: `features = ["metrics-http"]`

**Note**: HTTP/JSON protocol is not supported. Use `grpc` or `http/protobuf` protocols only.
{% /section %}

For a complete list of all shared OTLP environment variables, see [OpenTelemetry Environment Variables Interoperability](https://docs.datadoghq.com/opentelemetry/config/environment_variable_support.md).

## Migrate from other setups{% #migrate-from-other-setups %}

### Existing OTel setup{% #existing-otel-setup %}

If you are already using the OpenTelemetry SDK with a manual OTLP exporter configuration, follow these steps to migrate:

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

1. Add the Datadog SDK (`dd-trace-dotnet`) to your project and enable its instrumentation.
1. Remove any code that manually configures the `OtlpExporter` for metrics. The Datadog SDK handles this configuration automatically.
1. Remove the `OpenTelemetry` and `OpenTelemetry.Exporter.OpenTelemetryProtocol` packages from your project's dependencies.
1. Set the `DD_METRICS_OTEL_ENABLED=true` environment variable.
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

1. Add the Datadog SDK (`dd-trace`) to your project and enable its instrumentation.
1. Remove any code that manually configures the `OTLPMetricsExporter`. The Datadog SDK handles this configuration automatically.
1. Remove the `@opentelemetry/sdk-node` and `@opentelemetry/exporter-otlp` packages from your project's dependencies.
1. Set the `DD_METRICS_OTEL_ENABLED=true` environment variable.
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

1. Add the Datadog SDK (`dd-trace-py`) to your project and enable its instrumentation (for example, `ddtrace-run`).
1. Remove any code that manually configures the `OTLPMetricsExporter`. The Datadog SDK handles this configuration automatically.
1. Set the `DD_METRICS_OTEL_ENABLED=true` environment variable.
{% /section %}

{% section displayed-if="Language is Ruby" %}
This section only applies to users who meet the following criteria: Language is Ruby

1. Add the Datadog SDK (`datadog`) to your project and enable its instrumentation.
1. Remove any code that manually configures the `OTLPMetricsExporter`. The Datadog SDK handles this configuration automatically.
1. Set the `DD_METRICS_OTEL_ENABLED=true` environment variable.

{% alert level="warning" %}
Runtime and trace metrics continue to be submitted using StatsD. Only custom metrics created through the OpenTelemetry Metrics API are sent using OTLP. The `dd-trace-rb` implementation supports exporting OTLP metrics exclusively to a Datadog Agent or OpenTelemetry Collector. Multiple exporters are not supported.
{% /alert %}
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

1. Add the Datadog SDK (`dd-trace-go/v2`) to your project and enable its instrumentation.
1. Remove any code that manually configures the `OTLPMetricsExporter`. The Datadog SDK handles this configuration automatically.
1. Set the `DD_METRICS_OTEL_ENABLED=true` environment variable.

{% alert level="warning" %}
Runtime and trace metrics continue to be submitted using StatsD. Only custom metrics created through the OpenTelemetry Metrics API are sent using OTLP. The `dd-trace-go` implementation supports exporting OTLP metrics exclusively to a Datadog Agent or OpenTelemetry Collector. Multiple exporters are not supported.
{% /alert %}
{% /section %}

{% section displayed-if="Language is PHP" %}
This section only applies to users who meet the following criteria: Language is PHP

1. Install the Datadog PHP tracer following the [official installation instructions](https://docs.datadoghq.com/tracing/trace_collection/dd_libraries/php.md#install-the-extension).
1. Remove any code that manually configures the OTLP exporter. The Datadog SDK handles this configuration automatically.
1. Set the `DD_METRICS_OTEL_ENABLED=true` environment variable.
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

1. Add the Datadog SDK (`datadog-opentelemetry`) to your project and enable its instrumentation.
1. Remove any code that manually configures the `OTLPMetricsExporter`. The Datadog SDK handles this configuration automatically.
1. Set the `DD_METRICS_OTEL_ENABLED=true` environment variable.

{% alert level="warning" %}
Runtime and trace metrics continue to be submitted using StatsD. Only custom metrics created through the OpenTelemetry Metrics API are sent using OTLP. The `datadog-opentelemetry` implementation supports exporting OTLP metrics exclusively to a Datadog Agent or OpenTelemetry Collector. Multiple exporters are not supported.
{% /alert %}
{% /section %}

{% section displayed-if="Language is Java" %}
This section only applies to users who meet the following criteria: Language is Java

1. Add the Datadog SDK (`dd-trace-java`) to your project and [enable its instrumentation](https://docs.datadoghq.com/tracing/trace_collection/dd_libraries/java.md).
1. Make sure you only depend on the OpenTelemetry API (and not the OpenTelemetry SDK).
1. Set the `DD_METRICS_OTEL_ENABLED=true` environment variable.

{% alert level="warning" %}
Runtime and trace metrics continue to be submitted using StatsD. Only custom metrics created through the OpenTelemetry Metrics API are sent using OTLP.
{% /alert %}
{% /section %}

### Existing DogStatsD setup{% #existing-dogstatsd-setup %}

If you are currently using the Datadog DogStatsD client and want to migrate to the OpenTelemetry Metrics API, you need to update your instrumentation code. The main difference is that OTel metrics are configured using environment variables rather than code, and you create `Instrument` objects first.

## Troubleshooting{% #troubleshooting %}

- Verify `DD_METRICS_OTEL_ENABLED` is set to `true`.
- Verify that your OTLP destination is configured correctly to receive metrics.
- If you are sending data to the Datadog Agent, verify OTLP ingestion is enabled. See [Enabling OTLP Ingestion on the Datadog Agent](https://docs.datadoghq.com/opentelemetry/setup/otlp_ingest_in_the_agent.md?tab=host#enabling-otlp-ingestion-on-the-datadog-agent) for details.

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

- Verify Datadog automatic instrumentation is active. This feature relies on Datadog's automatic instrumentation to function. Verify you have completed all setup steps to enable the .NET instrumentation hooks, as these are required to intercept the metric data.
- If, after removing the OpenTelemetry SDK packages, your application fails to compile due to missing APIs in the [System.Diagnostics.Metrics namespace](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.metrics), you must update your application by either adding a direct NuGet package reference to `System.Diagnostics.DiagnosticSource` or upgrading the version of .NET. See .NET version and instrument support for more information.
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

- Verify `dd-trace` is initialized first. The Datadog SDK must be initialized at the top of your application, *before* any other modules are imported.
- Verify `@opentelemetry/api` is installed. The Node.js SDK requires this API package.
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

- Verify `opentelemetry-sdk` is installed. The Python SDK requires `opentelemetry-sdk` and `opentelemetry-exporter-otlp` to be installed in your Python environment.
- Verify that you are running your application with `ddtrace-run` (or have imported and initialized `ddtrace` manually).
{% /section %}

{% section displayed-if="Language is Ruby" %}
This section only applies to users who meet the following criteria: Language is Ruby

- Verify required gems (`opentelemetry-metrics-sdk` and `opentelemetry-exporter-otlp-metrics`) are installed in your Ruby environment.
- Verify `Datadog.configure` is called before `OpenTelemetry::SDK.configure`. The Datadog SDK must be configured first to properly set up the meter provider.
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

- Verify `DD_METRICS_OTEL_ENABLED=true` is set. Metrics are disabled by default in dd-trace-go.
- Verify the Datadog SDK is imported: `import "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry/metric"`
{% /section %}

{% section displayed-if="Language is PHP" %}
This section only applies to users who meet the following criteria: Language is PHP

- Verify OpenTelemetry SDK version. Version 1.0.0 or later is required.
- Verify `open-telemetry/exporter-otlp` package is installed.
- Verify `DD_METRICS_OTEL_ENABLED=true` is set before your application starts.
- Enable debug logging with `DD_TRACE_DEBUG=true` to see detailed logs.
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

- Verify that a transport feature (`metrics-grpc` or `metrics-http`) is enabled in your Cargo.toml file, depending on your protocol choice.
- Check protocol configuration. Only `grpc` and `http/protobuf` protocols are supported. HTTP/JSON is not supported.
- Verify `DD_METRICS_OTEL_ENABLED=true` is set before initializing the meter provider.
{% /section %}

{% section displayed-if="Language is Java" %}
This section only applies to users who meet the following criteria: Language is Java

- Verify Datadog automatic instrumentation is active. This feature relies on Datadog's automatic instrumentation to function.
{% /section %}

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

### .NET version and instrument support{% #net-version-and-instrument-support %}

Support for specific OpenTelemetry metric instruments is dependent on your .NET runtime version or the version of the `System.Diagnostics.DiagnosticSource` NuGet package you have installed.

Here is the minimum version required for each instrument type:

- **.NET 6+** (or `System.Diagnostics.DiagnosticSource` v6.0.0) supports:

  - `Counter`
  - `Histogram`
  - `ObservableCounter`
  - `ObservableGauge`

- **.NET 7+** (or `System.Diagnostics.DiagnosticSource` v7.0.0) supports:

  - `UpDownCounter`
  - `ObservableUpDownCounter`

- **.NET 9+** (or `System.Diagnostics.DiagnosticSource` v9.0.0) supports:

  - `Gauge`
{% /section %}
{% /section %}
{% /section %}

{% section displayed-if="Signal is Logs" %}
This section only applies to users who meet the following criteria: Signal is Logs

{% section
   displayed-if="The selected value for Language is included in the given list: '.NET, Node.js, Python, Go, Rust'" %}
This section only applies to users who meet the following criteria: The selected value for Language is included in the given list: '.NET, Node.js, Python, Go, Rust'

## Overview{% #overview-3 %}

Use the OpenTelemetry Logs API with Datadog SDKs to send custom application logs. This is an alternative to Datadog's traditional log injection.

{% section
   displayed-if="The selected value for Language is included in the given list: '.NET, Node.js, Go, Rust'" %}
This section only applies to users who meet the following criteria: The selected value for Language is included in the given list: '.NET, Node.js, Go, Rust'

The Datadog SDK provides a native implementation of the OpenTelemetry API. This means you can write code against the standard OTel interfaces without needing the official OpenTelemetry SDK.

{% alert level="info" %}
You should not install the official OpenTelemetry SDK or any OTLP Exporter packages. The Datadog SDK provides this functionality. Installing both can lead to runtime conflicts and duplicate data.
{% /alert %}
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

This approach works with the existing OpenTelemetry SDK. When you enable this feature, the Datadog SDK detects the OTel SDK and configures its OTLP exporter to send logs to the Datadog Agent.
{% /section %}

## Prerequisites{% #prerequisites-2 %}

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

- **Datadog SDK**: `dd-trace-dotnet` version [3.31.0](https://github.com/DataDog/dd-trace-dotnet/releases/tag/v3.31.0) or later.
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

- **Datadog SDK**: `dd-trace-js` version 5.73.0 or later.
- **OpenTelemetry Logs API**: The `@opentelemetry/api-logs` package is required, in a version from `v0.200.0` up to `v1.0`.

{% alert level="warning" %}
The `@opentelemetry/api-logs` package is still experimental, and version 1.0 has not yet been released. New versions of this package may introduce breaking changes that affect compatibility.

If you encounter an issue after upgrading `@opentelemetry/api-logs`, [open an issue in the `dd-trace-js` repository](https://github.com/DataDog/dd-trace-js/issues).
{% /alert %}
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

- **Datadog SDK**: `dd-trace-py` version 3.18.0 or later.
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

- **Datadog SDK**: `dd-trace-go` version 2.5.0 or later.
- **OpenTelemetry Go SDK**: `go.opentelemetry.io/otel/log` version 0.13.0 or later (provided automatically by the Datadog SDK).
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

- **Datadog SDK**: `datadog-opentelemetry` crate version 0.2.1 or later.
- **Rust**: MSRV 1.84.1 or later.
- **OpenTelemetry Rust SDK**: The SDK provides the logs implementation automatically.
{% /section %}

- **An OTLP-compatible destination**: You must have a destination (Agent or Collector) listening on ports 4317 (gRPC) or 4318 (HTTP) to receive OTel logs.

## Setup{% #setup-2 %}

Follow these steps to enable OTel Logs API support in your application.

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

1. Install the Datadog SDK. Follow the installation steps for your runtime:
   - [.NET Framework](https://docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/dd_libraries/dotnet-framework.md#install-the-tracer)
   - [.NET Core](https://docs.datadoghq.com/tracing/trace_collection/automatic_instrumentation/dd_libraries/dotnet-core.md#install-the-tracer)
1. Enable OTel logs export by setting the following environment variable:
   ```
   export DD_LOGS_OTEL_ENABLED=true
   ```
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

1. Install the Datadog SDK:
   ```
   npm install dd-trace
   ```
1. Install the OpenTelemetry Logs API package:
   ```
   npm install @opentelemetry/api-logs
   ```
1. Enable OTel logs export by setting the following environment variable:
   ```
   export DD_LOGS_OTEL_ENABLED=true
   ```
1. Initialize the Datadog SDK (`dd-trace`) at the beginning of your application, before any other modules are imported:
   ```
   // This must be the first line of your application
   require('dd-trace').init()
   
   // Other imports can follow
   const { logs } = require('@opentelemetry/api-logs')
   const express = require('express')
   ```
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

1. Install the Datadog SDK:
   ```
   pip install ddtrace
   ```
1. Install the OTel SDK and Exporter:
   ```
   pip install opentelemetry-sdk opentelemetry-exporter-otlp>=1.15.0
   ```
1. Enable OTel logs export by setting the following environment variable:
   ```
   export DD_LOGS_OTEL_ENABLED=true
   ```
1. Run your application using `ddtrace-run`:
   ```
   ddtrace-run python my_app.py
   ```
When enabled, `ddtrace` automatically detects the OTel packages and configures the `OTLPLogExporter` to send logs to your OTLP destination.
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

1. Install the Datadog SDK:
   ```
   go get github.com/DataDog/dd-trace-go/v2
   ```
1. Enable OTel logs export by setting the following environment variable:
   ```
   export DD_LOGS_OTEL_ENABLED=true
   ```
1. Initialize the logger provider in your application:
   ```
   import (
       "context"
       "log/slog"
       
       "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry/log"
       "go.opentelemetry.io/otel"
       otellog "go.opentelemetry.io/otel/log"
   )
   
   // Initialize the global logger provider
   err := log.InitGlobalLoggerProvider(context.Background())
   if err != nil {
       panic(err)
   }
   
   // Set as global logger provider
   otel.SetLoggerProvider(log.GetGlobalLoggerProvider())
   
   // Your application code here...
   
   // Shutdown to flush remaining logs
   defer log.ShutdownGlobalLoggerProvider(context.Background())
   ```
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

1. Add the Datadog SDK to your `Cargo.toml`:
   ```
   [dependencies]
   datadog-opentelemetry = { version = "0.3.0", features = ["logs-grpc"] }
   opentelemetry = { version = "0.31", features = ["logs"] }
   opentelemetry_sdk = { version = "0.31", features = ["logs"] }
   ```
**Note**: Use `features = ["logs-http"]` if you prefer HTTP/protobuf transport instead of gRPC.
1. Enable OTel logs export by setting the following environment variable:
   ```
   export DD_LOGS_OTEL_ENABLED=true
   ```
1. Initialize the logger provider in your application:
   ```
   use opentelemetry::global;
   
   // Initialize logs with default configuration
   let logger_provider = datadog_opentelemetry::logs().init();
   
   // Set as global logger provider
   global::set_logger_provider(logger_provider.clone());
   
   // Your application code here...
   
   // Shutdown to flush remaining logs
   let _ = logger_provider.shutdown();
   ```
{% /section %}

## Examples{% #examples-2 %}

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

### Standard logging{% #standard-logging-dotnet %}

```
using Microsoft.Extensions.Logging;

// For a Console application, manually create a logger factory
using var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.SetMinimumLevel(LogLevel.Debug);
});

// Get a logger instance
var logger = loggerFactory.CreateLogger<Program>();

// This log will be exported via OTLP
logger.LogInformation("This is a standard log message.");
```

### Trace and log correlation{% #trace-log-correlation-dotnet %}

This example shows how logs emitted within an active Datadog span are automatically correlated. If you are using the OTel Tracing API or built-in .NET Activity API to create spans, verify OTel Tracing API support is enabled by setting `DD_TRACE_OTEL_ENABLED=true`.

```
using Microsoft.Extensions.Logging;
using System.Diagnostics;
using System.Threading.Tasks;

// For a Console application, manually create a logger factory
using var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.SetMinimumLevel(LogLevel.Debug);
});

// Get a logger instance
var logger = loggerFactory.CreateLogger<Program>();

// Create an activity source
var activitySource = new ActivitySource("MyService", "1.0.0");

// Start an activity (span)
using (var activity = activitySource.StartActivity("do.work"))
{
    // This log is automatically correlated with the 'do.work' span
    logger.LogInformation("This log is correlated to the active span.");
    await Task.Delay(TimeSpan.FromMilliseconds(100));
    logger.LogWarning("So is this one.");
}
```
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

### Emitting a log{% #emitting-log-nodejs %}

After the Datadog SDK is initialized, you can use the standard OpenTelemetry Logs API to get a logger and emit log records.

```
// Tracer must be initialized first
require('dd-trace').init()

const { logs } = require('@opentelemetry/api-logs')
const logger = logs.getLogger('my-service', '1.0.0')

// Emit a log record
logger.emit({
  severityText: 'INFO',
  severityNumber: 9,
  body: `User clicked the checkout button.`,
  attributes: {
    'cart.id': 'c-12345',
    'user.id': 'u-54321'
  }
})
```

### Trace and log correlation{% #trace-log-correlation-nodejs %}

Trace and log correlation is automatic. When you emit a log using the OTel Logs API within an active Datadog trace, the `trace_id` and `span_id` are automatically added to the log record.

```
// Tracer must be initialized first
require('dd-trace').init()

const { logs } = require('@opentelemetry/api-logs')
const express = require('express')

const app = express()
const logger = logs.getLogger('my-service', '1.0.0')

app.get('/api/users/:id', (req, res) => {
  // This log is automatically correlated with the 'express.request' span
  logger.emit({
    severityText: 'INFO',
    severityNumber: 9,
    body: `Processing user request for ID: ${req.params.id}`,
  })
  res.json({ id: req.params.id, name: 'John Doe' })
})

app.listen(3000)
```
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

The Datadog SDK supports the OpenTelemetry Logs API for Python's built-in `logging` module. You do not need to change your existing logging code.

### Standard logging{% #standard-logging-python %}

This example shows a standard log message. With `DD_LOGS_OTEL_ENABLED=true`, this log is automatically captured, formatted as OTLP, and exported.

```
import logging
import time

# Get a logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

# Add a handler to see logs in the console (optional)
handler = logging.StreamHandler()
logger.addHandler(handler)

# This log will be exported via OTLP
logger.info("This is a standard log message.")
```

### Trace and log correlation{% #trace-log-correlation-python %}

This example shows how logs emitted within an active Datadog span are automatically correlated.

```
from ddtrace import tracer
import logging
import time

# Standard logging setup
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(message)s'))
logger.addHandler(handler)

@tracer.wrap("do.work")
def do_work():
    # This log is automatically correlated with the 'do.work' span
    logger.info("This log is correlated to the active span.")
    time.sleep(0.1)
    logger.warning("So is this one.")

print("Starting work...")
do_work()
print("Work complete.")
```
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

### Standard logging{% #standard-logging-go %}

After the Datadog SDK is initialized, you can use the standard OpenTelemetry Logs API to emit log records.

```
import (
    "context"

    "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry/log"
    "go.opentelemetry.io/otel"
    otellog "go.opentelemetry.io/otel/log"
)

// Initialize the logger provider (typically done once at startup)
err := log.InitGlobalLoggerProvider(context.Background())
if err != nil {
    panic(err)
}
otel.SetLoggerProvider(log.GetGlobalLoggerProvider())

// Get a logger
logger := otel.GetLoggerProvider().Logger("my-service")

// Create and emit a log record
var logRecord otellog.Record
logRecord.SetBody(otellog.StringValue("User clicked the checkout button"))
logRecord.SetSeverity(otellog.SeverityInfo)
logRecord.SetSeverityText("INFO")
logRecord.AddAttributes(
    otellog.String("cart.id", "c-12345"),
    otellog.String("user.id", "u-54321"),
)

logger.Emit(context.Background(), logRecord)
```

### Trace and log correlation{% #trace-log-correlation-go %}

Trace and log correlation is automatic. When you emit a log using the OTel Logs API within an active Datadog trace, the `trace_id` and `span_id` are automatically added to the log record.

```
import (
    "context"

    "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry/log"
    "go.opentelemetry.io/otel"
    otellog "go.opentelemetry.io/otel/log"
    "go.opentelemetry.io/otel/trace"
)

// Initialize logger (typically done once at startup)
err := log.InitGlobalLoggerProvider(context.Background())
if err != nil {
    panic(err)
}
otel.SetLoggerProvider(log.GetGlobalLoggerProvider())

// Get tracer and logger
tracer := otel.Tracer("my-service")
logger := otel.GetLoggerProvider().Logger("my-service")

// Start a span
ctx, span := tracer.Start(context.Background(), "process.user.request")
defer span.End()

// Create and emit a log record that will be automatically correlated with the active span
var logRecord otellog.Record
logRecord.SetBody(otellog.StringValue("Processing user request for ID: 12345"))
logRecord.SetSeverity(otellog.SeverityInfo)
logRecord.SetSeverityText("INFO")

logger.Emit(ctx, logRecord)
```
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

### Standard logging{% #standard-logging-rust %}

After the Datadog SDK is initialized, you can use the standard OpenTelemetry Logs API to emit log records.

```
use opentelemetry::global;
use opentelemetry::logs::{Logger, LogRecord, Severity};
use opentelemetry::KeyValue;

// Initialize logs (typically done once at startup)
let logger_provider = datadog_opentelemetry::logs().init();
global::set_logger_provider(logger_provider.clone());

// Get a logger
let logger = global::logger_provider().logger("my-service");

// Emit a log record
let mut log_record = LogRecord::default();
log_record.set_body("User clicked the checkout button".into());
log_record.set_severity_number(Severity::Info);
log_record.set_severity_text("INFO");
log_record.add_attribute(KeyValue::new("cart.id", "c-12345"));
log_record.add_attribute(KeyValue::new("user.id", "u-54321"));

logger.emit(log_record);
```

### Trace and log correlation{% #trace-log-correlation-rust %}

Trace and log correlation is automatic. When you emit a log using the OTel Logs API within an active Datadog trace, the `trace_id` and `span_id` are automatically added to the log record.

```
use opentelemetry::global;
use opentelemetry::logs::{Logger, LogRecord, Severity};
use opentelemetry::trace::{Tracer, TracerProvider};

// Initialize logs and traces (typically done once at startup)
let logger_provider = datadog_opentelemetry::logs().init();
global::set_logger_provider(logger_provider.clone());

// Get tracer and logger
let tracer = global::tracer("my-service");
let logger = global::logger_provider().logger("my-service");

// Start a span
let span = tracer.start("process.user.request");
let _guard = span.with_current_context();

// This log is automatically correlated with the active span
let mut log_record = LogRecord::default();
log_record.set_body("Processing user request for ID: 12345".into());
log_record.set_severity_number(Severity::Info);
log_record.set_severity_text("INFO");

logger.emit(log_record);
```
{% /section %}

## Supported configuration{% #supported-configuration-2 %}

To enable this feature, you must set `DD_LOGS_OTEL_ENABLED=true`.

All OTLP exporter settings (such as endpoints, protocols, and timeouts), resource attributes, and batch processor settings are configured using a shared set of OpenTelemetry environment variables.

For a complete list of all shared OTLP environment variables, see [OpenTelemetry Environment Variables Interoperability](https://docs.datadoghq.com/opentelemetry/config/environment_variable_support.md).

## Migrate from other setups{% #migrate-from-other-setups-2 %}

### Existing OTel setup{% #existing-otel-setup-2 %}

If you are already using the OpenTelemetry SDK with a manual OTLP exporter configuration, follow these steps to migrate:

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

1. Add the Datadog SDK (`dd-trace-dotnet`) to your project and enable its instrumentation.
1. Remove any code that manually configures the `OtlpExporter` for logs. The Datadog SDK handles this configuration automatically.
1. Remove the `OpenTelemetry` and `OpenTelemetry.Exporter.OpenTelemetryProtocol` packages from your project's dependencies.
1. Set the `DD_LOGS_OTEL_ENABLED=true` environment variable.
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

1. Remove the OTel SDK and OTLP Exporter packages:
   ```
   npm uninstall @opentelemetry/sdk-logs @opentelemetry/exporter-otlp-logs
   ```
1. Remove all manual OTel SDK initialization code (for example, `new LoggerProvider()`, `addLogRecordProcessor()`, `new OTLPLogExporter()`).
1. Install the Datadog SDK: `npm install dd-trace`
1. Keep the `@opentelemetry/api-logs` package.
1. Set `DD_LOGS_OTEL_ENABLED=true` and initialize `dd-trace` at the top of your application.

Your existing code that uses `logs.getLogger()` continues to work.
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

1. Remove your manual setup code (for example, `LoggerProvider`, `BatchLogRecordProcessor`, and `OTLPLogExporter` instantiation).
1. Enable `ddtrace-run` auto-instrumentation for your application.
1. Set the `DD_LOGS_OTEL_ENABLED=true` environment variable.

The Datadog SDK programmatically configures the OTel SDK for you.
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

1. Add the Datadog SDK (`dd-trace-go/v2`) to your project and enable its instrumentation.
1. Remove any code that manually configures the `OTLPLogExporter`. The Datadog SDK handles this configuration automatically.
1. Remove manual `LoggerProvider` setup and replace with `log.InitGlobalLoggerProvider()`.
1. Set the `DD_LOGS_OTEL_ENABLED=true` environment variable.
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

1. Add the Datadog SDK (`datadog-opentelemetry`) to your project and enable its instrumentation.
1. Remove any code that manually configures the `OTLPLogExporter`. The Datadog SDK handles this configuration automatically.
1. Replace manual logger provider setup with `datadog_opentelemetry::logs().init()`.
1. Set the `DD_LOGS_OTEL_ENABLED=true` environment variable.
{% /section %}

### Existing Datadog log injection{% #existing-datadog-log-injection %}

If you are using Datadog's traditional log injection (where `DD_LOGS_INJECTION=true` adds trace context to text logs) and an Agent to tail log files:

1. Set the `DD_LOGS_OTEL_ENABLED=true` environment variable.
1. The Datadog SDK automatically disables the old log injection style (`DD_LOGS_INJECTION`) to prevent duplicate trace metadata in your logs. Trace correlation is handled by the structured OTLP payload.
1. Verify your Datadog Agent is configured to receive OTLP logs (version 7.48.0 or greater is required)
1. Disable any file-based log collection for this service to avoid duplicate logs.

## Troubleshooting{% #troubleshooting-2 %}

- Verify `DD_LOGS_OTEL_ENABLED` is set to `true`.
- Verify that your OTLP destination is configured correctly to receive logs.
- If you are sending data to the Datadog Agent, verify OTLP ingestion is enabled. See [Enabling OTLP Ingestion on the Datadog Agent](https://docs.datadoghq.com/opentelemetry/setup/otlp_ingest_in_the_agent.md?tab=host#enabling-otlp-ingestion-on-the-datadog-agent) for details.

{% section displayed-if="Language is .NET" %}
This section only applies to users who meet the following criteria: Language is .NET

- Verify Datadog automatic instrumentation is active. This feature relies on Datadog's automatic instrumentation to function. Verify you have completed all setup steps to enable the .NET instrumentation hooks, as these are required to intercept the log data.
{% /section %}

{% section displayed-if="Language is Node.js" %}
This section only applies to users who meet the following criteria: Language is Node.js

- Verify `dd-trace` is initialized first. The Datadog SDK must be initialized at the top of your application, *before* any other modules are imported.
- Verify `@opentelemetry/api-logs` is installed. The Node.js SDK requires this API package.
{% /section %}

{% section displayed-if="Language is Python" %}
This section only applies to users who meet the following criteria: Language is Python

- Verify `opentelemetry-sdk` is installed. The Python SDK requires `opentelemetry-sdk` and `opentelemetry-exporter-otlp` to be installed in your Python environment.
- Verify `ddtrace-run` is active. Verify that you are running your application with `ddtrace-run` (or have imported and initialized `ddtrace` manually).
{% /section %}

{% section displayed-if="Language is Go" %}
This section only applies to users who meet the following criteria: Language is Go

- Verify `DD_LOGS_OTEL_ENABLED=true` is set. Logs are disabled by default in dd-trace-go.
- Verify the Datadog SDK is imported and initialized: `import "github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry/log"`
- Verify `log.InitGlobalLoggerProvider()` is called before using the logger.
{% /section %}

{% section displayed-if="Language is Rust" %}
This section only applies to users who meet the following criteria: Language is Rust

- Verify `DD_LOGS_OTEL_ENABLED=true` is set. Logs are disabled by default.
- Verify that a transport feature (`logs-grpc` or `logs-http`) is enabled in your `Cargo.toml` file.
- Verify `datadog_opentelemetry::logs().init()` is called before using the logger.
- Check protocol configuration. Only `grpc` and `http/protobuf` protocols are supported. HTTP/JSON is not supported.
{% /section %}
{% /section %}
{% /section %}

## Further reading{% #further-reading %}

- [Instrument a custom method to get deep visibility into your business logic](https://docs.datadoghq.com/tracing/guide/instrument_custom_method.md)
- [Connect your Logs and Traces together](https://docs.datadoghq.com/tracing/connect_logs_and_traces.md)
- [Explore your services, resources, and traces](https://docs.datadoghq.com/tracing/visualization.md)
- [Learn More about Datadog and the OpenTelemetry initiative](https://www.datadoghq.com/blog/opentelemetry-instrumentation/)
