VOOZH about

URL: https://docs.datadoghq.com/feature_flags/implementation_patterns/opentelemetry/

⇱ Feature Flags with OpenTelemetry


Feature Flags with OpenTelemetry

This product is not supported for your selected Datadog site. ().

Overview

You can add Datadog Feature Flags to an application that uses OpenTelemetry (OTel) for tracing. Datadog supports two integration options. In both cases, feature flags evaluate correctly and your existing OpenTelemetry API instrumentation code remains the same.

Choose an integration option based on whether you can change your tracing provider initialization:

  • Option A: Register the Datadog SDK as the TracerProvider
  • Option B: Run the Datadog SDK alongside OpenTelemetry

If you can change your tracing provider initialization, Option A is recommended. If you cannot change your tracing provider, use Option B to adopt feature flags without changing your OTel trace pipeline.

Prerequisites

Follow the Datadog Server-Side Feature Flags installation guide for your language.

Option A: Register the Datadog SDK as the TracerProvider

In this option, the Datadog SDK replaces the OTel SDK as the TracerProvider. Your existing OpenTelemetry API instrumentation code — spans, attributes, events, and context propagation — stays the same.

Remove any existing OpenTelemetry TracerProvider and exporter initialization to avoid duplicate traces.

.env

DD_TRACE_OTEL_ENABLED=true
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

Program.cs

using OpenFeature;
using Datadog.FeatureFlags.OpenFeature;
// Register the Datadog OpenFeature provider
var ffProvider = new DatadogProvider();
await Api.Instance.SetProviderAsync(ffProvider);
var client = Api.Instance.GetClient("my-service");
// Your existing OpenTelemetry API calls continue to work unchanged

For more detail, see OpenTelemetry API Support for .NET.

.env

DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

main.go

import("log"ddotel"github.com/DataDog/dd-trace-go/v2/ddtrace/opentelemetry""github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"ddopenfeature"github.com/DataDog/dd-trace-go/v2/openfeature""github.com/open-feature/go-sdk/openfeature""go.opentelemetry.io/otel")funcmain(){tracer.Start()defertracer.Stop()// Register the Datadog SDK as the OpenTelemetry TracerProviderotelProvider:=ddotel.NewTracerProvider()deferotelProvider.Shutdown()otel.SetTracerProvider(otelProvider)// Register the Datadog OpenFeature providerflagProvider,err:=ddopenfeature.NewDatadogProvider(ddopenfeature.ProviderConfig{})iferr!=nil{log.Fatalf("Failed to create provider: %v",err)}deferflagProvider.Shutdown()iferr:=openfeature.SetProviderAndWait(flagProvider);err!=nil{log.Fatalf("Failed to set provider: %v",err)}client:=openfeature.NewClient("my-service")// Your existing OpenTelemetry API calls continue to work unchanged}

For more detail, see OpenTelemetry API Support for Go.

.env

DD_TRACE_OTEL_ENABLED=true
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>
DD_VERSION=<YOUR_APP_VERSION>

Main.java

importdev.openfeature.sdk.OpenFeatureAPI;importdev.openfeature.sdk.Client;importdatadog.trace.api.openfeature.Provider;// Register the Datadog OpenFeature providerOpenFeatureAPIapi=OpenFeatureAPI.getInstance();api.setProviderAndWait(newProvider());Clientclient=api.getClient("my-app");/* Your existing OpenTelemetry API calls continue to work unchanged */

Note: Depend only on the OpenTelemetry API, not the OpenTelemetry SDK. For more detail, see OpenTelemetry API Support for Java.

.env

DD_TRACE_OTEL_ENABLED=true
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

app.js

import tracer from 'dd-trace';
import { OpenFeature } from '@openfeature/server-sdk';
import * as otel from '@opentelemetry/api';
tracer.init({
 experimental: {
 flaggingProvider: {
 enabled: true,
 },
 },
});
// Register the Datadog SDK as the OpenTelemetry TracerProvider
const provider = new tracer.TracerProvider();
provider.register();
// Register the Datadog OpenFeature provider
OpenFeature.setProvider(tracer.openfeature);
// Your existing @opentelemetry/api calls continue to work unchanged:
const otelTracer = otel.trace.getTracer('my-service');
otelTracer.startActiveSpan('my-operation', (span) => { ... });

For more detail, see OpenTelemetry API Support for Node.js.

.env

DD_TRACE_OTEL_ENABLED=true
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

app.py

from ddtrace import tracer
from openfeature import api
from ddtrace.openfeature import DataDogProvider
tracer.configure()
# Register the Datadog OpenFeature provider
provider = DataDogProvider()
api.set_provider(provider)
client = api.get_client()
# Your existing OpenTelemetry API calls continue to work unchanged

For more detail, see OpenTelemetry API Support for Python.

.env

DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

app.rb

require 'datadog'
require 'opentelemetry/sdk'
require 'datadog/opentelemetry'
require 'open_feature/sdk'
require 'datadog/open_feature/provider'
# Enable remote configuration and OpenFeature integration
Datadog.configure do |c|
 c.remote.enabled = true
 c.open_feature.enabled = true
end
# Register the Datadog OpenFeature provider
OpenFeature::SDK.configure do |c|
 c.set_provider_and_wait(Datadog::OpenFeature::Provider.new)
end
client = OpenFeature::SDK.build_client
# Your existing OpenTelemetry API calls continue to work unchanged

For more detail, see OpenTelemetry API Support for Ruby.

Note: The OpenFeature adapter requires PHP 8 or later.

.env

DD_TRACE_OTEL_ENABLED=true
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

app.php

<?php
require_once __DIR__ . '/vendor/autoload.php';
use DDTrace\OpenFeature\DataDogProvider;
use OpenFeature\OpenFeatureAPI;
// Register the Datadog OpenFeature provider
$api = OpenFeatureAPI::getInstance();
$api->setProvider(new DataDogProvider());
$client = $api->getClient('my-service');
// Your existing OpenTelemetry API calls continue to work unchanged

For more detail, see OpenTelemetry API Support for PHP.

Option B: Run the Datadog SDK alongside OpenTelemetry

In this option, your existing OTel SDK remains the TracerProvider for tracing. Add the Datadog SDK only for feature flag delivery through Remote Configuration. Your OTel SDK initialization, exporters, and instrumentation stay unchanged.

.env

DD_APM_TRACING_ENABLED=false
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

Program.cs

using OpenFeature;
using Datadog.FeatureFlags.OpenFeature;
// Register the Datadog OpenFeature provider
var provider = new DatadogProvider();
await Api.Instance.SetProviderAsync(provider);
var client = Api.Instance.GetClient("my-service");
// Your existing OpenTelemetry SDK initialization stays unchanged

.env

DD_APM_TRACING_ENABLED=false
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

main.go

import("log""github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"ddopenfeature"github.com/DataDog/dd-trace-go/v2/openfeature""github.com/open-feature/go-sdk/openfeature")funcmain(){tracer.Start()defertracer.Stop()// Register the Datadog OpenFeature providerflagProvider,err:=ddopenfeature.NewDatadogProvider(ddopenfeature.ProviderConfig{})iferr!=nil{log.Fatalf("Failed to create provider: %v",err)}deferflagProvider.Shutdown()iferr:=openfeature.SetProviderAndWait(flagProvider);err!=nil{log.Fatalf("Failed to set provider: %v",err)}client:=openfeature.NewClient("my-service")// Your existing OpenTelemetry SDK initialization stays unchanged}

.env

DD_APM_TRACING_ENABLED=false
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>
DD_VERSION=<YOUR_APP_VERSION>

Main.java

importdev.openfeature.sdk.OpenFeatureAPI;importdev.openfeature.sdk.Client;importdatadog.trace.api.openfeature.Provider;// Register the Datadog OpenFeature providerOpenFeatureAPIapi=OpenFeatureAPI.getInstance();api.setProviderAndWait(newProvider());Clientclient=api.getClient("my-app");/* Your existing OpenTelemetry SDK initialization stays unchanged */

.env

DD_APM_TRACING_ENABLED=false
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

app.js

import tracer from 'dd-trace';
import { OpenFeature } from '@openfeature/server-sdk';
tracer.init({
 plugins: false,
 experimental: {
 flaggingProvider: {
 enabled: true,
 },
 },
});
// Register the Datadog OpenFeature provider
OpenFeature.setProvider(tracer.openfeature);
// Your existing OpenTelemetry SDK initialization stays unchanged

.env

DD_APM_TRACING_ENABLED=false
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

app.py

from ddtrace import tracer
from openfeature import api
from ddtrace.openfeature import DataDogProvider
tracer.configure()
# Register the Datadog OpenFeature provider
provider = DataDogProvider()
api.set_provider(provider)
client = api.get_client()
# Your existing OpenTelemetry SDK initialization stays unchanged

.env

DD_APM_TRACING_ENABLED=false
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

app.rb

require 'datadog'
require 'open_feature/sdk'
require 'datadog/open_feature/provider'
Datadog.configure do |c|
 c.remote.enabled = true
 c.open_feature.enabled = true
end
# Register the Datadog OpenFeature provider
OpenFeature::SDK.configure do |c|
 c.set_provider_and_wait(Datadog::OpenFeature::Provider.new)
end
client = OpenFeature::SDK.build_client
# Your existing OpenTelemetry SDK initialization stays unchanged

Note: The OpenFeature adapter requires PHP 8 or later.

.env

DD_APM_TRACING_ENABLED=false
DD_EXPERIMENTAL_FLAGGING_PROVIDER_ENABLED=true
DD_METRICS_OTEL_ENABLED=true
DD_SERVICE=<YOUR_SERVICE_NAME>
DD_ENV=<YOUR_ENVIRONMENT>

app.php

<?php
require_once __DIR__ . '/vendor/autoload.php';
use DDTrace\OpenFeature\DataDogProvider;
use OpenFeature\OpenFeatureAPI;
// Register the Datadog OpenFeature provider
$api = OpenFeatureAPI::getInstance();
$api->setProvider(new DataDogProvider());
$client = $api->getClient('my-service');
// Your existing OpenTelemetry SDK initialization stays unchanged

Evaluate feature flags

After you initialize the provider, evaluate flags with the OpenFeature client. Flag evaluation uses locally cached configuration, so evaluations do not make network requests.

using OpenFeature.Model;
var evalCtx = EvaluationContext.Builder()
 .SetTargetingKey("user-123")
 .Set("companyID", "acme-corp")
 .Build();
var isEnabled = await client.GetBooleanValueAsync("my-flag", false, evalCtx);
evalCtx:=openfeature.NewEvaluationContext("user-123",map[string]interface{}{"companyID":"acme-corp",},)enabled,err:=client.BooleanValue(context.Background(),"my-flag",false,evalCtx)
importdev.openfeature.sdk.EvaluationContext;importdev.openfeature.sdk.MutableContext;EvaluationContextcontext=newMutableContext("user-123").add("companyID","acme-corp");booleanisEnabled=client.getBooleanValue("my-flag",false,context);
import { OpenFeature } from '@openfeature/server-sdk';
const client = OpenFeature.getClient();
const evaluationContext = {
 targetingKey: req.session?.userID ?? 'unknown',
 companyID: req.session?.companyID,
};
const isEnabled = await client.getBooleanValue(
 'my-flag',
 false,
 evaluationContext,
);
from openfeature.evaluation_context import EvaluationContext
eval_ctx = EvaluationContext(
 targeting_key="user-123",
 attributes={"companyID": "acme-corp"},
)
is_enabled = client.get_boolean_value("my-flag", False, eval_ctx)
context = OpenFeature::SDK::EvaluationContext.new(
 targeting_key: 'user-123',
 companyID: 'acme-corp',
)
is_enabled = client.fetch_boolean_value(
 flag_key: 'my-flag',
 default_value: false,
 evaluation_context: context,
)
use OpenFeature\implementation\flags\Attributes;
use OpenFeature\implementation\flags\EvaluationContext;
$context = new EvaluationContext(
 'user-123',
 new Attributes(['companyID' => 'acme-corp'])
);
$isEnabled = $client->getBooleanValue('my-flag', false, $context);

In both integration options, feature flags evaluate the same way. Flag configurations are delivered through the same Remote Configuration channel.

For complete setup instructions, typed getters, evaluation context requirements, and testing patterns, see your language’s server-side SDK documentation.

Further reading