Skip to content

Commit 504f6ba

Browse files
authored
Merge pull request #819 from open-telemetry/main
[SDK] Enable W3C Trace Context v2 (open-telemetry#4012)
2 parents 0568e89 + 99300d1 commit 504f6ba

6 files changed

Lines changed: 149 additions & 2 deletions

File tree

.github/workflows/ci.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,3 +1243,47 @@ jobs:
12431243
|
12441244
python ${GITHUB_WORKSPACE}/trace-context/test/test.py http://localhost:30000/test TraceContextTest AdvancedTest
12451245
curl http://localhost:30000/stop
1246+
1247+
w3c_trace_context_compliance_v2:
1248+
name: W3C Distributed Tracing Validation V2
1249+
runs-on: ubuntu-latest
1250+
steps:
1251+
- name: Harden the runner (Audit all outbound calls)
1252+
uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0
1253+
with:
1254+
egress-policy: audit
1255+
1256+
- name: Checkout open-telemetry/opentelemetry-cpp
1257+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
1258+
with:
1259+
submodules: 'recursive'
1260+
- name: setup
1261+
env:
1262+
CC: /usr/bin/gcc-12
1263+
CXX: /usr/bin/g++-12
1264+
run: |
1265+
sudo -E ./ci/setup_ci_environment.sh
1266+
sudo -E apt-get install -y zlib1g-dev libcurl4-openssl-dev
1267+
- name: run w3c trace-context test server (background)
1268+
env:
1269+
CXX_STANDARD: '17'
1270+
run: |
1271+
./ci/do_ci.sh cmake.w3c.trace-context.build-server
1272+
cd $HOME/build/ext/test/w3c_tracecontext_http_test_server
1273+
./w3c_tracecontext_http_test_server &
1274+
- name: Checkout w3c/trace-context repo
1275+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
1276+
with:
1277+
repository: w3c/trace-context
1278+
path: trace-context
1279+
- name: install dependencies
1280+
run: |
1281+
sudo apt update && sudo apt install python3-pip
1282+
sudo pip3 install aiohttp==3.11.18
1283+
- name: run w3c trace-context test suite
1284+
env:
1285+
SPEC_LEVEL: 2
1286+
run:
1287+
|
1288+
python ${GITHUB_WORKSPACE}/trace-context/test/test.py http://localhost:30000/test TraceContext2Test
1289+
curl http://localhost:30000/stop

api/test/trace/propagation/http_text_format_test.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,27 @@ TEST(TextMapPropagatorTest, TraceFlagsBufferGeneration)
6161
EXPECT_EQ(MapHttpTraceContext::TraceFlagsFromHex("00"), trace::TraceFlags());
6262
}
6363

64+
TEST(TextMapPropagatorTest, PropagateRandomTraceFlag)
65+
{
66+
TextMapCarrierTest carrier;
67+
// Only random bit is set(02), sampled bit is not set
68+
auto const traceparent = "00-4bf92f3577b34da6a3ce929d0e0e4736-0102030405060708-02";
69+
// Use a remote traceparent with only the random bit set and verify the
70+
// propagator round-trips it without forcing the sampled bit on.
71+
carrier.headers_ = {{"traceparent", traceparent}};
72+
auto ctx1 = context::Context{};
73+
auto ctx2 = format.Extract(carrier, ctx1);
74+
75+
auto span = trace::GetSpan(ctx2);
76+
EXPECT_TRUE(span->GetContext().IsValid());
77+
EXPECT_FALSE(span->GetContext().IsSampled()); // Sampled bit is not set
78+
EXPECT_TRUE(span->GetContext().trace_flags().IsRandom()); // Random bit is set
79+
80+
TextMapCarrierTest carrier2;
81+
format.Inject(carrier2, ctx2);
82+
EXPECT_EQ(carrier2.headers_["traceparent"], traceparent);
83+
}
84+
6485
TEST(TextMapPropagatorTest, NoSendEmptyTraceState)
6586
{
6687
// If the trace state is empty, do not set the header.

api/test/trace/trace_flags_test.cc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,28 @@ TEST(TraceFlagsTest, Sampled)
4040
EXPECT_EQ(1, buf[0]);
4141
}
4242

43+
TEST(TraceFlagsTest, Random)
44+
{
45+
TraceFlags flags{TraceFlags::kIsRandom};
46+
EXPECT_TRUE(flags.IsRandom());
47+
EXPECT_EQ(2, flags.flags());
48+
EXPECT_EQ("02", Hex(flags));
49+
50+
uint8_t buf[1];
51+
flags.CopyBytesTo(buf);
52+
EXPECT_EQ(2, buf[0]);
53+
}
54+
55+
TEST(TraceFlagsTest, Both)
56+
{
57+
TraceFlags flags{TraceFlags::kIsSampled | TraceFlags::kIsRandom};
58+
EXPECT_TRUE(flags.IsSampled());
59+
EXPECT_TRUE(flags.IsRandom());
60+
EXPECT_EQ(3, flags.flags());
61+
EXPECT_EQ("03", Hex(flags));
62+
63+
uint8_t buf[1];
64+
flags.CopyBytesTo(buf);
65+
EXPECT_EQ(3, buf[0]);
66+
}
4367
} // namespace

exporters/otlp/test/otlp_recordable_test.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,19 @@ TEST(OtlpRecordable, SetStatus)
188188
EXPECT_EQ(rec2.span().status().message(), "");
189189
}
190190

191+
TEST(OtlpRecordable, SetTraceFlags)
192+
{
193+
OtlpRecordable rec;
194+
// OTLP stores the W3C trace-flags bits on the exported span so downstream
195+
// processors can observe the random flag.
196+
trace_api::TraceFlags flags{
197+
static_cast<uint8_t>(trace_api::TraceFlags::kIsSampled | trace_api::TraceFlags::kIsRandom)};
198+
199+
rec.SetTraceFlags(flags);
200+
201+
EXPECT_EQ(rec.span().flags(), static_cast<uint32_t>(flags.flags()));
202+
}
203+
191204
TEST(OtlpRecordable, AddEventDefault)
192205
{
193206
OtlpRecordable rec;

sdk/src/trace/tracer.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,13 +130,13 @@ nostd::shared_ptr<opentelemetry::trace::Span> Tracer::StartSpan(
130130
flags &= ~opentelemetry::trace::TraceFlags::kIsSampled;
131131
}
132132

133-
#if 1
133+
#if 0
134134
/* https://github.com/open-telemetry/opentelemetry-specification as of v1.29.0 */
135135
/* Support W3C Trace Context version 1. */
136136
flags &= opentelemetry::trace::TraceFlags::kAllW3CTraceContext1Flags;
137137
#endif
138138

139-
#if 0
139+
#if 1
140140
/* Waiting for https://github.com/open-telemetry/opentelemetry-specification/issues/3411 */
141141
/* Support W3C Trace Context version 2. */
142142
flags &= opentelemetry::trace::TraceFlags::kAllW3CTraceContext2Flags;

sdk/test/trace/tracer_test.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "opentelemetry/trace/span_id.h"
5151
#include "opentelemetry/trace/span_metadata.h"
5252
#include "opentelemetry/trace/span_startoptions.h"
53+
#include "opentelemetry/trace/trace_flags.h"
5354
#include "opentelemetry/trace/trace_id.h"
5455
#include "opentelemetry/trace/trace_state.h"
5556
#include "opentelemetry/trace/tracer.h"
@@ -274,6 +275,50 @@ TEST(Tracer, StartSpanSampleOff)
274275
ASSERT_EQ(0, span_data->GetSpans().size());
275276
}
276277

278+
TEST(Tracer, StartSpanSetsRandomTraceFlagForRootSpan)
279+
{
280+
InMemorySpanExporter *exporter = new InMemorySpanExporter();
281+
// AlwaysOn keeps the sampled bit set while RandomIdGenerator marks the
282+
// locally generated trace-id as random.
283+
auto tracer = initTracer(std::unique_ptr<SpanExporter>{exporter}, new AlwaysOnSampler(),
284+
new RandomIdGenerator());
285+
286+
auto span = tracer->StartSpan("span 1");
287+
auto context = span->GetContext();
288+
289+
EXPECT_TRUE(context.IsValid());
290+
EXPECT_TRUE(context.IsSampled());
291+
EXPECT_TRUE(context.trace_flags().IsRandom());
292+
EXPECT_EQ(context.trace_flags().flags(),
293+
trace_api::TraceFlags::kIsSampled | trace_api::TraceFlags::kIsRandom);
294+
}
295+
296+
TEST(Tracer, StartSpanPreservesRandomTraceFlagFromParent)
297+
{
298+
InMemorySpanExporter *exporter = new InMemorySpanExporter();
299+
constexpr uint8_t parent_span_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8};
300+
constexpr uint8_t parent_trace_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1};
301+
// Build a remote parent with only the random bit set so the child path can
302+
// prove it preserves the incoming signal even when sampling is turned off.
303+
trace_api::SpanContext parent_context{
304+
trace_api::TraceId{parent_trace_id_buf}, trace_api::SpanId{parent_span_id_buf},
305+
trace_api::TraceFlags{trace_api::TraceFlags::kIsRandom}, true};
306+
307+
auto tracer = initTracer(std::unique_ptr<SpanExporter>{exporter}, new AlwaysOffSampler());
308+
309+
trace_api::StartSpanOptions options;
310+
options.parent = parent_context;
311+
312+
auto span = tracer->StartSpan("span 1", options);
313+
auto context = span->GetContext();
314+
315+
EXPECT_TRUE(context.IsValid());
316+
EXPECT_FALSE(context.IsSampled());
317+
EXPECT_TRUE(context.trace_flags().IsRandom());
318+
EXPECT_EQ(context.trace_flags().flags(), static_cast<uint8_t>(trace_api::TraceFlags::kIsRandom));
319+
EXPECT_EQ(context.trace_id(), parent_context.trace_id());
320+
}
321+
277322
TEST(Tracer, StartSpanCustomIdGenerator)
278323
{
279324
IdGenerator *id_generator = new MockIdGenerator();

0 commit comments

Comments
 (0)