OpenTelemetry in server-side SDKs

Overview

This topic explains how to enable OpenTelemetry in server-side SDKs.

Looking for information on OpenTelemetry in client-side SDKs?

For information on using OpenTelemetry in LaunchDarkly’s client-side SDKs, read OpenTelemetry in client-side SDKs.

About OpenTelemetry

OpenTelemetry (OTel) is an open source observability framework and toolkit designed to create and manage telemetry data such as traces, metrics, and logs. You can use LaunchDarkly’s OpenTelemetry protocol (OTLP) endpoint to send OpenTelemetry traces to LaunchDarkly.

We recommend enabling OpenTelemetry in LaunchDarkly server-side SDKs if you use LaunchDarkly’s Observability, Experimentation, or Guarded rollouts features, or if you use third-party observability tools that support the OpenTelemetry framework.

Because OpenTelemetry is vendor- and tool-agnostic, you can reuse the instrumentation that may already exist in your code to create LaunchDarkly metrics without changing application code.

Viewing OpenTelemetry data in LaunchDarkly

In the LaunchDarkly UI, OpenTelemetry data is used in the Observability, Experimentation, and Guarded rollouts features.

OpenTelemetry metrics, logs, and traces are available in the Observability features.

OpenTelemetry traces generate LaunchDarkly metrics, which are available in the Experimentation and Guarded rollouts features. Specifically, when LaunchDarkly receives OpenTelemetry trace data, it processes and converts this data into events that LaunchDarkly metrics track over time. When trace data contains spans that carry both metrics and feature flag evaluation events, LaunchDarkly correlates these and uses the result to power guarded rollouts. To learn more, read Metrics autogenerated from OpenTelemetry data.

Sending OpenTelemetry data to LaunchDarkly

To configure and send OpenTelemetry data to LaunchDarkly when you are using LaunchDarkly server-side SDKs:

  1. (Optional) Add flag evaluation information to OpenTelemetry spans in your application. To learn how, read the section for your SDK under Server-side SDKs, below.
    • This step is optional but highly recommended, as it means that flag-specific details are available when you review your OTel data in the LaunchDarkly UI.
    • You might choose to skip this step, at least temporarily, if you have already instrumented OpenTelemetry in your application and only want to send your existing OTel data to LaunchDarkly.
  2. Configure your application or OpenTelemetry collector to use LaunchDarkly’s OTel endpoints:
    • For HTTP, use https://otel.observability.app.launchdarkly.com:4318 or https://otel.observability.app.launchdarkly.com:443
    • For gRPC, use https://otel.observability.app.launchdarkly.com:4317.
    • You may need to update your infrastructure to make sure your application can reach this domain. To learn more, read Accessing LaunchDarkly by domain.
  3. (Optional) Include your LaunchDarkly SDK key as a resource attribute in your telemetry data.
    • This step is required if you are using OpenTelemetry SDKs. To learn how, read Setting resource attributes, below.
    • This step is not required if you are using LaunchDarkly server-side SDKs. If you use LaunchDarkly SDKs, this is set automatically.
  4. Send the OpenTelemetry data to LaunchDarkly. LaunchDarkly supports traces, metrics, and logs. Only traces data generates LaunchDarkly metrics. You can view metrics and logs in the LaunchDarkly UI when you use the Observability features.

Setting resource attributes

If you use LaunchDarkly SDKs, resource attributes are set automatically. You do not need set the resource attributes yourself, and you can skip this section.

If you use only OpenTelemetry SDKs, you must include your LaunchDarkly SDK key as a resource attribute in your telemetry data, so that the data is correctly associated with your LaunchDarkly project and environment. You have several options for how to set the resource attribute.

To set the resource attribute using an environment variable, use the OTEL_RESOURCE_ATTRIBUTES environment variable:

Configure resource attribute with environment variable
$export OTEL_RESOURCE_ATTRIBUTES="highlight.project_id=$YOUR_SDK_KEY"

To set the resource attribute in your OpenTelemetry collector configuration, use the resource processor:

Configure resource attribute with collector configuration
1# excerpt from otel-collector-config.yaml
2processors:
3 resource:
4 attributes:
5 - key: highlight.project_id
6 value: YOUR_SDK_KEY
7 action: upsert

For a complete example of a collector configuration, read Configuring the collector, below.

To set the resource attribute programmatically in your application code, use the OpenTelemetry resource available for your language. Here are a few examples:

1import (
2 "go.opentelemetry.io/otel/sdk/resource"
3 "go.opentelemetry.io/otel/sdk/trace"
4)
5
6res := resource.NewWithAttributes(
7 semconv.SchemaURL,
8 attribute.String("highlight.project_id", "YOUR_SDK_KEY"),
9)
10
11tp := trace.NewTracerProvider(
12 trace.WithResource(res),
13)

Configuring the collector

To configure a pipeline to send trace data to LaunchDarkly, modify the collector’s config file, otel-collector-config.yaml, as follows:

1# The receivers specify how the collector receives data.
2# In this example, it receives data using the OpenTelemetry Protocol (OTLP) over gRPC and HTTP.
3receivers:
4 otlp:
5 protocols:
6 grpc:
7 http:
8
9# The exporters specify how the collector sends data.
10exporters:
11 otlphttp:
12 endpoint: https://otel.observability.app.launchdarkly.com:4318
13 otlp:
14 endpoint: https://otel.observability.app.launchdarkly.com:4317
15
16# The processors specify how the collector processes the trace data.
17# In this example, it groups spans that belong to the same trace,
18# and only passes along span events related to feature flags or exceptions.
19processors:
20
21 # This processor sets the resource attribute to your SDK key.
22 # If you are using a LaunchDarkly SDK, you do NOT need to include this.
23 # If you are using an OpenTelemetry SDK, you do need to set the
24 # resource attributes. You can set them here, set them in your application,
25 # or set them using an environment variable.
26 resource:
27 attributes:
28 - key: highlight.project_id
29 value: YOUR_SDK_KEY
30 action: upsert
31
32 # The groupbytrace processor groups all spans that belong to the same trace together.
33 # This is required to ensure LaunchDarkly receives a complete trace in a single request.
34 groupbytrace:
35 wait_duration: 10s
36
37 # This filter removes all span events that are not "feature_flag" or "exception".
38 # It is optional, but useful to limit the amount of data you send to LaunchDarkly.
39 filter/launchdarkly-spanevents:
40 error_mode: ignore
41 traces:
42 spanevent:
43 - 'not (name == "feature_flag" or name == "exception")'
44
45 # This filter removes all spans that do not have an HTTP route or any span events
46 # remaining after the previous filter has been applied
47 filter/launchdarkly-spans:
48 error_mode: ignore
49 traces:
50 span:
51 - 'not (attributes["http.route"] != nil or Len(events) > 0)'
52
53 batch:
54
55extensions:
56 health_check:
57
58service:
59 pipelines:
60 # Add a new pipeline to send data to LaunchDarkly
61 traces/ld:
62 receivers: [otlp]
63 processors:
64 [
65 filter/launchdarkly-spanevents,
66 filter/launchdarkly-spans,
67 groupbytrace,
68 batch,
69 ]
70 exporters: [otlphttp/launchdarkly]
71 metrics:
72 receivers: [otlp]
73 processors: [resource]
74 exporters: [otlphttp]
75 logs:
76 receivers: [otlp]
77 processors: [resource]
78 exporters: [otlphttp]

Server-side SDKs

LaunchDarkly processes metrics, logs, and traces from your OpenTelemetry data:

  • LaunchDarkly processes OpenTelemetry metrics and logs and displays them in the LaunchDarkly UI under Observability
  • LaunchDarkly processes two types of data from OpenTelemetry traces:
    • HTTP span attributes, including latency, 5xx occurrences, and other errors, where the span has or overlaps with another span that has at least one feature flag span event. This includes nested spans.
    • Exception span events that occur after a feature flag span event on the same trace. If the exception occurs before the feature flag event, LaunchDarkly does not capture it.

A feature flag span event is defined as any span event that contains a feature_flag.context.key attribute. LaunchDarkly ignores traces that do not include span events with this attribute.

In most server-side SDKs, the OTel traces are specific to the LaunchDarkly project and environment that you specify in the collector configuration, described above. In the .NET (server-side) and Node.js (server-side) SDKs, you can provide the LaunchDarkly client-side ID in the tracing hook. This enables you to send traces for multiple projects and environments using one collector.

The following sections describe, for each supported SDK, how to ensure your spans have compatible feature flag events.

Sending feature flag event data requires feature flag evaluation

The OpenTelemetry tracing hook automatically attaches feature flag event data to your OTel traces for you. Feature flag event data, including the feature_flag.context.key attribute, is only generated when your application uses the LaunchDarkly SDK to evaluate a feature flag.

This feature is available in the following SDKs:

.NET (server-side)

To add flag evaluation information to OpenTelemetry spans, first add the Telemetry package from NuGet:

Import Telemetry package
$dotnet add package LaunchDarkly.ServerSdk.Telemetry

Next, import the LaunchDarkly.Sdk.Server.Hooks and LaunchDarkly.Sdk.Server.Telemetry namespaces. Then you can create a new tracing hook and reference it in the configuration options when you initialize the SDK client.

Here’s how:

.NET (server-side), v8.4+
1using LaunchDarkly.Sdk.Server.Hooks;
2using LaunchDarkly.Sdk.Server.Telemetry;
3
4var config = Configuration.Builder("sdk-key-123abc")
5 .Hooks(Components.Hooks()
6 .Add(TracingHook.Default())
7 ).Build();
8
9var client = new LdClient(config);

When this configuration is set, the SDK adds span events for each call to a *Variation* method to your active span. The span event information contains details of each flag evaluation, including the flag key and the context key. You can collect this information in Guarded rollouts or other observability tools.

Optionally, you can configure the tracing hook to:

  • create new spans that contain span events with flag evaluation information, rather than adding the events to the active span. The new spans are nested in the current active span, and are not active.
  • include the flag’s evaluated flag value in the span event.

Here’s how:

.NET (server-side), v8.4+
1using LaunchDarkly.Sdk.Server.Hooks;
2using LaunchDarkly.Sdk.Server.OpenTelemetry;
3
4var config = Configuration.Builder("sdk-key-123abc")
5 .Hooks(Components.Hooks()
6 .Add(TracingHook.Builder()
7 .CreateActivities()
8 .IncludeVariant()
9 .Build()
10 )
11 ).Build();
12
13var client = new LdClient(config);

Starting with v1.1 of the LaunchDarkly.Sdk.Server.Hooks package, which requires v8.7+ of the .NET (server-side) SDK, LaunchDarkly automatically decorates any OpenTelemetry spans with project and environment information.

When you create the hook, you can optionally pass in a client-side ID to specify the environment. Use the builder’s .EnvironmentId() method and pass in the client-side ID. To learn more about client-side IDs, read Keys.

Because LaunchDarkly automatically decorates the OpenTelemetry spans, you should not need to call this method explicitly. The exception is if you are using persistent stores, in which case you must either call .EnvironmentId() or use the single-environment OpenTelemetry Collector configuration to ensure that your OpenTelemetry spans have correct project and environment information.

Here’s how:

.NET (server-side), v8.7+
1using LaunchDarkly.Sdk.Server.Hooks; // v1.1 or later
2using LaunchDarkly.Sdk.Server.OpenTelemetry;
3
4var config = Configuration.Builder("sdk-key-123abc")
5 .Hooks(Components.Hooks()
6 .Add(TracingHook.Builder()
7 .EnvironmentId("client-side-id-123abc")
8 )
9 ).Build();
10
11var client = new LdClient(config);

In the .NET (server-side) SDK, the tracing hook is implemented using Microsoft’s System.Diagnostics APIs. Therefore, some of the terminology it uses differs from the OpenTelemetry standard. For example, the tracing hook in the .NET (server-side) SDK uses the method CreateActivities(), rather than CreateSpans(), to enable child spans.

To learn more, read TracingHookBuilder and ConfigurationBuilder.

Go

To add flag evaluation information to OpenTelemetry spans, import the ldhooks package. Then, create a new tracing hook and reference it in the configuration options when you initialize the SDK client.

Here’s how:

Go SDK, v7.4+
1import (
2 ld "github.com/launchdarkly/go-server-sdk/v7"
3 "github.com/launchdarkly/go-server-sdk/v7/ldhooks"
4 "github.com/launchdarkly/go-server-sdk/ldotel"
5)
6
7var config ld.Config
8
9client, _ = ld.MakeCustomClient("sdk-key-123abc",
10 ld.Config{
11 Hooks: []ldhooks.Hook{ldotel.NewTracingHook()},
12 }, 5*time.Second)

When this configuration is set, the SDK adds span events for each call to a *Variation*Ctx method to your active span.

The span event information contains details of each flag evaluation, including the flag key and the context key. You can collect this information in Guarded rollouts or other observability tools.

The *Variation*Ctx methods are the same as the *Variation* methods, except that they also require a Go context. For example, BoolVariationCtx and BoolVariationDetailCtx are the same as the BoolVariation and BoolVariationDetail methods, except that they also require a Go context. This Go context is used in the hook implementation. If you are using hooks, you must update your variation calls to use the Ctx methods.

Optionally, you can configure the tracing hook to:

  • create new spans that contain span events with flag evaluation information, rather than adding the events to the active span. The new spans are nested in the current active span, and are not active.
  • include the flag’s evaluated flag value in the span event.

Here’s how:

Go SDK, v7.4+
1import (
2 ld "github.com/launchdarkly/go-server-sdk/v7"
3 "github.com/launchdarkly/go-server-sdk/v7/ldhooks"
4 "github.com/launchdarkly/go-server-sdk/ldotel"
5)
6
7var config ld.Config
8
9client, _ = ld.MakeCustomClient("sdk-key-123abc",
10 ld.Config{
11 Hooks: []ldhooks.Hook{ldotel.NewTracingHook(ldotel.WithSpans(), ldotel.WithVariant())},
12 }, 5*time.Second)

To learn more, read Config.

Java

To add flag evaluation information to OpenTelemetry spans, import the LaunchDarkly Java Otel Hook package. Then, create a new tracing hook and reference it in the configuration options when you initialize the SDK client.

Here’s how:

Java SDK, v7.4+
1import com.launchdarkly.sdk.*;
2import com.launchdarkly.sdk.server.*;
3import com.launchdarkly.integrations.*;
4
5TracingHook tracingHook = new TracingHook.Builder().build();
6
7LDConfig config = new LDConfig.Builder()
8 .hooks(
9 Components.hooks().setHooks(Collections.singletonList(tracingHook)))
10 .build();
11
12LDClient client = new LDClient("sdk-key-123abc", config);

When this configuration is set, the SDK adds span events for each call to a *Variation* method to your active span. The span event information contains details of each flag evaluation, including the flag key and the context key. You can collect this information in Guarded rollouts or other observability tools.

Optionally, you can configure the tracing hook to:

  • create new spans that contain span events with flag evaluation information, rather than adding the events to the active span. The new spans are nested in the current active span, and are not active.
  • include the flag’s evaluated flag value in the span event.

Here’s how:

Java SDK, v7.4+
1import com.launchdarkly.sdk.*;
2import com.launchdarkly.sdk.server.*;
3import com.launchdarkly.integrations.*;
4
5TracingHook tracingHook = new TracingHook.Builder().withSpans().withVariant().build();
6
7LDConfig config = new LDConfig.Builder()
8 .hooks(
9 Components.hooks().setHooks(Collections.singletonList(tracingHook)))
10 .build();
11
12LDClient client = new LDClient("sdk-key-123abc", config);

To learn more, read LDConfig.Builder.

Node.js (server-side)

To add flag evaluation information to OpenTelemetry spans, import the TracingHook package. Then, create a new tracing hook and reference it in the configuration options when you initialize the SDK client.

Here’s how:

Node.js (server-side) SDK, v9.3+
1import { LDOptions, init } from '@launchdarkly/node-server-sdk';
2import { TracingHook } from '@launchdarkly/node-server-sdk-otel';
3
4const options: LDOptions = {
5 hooks: [new TracingHook()]
6}
7
8const client = init('sdk-key-123abc', options);

When this configuration is set, the SDK adds span events for each call to a *Variation* method to your active span. The span event information contains details of each flag evaluation, including the flag key and the context key. You can collect this information in Guarded rollouts or other observability tools.

Optionally, you can configure the tracing hook to:

  • create new spans that contain span events with flag evaluation information, rather than adding the events to the active span. The new spans are nested in the current active span, and are not active.
  • include the flag’s evaluated flag value in the span event.

Here’s how:

Node.js (server-side) SDK, v9.3+
1import { LDOptions, init } from '@launchdarkly/node-server-sdk';
2import { TracingHook } from '@launchdarkly/node-server-sdk-otel';
3
4const options: LDOptions = {
5 hooks: [new TracingHook({spans: true, includeVariant: true})]
6}
7
8const client = init('sdk-key-123abc', options);

Starting with v1.2 of the @LaunchDarkly/node-server-sdk-otel package, which requires v9.9+ of the Node.js (server-side) SDK, LaunchDarkly automatically decorates any OpenTelemetry spans with project and environment information.

When you create the hook, you can optionally pass in a client-side ID to specify the environment. Use the environmentId option when you create the new TracingHook. To learn more about client-side IDs, read Keys.

Because LaunchDarkly automatically decorates the OpenTelemetry spans, you should not need to set this option explicitly. The exception is if you are using persistent stores, in which case you must either set the environmentId or use the single-environment OpenTelemetry Collector configuration to ensure that your OpenTelemetry spans have correct project and environment information.

Here’s how:

Node.js (server-side) SDK, v9.9+
1import { LDOptions, init } from '@launchdarkly/node-server-sdk';
2import { TracingHook } from '@launchdarkly/node-server-sdk-otel'; // v1.2 or later
3
4const options: LDOptions = {
5 hooks: [new TracingHook({
6 spans: true,
7 includeVariant: true,
8 environmentId: 'client-side-id-123abc'
9 })]
10}
11
12const client = init('sdk-key-123abc', options);

To learn more, read LDOptions.

Python

To add flag evaluation information to OpenTelemetry spans, first install the package:

Install OTEL library
$pip install launchdarkly-server-sdk-otel

Next, import Hook and HookOptions from the package. Then you can create a new tracing hook and reference it in the configuration options when you initialize the SDK client.

Here’s how:

Python, v9.4+
1import ldclient
2from ldclient import Config
3from ldotel.tracing import Hook, HookOptions
4
5config = Config(sdk_key, hooks=[Hook()])
6ldclient.set_config(config=config)
7client = ldclient.get()

When this configuration is set, the SDK adds span events for each call to a *variation* method to your active span. The span event information contains details of each flag evaluation, including the flag key and the context key. You can collect this information in Guarded rollouts or other observability tools.

Optionally, you can configure the tracing hook to:

  • create new spans. The new spans are nested in the current active span, and are not active. The span events with flag evaluation information are still added to the parent span.
  • include the flag’s evaluated flag value in the span event.

Here’s how:

Python, v9.4+
1import ldclient
2from ldclient.config import Config
3
4from ldotel.tracing import Hook, HookOptions
5
6config = Config(sdk_key, hooks=[Hook(HookOptions(add_spans=True, include_variant=True))])
7ldclient.set_config(config=config)
8client = ldclient.get()

To learn more, read Config.

Ruby

To add flag evaluation information to OpenTelemetry spans, first install the gem:

Install OTEL library
$gem install launchdarkly-server-sdk-otel

Then, you can create a new tracing hook and reference it in the configuration options when you initialize the SDK client.

Here’s how:

Ruby, v8.4+
1require 'ldclient-otel'
2
3config = LaunchDarkly::Config.new(hooks: [LaunchDarkly::Otel::TracingHook.new])
4
5client = LaunchDarkly::LDClient.new("sdk-key-123abc", config);

When this configuration is set, the SDK adds span events for each call to a *Variation* method to your active span. The span event information contains details of each flag evaluation, including the flag key and the context key. You can collect this information in Guarded rollouts or other observability tools.

Optionally, you can configure the tracing hook to:

  • create new spans. The new spans are nested in the current active span, and are not active. The span events with flag evaluation information are still added to the parent span.
  • include the flag’s evaluated flag value in the span event.

Here’s how:

Ruby, v8.4+
1require 'ldclient-otel'
2
3tracing_hook_options = LaunchDarkly::Otel::TracingHookOptions.new(add_spans: true, include_variant: true)
4hook = LaunchDarkly::Otel::TracingHook.new(tracing_hook_options)
5config = LaunchDarkly::Config.new(hooks: [hook])
6
7client = LaunchDarkly::LDClient.new("sdk-key-123abc", config);

To learn more, read Config.