Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
20545c7
Add support for x-cloud-trace-context
rob-spotify Jul 30, 2020
d311a7a
needs debugging
rob-spotify Jul 30, 2020
c5b1149
Fixed one test, just need to implement the parsing and serialising of…
rob-spotify Jul 31, 2020
dbbcff6
Now supports long format trace_id/span_id;o=[0-1] - TODO support the …
rob-spotify Jul 31, 2020
d6d90b9
Support medium and short form cloud-trace headers (TODO verify what w…
rob-spotify Jul 31, 2020
252358a
Revert CMakeLists.txt change
rob-spotify Aug 3, 2020
6181730
Revert CMakeLists.txt
rob-spotify Aug 3, 2020
be25e16
Expect span ID to be 0 when none is given (need to confirm this is th…
rob-spotify Aug 3, 2020
b1d0b38
Add cloud_trace propagator to CMakeLists.txt
rob-spotify Aug 3, 2020
4ae8982
span ID is an unsigned long, not a hex value
rob-spotify Aug 3, 2020
4630635
Fix the max length of the x-cloud-trace-context header
rob-spotify Aug 3, 2020
022b2e2
incorrect char array assignment
rob-spotify Aug 3, 2020
8176a65
set the header length properly
rob-spotify Aug 3, 2020
1fbdd02
Revert CMakeLists.txt
rob-spotify Aug 4, 2020
13e0e72
fix style error
rob-spotify Aug 4, 2020
cf4eb97
fix linting issues
rob-spotify Aug 4, 2020
5f7a33f
unused import
rob-spotify Aug 4, 2020
395af64
Add cloud_trace_propagator.cpp to build list
rob-spotify Aug 4, 2020
7a230b2
Fix portability error
rob-spotify Aug 4, 2020
2c9c678
use string_view and nullptr
rob-spotify Aug 6, 2020
0c0f66a
Fix span_id short parsing
rob-spotify Aug 6, 2020
19d62e0
Simplify serialize method with snprintf
rob-spotify Aug 6, 2020
54323d6
cover error handling with additional test cases
rob-spotify Aug 6, 2020
c9bd7ab
Test potential maximum ID values for propagators
rob-spotify Aug 6, 2020
6c18e33
Cover the bad trace flag case
rob-spotify Aug 6, 2020
4e3411d
Was adding a space at the end of the header
rob-spotify Aug 7, 2020
b214ed8
Extend header length (it looks like it does need it for the possible …
rob-spotify Aug 7, 2020
146f9b9
Review comments
rob-spotify Aug 10, 2020
baa3603
Fix clang-tidy error
rob-spotify Aug 10, 2020
fc3d3c3
Review comments
rob-spotify Aug 12, 2020
b7bd88d
Fix failing test
rob-spotify Aug 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ set(LIGHTSTEP_SRCS src/common/utility.cpp
src/tracer/propagation/trace_context_propagator.cpp
src/tracer/propagation/lightstep_propagator.cpp
src/tracer/propagation/multiheader_propagator.cpp
src/tracer/propagation/cloud_trace_propagator.cpp
src/tracer/propagation/propagation.cpp
src/tracer/propagation/propagation_options.cpp
src/tracer/immutable_span_context.cpp
Expand Down
3 changes: 2 additions & 1 deletion include/lightstep/tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ enum class PropagationMode {
lightstep = 1,
b3 = 2,
envoy = 3,
trace_context = 4
trace_context = 4,
cloud_trace = 5
};

// DynamicConfigurationValue is used for configuration values that can
Expand Down
3 changes: 3 additions & 0 deletions src/tracer/json_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ static PropagationMode GetPropagationMode(opentracing::string_view s) {
if (s == "trace_context") {
return PropagationMode::trace_context;
}
if (s == "cloud_trace") {
return PropagationMode::cloud_trace;
}
std::ostringstream oss;
oss << "invalid propagation mode " << s;
throw std::runtime_error{oss.str()};
Expand Down
16 changes: 16 additions & 0 deletions src/tracer/propagation/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ lightstep_cc_library(
":envoy_propagator_lib",
":trace_context_propagator_lib",
":baggage_propagator_lib",
":cloud_trace_propagator_lib",
],
)

Expand All @@ -184,3 +185,18 @@ lightstep_cc_library(
":trace_context_lib",
],
)

lightstep_cc_library(
name = "cloud_trace_propagator_lib",
private_hdrs = [
"cloud_trace_propagator.h",
],
srcs = [
"cloud_trace_propagator.cpp",
],
deps = [
"//3rd_party/base64:base64_lib",
":binary_propagation_lib",
":utility_lib",
],
)
191 changes: 191 additions & 0 deletions src/tracer/propagation/cloud_trace_propagator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#include "tracer/propagation/cloud_trace_propagator.h"

#include "common/hex_conversion.h"

#include "tracer/propagation/binary_propagation.h"
#include "tracer/propagation/utility.h"

const opentracing::string_view PropagationSingleKey = "x-cloud-trace-context";
const opentracing::string_view PrefixBaggage = "ot-baggage-";

namespace lightstep {
//--------------------------------------------------------------------------------------------------
// InjectSpanContext
//--------------------------------------------------------------------------------------------------
opentracing::expected<void> CloudTracePropagator::InjectSpanContext(
const opentracing::TextMapWriter& carrier,
const TraceContext& trace_context, opentracing::string_view /*trace_state*/,
const BaggageProtobufMap& /*baggage*/) const {
return this->InjectSpanContextImpl(carrier, trace_context);
}

opentracing::expected<void> CloudTracePropagator::InjectSpanContext(
const opentracing::TextMapWriter& carrier,
const TraceContext& trace_context, opentracing::string_view /*trace_state*/,
const BaggageFlatMap& /*baggage*/) const {
return this->InjectSpanContextImpl(carrier, trace_context);
}
//--------------------------------------------------------------------------------------------------
// ExtractSpanContext
//--------------------------------------------------------------------------------------------------
opentracing::expected<bool> CloudTracePropagator::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);
});
};
opentracing::expected<bool> result;
if (case_sensitive) {
result = this->ExtractSpanContextImpl(carrier, trace_context,
std::equal_to<opentracing::string_view>{}, baggage);
} else {
result = this->ExtractSpanContextImpl(carrier, trace_context, iequals, baggage);
}
if (!result || !*result) {
return result;
}

return result;
}

//--------------------------------------------------------------------------------------------------
// InjectSpanContextImpl
//--------------------------------------------------------------------------------------------------
opentracing::expected<void> CloudTracePropagator::InjectSpanContextImpl(
const opentracing::TextMapWriter& carrier,
const TraceContext& trace_context) const {
std::array<char, CloudContextLength> buffer;
auto data_length = this->SerializeCloudTrace(trace_context, buffer.data());
return carrier.Set(PropagationSingleKey,
opentracing::string_view{buffer.data(), data_length});
}

//--------------------------------------------------------------------------------------------------
// ExtractSpanContextImpl
//--------------------------------------------------------------------------------------------------
template <class KeyCompare>
opentracing::expected<bool> CloudTracePropagator::ExtractSpanContextImpl(
const opentracing::TextMapReader& carrier, TraceContext& trace_context, const KeyCompare& key_compare,
BaggageProtobufMap& baggage) const {
bool parent_header_found = false;
auto result =
carrier.ForeachKey([&](opentracing::string_view key,
opentracing::string_view
value) noexcept->opentracing::expected<void> {
if (key_compare(key, PropagationSingleKey)) {
auto was_successful = this->ParseCloudTrace(value, trace_context);
if (!was_successful) {
return opentracing::make_unexpected(was_successful.error());
}
parent_header_found = true;
} 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;
}

opentracing::expected<void> CloudTracePropagator::ParseCloudTrace(
opentracing::string_view s, TraceContext& trace_context) const noexcept {
if (s.size() < Num128BitHexDigits) {
return opentracing::make_unexpected(
std::make_error_code(std::errc::invalid_argument));
}
size_t offset = 0;

// default sampled to on (this comes from the ;o=1 part of
// x-cloud-trace-context; if it is not set we will default to sampling
// this request)
trace_context.trace_flags = SetTraceFlag<SampledFlagMask>(trace_context.trace_flags, true);

// 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.size() - offset < 2) {
// only a short form trace ID has been given (not a "trace id/span id")
return {};
}

if (s[offset] != '/') {
return opentracing::make_unexpected(
std::make_error_code(std::errc::invalid_argument));
}
++offset;

std::array<char, Num64BitDecimalDigits + 1> parent_id;
size_t i;
for (i=0; i < Num64BitDecimalDigits; ++i) {
if (offset == s.length() || std::isdigit(s[offset]) == 0) {
break;
}
parent_id[i] = s[offset];
++offset;
}
parent_id[i] = '\0';

// parent-id
errno = 0;
trace_context.parent_id = std::strtoull(parent_id.data(), nullptr, 10);
if (errno == ERANGE) {
return opentracing::make_unexpected(
std::make_error_code(std::errc::result_out_of_range));
}

if (s.size() - offset < 4) {
// only a "trace ID/span ID" has been given (not a "trace id/span id;o=[0-1]")
return {};
}

if(opentracing::string_view(s.begin() + offset, 3) != opentracing::string_view(";o=")) {
return opentracing::make_unexpected(
std::make_error_code(std::errc::invalid_argument));
}

offset += 3;

// trace-flags
if (s[offset] == '0') {
// don't sample
trace_context.trace_flags = SetTraceFlag<SampledFlagMask>(trace_context.trace_flags, false);
}

return {};
}

size_t CloudTracePropagator::SerializeCloudTrace(const TraceContext& trace_context,
char* s) const noexcept {
size_t offset = 0;
// trace-id
Uint64ToHex(trace_context.trace_id_high, s);
offset += Num64BitHexDigits;
Uint64ToHex(trace_context.trace_id_low, s + offset);
offset += Num64BitHexDigits;

offset += snprintf(s + offset, CloudContextLength - offset, "/%lu;o=%d", trace_context.parent_id, IsTraceFlagSet<SampledFlagMask>(trace_context.trace_flags) ? 1 : 0);

return offset;
}
} // namespace lightstep
45 changes: 45 additions & 0 deletions src/tracer/propagation/cloud_trace_propagator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include "tracer/propagation/propagator.h"

namespace lightstep {

const size_t CloudContextLength = 58; // max x-cloud-trace-context header

const size_t Num64BitDecimalDigits = 20;

class CloudTracePropagator final : public Propagator {
public:
// Propagator
opentracing::expected<void> InjectSpanContext(
const opentracing::TextMapWriter& carrier,
const TraceContext& trace_context, opentracing::string_view trace_state,
const BaggageProtobufMap& baggage) const override;

opentracing::expected<void> InjectSpanContext(
const opentracing::TextMapWriter& carrier,
const TraceContext& trace_context, opentracing::string_view trace_state,
const BaggageFlatMap& baggage) const override;

opentracing::expected<bool> ExtractSpanContext(
const opentracing::TextMapReader& carrier, bool case_sensitive,
TraceContext& trace_context, std::string& trace_state,
BaggageProtobufMap& baggage) const override;

private:
opentracing::expected<void> InjectSpanContextImpl(
const opentracing::TextMapWriter& carrier,
const TraceContext& trace_context) const;

template <class KeyCompare>
opentracing::expected<bool> ExtractSpanContextImpl(
const opentracing::TextMapReader& carrier, TraceContext& trace_context,
const KeyCompare& key_compare, BaggageProtobufMap& baggage) const;

opentracing::expected<void> ParseCloudTrace(
opentracing::string_view s, lightstep::TraceContext& trace_context) const noexcept;

size_t SerializeCloudTrace(const TraceContext& trace_context,
char* s) const noexcept;
};
} // namespace lightstep
4 changes: 4 additions & 0 deletions src/tracer/propagation/propagation_options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "tracer/propagation/envoy_propagator.h"
#include "tracer/propagation/lightstep_propagator.h"
#include "tracer/propagation/trace_context_propagator.h"
#include "tracer/propagation/cloud_trace_propagator.h"

namespace lightstep {
//--------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -76,6 +77,9 @@ static std::vector<std::unique_ptr<Propagator>> MakePropagators(
case PropagationMode::trace_context:
result.emplace_back(new TraceContextPropagator{});
break;
case PropagationMode::cloud_trace:
result.emplace_back(new CloudTracePropagator{});
break;
}
}
return result;
Expand Down
14 changes: 14 additions & 0 deletions test/tracer/propagation/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,17 @@ lightstep_catch_test(
"//src/tracer/propagation:propagation_lib",
],
)

lightstep_catch_test(
name = "cloud_trace_propagation_test",
srcs = [
"cloud_trace_propagation_test.cpp",
],
deps = [
"//:manual_tracer_lib",
"//test/recorder:in_memory_recorder_lib",
":text_map_carrier_lib",
":http_headers_carrier_lib",
":utility_lib",
],
)
Loading