Skip to content

Commit bcacfec

Browse files
authored
[API] Preserve parent SpanContext in NoopTracer spans (#4717)
1 parent cdd17eb commit bcacfec

3 files changed

Lines changed: 108 additions & 2 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ Increment the:
2323
* [SDK] Fix disabled Tracer StartSpan noop behavior
2424
[#4710](https://github.com/open-telemetry/opentelemetry-cpp/pull/4710)
2525

26+
* [API] Preserve a valid parent SpanContext in spans returned by the no-op tracer.
27+
[#4711](https://github.com/open-telemetry/opentelemetry-cpp/issues/4711)
28+
2629
* [SDK] Preserve the default unlimited timeout when batch span and log record
2730
processors shut down their exporters.
2831
[#4686](https://github.com/open-telemetry/opentelemetry-cpp/pull/4686)

‎api/include/opentelemetry/trace/noop.h‎

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,19 @@
88
// used directly. Please refer to span.h and tracer.h for documentation on these interfaces.
99

1010
#include <cstdint>
11+
#include <memory>
1112
#include <utility>
1213

1314
#include "opentelemetry/common/attribute_value.h"
1415
#include "opentelemetry/common/key_value_iterable.h"
1516
#include "opentelemetry/common/timestamp.h"
1617
#include "opentelemetry/context/context_value.h"
18+
#include "opentelemetry/context/runtime_context.h"
1719
#include "opentelemetry/nostd/shared_ptr.h"
1820
#include "opentelemetry/nostd/string_view.h"
1921
#include "opentelemetry/nostd/unique_ptr.h"
22+
#include "opentelemetry/trace/context.h"
23+
#include "opentelemetry/trace/default_span.h"
2024
#include "opentelemetry/trace/span.h"
2125
#include "opentelemetry/trace/span_context.h"
2226
#include "opentelemetry/trace/span_context_kv_iterable.h"
@@ -102,9 +106,36 @@ class OPENTELEMETRY_EXPORT NoopTracer final : public Tracer,
102106
nostd::shared_ptr<Span> StartSpan(nostd::string_view /*name*/,
103107
const common::KeyValueIterable & /*attributes*/,
104108
const SpanContextKeyValueIterable & /*links*/,
105-
const StartSpanOptions & /*options*/) noexcept override
109+
const StartSpanOptions &options) noexcept override
106110
{
107-
return noop_span_;
111+
const auto parent_context = [&options]() noexcept -> SpanContext {
112+
if (const auto *span_context = nostd::get_if<SpanContext>(&options.parent))
113+
{
114+
if (span_context->IsValid())
115+
{
116+
return *span_context;
117+
}
118+
}
119+
else if (const auto *context = nostd::get_if<context::Context>(&options.parent))
120+
{
121+
auto ctx_span_context = GetSpanContext(*context);
122+
if (ctx_span_context.IsValid())
123+
{
124+
return ctx_span_context;
125+
}
126+
if (IsRootSpan(*context))
127+
{
128+
return SpanContext::GetInvalid();
129+
}
130+
}
131+
132+
return GetSpanContext(context::RuntimeContext::GetCurrent());
133+
}();
134+
if (!parent_context.IsValid())
135+
{
136+
return noop_span_;
137+
}
138+
return nostd::shared_ptr<Span>{std::make_shared<DefaultSpan>(parent_context)};
108139
}
109140

110141
#if OPENTELEMETRY_ABI_VERSION_NO == 1

‎api/test/trace/noop_test.cc‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,21 @@
1010
#include <vector>
1111

1212
#include "opentelemetry/common/timestamp.h"
13+
#include "opentelemetry/context/context.h"
1314
#include "opentelemetry/nostd/shared_ptr.h"
1415
#include "opentelemetry/nostd/span.h"
16+
#include "opentelemetry/nostd/string_view.h"
1517
#include "opentelemetry/nostd/unique_ptr.h"
1618
#include "opentelemetry/nostd/utility.h"
19+
#include "opentelemetry/nostd/variant.h"
20+
#include "opentelemetry/trace/context.h"
1721
#include "opentelemetry/trace/noop.h"
22+
#include "opentelemetry/trace/scope.h"
1823
#include "opentelemetry/trace/span.h"
1924
#include "opentelemetry/trace/span_context.h"
2025
#include "opentelemetry/trace/span_id.h"
2126
#include "opentelemetry/trace/span_metadata.h"
27+
#include "opentelemetry/trace/span_startoptions.h"
2228
#include "opentelemetry/trace/trace_flags.h"
2329
#include "opentelemetry/trace/trace_id.h"
2430
#include "opentelemetry/trace/tracer.h"
@@ -107,3 +113,69 @@ TEST(NoopTest, CreateSpanValidSpanContext)
107113
s1->AddEvent("even1"); // noop
108114
s1->End(); // noop
109115
}
116+
117+
TEST(NoopTest, StartSpanPreservesParentSpanContext)
118+
{
119+
constexpr uint8_t buf_span[] = {1, 2, 3, 4, 5, 6, 7, 8};
120+
constexpr uint8_t buf_trace[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
121+
const trace_api::SpanContext parent{trace_api::TraceId{buf_trace}, trace_api::SpanId{buf_span},
122+
trace_api::TraceFlags{true}, false};
123+
std::shared_ptr<trace_api::Tracer> tracer{new trace_api::NoopTracer()};
124+
125+
trace_api::StartSpanOptions parent_options;
126+
parent_options.parent = parent;
127+
auto child = tracer->StartSpan("child", parent_options);
128+
EXPECT_FALSE(child->IsRecording());
129+
EXPECT_EQ(child->GetContext(), parent);
130+
131+
auto parent_span = nonstd::shared_ptr<trace_api::Span>(new trace_api::NoopSpan(
132+
nonstd::unique_ptr<trace_api::SpanContext>(new trace_api::SpanContext(parent))));
133+
opentelemetry::context::Context context;
134+
context = trace_api::SetSpan(context, parent_span);
135+
136+
trace_api::StartSpanOptions context_options;
137+
context_options.parent = context;
138+
auto child_from_context = tracer->StartSpan("child", context_options);
139+
EXPECT_FALSE(child_from_context->IsRecording());
140+
EXPECT_EQ(child_from_context->GetContext(), parent);
141+
142+
auto context_with_span_context = opentelemetry::context::Context{
143+
trace_api::kSpanKey,
144+
nonstd::shared_ptr<trace_api::SpanContext>(new trace_api::SpanContext(parent))};
145+
context_options.parent = context_with_span_context;
146+
auto child_from_stored_span_context = tracer->StartSpan("child", context_options);
147+
EXPECT_FALSE(child_from_stored_span_context->IsRecording());
148+
EXPECT_EQ(child_from_stored_span_context->GetContext(), parent);
149+
150+
{
151+
trace_api::Scope scope(parent_span);
152+
auto child_from_current_context = tracer->StartSpan("child");
153+
EXPECT_FALSE(child_from_current_context->IsRecording());
154+
EXPECT_EQ(child_from_current_context->GetContext(), parent);
155+
156+
trace_api::StartSpanOptions invalid_span_context_options;
157+
invalid_span_context_options.parent = trace_api::SpanContext::GetInvalid();
158+
auto child_from_invalid_span_context = tracer->StartSpan("child", invalid_span_context_options);
159+
EXPECT_FALSE(child_from_invalid_span_context->IsRecording());
160+
EXPECT_EQ(child_from_invalid_span_context->GetContext(), parent);
161+
162+
trace_api::StartSpanOptions empty_context_options;
163+
empty_context_options.parent = opentelemetry::context::Context{};
164+
auto child_from_empty_context = tracer->StartSpan("child", empty_context_options);
165+
EXPECT_FALSE(child_from_empty_context->IsRecording());
166+
EXPECT_EQ(child_from_empty_context->GetContext(), parent);
167+
168+
trace_api::StartSpanOptions invalid_span_options;
169+
invalid_span_options.parent = opentelemetry::context::Context{
170+
trace_api::kSpanKey, nonstd::shared_ptr<trace_api::Span>(new trace_api::NoopSpan())};
171+
auto child_from_invalid_span = tracer->StartSpan("child", invalid_span_options);
172+
EXPECT_FALSE(child_from_invalid_span->IsRecording());
173+
EXPECT_EQ(child_from_invalid_span->GetContext(), parent);
174+
175+
trace_api::StartSpanOptions root_context_options;
176+
root_context_options.parent = opentelemetry::context::Context{trace_api::kIsRootSpanKey, true};
177+
auto root_child = tracer->StartSpan("root", root_context_options);
178+
EXPECT_FALSE(root_child->IsRecording());
179+
EXPECT_FALSE(root_child->GetContext().IsValid());
180+
}
181+
}

0 commit comments

Comments
 (0)