diff --git a/CMakeLists.txt b/CMakeLists.txt index 046f347c..3bb07327 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -217,6 +217,8 @@ set(LIGHTSTEP_SRCS src/common/utility.cpp src/tracer/propagation/baggage_propagator.cpp src/tracer/propagation/binary_propagation.cpp src/tracer/propagation/envoy_propagator.cpp + src/tracer/propagation/trace_context.cpp + src/tracer/propagation/trace_context_propagator.cpp src/tracer/propagation/lightstep_propagator.cpp src/tracer/propagation/multiheader_propagator.cpp src/tracer/propagation/propagation.cpp @@ -231,6 +233,7 @@ set(LIGHTSTEP_SRCS src/common/utility.cpp src/tracer/tracer_impl.cpp src/tracer/tracer.cpp src/tracer/tag.cpp + src/tracer/utility.cpp ) if (WIN32) diff --git a/include/lightstep/tracer.h b/include/lightstep/tracer.h index 686b6e84..b77070b0 100644 --- a/include/lightstep/tracer.h +++ b/include/lightstep/tracer.h @@ -22,7 +22,12 @@ const std::string& CollectorMethodName(); enum class LogLevel { debug = 1, info = 2, warn = 3, error = 4, off = 6 }; // Denotes different span context propagation formats. -enum class PropagationMode { lightstep = 1, b3 = 2, envoy = 3 }; +enum class PropagationMode { + lightstep = 1, + b3 = 2, + envoy = 3, + trace_context = 4 +}; // DynamicConfigurationValue is used for configuration values that can // be either fixed or changed at runtime. To specify a fixed value, just assign diff --git a/lightstep-tracer-configuration/tracer_configuration.schema.json b/lightstep-tracer-configuration/tracer_configuration.schema.json index 43d53085..a1210bf2 100644 --- a/lightstep-tracer-configuration/tracer_configuration.schema.json +++ b/lightstep-tracer-configuration/tracer_configuration.schema.json @@ -79,7 +79,7 @@ "items": { "type": "string" }, - "description": "A list of propagation modes to use for injection/extraction.\nValid values are \"lightstep\", \"b3\", and \"envoy\"." + "description": "A list of propagation modes to use for injection/extraction.\nValid values are \"lightstep\", \"b3\", \"envoy\", and \"trace_context\"." } }, "definitions": { diff --git a/src/common/hex_conversion.cpp b/src/common/hex_conversion.cpp index 2ea3b456..9da58553 100644 --- a/src/common/hex_conversion.cpp +++ b/src/common/hex_conversion.cpp @@ -1,37 +1,46 @@ #include "common/hex_conversion.h" +#include + namespace lightstep { +//-------------------------------------------------------------------------------------------------- +// Nil +//-------------------------------------------------------------------------------------------------- +static const unsigned char Nil = std::numeric_limits::max(); + +//-------------------------------------------------------------------------------------------------- +// HexTable +//-------------------------------------------------------------------------------------------------- +static const std::array HexTable = { + {Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, 10, 11, 12, 13, 14, 15, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, 10, 11, 12, 13, 14, 15, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil, + Nil}}; //-------------------------------------------------------------------------------------------------- -// HexToUint64Impl -//-------------------------------------------------------------------------------------------------- -static opentracing::expected HexToUint64Impl( - const char* i, const char* last) noexcept { - static const unsigned char nil = std::numeric_limits::max(); - static const std::array hextable = { - {nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, nil, nil, nil, nil, nil, nil, nil, 10, 11, 12, 13, 14, - 15, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 10, - 11, 12, 13, 14, 15, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, - nil, nil, nil, nil}}; - uint64_t result = 0; +// HexToUintImpl +//-------------------------------------------------------------------------------------------------- +template +static opentracing::expected HexToUintImpl(const char* i, + const char* last) noexcept { + T result = 0; for (; i != last; ++i) { - auto value = hextable[*i]; - if (value == nil) { + auto value = HexTable[*i]; + if (value == Nil) { return opentracing::make_unexpected( std::make_error_code(std::errc::invalid_argument)); } @@ -114,7 +123,7 @@ opentracing::expected HexToUint64( std::make_error_code(std::errc::invalid_argument)); } - return HexToUint64Impl(i, last); + return HexToUintImpl(i, last); } //-------------------------------------------------------------------------------------------------- @@ -158,7 +167,7 @@ opentracing::expected HexToUint128(opentracing::string_view s, // handle the case when we have a number that fits in a 64-bit integer if (length <= Num64BitHexDigits) { x_high = 0; - auto x_maybe = HexToUint64Impl(i, last); + auto x_maybe = HexToUintImpl(i, last); if (!x_maybe) { return opentracing::make_unexpected(x_maybe.error()); } @@ -169,17 +178,57 @@ opentracing::expected HexToUint128(opentracing::string_view s, // handle the case when we have a number that requires more than a single // 64-bit integer auto boundary = i + (length - Num64BitHexDigits); - auto x_high_maybe = HexToUint64Impl(i, boundary); + auto x_high_maybe = HexToUintImpl(i, boundary); if (!x_high_maybe) { return opentracing::make_unexpected(x_high_maybe.error()); } x_high = *x_high_maybe; - auto x_low_maybe = HexToUint64Impl(boundary, last); + auto x_low_maybe = HexToUintImpl(boundary, last); if (!x_low_maybe) { return opentracing::make_unexpected(x_low_maybe.error()); } x_low = *x_low_maybe; return {}; } + +//-------------------------------------------------------------------------------------------------- +// NormalizedHexToUint8 +//-------------------------------------------------------------------------------------------------- +opentracing::expected NormalizedHexToUint8( + opentracing::string_view s) noexcept { + assert(s.size() == Num8BitHexDigits); + return HexToUintImpl(s.data(), s.data() + s.size()); +} + +//-------------------------------------------------------------------------------------------------- +// NormalizedHexToUint64 +//-------------------------------------------------------------------------------------------------- +opentracing::expected NormalizedHexToUint64( + opentracing::string_view s) noexcept { + assert(s.size() == Num64BitHexDigits); + return HexToUintImpl(s.data(), s.data() + s.size()); +} + +//-------------------------------------------------------------------------------------------------- +// NormalizedHexToUint128 +//-------------------------------------------------------------------------------------------------- +opentracing::expected NormalizedHexToUint128(opentracing::string_view s, + uint64_t& x_high, + uint64_t& x_low) noexcept { + assert(s.size() == 2 * Num64BitHexDigits); + auto x_maybe = + HexToUintImpl(s.data(), s.data() + Num64BitHexDigits); + if (!x_maybe) { + return opentracing::make_unexpected(x_maybe.error()); + } + x_high = *x_maybe; + x_maybe = HexToUintImpl(s.data() + Num64BitHexDigits, + s.data() + 2 * Num64BitHexDigits); + if (!x_maybe) { + return opentracing::make_unexpected(x_maybe.error()); + } + x_low = *x_maybe; + return {}; +} } // namespace lightstep diff --git a/src/common/hex_conversion.h b/src/common/hex_conversion.h index cd3e4520..9d6625e1 100644 --- a/src/common/hex_conversion.h +++ b/src/common/hex_conversion.h @@ -10,8 +10,12 @@ namespace lightstep { const size_t Num64BitHexDigits = std::numeric_limits::digits / 4; +const size_t Num128BitHexDigits = 2 * Num64BitHexDigits; + const size_t Num32BitHexDigits = std::numeric_limits::digits / 4; +const size_t Num8BitHexDigits = std::numeric_limits::digits / 4; + extern const unsigned char HexDigitLookupTable[513]; /** @@ -46,6 +50,19 @@ inline opentracing::string_view Uint32ToHex(uint32_t x, char* output) noexcept { return {output, Num32BitHexDigits}; } +/** + * Writes a 8-bit number in hex. + * @param x the number to write + * @param output where to output the number + * @return x as a hex string + */ +inline opentracing::string_view Uint8ToHex(uint8_t x, char* output) noexcept { + auto lookup_index = static_cast(x) * 2; + output[0] = HexDigitLookupTable[lookup_index]; + output[1] = HexDigitLookupTable[lookup_index + 1]; + return {output, Num8BitHexDigits}; +} + // Converts a hexidecimal number to a 64-bit integer. Either returns the number // or an error code. opentracing::expected HexToUint64( @@ -55,6 +72,16 @@ opentracing::expected HexToUint128(opentracing::string_view s, uint64_t& x_high, uint64_t& x_low) noexcept; +opentracing::expected NormalizedHexToUint8( + opentracing::string_view s) noexcept; + +opentracing::expected NormalizedHexToUint64( + opentracing::string_view s) noexcept; + +opentracing::expected NormalizedHexToUint128(opentracing::string_view s, + uint64_t& x_high, + uint64_t& x_low) noexcept; + /** * Serialize integers to hex */ diff --git a/src/tracer/BUILD b/src/tracer/BUILD index e4d023ad..8db48d8f 100644 --- a/src/tracer/BUILD +++ b/src/tracer/BUILD @@ -36,6 +36,9 @@ lightstep_cc_library( private_hdrs = [ "utility.h", ], + srcs = [ + "utility.cpp", + ], deps = [ "//src/recorder:recorder_interface", ], @@ -77,6 +80,7 @@ lightstep_cc_library( ], deps = [ "//src/tracer:lightstep_span_context_interface", + "//src/tracer/propagation:trace_context_lib", ], ) diff --git a/src/tracer/immutable_span_context.cpp b/src/tracer/immutable_span_context.cpp index d9ec21b7..ddea0eb4 100644 --- a/src/tracer/immutable_span_context.cpp +++ b/src/tracer/immutable_span_context.cpp @@ -7,10 +7,8 @@ namespace lightstep { ImmutableSpanContext::ImmutableSpanContext( uint64_t trace_id_high, uint64_t trace_id_low, uint64_t span_id, bool sampled, const std::unordered_map& baggage) - : trace_id_high_{trace_id_high}, - trace_id_low_{trace_id_low}, - span_id_{span_id}, - sampled_{sampled} { + : ImmutableSpanContext{trace_id_high, trace_id_low, span_id, sampled, + BaggageProtobufMap{}} { for (auto& baggage_item : baggage) { baggage_.insert(BaggageProtobufMap::value_type(baggage_item.first, baggage_item.second)); @@ -28,7 +26,17 @@ ImmutableSpanContext::ImmutableSpanContext( : trace_id_high_{trace_id_high}, trace_id_low_{trace_id_low}, span_id_{span_id}, - sampled_{sampled}, + trace_flags_{SetTraceFlag(0, sampled)}, + baggage_{std::move(baggage)} {} + +ImmutableSpanContext::ImmutableSpanContext( + const TraceContext& trace_context, std::string&& trace_state, + BaggageProtobufMap&& baggage) noexcept + : trace_id_high_{trace_context.trace_id_high}, + trace_id_low_{trace_context.trace_id_low}, + span_id_{trace_context.parent_id}, + trace_flags_{trace_context.trace_flags}, + trace_state_{std::move(trace_state)}, baggage_{std::move(baggage)} {} //------------------------------------------------------------------------------ diff --git a/src/tracer/immutable_span_context.h b/src/tracer/immutable_span_context.h index 86636058..1033697e 100644 --- a/src/tracer/immutable_span_context.h +++ b/src/tracer/immutable_span_context.h @@ -1,6 +1,7 @@ #pragma once #include "tracer/lightstep_span_context.h" +#include "tracer/propagation/trace_context.h" namespace lightstep { /** @@ -20,6 +21,10 @@ class ImmutableSpanContext final : public LightStepSpanContext { uint64_t span_id, bool sampled, BaggageProtobufMap&& baggage) noexcept; + ImmutableSpanContext(const TraceContext& trace_context, + std::string&& trace_state, + BaggageProtobufMap&& baggage) noexcept; + // LightStepSpanContext uint64_t trace_id_high() const noexcept override { return trace_id_high_; } @@ -27,7 +32,11 @@ class ImmutableSpanContext final : public LightStepSpanContext { uint64_t span_id() const noexcept override { return span_id_; } - bool sampled() const noexcept override { return sampled_; } + uint8_t trace_flags() const noexcept override { return trace_flags_; } + + opentracing::string_view trace_state() const noexcept override { + return trace_state_; + } void ForeachBaggageItem( std::function f) @@ -55,14 +64,20 @@ class ImmutableSpanContext final : public LightStepSpanContext { uint64_t trace_id_high_; uint64_t trace_id_low_; uint64_t span_id_; - bool sampled_; + uint8_t trace_flags_; + std::string trace_state_; BaggageProtobufMap baggage_; template opentracing::expected InjectImpl( const PropagationOptions& propagation_options, Carrier& writer) const { - return InjectSpanContext(propagation_options, writer, trace_id_high_, - trace_id_low_, span_id_, sampled_, baggage_); + TraceContext trace_context; + trace_context.trace_id_high = trace_id_high_; + trace_context.trace_id_low = trace_id_low_; + trace_context.parent_id = span_id_; + trace_context.trace_flags = trace_flags_; + return InjectSpanContext(propagation_options, writer, trace_context, + trace_state_, baggage_); } }; } // namespace lightstep diff --git a/src/tracer/json_options.cpp b/src/tracer/json_options.cpp index afbe4f42..9d2d21f6 100644 --- a/src/tracer/json_options.cpp +++ b/src/tracer/json_options.cpp @@ -44,6 +44,9 @@ static PropagationMode GetPropagationMode(opentracing::string_view s) { if (s == "envoy") { return PropagationMode::envoy; } + if (s == "trace_context") { + return PropagationMode::trace_context; + } std::ostringstream oss; oss << "invalid propagation mode " << s; throw std::runtime_error{oss.str()}; diff --git a/src/tracer/legacy/legacy_span.cpp b/src/tracer/legacy/legacy_span.cpp index 175fc95e..4f07c327 100644 --- a/src/tracer/legacy/legacy_span.cpp +++ b/src/tracer/legacy/legacy_span.cpp @@ -31,7 +31,7 @@ LegacySpan::LegacySpan(std::shared_ptr&& tracer, *span_.mutable_start_timestamp() = ToTimestamp(start_timestamp); // Set any span references. - sampled_ = false; + trace_flags_ = 0; collector::Reference collector_reference; auto& references = *span_.mutable_references(); references.Reserve(static_cast(options.references.size())); @@ -39,7 +39,7 @@ LegacySpan::LegacySpan(std::shared_ptr&& tracer, uint64_t trace_id_high; uint64_t trace_id; if (!SetSpanReference(reference, baggage, collector_reference, - trace_id_high, trace_id, sampled_)) { + trace_id_high, trace_id)) { continue; } *references.Add() = collector_reference; @@ -52,7 +52,7 @@ LegacySpan::LegacySpan(std::shared_ptr&& tracer, // If there are any span references, sampled should be true if any of the // references are sampled; with no refences, we set sampled to true. if (references.empty()) { - sampled_ = true; + trace_flags_ = SetTraceFlag(trace_flags_, true); } // Set tags. @@ -64,7 +64,8 @@ LegacySpan::LegacySpan(std::shared_ptr&& tracer, // If sampling_priority is set, it overrides whatever sampling decision was // derived from the referenced spans. if (tag.first == SamplingPriorityKey) { - sampled_ = is_sampled(tag.second); + trace_flags_ = + SetTraceFlag(trace_flags_, is_sampled(tag.second)); } } @@ -96,7 +97,7 @@ void LegacySpan::FinishWithOptions( } std::lock_guard lock_guard{mutex_}; - if (!sampled_) { + if (!IsTraceFlagSet(trace_flags_)) { return; } @@ -147,7 +148,8 @@ void LegacySpan::SetTag(opentracing::string_view key, *span_.mutable_tags()->Add() = ToKeyValue(key, value); if (key == SamplingPriorityKey) { - sampled_ = is_sampled(value); + trace_flags_ = + SetTraceFlag(trace_flags_, is_sampled(value)); } } catch (const std::exception& e) { logger_.Error("SetTag failed: ", e.what()); @@ -214,11 +216,11 @@ void LegacySpan::ForeachBaggageItem( } //------------------------------------------------------------------------------ -// sampled +// trace_flags //------------------------------------------------------------------------------ -bool LegacySpan::sampled() const noexcept { +uint8_t LegacySpan::trace_flags() const noexcept { std::lock_guard lock_guard{mutex_}; - return sampled_; + return trace_flags_; } //-------------------------------------------------------------------------------------------------- @@ -228,7 +230,7 @@ bool LegacySpan::SetSpanReference( const std::pair& reference, BaggageProtobufMap& baggage, collector::Reference& collector_reference, - uint64_t& trace_id_high, uint64_t& trace_id, bool& sampled) { + uint64_t& trace_id_high, uint64_t& trace_id) { collector_reference.Clear(); switch (reference.first) { case opentracing::SpanReferenceType::ChildOfRef: @@ -253,7 +255,9 @@ bool LegacySpan::SetSpanReference( collector_reference.mutable_span_context()->set_trace_id(trace_id); collector_reference.mutable_span_context()->set_span_id( referenced_context->span_id()); - sampled = sampled || referenced_context->sampled(); + + trace_flags_ |= referenced_context->trace_flags(); + AppendTraceState(trace_state_, referenced_context->trace_state()); referenced_context->ForeachBaggageItem( [&baggage](const std::string& key, const std::string& value) { diff --git a/src/tracer/legacy/legacy_span.h b/src/tracer/legacy/legacy_span.h index 3bcd42b9..fc4c8f53 100644 --- a/src/tracer/legacy/legacy_span.h +++ b/src/tracer/legacy/legacy_span.h @@ -65,7 +65,11 @@ class LegacySpan final : public opentracing::Span, public LightStepSpanContext { return span_.span_context().span_id(); } - bool sampled() const noexcept override; + uint8_t trace_flags() const noexcept override; + + opentracing::string_view trace_state() const noexcept override { + return trace_state_; + } opentracing::expected Inject( const PropagationOptions& propagation_options, @@ -88,9 +92,10 @@ class LegacySpan final : public opentracing::Span, public LightStepSpanContext { private: // Fields set in StartSpan() are not protected by a mutex. uint64_t trace_id_high_{0}; + uint8_t trace_flags_; collector::Span span_; + std::string trace_state_; std::chrono::steady_clock::time_point start_steady_; - bool sampled_; mutable std::mutex mutex_; std::shared_ptr tracer_; Logger& logger_; @@ -103,15 +108,19 @@ class LegacySpan final : public opentracing::Span, public LightStepSpanContext { const PropagationOptions& propagation_options, Carrier& writer) const { std::lock_guard lock_guard{mutex_}; auto& span_context = span_.span_context(); - return InjectSpanContext(propagation_options, writer, trace_id_high_, - span_context.trace_id(), span_context.span_id(), - sampled_, span_context.baggage()); + TraceContext trace_context; + trace_context.trace_id_high = trace_id_high_; + trace_context.trace_id_low = span_context.trace_id(); + trace_context.parent_id = span_context.span_id(); + trace_context.trace_flags = trace_flags_; + return InjectSpanContext(propagation_options, writer, trace_context, + trace_state_, span_context.baggage()); } bool SetSpanReference( const std::pair& reference, BaggageProtobufMap& baggage, collector::Reference& collector_reference, - uint64_t& trace_id_high, uint64_t& trace_id, bool& sampled); + uint64_t& trace_id_high, uint64_t& trace_id); }; } // namespace lightstep diff --git a/src/tracer/legacy/legacy_tracer_impl.cpp b/src/tracer/legacy/legacy_tracer_impl.cpp index 2b685198..2ac398f8 100644 --- a/src/tracer/legacy/legacy_tracer_impl.cpp +++ b/src/tracer/legacy/legacy_tracer_impl.cpp @@ -27,12 +27,12 @@ static opentracing::expected InjectImpl( template opentracing::expected> ExtractImpl( const PropagationOptions& propagation_options, Carrier& reader) try { - uint64_t trace_id_high, trace_id_low, span_id; - bool sampled; + TraceContext trace_context; + std::string trace_state; BaggageProtobufMap baggage; - auto extract_maybe = - ExtractSpanContext(propagation_options, reader, trace_id_high, - trace_id_low, span_id, sampled, baggage); + + auto extract_maybe = ExtractSpanContext(propagation_options, reader, + trace_context, trace_state, baggage); if (!extract_maybe) { return opentracing::make_unexpected(extract_maybe.error()); @@ -41,7 +41,7 @@ opentracing::expected> ExtractImpl( return std::unique_ptr{nullptr}; } std::unique_ptr result{new ImmutableSpanContext{ - trace_id_high, trace_id_low, span_id, sampled, std::move(baggage)}}; + trace_context, std::move(trace_state), std::move(baggage)}}; return std::move(result); } catch (const std::bad_alloc&) { return opentracing::make_unexpected( diff --git a/src/tracer/lightstep_span_context.cpp b/src/tracer/lightstep_span_context.cpp index f24a9fd8..e25a406c 100644 --- a/src/tracer/lightstep_span_context.cpp +++ b/src/tracer/lightstep_span_context.cpp @@ -18,7 +18,9 @@ bool operator==(const LightStepSpanContext& lhs, return lhs.trace_id_high() == rhs.trace_id_high() && lhs.trace_id_low() == rhs.trace_id_low() && - lhs.span_id() == rhs.span_id() && lhs.sampled() == rhs.sampled() && + lhs.span_id() == rhs.span_id() && + lhs.trace_state() == rhs.trace_state() && + lhs.trace_flags() == rhs.trace_flags() && extract_baggage(lhs) == extract_baggage(rhs); } } // namespace lightstep diff --git a/src/tracer/lightstep_span_context.h b/src/tracer/lightstep_span_context.h index b73cce97..b63aae1d 100644 --- a/src/tracer/lightstep_span_context.h +++ b/src/tracer/lightstep_span_context.h @@ -17,7 +17,13 @@ class LightStepSpanContext : public opentracing::SpanContext { virtual uint64_t span_id() const noexcept = 0; - virtual bool sampled() const noexcept = 0; + bool sampled() const noexcept { + return IsTraceFlagSet(this->trace_flags()); + } + + virtual uint8_t trace_flags() const noexcept = 0; + + virtual opentracing::string_view trace_state() const noexcept = 0; virtual opentracing::expected Inject( const PropagationOptions& propagation_options, diff --git a/src/tracer/propagation/BUILD b/src/tracer/propagation/BUILD index daa257f4..6675deb5 100644 --- a/src/tracer/propagation/BUILD +++ b/src/tracer/propagation/BUILD @@ -26,6 +26,7 @@ lightstep_cc_library( ], deps = [ "//src/tracer:baggage_flat_map_lib", + ":trace_context_lib", ], external_deps = [ "@com_google_protobuf//:protobuf", @@ -89,6 +90,35 @@ lightstep_cc_library( ], ) +lightstep_cc_library( + name = "trace_context_lib", + private_hdrs = [ + "trace_context.h", + ], + srcs = [ + "trace_context.cpp", + ], + deps = [ + "//src/common:hex_conversion_lib", + "@io_opentracing_cpp//:opentracing", + ], +) + +lightstep_cc_library( + name = "trace_context_propagator_lib", + private_hdrs = [ + "trace_context_propagator.h", + ], + srcs = [ + "trace_context_propagator.cpp", + ], + deps = [ + "//src/common:hex_conversion_lib", + "//src/common:utility_lib", + ":propagator_interface", + ], +) + lightstep_cc_library( name = "baggage_propagator_lib", private_hdrs = [ @@ -135,6 +165,7 @@ lightstep_cc_library( ":lightstep_propagator_lib", ":b3_propagator_lib", ":envoy_propagator_lib", + ":trace_context_propagator_lib", ":baggage_propagator_lib", ], ) @@ -150,5 +181,6 @@ lightstep_cc_library( deps = [ "//src/common:in_memory_stream_lib", ":propagation_options_lib", + ":trace_context_lib", ], ) diff --git a/src/tracer/propagation/baggage_propagator.cpp b/src/tracer/propagation/baggage_propagator.cpp index 8b948210..61e3032a 100644 --- a/src/tracer/propagation/baggage_propagator.cpp +++ b/src/tracer/propagation/baggage_propagator.cpp @@ -44,15 +44,17 @@ BaggagePropagator::BaggagePropagator( // InjectSpanContext //-------------------------------------------------------------------------------------------------- opentracing::expected BaggagePropagator::InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t /*trace_id_high*/, - uint64_t /*trace_id_low*/, uint64_t /*span_id*/, bool /*sampled*/, + const opentracing::TextMapWriter& carrier, + const TraceContext& /*trace_context*/, + opentracing::string_view /*trace_state*/, const BaggageProtobufMap& baggage) const { return this->InjectSpanContextImpl(carrier, baggage); } opentracing::expected BaggagePropagator::InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t /*trace_id_high*/, - uint64_t /*trace_id_low*/, uint64_t /*span_id*/, bool /*sampled*/, + const opentracing::TextMapWriter& carrier, + const TraceContext& /*trace_context*/, + opentracing::string_view /*trace_state*/, const BaggageFlatMap& baggage) const { return this->InjectSpanContextImpl(carrier, baggage); } diff --git a/src/tracer/propagation/baggage_propagator.h b/src/tracer/propagation/baggage_propagator.h index c356a73d..128128bd 100644 --- a/src/tracer/propagation/baggage_propagator.h +++ b/src/tracer/propagation/baggage_propagator.h @@ -9,22 +9,21 @@ class BaggagePropagator final : public Propagator { // Propagator opentracing::expected InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageProtobufMap& baggage) const override; opentracing::expected InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageFlatMap& baggage) const override; opentracing::expected ExtractSpanContext( const opentracing::TextMapReader& /*carrier*/, bool /*case_sensitive*/, - uint64_t& /*trace_id_high*/, uint64_t& /*trace_id_low*/, - uint64_t& /*span_id*/, bool& /*sampled*/, + TraceContext& /*trace_context*/, std::string& /*trace_state*/, BaggageProtobufMap& /*baggage*/) const override { // Do nothing: baggage is extracted in the other propagators - return true; + return false; } private: diff --git a/src/tracer/propagation/envoy_propagator.cpp b/src/tracer/propagation/envoy_propagator.cpp index 166ec557..2e32b73a 100644 --- a/src/tracer/propagation/envoy_propagator.cpp +++ b/src/tracer/propagation/envoy_propagator.cpp @@ -13,19 +13,17 @@ namespace lightstep { // InjectSpanContext //-------------------------------------------------------------------------------------------------- opentracing::expected EnvoyPropagator::InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view /*trace_state*/, const BaggageProtobufMap& baggage) const { - return this->InjectSpanContextImpl(carrier, trace_id_high, trace_id_low, - span_id, sampled, baggage); + return this->InjectSpanContextImpl(carrier, trace_context, baggage); } opentracing::expected EnvoyPropagator::InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view /*trace_state*/, const BaggageFlatMap& baggage) const { - return this->InjectSpanContextImpl(carrier, trace_id_high, trace_id_low, - span_id, sampled, baggage); + return this->InjectSpanContextImpl(carrier, trace_context, baggage); } //-------------------------------------------------------------------------------------------------- @@ -33,8 +31,8 @@ opentracing::expected EnvoyPropagator::InjectSpanContext( //-------------------------------------------------------------------------------------------------- opentracing::expected EnvoyPropagator::ExtractSpanContext( const opentracing::TextMapReader& carrier, bool case_sensitive, - uint64_t& trace_id_high, uint64_t& trace_id_low, uint64_t& span_id, - bool& sampled, BaggageProtobufMap& baggage) const { + TraceContext& trace_context, std::string& /*trace_state*/, + BaggageProtobufMap& baggage) const { auto iequals = [](opentracing::string_view lhs, opentracing::string_view rhs) noexcept { return lhs.length() == rhs.length() && @@ -43,13 +41,23 @@ opentracing::expected EnvoyPropagator::ExtractSpanContext( return std::tolower(a) == std::tolower(b); }); }; + bool sampled; + opentracing::expected result; if (case_sensitive) { - return ExtractSpanContextImpl(carrier, trace_id_high, trace_id_low, span_id, - sampled, baggage, - std::equal_to{}); + result = ExtractSpanContextImpl(carrier, trace_context.trace_id_high, + trace_context.trace_id_low, + trace_context.parent_id, sampled, baggage, + std::equal_to{}); + } else { + result = result = ExtractSpanContextImpl( + carrier, trace_context.trace_id_high, trace_context.trace_id_low, + trace_context.parent_id, sampled, baggage, iequals); } - return ExtractSpanContextImpl(carrier, trace_id_high, trace_id_low, span_id, - sampled, baggage, iequals); + if (!result || !*result) { + return result; + } + trace_context.trace_flags = SetTraceFlag(0, sampled); + return result; } //-------------------------------------------------------------------------------------------------- @@ -57,12 +65,12 @@ opentracing::expected EnvoyPropagator::ExtractSpanContext( //-------------------------------------------------------------------------------------------------- template opentracing::expected EnvoyPropagator::InjectSpanContextImpl( - const opentracing::TextMapWriter& carrier, uint64_t /*trace_id_high*/, - uint64_t trace_id_low, uint64_t span_id, bool sampled, - const BaggageMap& baggage) const { + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, const BaggageMap& baggage) const { std::ostringstream ostream; - auto result = lightstep::InjectSpanContext(ostream, trace_id_low, span_id, - sampled, baggage); + auto result = lightstep::InjectSpanContext( + ostream, trace_context.trace_id_low, trace_context.parent_id, + IsTraceFlagSet(trace_context.trace_flags), baggage); if (!result) { return result; } diff --git a/src/tracer/propagation/envoy_propagator.h b/src/tracer/propagation/envoy_propagator.h index 7a0e597a..8ce2883a 100644 --- a/src/tracer/propagation/envoy_propagator.h +++ b/src/tracer/propagation/envoy_propagator.h @@ -10,26 +10,25 @@ class EnvoyPropagator final : public Propagator { public: // Propagator opentracing::expected InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageProtobufMap& baggage) const override; opentracing::expected InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageFlatMap& baggage) const override; opentracing::expected ExtractSpanContext( const opentracing::TextMapReader& carrier, bool case_sensitive, - uint64_t& trace_id_high, uint64_t& trace_id_low, uint64_t& span_id, - bool& sampled, BaggageProtobufMap& baggage) const override; + TraceContext& trace_context, std::string& trace_state, + BaggageProtobufMap& baggage) const override; private: template opentracing::expected InjectSpanContextImpl( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, - const BaggageMap& baggage) const; + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, const BaggageMap& baggage) const; template opentracing::expected ExtractSpanContextImpl( diff --git a/src/tracer/propagation/multiheader_propagator.cpp b/src/tracer/propagation/multiheader_propagator.cpp index 434e1a14..12632e18 100644 --- a/src/tracer/propagation/multiheader_propagator.cpp +++ b/src/tracer/propagation/multiheader_propagator.cpp @@ -26,19 +26,17 @@ MultiheaderPropagator::MultiheaderPropagator( // InjectSpanContext //-------------------------------------------------------------------------------------------------- opentracing::expected MultiheaderPropagator::InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view /*trace_state*/, const BaggageProtobufMap& /*baggage*/) const { - return this->InjectSpanContextImpl(carrier, trace_id_high, trace_id_low, - span_id, sampled); + return this->InjectSpanContextImpl(carrier, trace_context); } opentracing::expected MultiheaderPropagator::InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view /*trace_state*/, const BaggageFlatMap& /*baggage*/) const { - return this->InjectSpanContextImpl(carrier, trace_id_high, trace_id_low, - span_id, sampled); + return this->InjectSpanContextImpl(carrier, trace_context); } //-------------------------------------------------------------------------------------------------- @@ -46,8 +44,8 @@ opentracing::expected MultiheaderPropagator::InjectSpanContext( //-------------------------------------------------------------------------------------------------- opentracing::expected MultiheaderPropagator::ExtractSpanContext( const opentracing::TextMapReader& carrier, bool case_sensitive, - uint64_t& trace_id_high, uint64_t& trace_id_low, uint64_t& span_id, - bool& sampled, BaggageProtobufMap& baggage) const { + TraceContext& trace_context, std::string& /*trace_state*/, + BaggageProtobufMap& baggage) const { auto iequals = [](opentracing::string_view lhs, opentracing::string_view rhs) noexcept { return lhs.length() == rhs.length() && @@ -56,38 +54,50 @@ opentracing::expected MultiheaderPropagator::ExtractSpanContext( return std::tolower(a) == std::tolower(b); }); }; + bool sampled; + opentracing::expected result; if (case_sensitive) { - return ExtractSpanContextImpl(carrier, trace_id_high, trace_id_low, span_id, - sampled, baggage, - std::equal_to{}); + result = ExtractSpanContextImpl(carrier, trace_context.trace_id_high, + trace_context.trace_id_low, + trace_context.parent_id, sampled, baggage, + std::equal_to{}); + } else { + result = result = ExtractSpanContextImpl( + carrier, trace_context.trace_id_high, trace_context.trace_id_low, + trace_context.parent_id, sampled, baggage, iequals); + } + if (!result || !*result) { + return result; } - return ExtractSpanContextImpl(carrier, trace_id_high, trace_id_low, span_id, - sampled, baggage, iequals); + trace_context.trace_flags = SetTraceFlag(0, sampled); + return result; } //-------------------------------------------------------------------------------------------------- // InjectSpanContextImpl //-------------------------------------------------------------------------------------------------- opentracing::expected MultiheaderPropagator::InjectSpanContextImpl( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled) const { + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context) const { HexSerializer hex_serializer; opentracing::expected result; if (supports_128bit_) { - result = carrier.Set(trace_id_key_, hex_serializer.Uint128ToHex( - trace_id_high, trace_id_low)); + result = carrier.Set( + trace_id_key_, hex_serializer.Uint128ToHex(trace_context.trace_id_high, + trace_context.trace_id_low)); } else { - result = - carrier.Set(trace_id_key_, hex_serializer.Uint64ToHex(trace_id_low)); + result = carrier.Set( + trace_id_key_, hex_serializer.Uint64ToHex(trace_context.trace_id_low)); } if (!result) { return result; } - result = carrier.Set(span_id_key_, hex_serializer.Uint64ToHex(span_id)); + result = carrier.Set(span_id_key_, + hex_serializer.Uint64ToHex(trace_context.parent_id)); if (!result) { return result; } - if (sampled) { + if (IsTraceFlagSet(trace_context.trace_flags)) { result = carrier.Set(sampled_key_, OneStr); } else { result = carrier.Set(sampled_key_, ZeroStr); diff --git a/src/tracer/propagation/multiheader_propagator.h b/src/tracer/propagation/multiheader_propagator.h index 345f6062..7c2823ab 100644 --- a/src/tracer/propagation/multiheader_propagator.h +++ b/src/tracer/propagation/multiheader_propagator.h @@ -16,19 +16,19 @@ class MultiheaderPropagator final : public Propagator { // Propagator opentracing::expected InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageProtobufMap& baggage) const override; opentracing::expected InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageFlatMap& baggage) const override; opentracing::expected ExtractSpanContext( const opentracing::TextMapReader& carrier, bool case_sensitive, - uint64_t& trace_id_high, uint64_t& trace_id_low, uint64_t& span_id, - bool& sampled, BaggageProtobufMap& baggage) const override; + TraceContext& trace_context, std::string& trace_state, + BaggageProtobufMap& baggage) const override; private: opentracing::string_view trace_id_key_; @@ -38,8 +38,8 @@ class MultiheaderPropagator final : public Propagator { bool supports_128bit_; opentracing::expected InjectSpanContextImpl( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled) const; + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context) const; template opentracing::expected ExtractSpanContextImpl( diff --git a/src/tracer/propagation/propagation.cpp b/src/tracer/propagation/propagation.cpp index a1f140d6..57fa67ff 100644 --- a/src/tracer/propagation/propagation.cpp +++ b/src/tracer/propagation/propagation.cpp @@ -13,18 +13,43 @@ #include "common/in_memory_stream.h" namespace lightstep { +//-------------------------------------------------------------------------------------------------- +// ExtractSpanContextImpl +//-------------------------------------------------------------------------------------------------- +static opentracing::expected ExtractSpanContextImpl( + const PropagationOptions& propagation_options, + const opentracing::TextMapReader& carrier, bool case_sensitive, + TraceContext& trace_context, std::string& trace_state, + BaggageProtobufMap& baggage) { + for (auto& propagator : propagation_options.extract_propagators) { + baggage.clear(); + auto result = propagator->ExtractSpanContext( + carrier, case_sensitive, trace_context, trace_state, baggage); + if (!result) { + // One of the injected span contexts is corrupt, return immediately + // without trying the other extractors. + return result; + } + if (*result) { + // An extractor succeeded, return without trying other extractors + return result; + } + } + return false; +} + //------------------------------------------------------------------------------ // InjectSpanContext //------------------------------------------------------------------------------ template opentracing::expected InjectSpanContext( const PropagationOptions& propagation_options, - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageMap& baggage) { for (auto& propagator : propagation_options.inject_propagators) { - auto was_successful = propagator->InjectSpanContext( - carrier, trace_id_high, trace_id_low, span_id, sampled, baggage); + auto was_successful = propagator->InjectSpanContext(carrier, trace_context, + trace_state, baggage); if (!was_successful) { return was_successful; } @@ -34,59 +59,32 @@ opentracing::expected InjectSpanContext( template opentracing::expected InjectSpanContext( const PropagationOptions& propagation_options, - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageProtobufMap& baggage); template opentracing::expected InjectSpanContext( const PropagationOptions& propagation_options, - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageFlatMap& baggage); //------------------------------------------------------------------------------ // ExtractSpanContext //------------------------------------------------------------------------------ -static opentracing::expected ExtractSpanContextImpl( - const PropagationOptions& propagation_options, - const opentracing::TextMapReader& carrier, bool case_sensitive, - uint64_t& trace_id_high, uint64_t& trace_id_low, uint64_t& span_id, - bool& sampled, BaggageProtobufMap& baggage) { - for (auto& propagator : propagation_options.extract_propagators) { - baggage.clear(); - auto result = - propagator->ExtractSpanContext(carrier, case_sensitive, trace_id_high, - trace_id_low, span_id, sampled, baggage); - if (!result) { - // One of the injected span contexts is corrupt, return immediately - // without trying the other extractors. - return result; - } - if (*result) { - // An extractor succeeded, return without trying other extractors - return result; - } - } - return false; -} - opentracing::expected ExtractSpanContext( const PropagationOptions& propagation_options, - const opentracing::TextMapReader& carrier, uint64_t& trace_id_high, - uint64_t& trace_id_low, uint64_t& span_id, bool& sampled, - BaggageProtobufMap& baggage) { + const opentracing::TextMapReader& carrier, TraceContext& trace_context, + std::string& trace_state, BaggageProtobufMap& baggage) { return ExtractSpanContextImpl(propagation_options, carrier, true, - trace_id_high, trace_id_low, span_id, sampled, - baggage); + trace_context, trace_state, baggage); } opentracing::expected ExtractSpanContext( const PropagationOptions& propagation_options, - const opentracing::HTTPHeadersReader& carrier, uint64_t& trace_id_high, - uint64_t& trace_id_low, uint64_t& span_id, bool& sampled, - BaggageProtobufMap& baggage) { + const opentracing::HTTPHeadersReader& carrier, TraceContext& trace_context, + std::string& trace_state, BaggageProtobufMap& baggage) { return ExtractSpanContextImpl(propagation_options, carrier, false, - trace_id_high, trace_id_low, span_id, sampled, - baggage); + trace_context, trace_state, baggage); } } // namespace lightstep diff --git a/src/tracer/propagation/propagation.h b/src/tracer/propagation/propagation.h index 33cb6c4f..22daa967 100644 --- a/src/tracer/propagation/propagation.h +++ b/src/tracer/propagation/propagation.h @@ -20,6 +20,16 @@ opentracing::expected InjectSpanContext( return InjectSpanContext(carrier, trace_id, span_id, sampled, baggage); } +template +opentracing::expected InjectSpanContext( + const PropagationOptions& /*propagation_options*/, std::ostream& carrier, + const TraceContext& trace_context, opentracing::string_view /*trace_state*/, + const BaggageMap& baggage) { + return InjectSpanContext( + carrier, trace_context.trace_id_low, trace_context.parent_id, + IsTraceFlagSet(trace_context.trace_flags), baggage); +} + template opentracing::expected InjectSpanContext( const PropagationOptions& propagation_options, @@ -27,23 +37,35 @@ opentracing::expected InjectSpanContext( uint64_t trace_id_low, uint64_t span_id, bool sampled, const BaggageMap& baggage); +template +opentracing::expected InjectSpanContext( + const PropagationOptions& propagation_options, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, + const BaggageMap& baggage); + inline opentracing::expected ExtractSpanContext( const PropagationOptions& /*propagation_options*/, std::istream& carrier, - uint64_t& trace_id_high, uint64_t& trace_id_low, uint64_t& span_id, - bool& sampled, BaggageProtobufMap& baggage) { - trace_id_high = 0; - return ExtractSpanContext(carrier, trace_id_low, span_id, sampled, baggage); + TraceContext& trace_context, std::string& /*trace_state*/, + BaggageProtobufMap& baggage) { + trace_context.trace_id_high = 0; + bool sampled; + auto result = ExtractSpanContext(carrier, trace_context.trace_id_low, + trace_context.parent_id, sampled, baggage); + if (!result || !*result) { + return result; + } + trace_context.trace_flags = SetTraceFlag(0, sampled); + return result; } opentracing::expected ExtractSpanContext( const PropagationOptions& propagation_options, - const opentracing::TextMapReader& carrier, uint64_t& trace_id_high, - uint64_t& trace_id_low, uint64_t& span_id, bool& sampled, - BaggageProtobufMap& baggage); + const opentracing::TextMapReader& carrier, TraceContext& trace_context, + std::string& trace_state, BaggageProtobufMap& baggage); opentracing::expected ExtractSpanContext( const PropagationOptions& propagation_options, - const opentracing::HTTPHeadersReader& carrier, uint64_t& trace_id_high, - uint64_t& trace_id_low, uint64_t& span_id, bool& sampled, - BaggageProtobufMap& baggage); + const opentracing::HTTPHeadersReader& carrier, TraceContext& trace_context, + std::string& trace_state, BaggageProtobufMap& baggage); } // namespace lightstep diff --git a/src/tracer/propagation/propagation_options.cpp b/src/tracer/propagation/propagation_options.cpp index 128d581d..0dc4eba3 100644 --- a/src/tracer/propagation/propagation_options.cpp +++ b/src/tracer/propagation/propagation_options.cpp @@ -6,6 +6,7 @@ #include "tracer/propagation/baggage_propagator.h" #include "tracer/propagation/envoy_propagator.h" #include "tracer/propagation/lightstep_propagator.h" +#include "tracer/propagation/trace_context_propagator.h" namespace lightstep { //-------------------------------------------------------------------------------------------------- @@ -72,6 +73,9 @@ static std::vector> MakePropagators( case PropagationMode::envoy: result.emplace_back(new EnvoyPropagator{}); break; + case PropagationMode::trace_context: + result.emplace_back(new TraceContextPropagator{}); + break; } } return result; diff --git a/src/tracer/propagation/propagator.h b/src/tracer/propagation/propagator.h index 346af207..39c7c1f4 100644 --- a/src/tracer/propagation/propagator.h +++ b/src/tracer/propagation/propagator.h @@ -1,6 +1,7 @@ #pragma once #include "tracer/baggage_flat_map.h" +#include "tracer/propagation/trace_context.h" #include @@ -14,18 +15,18 @@ class Propagator { virtual ~Propagator() noexcept = default; virtual opentracing::expected InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageProtobufMap& baggage) const = 0; virtual opentracing::expected InjectSpanContext( - const opentracing::TextMapWriter& carrier, uint64_t trace_id_high, - uint64_t trace_id_low, uint64_t span_id, bool sampled, + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, const BaggageFlatMap& baggage) const = 0; virtual opentracing::expected ExtractSpanContext( const opentracing::TextMapReader& carrier, bool case_sensitive, - uint64_t& trace_id_high, uint64_t& trace_id_low, uint64_t& span_id, - bool& sampled, BaggageProtobufMap& baggage) const = 0; + TraceContext& trace_context, std::string& trace_state, + BaggageProtobufMap& baggage) const = 0; }; } // namespace lightstep diff --git a/src/tracer/propagation/trace_context.cpp b/src/tracer/propagation/trace_context.cpp new file mode 100644 index 00000000..e6255675 --- /dev/null +++ b/src/tracer/propagation/trace_context.cpp @@ -0,0 +1,100 @@ +#include "tracer/propagation/trace_context.h" + +#include "common/hex_conversion.h" + +namespace lightstep { +//-------------------------------------------------------------------------------------------------- +// ParseTraceContext +//-------------------------------------------------------------------------------------------------- +opentracing::expected ParseTraceContext( + opentracing::string_view s, TraceContext& trace_context) noexcept { + if (s.size() < TraceContextLength) { + return opentracing::make_unexpected( + std::make_error_code(std::errc::invalid_argument)); + } + size_t offset = 0; + + // version + auto version_maybe = NormalizedHexToUint8( + opentracing::string_view{s.data() + offset, Num8BitHexDigits}); + if (!version_maybe) { + return opentracing::make_unexpected(version_maybe.error()); + } + trace_context.version = *version_maybe; + offset += Num8BitHexDigits; + if (s[offset] != '-') { + return opentracing::make_unexpected( + std::make_error_code(std::errc::invalid_argument)); + } + ++offset; + + // trace-id + auto error_maybe = NormalizedHexToUint128( + opentracing::string_view{s.data() + offset, Num128BitHexDigits}, + trace_context.trace_id_high, trace_context.trace_id_low); + if (!error_maybe) { + return error_maybe; + } + offset += Num128BitHexDigits; + if (s[offset] != '-') { + return opentracing::make_unexpected( + std::make_error_code(std::errc::invalid_argument)); + } + ++offset; + + // parent-id + auto parent_id_maybe = NormalizedHexToUint64( + opentracing::string_view{s.data() + offset, Num64BitHexDigits}); + if (!parent_id_maybe) { + return opentracing::make_unexpected(parent_id_maybe.error()); + } + trace_context.parent_id = *parent_id_maybe; + offset += Num64BitHexDigits; + if (s[offset] != '-') { + return opentracing::make_unexpected( + std::make_error_code(std::errc::invalid_argument)); + } + ++offset; + + // trace-flags + auto trace_flags_maybe = NormalizedHexToUint8( + opentracing::string_view{s.data() + offset, Num8BitHexDigits}); + if (!trace_flags_maybe) { + return opentracing::make_unexpected(trace_flags_maybe.error()); + } + trace_context.trace_flags = *trace_flags_maybe; + + return {}; +} + +//-------------------------------------------------------------------------------------------------- +// SerializeTraceContext +//-------------------------------------------------------------------------------------------------- +void SerializeTraceContext(const TraceContext& trace_context, + char* s) noexcept { + size_t offset = 0; + + // version + Uint8ToHex(trace_context.version, s); + offset += Num8BitHexDigits; + *(s + offset) = '-'; + ++offset; + + // trace-id + Uint64ToHex(trace_context.trace_id_high, s + offset); + offset += Num64BitHexDigits; + Uint64ToHex(trace_context.trace_id_low, s + offset); + offset += Num64BitHexDigits; + *(s + offset) = '-'; + ++offset; + + // parent-id + Uint64ToHex(trace_context.parent_id, s + offset); + offset += Num64BitHexDigits; + *(s + offset) = '-'; + ++offset; + + // trace-flags + Uint8ToHex(trace_context.trace_flags, s + offset); +} +} // namespace lightstep diff --git a/src/tracer/propagation/trace_context.h b/src/tracer/propagation/trace_context.h new file mode 100644 index 00000000..718ccf83 --- /dev/null +++ b/src/tracer/propagation/trace_context.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include +#include + +namespace lightstep { +const size_t TraceContextLength = 55; +const uint8_t SampledFlagMask = 1; +const uint8_t TraceContextVersion = 0; + +struct TraceContext { + uint64_t trace_id_high{0}; + uint64_t trace_id_low{0}; + uint64_t parent_id{0}; + uint8_t trace_flags{0}; + uint8_t version{TraceContextVersion}; +}; + +opentracing::expected ParseTraceContext( + opentracing::string_view s, TraceContext& trace_context) noexcept; + +void SerializeTraceContext(const TraceContext& trace_context, char* s) noexcept; + +template +inline bool IsTraceFlagSet(uint8_t flags) noexcept { + return static_cast(flags & Mask); +} + +template +inline uint8_t SetTraceFlag(uint8_t flags, bool value) noexcept { + if (value) { + return flags | Mask; + } + return flags & ~Mask; +} +} // namespace lightstep diff --git a/src/tracer/propagation/trace_context_propagator.cpp b/src/tracer/propagation/trace_context_propagator.cpp new file mode 100644 index 00000000..e64512ce --- /dev/null +++ b/src/tracer/propagation/trace_context_propagator.cpp @@ -0,0 +1,111 @@ +#include "tracer/propagation/trace_context_propagator.h" + +#include + +#include "common/utility.h" + +const opentracing::string_view TraceParentHeaderKey = "traceparent"; +const opentracing::string_view TraceStateHeaderKey = "tracestate"; +const opentracing::string_view PrefixBaggage = "ot-baggage-"; + +namespace lightstep { +//-------------------------------------------------------------------------------------------------- +// InjectSpanContextImpl +//-------------------------------------------------------------------------------------------------- +static opentracing::expected InjectSpanContextImpl( + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state) { + std::array buffer; + SerializeTraceContext(trace_context, buffer.data()); + auto result = + carrier.Set(TraceParentHeaderKey, + opentracing::string_view{buffer.data(), buffer.size()}); + if (!result) { + return result; + } + if (trace_state.empty()) { + return {}; + } + return carrier.Set(TraceStateHeaderKey, trace_state); +} + +//-------------------------------------------------------------------------------------------------- +// ExtractSpanContextImpl +//-------------------------------------------------------------------------------------------------- +template +static opentracing::expected ExtractSpanContextImpl( + const opentracing::TextMapReader& carrier, TraceContext& trace_context, + std::string& trace_state, const KeyCompare& key_compare, + BaggageProtobufMap& baggage) { + bool parent_header_found = false; + auto result = + carrier.ForeachKey([&](opentracing::string_view key, + opentracing::string_view + value) noexcept->opentracing::expected { + if (key_compare(key, TraceParentHeaderKey)) { + auto was_successful = ParseTraceContext(value, trace_context); + if (!was_successful) { + return opentracing::make_unexpected(was_successful.error()); + } + parent_header_found = true; + } else if (key_compare(key, TraceStateHeaderKey)) { + trace_state.assign(value.data(), value.size()); + } else if (key.length() > PrefixBaggage.size() && + key_compare(opentracing::string_view{key.data(), + PrefixBaggage.size()}, + PrefixBaggage)) { + baggage.insert(BaggageProtobufMap::value_type( + ToLower( + opentracing::string_view{key.data() + PrefixBaggage.size(), + key.size() - PrefixBaggage.size()}), + value)); + } + return {}; + }); + if (!result) { + return opentracing::make_unexpected(result.error()); + } + return parent_header_found; +} + +//-------------------------------------------------------------------------------------------------- +// InjectSpanContext +//-------------------------------------------------------------------------------------------------- +opentracing::expected TraceContextPropagator::InjectSpanContext( + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, + const BaggageProtobufMap& /*baggage*/) const { + return InjectSpanContextImpl(carrier, trace_context, trace_state); +} + +opentracing::expected TraceContextPropagator::InjectSpanContext( + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, + const BaggageFlatMap& /*baggage*/) const { + return InjectSpanContextImpl(carrier, trace_context, trace_state); +} + +//-------------------------------------------------------------------------------------------------- +// ExtractSpanContext +//-------------------------------------------------------------------------------------------------- +opentracing::expected TraceContextPropagator::ExtractSpanContext( + const opentracing::TextMapReader& carrier, bool case_sensitive, + TraceContext& trace_context, std::string& trace_state, + BaggageProtobufMap& baggage) const { + auto iequals = + [](opentracing::string_view lhs, opentracing::string_view rhs) noexcept { + return lhs.length() == rhs.length() && + std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs), + [](char a, char b) { + return std::tolower(a) == std::tolower(b); + }); + }; + if (case_sensitive) { + return ExtractSpanContextImpl(carrier, trace_context, trace_state, + std::equal_to{}, + baggage); + } + return ExtractSpanContextImpl(carrier, trace_context, trace_state, iequals, + baggage); +} +} // namespace lightstep diff --git a/src/tracer/propagation/trace_context_propagator.h b/src/tracer/propagation/trace_context_propagator.h new file mode 100644 index 00000000..66d701cf --- /dev/null +++ b/src/tracer/propagation/trace_context_propagator.h @@ -0,0 +1,24 @@ +#pragma once + +#include "tracer/propagation/propagator.h" + +namespace lightstep { +class TraceContextPropagator final : public Propagator { + public: + // Propagator + opentracing::expected InjectSpanContext( + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, + const BaggageProtobufMap& baggage) const override; + + opentracing::expected InjectSpanContext( + const opentracing::TextMapWriter& carrier, + const TraceContext& trace_context, opentracing::string_view trace_state, + const BaggageFlatMap& baggage) const override; + + opentracing::expected ExtractSpanContext( + const opentracing::TextMapReader& carrier, bool case_sensitive, + TraceContext& trace_context, std::string& trace_state, + BaggageProtobufMap& baggage) const override; +}; +} // namespace lightstep diff --git a/src/tracer/span.cpp b/src/tracer/span.cpp index a3010ef2..2cda12e3 100644 --- a/src/tracer/span.cpp +++ b/src/tracer/span.cpp @@ -35,7 +35,7 @@ Span::Span(std::shared_ptr&& tracer, WriteStartTimestamp(stream_, start_timestamp); // Set any span references. - sampled_ = false; + trace_flags_ = 0; int reference_count = 0; for (auto& reference : options.references) { uint64_t trace_id_high; @@ -51,7 +51,7 @@ Span::Span(std::shared_ptr&& tracer, // If there are any span references, sampled should be true if any of the // references are sampled; with no refences, we set sampled to true. if (reference_count == 0) { - sampled_ = true; + trace_flags_ = SetTraceFlag(trace_flags_, true); auto ids = GenerateIds<2>(); trace_id_high_ = 0; trace_id_ = ids[0]; @@ -67,7 +67,8 @@ Span::Span(std::shared_ptr&& tracer, // If sampling_priority is set, it overrides whatever sampling decision was // derived from the referenced spans. if (tag.first == SamplingPriorityKey) { - sampled_ = is_sampled(tag.second); + trace_flags_ = + SetTraceFlag(trace_flags_, is_sampled(tag.second)); } } } @@ -115,7 +116,8 @@ void Span::SetTag(opentracing::string_view key, } WriteTag(stream_, key, value); if (key == SamplingPriorityKey) { - sampled_ = is_sampled(value); + trace_flags_ = + SetTraceFlag(trace_flags_, is_sampled(value)); } } catch (const std::exception& e) { tracer_->logger().Error("SetTag failed: ", e.what()); @@ -185,11 +187,11 @@ void Span::ForeachBaggageItem( } //------------------------------------------------------------------------------ -// sampled +// trace_flags //------------------------------------------------------------------------------ -bool Span::sampled() const noexcept { +uint8_t Span::trace_flags() const noexcept { SpinLockGuard lock_guard{mutex_}; - return sampled_; + return trace_flags_; } //-------------------------------------------------------------------------------------------------- @@ -213,7 +215,8 @@ bool Span::SetSpanReference( trace_id = referenced_context->trace_id_low(); WriteSpanReference(stream_, reference.first, trace_id, referenced_context->span_id()); - sampled_ = sampled_ || referenced_context->sampled(); + trace_flags_ |= referenced_context->trace_flags(); + AppendTraceState(trace_state_, referenced_context->trace_state()); referenced_context->ForeachBaggageItem( [this](const std::string& key, const std::string& value) { this->baggage_.insert_or_assign(std::string{key}, std::string{value}); @@ -232,7 +235,7 @@ void Span::FinishImpl( return; } - if (!sampled_) { + if (!IsTraceFlagSet(trace_flags_)) { return; } diff --git a/src/tracer/span.h b/src/tracer/span.h index 4419607d..275584d0 100644 --- a/src/tracer/span.h +++ b/src/tracer/span.h @@ -83,7 +83,11 @@ class Span final : public opentracing::Span, public LightStepSpanContext { uint64_t span_id() const noexcept override { return span_id_; } - bool sampled() const noexcept override; + uint8_t trace_flags() const noexcept override; + + opentracing::string_view trace_state() const noexcept override { + return trace_state_; + } private: // Profiling shows that even with no contention, locking and unlocking a @@ -104,15 +108,21 @@ class Span final : public opentracing::Span, public LightStepSpanContext { uint64_t trace_id_high_{0}; uint64_t trace_id_; uint64_t span_id_; + uint8_t trace_flags_; BaggageFlatMap baggage_; - bool sampled_; + std::string trace_state_; template opentracing::expected InjectImpl( const PropagationOptions& propagation_options, Carrier& writer) const { SpinLockGuard lock_guard{mutex_}; - return InjectSpanContext(propagation_options, writer, trace_id_high_, - trace_id_, span_id_, sampled_, baggage_); + TraceContext trace_context; + trace_context.trace_id_high = trace_id_high_; + trace_context.trace_id_low = trace_id_; + trace_context.parent_id = span_id_; + trace_context.trace_flags = trace_flags_; + return InjectSpanContext(propagation_options, writer, trace_context, + trace_state_, baggage_); } bool SetSpanReference( diff --git a/src/tracer/tracer_impl.cpp b/src/tracer/tracer_impl.cpp index e977b680..dc5d1608 100644 --- a/src/tracer/tracer_impl.cpp +++ b/src/tracer/tracer_impl.cpp @@ -26,14 +26,15 @@ static opentracing::expected InjectImpl( // ExtractImpl //------------------------------------------------------------------------------ template -opentracing::expected> ExtractImpl( - const PropagationOptions& propagation_options, Carrier& reader) try { - uint64_t trace_id_high, trace_id_low, span_id; - bool sampled; +static opentracing::expected> +ExtractImpl(const PropagationOptions& propagation_options, + Carrier& reader) try { + TraceContext trace_context; + std::string trace_state; BaggageProtobufMap baggage; - auto extract_maybe = - ExtractSpanContext(propagation_options, reader, trace_id_high, - trace_id_low, span_id, sampled, baggage); + + auto extract_maybe = ExtractSpanContext(propagation_options, reader, + trace_context, trace_state, baggage); if (!extract_maybe) { return opentracing::make_unexpected(extract_maybe.error()); @@ -42,7 +43,7 @@ opentracing::expected> ExtractImpl( return std::unique_ptr{nullptr}; } std::unique_ptr result{new ImmutableSpanContext{ - trace_id_high, trace_id_low, span_id, sampled, std::move(baggage)}}; + trace_context, std::move(trace_state), std::move(baggage)}}; return std::move(result); } catch (const std::bad_alloc&) { return opentracing::make_unexpected( diff --git a/src/tracer/utility.cpp b/src/tracer/utility.cpp new file mode 100644 index 00000000..f0021d77 --- /dev/null +++ b/src/tracer/utility.cpp @@ -0,0 +1,17 @@ +#include "tracer/utility.h" + +namespace lightstep { +//-------------------------------------------------------------------------------------------------- +// AppendTraceState +//-------------------------------------------------------------------------------------------------- +void AppendTraceState(std::string& trace_state, + opentracing::string_view key_values) { + if (trace_state.empty()) { + trace_state.assign(key_values.data(), key_values.size()); + return; + } + trace_state.reserve(trace_state.size() + 1 + key_values.size()); + trace_state.append(","); + trace_state.append(key_values.data(), key_values.size()); +} +} // namespace lightstep diff --git a/src/tracer/utility.h b/src/tracer/utility.h index e6fe804f..51272ef5 100644 --- a/src/tracer/utility.h +++ b/src/tracer/utility.h @@ -5,6 +5,7 @@ #include "recorder/recorder.h" +#include #include namespace lightstep { @@ -56,4 +57,12 @@ inline std::tuple ComputeStartTimestamps( return std::tuple{start_system_timestamp, start_steady_timestamp}; } + +/** + * Appends trace-states to have the union of the two key-value lists. + * @param trace_state the trace_state to append to + * @param key_values the key-values of another trace-state + */ +void AppendTraceState(std::string& trace_state, + opentracing::string_view key_values); } // namespace lightstep diff --git a/test/common/BUILD b/test/common/BUILD index a34070a4..1d3a3265 100644 --- a/test/common/BUILD +++ b/test/common/BUILD @@ -18,7 +18,7 @@ lightstep_catch_test( ) lightstep_catch_test( - name = "spin_lock_mute_test", + name = "spin_lock_mutex_test", srcs = [ "spin_lock_mutex_test.cpp", ], diff --git a/test/common/hex_conversion_test.cpp b/test/common/hex_conversion_test.cpp index 4d53341d..bbd8341f 100644 --- a/test/common/hex_conversion_test.cpp +++ b/test/common/hex_conversion_test.cpp @@ -19,6 +19,20 @@ static void Generate128BitTestCases(uint64_t x, F f) { f(max - x, max - x); } +TEST_CASE("hex-integer conversions (8-bit)") { + char data[Num8BitHexDigits]; + + SECTION("Verify hex conversion and back against a range of values.") { + for (uint8_t x = 0; x < std::numeric_limits::max(); ++x) { + { + REQUIRE(x == *NormalizedHexToUint8(Uint8ToHex(x, data))); + auto y = std::numeric_limits::max() - x; + REQUIRE(y == *NormalizedHexToUint8(Uint8ToHex(y, data))); + } + } + } +} + TEST_CASE("hex-integer conversions (64-bit)") { char data[Num64BitHexDigits]; diff --git a/test/tracer/BUILD b/test/tracer/BUILD index c5e5a512..1042d772 100644 --- a/test/tracer/BUILD +++ b/test/tracer/BUILD @@ -21,6 +21,16 @@ lightstep_catch_test( ], ) +lightstep_catch_test( + name = "utility_test", + srcs = [ + "utility_test.cpp", + ], + deps = [ + "//src/tracer:utility_lib", + ], +) + lightstep_catch_test( name = "dynamic_load_test", srcs = [ diff --git a/test/tracer/json_options_test.cpp b/test/tracer/json_options_test.cpp index 4bdba2f6..4990d2db 100644 --- a/test/tracer/json_options_test.cpp +++ b/test/tracer/json_options_test.cpp @@ -9,13 +9,13 @@ TEST_CASE("LightStepTracerFactory") { SECTION("We can specify propagation modes via a tracers json configuration") { const char* config = R"({ "component_name": "t", - "propagation_modes": ["lightstep", "b3", "envoy"] + "propagation_modes": ["lightstep", "b3", "envoy", "trace_context"] })"; auto options_maybe = MakeTracerOptions(config, error_message); REQUIRE(options_maybe); std::vector expected_propagation_modes = { - PropagationMode::lightstep, PropagationMode::b3, - PropagationMode::envoy}; + PropagationMode::lightstep, PropagationMode::b3, PropagationMode::envoy, + PropagationMode::trace_context}; REQUIRE(options_maybe->propagation_modes == expected_propagation_modes); } diff --git a/test/tracer/propagation/BUILD b/test/tracer/propagation/BUILD index 595ca67b..435cb54c 100644 --- a/test/tracer/propagation/BUILD +++ b/test/tracer/propagation/BUILD @@ -94,6 +94,16 @@ lightstep_catch_test( ], ) +lightstep_catch_test( + name = "trace_context_test", + srcs = [ + "trace_context_test.cpp", + ], + deps = [ + "//src/tracer/propagation:trace_context_lib", + ], +) + lightstep_catch_test( name = "lightstep_propagation_test", srcs = [ @@ -108,6 +118,20 @@ lightstep_catch_test( ], ) +lightstep_catch_test( + name = "trace_context_propagation_test", + srcs = [ + "trace_context_propagation_test.cpp", + ], + deps = [ + "//:manual_tracer_lib", + "//test/recorder:in_memory_recorder_lib", + ":text_map_carrier_lib", + ":http_headers_carrier_lib", + ":utility_lib", + ], +) + lightstep_catch_test( name = "propagation_options_test", srcs = [ diff --git a/test/tracer/propagation/trace_context_propagation_test.cpp b/test/tracer/propagation/trace_context_propagation_test.cpp new file mode 100644 index 00000000..9428f0de --- /dev/null +++ b/test/tracer/propagation/trace_context_propagation_test.cpp @@ -0,0 +1,96 @@ +#include "test/recorder/in_memory_recorder.h" +#include "test/tracer/propagation/http_headers_carrier.h" +#include "test/tracer/propagation/text_map_carrier.h" +#include "test/tracer/propagation/utility.h" +#include "tracer/immutable_span_context.h" +#include "tracer/legacy/legacy_tracer_impl.h" +#include "tracer/tracer_impl.h" + +#include "3rd_party/catch2/catch.hpp" +using namespace lightstep; + +TEST_CASE("trace-context propagation") { + LightStepTracerOptions tracer_options; + tracer_options.propagation_modes = {PropagationMode::trace_context}; + auto recorder = new InMemoryRecorder{}; + auto tracer = std::shared_ptr{ + new TracerImpl{MakePropagationOptions(tracer_options), + std::unique_ptr{recorder}}}; + std::unordered_map text_map; + TextMapCarrier text_map_carrier{text_map}; + HTTPHeadersCarrier http_headers_carrier{text_map}; + + SECTION("Inject, extract yields the same span context.") { + auto test_span_contexts = MakeTestSpanContexts(true); + for (auto& span_context : test_span_contexts) { + // text map carrier + CHECK_NOTHROW( + VerifyInjectExtract(*tracer, *span_context, text_map_carrier)); + text_map.clear(); + + // http headers carrier + CHECK_NOTHROW( + VerifyInjectExtract(*tracer, *span_context, http_headers_carrier)); + text_map.clear(); + } + } + + SECTION("trace_flags is copied over to children") { + TraceContext trace_context; + trace_context.trace_id_high = 123; + trace_context.trace_id_low = 456; + trace_context.parent_id = 789; + trace_context.trace_flags = 2; + ImmutableSpanContext span_context{trace_context, "", BaggageProtobufMap{}}; + auto span = tracer->StartSpan("abc", {opentracing::ChildOf(&span_context)}); + REQUIRE(dynamic_cast(span->context()) + .trace_flags() == 2); + } + + SECTION("trace_flags are ORed from multiple parents") { + TraceContext trace_context; + trace_context.trace_id_high = 123; + trace_context.trace_id_low = 456; + trace_context.parent_id = 789; + trace_context.trace_flags = 2; + ImmutableSpanContext span_context1{trace_context, "", BaggageProtobufMap{}}; + trace_context.trace_flags = 1; + ImmutableSpanContext span_context2{trace_context, "", BaggageProtobufMap{}}; + auto span = + tracer->StartSpan("abc", {opentracing::ChildOf(&span_context1), + opentracing::ChildOf(&span_context2)}); + REQUIRE(dynamic_cast(span->context()) + .trace_flags() == 3); + } + + SECTION("trace_state is copied over to children") { + TraceContext trace_context; + trace_context.trace_id_high = 123; + trace_context.trace_id_low = 456; + trace_context.parent_id = 789; + trace_context.trace_flags = 2; + ImmutableSpanContext span_context{trace_context, "abc=123", + BaggageProtobufMap{}}; + auto span = tracer->StartSpan("abc", {opentracing::ChildOf(&span_context)}); + REQUIRE(dynamic_cast(span->context()) + .trace_state() == "abc=123"); + } + + SECTION("trace_states are joined from multiple parents") { + TraceContext trace_context; + trace_context.trace_id_high = 123; + trace_context.trace_id_low = 456; + trace_context.parent_id = 789; + trace_context.trace_flags = 2; + ImmutableSpanContext span_context1{trace_context, "abc=123", + BaggageProtobufMap{}}; + trace_context.trace_flags = 1; + ImmutableSpanContext span_context2{trace_context, "xyz=456", + BaggageProtobufMap{}}; + auto span = + tracer->StartSpan("abc", {opentracing::ChildOf(&span_context1), + opentracing::ChildOf(&span_context2)}); + REQUIRE(dynamic_cast(span->context()) + .trace_state() == "abc=123,xyz=456"); + } +} diff --git a/test/tracer/propagation/trace_context_test.cpp b/test/tracer/propagation/trace_context_test.cpp new file mode 100644 index 00000000..bcc5b104 --- /dev/null +++ b/test/tracer/propagation/trace_context_test.cpp @@ -0,0 +1,52 @@ +#include "tracer/propagation/trace_context.h" + +#include "3rd_party/catch2/catch.hpp" +using namespace lightstep; + +TEST_CASE("We can serialize and deserialize a trace-context") { + TraceContext trace_context; + + opentracing::string_view valid_trace_context = + "00-0af7651916cd43dd8448eb211c80319c-b9c7c989f97918e1-01"; + + SECTION("We can parse a valid trace context") { + REQUIRE(ParseTraceContext(valid_trace_context, trace_context)); + REQUIRE(trace_context.version == 0); + REQUIRE(trace_context.trace_id_high == 0x0af7651916cd43dd); + REQUIRE(trace_context.trace_id_low == 0x8448eb211c80319c); + REQUIRE(trace_context.parent_id == 0xb9c7c989f97918e1); + REQUIRE(trace_context.trace_flags == 1); + } + + SECTION("We fail if we encounter an invalid character") { + for (int i = 0; i < static_cast(valid_trace_context.size()); ++i) { + std::string s = valid_trace_context; + s[i] = 'X'; + REQUIRE(!ParseTraceContext(s, trace_context)); + } + } + + SECTION( + "We fail when trace context has fewer than the minimum required " + "characters") { + std::string s = valid_trace_context; + s = s.substr(1); + REQUIRE(!ParseTraceContext(s, trace_context)); + } + + SECTION( + "We can parse a trace-context with more than the required characters") { + std::string s = valid_trace_context; + s += ' '; + REQUIRE(ParseTraceContext(s, trace_context)); + } + + SECTION( + "If we deserialize a trace-context and serialize again, we'll get the " + "same value") { + REQUIRE(ParseTraceContext(valid_trace_context, trace_context)); + std::string s(TraceContextLength, ' '); + SerializeTraceContext(trace_context, &s[0]); + REQUIRE(s == valid_trace_context); + } +} diff --git a/test/tracer/utility_test.cpp b/test/tracer/utility_test.cpp new file mode 100644 index 00000000..0fe1834d --- /dev/null +++ b/test/tracer/utility_test.cpp @@ -0,0 +1,19 @@ +#include "tracer/utility.h" + +#include "3rd_party/catch2/catch.hpp" +using namespace lightstep; + +TEST_CASE("We can append the values of w3 trace-state values") { + std::string trace_state; + + SECTION("We can append to an empty trace-state") { + AppendTraceState(trace_state, "abc=123"); + REQUIRE(trace_state == "abc=123"); + } + + SECTION("We can append to a non-empty trace-state") { + trace_state = "abc=123"; + AppendTraceState(trace_state, "xyz=456"); + REQUIRE(trace_state == "abc=123,xyz=456"); + } +}