From 84e38b215f7b166e80353ff2c60836d9d375861d Mon Sep 17 00:00:00 2001 From: karusher Date: Mon, 26 Feb 2024 16:31:09 -0500 Subject: [PATCH] Provide span for opentracing shim inject (#2535) The various propagation injectors will attempt to retrieve the Span from the context that is provided in the api call: void Inject(context::propagation::TextMapCarrier &carrier, const context::Context &context) noexcept override The shim will provide the RuntimeContext augmented with the baggage to the Inject method. Unfortunately, the shim does not add the span to the RuntimeContext. It does not instantiate a Scope. This commit adds a Span to the context passed into Inject by modifying the shim. --- opentracing-shim/src/tracer_shim.cc | 17 +++- opentracing-shim/test/shim_mocks.h | 30 ++++++++ opentracing-shim/test/tracer_shim_test.cc | 94 +++++++++++++++++++++++ 3 files changed, 137 insertions(+), 4 deletions(-) diff --git a/opentracing-shim/src/tracer_shim.cc b/opentracing-shim/src/tracer_shim.cc index 41c6404624..7848cba478 100644 --- a/opentracing-shim/src/tracer_shim.cc +++ b/opentracing-shim/src/tracer_shim.cc @@ -124,12 +124,21 @@ opentracing::expected TracerShim::injectImpl(const opentracing::SpanContex if (auto context_shim = SpanContextShim::extractFrom(&sc)) { auto current_context = opentelemetry::context::RuntimeContext::GetCurrent(); - // It MUST inject any non-empty Baggage even amidst no valid SpanContext. - const auto &context = - opentelemetry::baggage::SetBaggage(current_context, context_shim->baggage()); + + // Inject dummy span to provide SpanContext information + auto span_context = opentelemetry::trace::SpanContext( + context_shim->context().trace_id(), context_shim->context().span_id(), + context_shim->context().trace_flags(), false); + opentelemetry::nostd::shared_ptr sp{ + new opentelemetry::trace::DefaultSpan(span_context)}; + auto context_with_span = opentelemetry::trace::SetSpan(current_context, sp); + + // Inject any non-empty Baggage + const auto &context_with_span_baggage = + opentelemetry::baggage::SetBaggage(context_with_span, context_shim->baggage()); CarrierWriterShim carrier{writer}; - propagator->Inject(carrier, context); + propagator->Inject(carrier, context_with_span_baggage); return opentracing::make_expected(); } diff --git a/opentracing-shim/test/shim_mocks.h b/opentracing-shim/test/shim_mocks.h index 555f6cb65b..b39b2b3781 100644 --- a/opentracing-shim/test/shim_mocks.h +++ b/opentracing-shim/test/shim_mocks.h @@ -117,6 +117,18 @@ struct MockTracerProvider final : public trace_api::TracerProvider struct MockPropagator : public context::propagation::TextMapPropagator { + static constexpr const char *kTraceIdKey = "trace_id"; + static constexpr const char *kSpanIdKey = "span_id"; + static constexpr const char *kTraceFlagsKey = "trace_flags_id"; + + template + static inline std::string ToLowerBase16(const T &id) + { + char buf[N] = {0}; + id.ToLowerBase16(buf); + return std::string(buf, sizeof(buf)); + } + // Returns the context that is stored in the carrier with the TextMapCarrier as extractor. context::Context Extract(const context::propagation::TextMapCarrier &carrier, context::Context &context) noexcept override @@ -140,6 +152,24 @@ struct MockPropagator : public context::propagation::TextMapPropagator carrier.Set(k, v); return true; }); + + auto span_key_value = context.GetValue(trace_api::kSpanKey); + if (nostd::holds_alternative>(span_key_value)) + { + auto span = nostd::get>(span_key_value); + if (span) + { + // Store span context information in TextMapCarrier to allow verifying propagation + auto span_context = span->GetContext(); + carrier.Set(kTraceIdKey, ToLowerBase16( + span_context.trace_id())); + carrier.Set(kSpanIdKey, ToLowerBase16( + span_context.span_id())); + carrier.Set(kTraceFlagsKey, + ToLowerBase16(span_context.trace_flags())); + } + } + is_injected = true; } diff --git a/opentracing-shim/test/tracer_shim_test.cc b/opentracing-shim/test/tracer_shim_test.cc index 9d57bbcae4..2d3f2618da 100644 --- a/opentracing-shim/test/tracer_shim_test.cc +++ b/opentracing-shim/test/tracer_shim_test.cc @@ -218,3 +218,97 @@ TEST_F(TracerShimTest, ExtractOnlyBaggage) ASSERT_TRUE(span_context_shim->BaggageItem("foo", value)); ASSERT_EQ(value, "bar"); } + +class TracerWithSpanContext : public trace_api::Tracer +{ +public: + nostd::shared_ptr StartSpan( + nostd::string_view name, + const common::KeyValueIterable & /* attributes */, + const trace_api::SpanContextKeyValueIterable & /* links */, + const trace_api::StartSpanOptions & /* options */) noexcept override + { + constexpr uint8_t trace_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8}; + trace_api::TraceId trace_id(trace_id_buf); + + constexpr uint8_t span_id_buf[] = {1, 2, 3, 4, 5, 6, 7, 8}; + trace_api::SpanId span_id(span_id_buf); + + auto span_context = trace_api::SpanContext(trace_id, span_id, GetTraceFlags(), false); + nostd::shared_ptr result(new trace_api::DefaultSpan(span_context)); + + return result; + } + + void ForceFlushWithMicroseconds(uint64_t /* timeout */) noexcept override {} + + void CloseWithMicroseconds(uint64_t /* timeout */) noexcept override {} + + static trace_api::TraceFlags GetTraceFlags() + { + return trace_api::TraceFlags(trace_api::TraceFlags::kIsSampled); + } +}; + +class TracerWithSpanContextProvider : public trace_api::TracerProvider +{ +public: + static nostd::shared_ptr Create() + { + nostd::shared_ptr result(new TracerWithSpanContextProvider()); + return result; + } + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + nostd::shared_ptr GetTracer( + nostd::string_view /* name */, + nostd::string_view /* version */, + nostd::string_view /* schema_url */, + const common::KeyValueIterable * /* attributes */) noexcept override + { + nostd::shared_ptr result(new TracerWithSpanContext()); + return result; + } +#else + nostd::shared_ptr GetTracer( + nostd::string_view /* name */, + nostd::string_view /* version */, + nostd::string_view /* schema_url */) noexcept override + { + nostd::shared_ptr result(new TracerWithSpanContext()); + return result; + } +#endif +}; + +TEST_F(TracerShimTest, InjectSpanKey) +{ + using context::propagation::TextMapPropagator; + + auto local_text_map_format = new MockPropagator(); + auto local_http_headers_format = new MockPropagator(); + + ASSERT_FALSE(local_text_map_format->is_injected); + ASSERT_FALSE(local_http_headers_format->is_injected); + + nostd::shared_ptr tracer_provider = + TracerWithSpanContextProvider::Create(); + auto local_tracer_shim = shim::TracerShim::createTracerShim( + tracer_provider, + {.text_map = nostd::shared_ptr(local_text_map_format), + .http_headers = nostd::shared_ptr(local_http_headers_format)}); + + std::unordered_map text_map; + auto span_shim = local_tracer_shim->StartSpan("a"); + local_tracer_shim->Inject(span_shim->context(), TextMapCarrier{text_map}); + + ASSERT_TRUE(local_text_map_format->is_injected); + ASSERT_FALSE(local_http_headers_format->is_injected); + + ASSERT_EQ(span_shim->context().ToTraceID(), text_map[MockPropagator::kTraceIdKey]); + ASSERT_EQ(span_shim->context().ToSpanID(), text_map[MockPropagator::kSpanIdKey]); + + char flag_buffer[2]; + TracerWithSpanContext::GetTraceFlags().ToLowerBase16(flag_buffer); + ASSERT_EQ(std::string(flag_buffer, 2), text_map[MockPropagator::kTraceFlagsKey]); +}