Skip to content

Commit 57a75d8

Browse files
committed
Update example to use LD Observability by default.
1 parent 52a49fc commit 57a75d8

2 files changed

Lines changed: 50 additions & 92 deletions

File tree

examples/hello-cpp-server-otel/README.md

Lines changed: 29 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ This example demonstrates how to integrate the LaunchDarkly C++ Server SDK with
1616
- CMake 3.19 or later
1717
- Boost 1.81 or later
1818
- LaunchDarkly SDK key
19-
- OpenTelemetry collector (or compatible backend) running on `localhost:4318`
2019

2120
## Building
2221

@@ -30,33 +29,19 @@ cmake --build . --target hello-cpp-server-otel
3029

3130
## Running
3231

33-
### 1. Start an OpenTelemetry Collector
34-
35-
The easiest way is using Docker:
36-
37-
```bash
38-
docker run -p 4318:4318 otel/opentelemetry-collector:latest
39-
```
40-
41-
Or use Jaeger (which has a built-in OTLP receiver):
42-
43-
```bash
44-
docker run -d -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest
45-
```
46-
47-
### 2. Set Your LaunchDarkly SDK Key
32+
### 1. Set Your LaunchDarkly SDK Key
4833

4934
Either edit `main.cpp` and set the `SDK_KEY` constant, or use an environment variable:
5035

5136
```bash
5237
export LD_SDK_KEY=your-sdk-key-here
5338
```
5439

55-
### 3. Create a Feature Flag
40+
### 2. Create a Feature Flag
5641

5742
In your LaunchDarkly dashboard, create a boolean flag named `show-detailed-weather`.
5843

59-
### 4. Run the Example
44+
### 3. Run the Example
6045

6146
```bash
6247
./build/examples/hello-cpp-server-otel/hello-cpp-server-otel
@@ -69,74 +54,36 @@ You should see:
6954
7055
*** Weather server running on http://0.0.0.0:8080
7156
*** Try: curl http://localhost:8080/weather
72-
*** OpenTelemetry tracing enabled (OTLP HTTP to localhost:4318)
57+
*** OpenTelemetry tracing enabled, sending traces to LaunchDarkly
7358
*** LaunchDarkly integration enabled with OpenTelemetry tracing hook
7459
```
7560

76-
### 5. Make Requests
61+
### 4. Make Requests
7762

7863
```bash
7964
curl http://localhost:8080/weather
8065
```
8166

82-
### 6. View Traces
83-
84-
If using Jaeger, open http://localhost:16686 in your browser. You should see traces with:
67+
### 5. View Traces in LaunchDarkly
8568

86-
- HTTP request spans
87-
- Feature flag evaluation events with attributes:
88-
- `feature_flag.key`: "show-detailed-weather"
89-
- `feature_flag.provider.name`: "LaunchDarkly"
90-
- `feature_flag.context.id`: Context canonical key
91-
- `feature_flag.result.value`: The flag value (since `IncludeValue` is enabled)
69+
1. Go to your LaunchDarkly project
70+
2. Navigate to the Observability section
71+
3. View traces containing your feature flag evaluations with attributes:
72+
- `feature_flag.key`: "show-detailed-weather"
73+
- `feature_flag.provider.name`: "LaunchDarkly"
74+
- `feature_flag.context.id`: Context canonical key
75+
- `feature_flag.result.value`: The flag value (since `IncludeValue` is enabled)
9276

93-
## How It Works
77+
### Custom OTLP Endpoint
9478

95-
### OpenTelemetry Setup
79+
To send traces to a different OpenTelemetry collector, set the `LD_OTEL_ENDPOINT` environment variable:
9680

97-
```cpp
98-
void InitTracer() {
99-
opentelemetry::exporter::otlp::OtlpHttpExporterOptions opts;
100-
opts.url = "http://localhost:4318/v1/traces";
101-
102-
auto exporter = opentelemetry::exporter::otlp::OtlpHttpExporterFactory::Create(opts);
103-
auto processor = trace_sdk::SimpleSpanProcessorFactory::Create(std::move(exporter));
104-
std::shared_ptr<trace_api::TracerProvider> provider =
105-
trace_sdk::TracerProviderFactory::Create(std::move(processor));
106-
trace_api::Provider::SetTracerProvider(provider);
107-
}
108-
```
109-
110-
### LaunchDarkly Hook Setup
111-
112-
```cpp
113-
auto hook_options = launchdarkly::server_side::integrations::otel::TracingHookOptionsBuilder()
114-
.IncludeValue(true) // Include flag values in traces
115-
.CreateSpans(false) // Only create span events, not full spans
116-
.Build();
117-
auto tracing_hook = std::make_shared<launchdarkly::server_side::integrations::otel::TracingHook>(hook_options);
118-
119-
auto config = launchdarkly::server_side::ConfigBuilder(sdk_key)
120-
.Hooks(tracing_hook)
121-
.Build();
122-
```
123-
124-
### Passing Parent Span Context
125-
126-
When using async frameworks like Boost.Beast, you need to manually pass the parent span:
127-
128-
```cpp
129-
auto span = tracer->StartSpan("HTTP GET /weather");
130-
auto scope = trace_api::Scope(span);
131-
132-
// Create hook context with the span
133-
auto hook_ctx = launchdarkly::server_side::integrations::otel::MakeHookContextWithSpan(span);
134-
135-
// Pass it to the evaluation
136-
auto flag_value = ld_client->BoolVariation(context, "my-flag", false, hook_ctx);
81+
```bash
82+
export LD_OTEL_ENDPOINT=http://localhost:4318/v1/traces
83+
./build/examples/hello-cpp-server-otel/hello-cpp-server-otel
13784
```
13885

139-
This ensures feature flag events appear as children of the correct span.
86+
Note: The `/v1/traces` path is automatically appended to the endpoint.
14087

14188
## What You'll See
14289

@@ -146,23 +93,20 @@ This ensures feature flag events appear as children of the correct span.
14693
*** SDK successfully initialized!
14794
14895
*** Weather server running on http://0.0.0.0:8080
96+
*** Try: curl http://localhost:8080/weather
97+
*** OpenTelemetry tracing enabled, sending traces to LaunchDarkly
98+
*** LaunchDarkly integration enabled with OpenTelemetry tracing hook
14999
```
150100

151-
### In Your Traces
101+
### In LaunchDarkly Observability
152102

153-
Each HTTP request will have:
103+
Navigate to your LaunchDarkly project's Observability section to view traces. Each HTTP request will have:
154104
1. **Root Span**: "HTTP GET /weather" with HTTP attributes
155-
2. **Span Event**: "feature_flag" with LaunchDarkly evaluation details
156-
157-
Example trace structure:
158-
```
159-
HTTP GET /weather (span)
160-
└─ feature_flag (event)
161-
├─ feature_flag.key: "show-detailed-weather"
162-
├─ feature_flag.provider.name: "LaunchDarkly"
163-
├─ feature_flag.context.id: "user:weather-api-user"
164-
└─ feature_flag.result.value: "true"
165-
```
105+
2. **Feature Flag Event**: Attached to the span with evaluation details:
106+
- `feature_flag.key`: "show-detailed-weather"
107+
- `feature_flag.provider.name`: "LaunchDarkly"
108+
- `feature_flag.context.id`: "user:weather-api-user"
109+
- `feature_flag.result.value`: The evaluated flag value
166110

167111
## Customization
168112

examples/hello-cpp-server-otel/main.cpp

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <opentelemetry/exporters/otlp/otlp_http_exporter_factory.h>
1313
#include <opentelemetry/exporters/otlp/otlp_http_exporter_options.h>
14+
#include <opentelemetry/sdk/resource/resource.h>
1415
#include <opentelemetry/sdk/trace/simple_processor_factory.h>
1516
#include <opentelemetry/sdk/trace/tracer_provider_factory.h>
1617
#include <opentelemetry/trace/provider.h>
@@ -46,16 +47,29 @@ namespace trace_sdk = opentelemetry::sdk::trace;
4647
namespace nostd = opentelemetry::nostd;
4748

4849
// Initialize OpenTelemetry
49-
void InitTracer() {
50+
void InitTracer(char const* sdk_key) {
5051
opentelemetry::exporter::otlp::OtlpHttpExporterOptions opts;
51-
opts.url = "http://localhost:4318/v1/traces";
52+
53+
// Check for custom endpoint from environment variable
54+
if (char const* custom_endpoint = std::getenv("LD_OTEL_ENDPOINT");
55+
custom_endpoint && strlen(custom_endpoint)) {
56+
opts.url = std::string(custom_endpoint);
57+
} else {
58+
opts.url = "https://otel.observability.app.launchdarkly.com:4318/v1/traces";
59+
}
60+
61+
// Create resource with highlight.project_id attribute
62+
auto resource_attributes = opentelemetry::sdk::resource::ResourceAttributes{
63+
{"highlight.project_id", sdk_key}
64+
};
65+
auto resource = opentelemetry::sdk::resource::Resource::Create(resource_attributes);
5266

5367
auto exporter =
5468
opentelemetry::exporter::otlp::OtlpHttpExporterFactory::Create(opts);
5569
auto processor = trace_sdk::SimpleSpanProcessorFactory::Create(
5670
std::move(exporter));
5771
const std::shared_ptr<trace_api::TracerProvider> provider =
58-
trace_sdk::TracerProviderFactory::Create(std::move(processor));
72+
trace_sdk::TracerProviderFactory::Create(std::move(processor), resource);
5973
trace_api::Provider::SetTracerProvider(provider);
6074
}
6175

@@ -253,9 +267,6 @@ class listener : public std::enable_shared_from_this<listener> {
253267
};
254268

255269
int main() {
256-
// Initialize OpenTelemetry
257-
InitTracer();
258-
259270
// Initialize LaunchDarkly
260271
char const* sdk_key = get_with_env_fallback(
261272
SDK_KEY, "LD_SDK_KEY",
@@ -264,6 +275,9 @@ int main() {
264275
"variable.\n"
265276
"The value of SDK_KEY in main.cpp takes priority over LD_SDK_KEY.");
266277

278+
// Initialize OpenTelemetry
279+
InitTracer(sdk_key);
280+
267281
// Create the OpenTelemetry tracing hook using builder pattern
268282
auto hook_options =
269283
launchdarkly::server_side::integrations::otel::TracingHookOptionsBuilder()
@@ -315,7 +329,7 @@ int main() {
315329
std::cout << "*** Weather server running on http://0.0.0.0:8080\n";
316330
std::cout << "*** Try: curl http://localhost:8080/weather\n";
317331
std::cout <<
318-
"*** OpenTelemetry tracing enabled (OTLP HTTP to localhost:4318)\n";
332+
"*** OpenTelemetry tracing enabled, sending traces to LaunchDarkly\n";
319333
std::cout <<
320334
"*** LaunchDarkly integration enabled with OpenTelemetry tracing hook\n\n";
321335

0 commit comments

Comments
 (0)