Skip to content

Commit

Permalink
Add Jaeger propagator (#599)
Browse files Browse the repository at this point in the history
  • Loading branch information
seemk authored Mar 12, 2021
1 parent b80cc91 commit 1e5751e
Show file tree
Hide file tree
Showing 12 changed files with 468 additions and 27 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Increment the:
## [Unreleased]

* [EXPORTER] Added Zipkin Exporter. ([#471](https://github.com/open-telemetry/opentelemetry-cpp/pull/471))
* [API] Added Jaeger propagator. ([#599](https://github.com/open-telemetry/opentelemetry-cpp/pull/599))
* [PROPAGATOR] Added Composite Propagator ([#597](https://github.com/open-telemetry/opentelemetry-cpp/pull/597))

## [0.2.0] 2021-03-02
Expand Down
2 changes: 1 addition & 1 deletion api/include/opentelemetry/context/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class Context
}

// Returns the value associated with the passed in key.
context::ContextValue GetValue(const nostd::string_view key) noexcept
context::ContextValue GetValue(const nostd::string_view key) const noexcept
{
for (DataList *data = head_.get(); data != nullptr; data = data->next_.get())
{
Expand Down
16 changes: 3 additions & 13 deletions api/include/opentelemetry/trace/propagation/b3_propagator.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <iostream>
#include <map>
#include <string>
#include "detail/context.h"
#include "opentelemetry/common/key_value_iterable.h"
#include "opentelemetry/context/context.h"
#include "opentelemetry/nostd/shared_ptr.h"
Expand Down Expand Up @@ -62,17 +63,6 @@ class B3PropagatorExtractor : public TextMapPropagator<T>
return context.SetValue(kSpanKey, sp);
}

static SpanContext GetCurrentSpan(const context::Context &context)
{
context::Context ctx(context);
context::ContextValue span = ctx.GetValue(kSpanKey);
if (nostd::holds_alternative<nostd::shared_ptr<Span>>(span))
{
return nostd::get<nostd::shared_ptr<Span>>(span).get()->GetContext();
}
return SpanContext::GetInvalid();
}

static TraceId GenerateTraceIdFromString(nostd::string_view trace_id)
{
uint8_t buf[kTraceIdHexStrLength / 2];
Expand Down Expand Up @@ -213,7 +203,7 @@ class B3Propagator : public B3PropagatorExtractor<T>
// Sets the context for a HTTP header carrier with self defined rules.
void Inject(Setter setter, T &carrier, const context::Context &context) noexcept override
{
SpanContext span_context = B3PropagatorExtractor<T>::GetCurrentSpan(context);
SpanContext span_context = detail::GetCurrentSpan(context);
if (!span_context.IsValid())
{
return;
Expand Down Expand Up @@ -251,7 +241,7 @@ class B3PropagatorMultiHeader : public B3PropagatorExtractor<T>
nostd::string_view trace_description);
void Inject(Setter setter, T &carrier, const context::Context &context) noexcept override
{
SpanContext span_context = B3PropagatorExtractor<T>::GetCurrentSpan(context);
SpanContext span_context = detail::GetCurrentSpan(context);
if (!span_context.IsValid())
{
return;
Expand Down
26 changes: 26 additions & 0 deletions api/include/opentelemetry/trace/propagation/detail/context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once
#include "opentelemetry/context/context.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace trace
{
namespace propagation
{
namespace detail
{

trace::SpanContext GetCurrentSpan(const context::Context &context)
{
context::ContextValue span = context.GetValue(trace::kSpanKey);
if (nostd::holds_alternative<nostd::shared_ptr<trace::Span>>(span))
{
return nostd::get<nostd::shared_ptr<trace::Span>>(span).get()->GetContext();
}

return trace::SpanContext::GetInvalid();
}

} // namespace detail
} // namespace propagation
} // namespace trace
OPENTELEMETRY_END_NAMESPACE
74 changes: 74 additions & 0 deletions api/include/opentelemetry/trace/propagation/detail/hex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#pragma once

#include "opentelemetry/nostd/string_view.h"

#include <algorithm>
#include <cstring>

OPENTELEMETRY_BEGIN_NAMESPACE
namespace trace
{
namespace propagation
{
namespace detail
{

constexpr int8_t kHexDigits[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};

inline int8_t HexToInt(char c)
{
return kHexDigits[uint8_t(c)];
}

bool IsValidHex(nostd::string_view s)
{
return std::all_of(s.begin(), s.end(), [](char c) { return HexToInt(c) != -1; });
}

/**
* Converts a hexadecimal to binary format if the hex string will fit the buffer.
* Smaller hex strings are left padded with zeroes.
*/
bool HexToBinary(nostd::string_view hex, uint8_t *buffer, size_t buffer_size)
{
if (hex.size() > buffer_size * 2)
{
return false;
}

std::memset(buffer, 0, buffer_size);

int64_t hex_size = int64_t(hex.size());
int64_t buffer_pos = int64_t(buffer_size) - (hex_size + 1) / 2;
int64_t last_hex_pos = hex_size - 1;

int64_t i = 0;
for (; i < last_hex_pos; i += 2)
{
buffer[buffer_pos++] = (HexToInt(hex[i]) << 4) | HexToInt(hex[i + 1]);
}

if (i == last_hex_pos)
{
buffer[buffer_pos] = HexToInt(hex[i]);
}

return true;
}

} // namespace detail
} // namespace propagation
} // namespace trace
OPENTELEMETRY_END_NAMESPACE
54 changes: 54 additions & 0 deletions api/include/opentelemetry/trace/propagation/detail/string.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#pragma once

#include "opentelemetry/nostd/string_view.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace trace
{
namespace propagation
{
namespace detail
{

/**
* Splits a string by separator, up to given buffer count words.
* Returns the amount of words the input was split into.
*/
size_t SplitString(nostd::string_view s, char separator, nostd::string_view *results, size_t count)
{
if (count == 0)
{
return count;
}

size_t filled = 0;
size_t token_start = 0;
for (size_t i = 0; i < s.size(); i++)
{
if (s[i] != separator)
{
continue;
}

results[filled++] = s.substr(token_start, i - token_start);

if (filled == count)
{
return count;
}

token_start = i + 1;
}

if (filled < count)
{
results[filled++] = s.substr(token_start);
}

return filled;
}

} // namespace detail
} // namespace propagation
} // namespace trace
OPENTELEMETRY_END_NAMESPACE
14 changes: 2 additions & 12 deletions api/include/opentelemetry/trace/propagation/http_trace_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <iostream>
#include <map>
#include <string>
#include "detail/context.h"
#include "opentelemetry/common/key_value_iterable.h"
#include "opentelemetry/context/context.h"
#include "opentelemetry/nostd/shared_ptr.h"
Expand Down Expand Up @@ -65,7 +66,7 @@ class HttpTraceContext : public TextMapPropagator<T>

void Inject(Setter setter, T &carrier, const context::Context &context) noexcept override
{
SpanContext span_context = GetCurrentSpan(context);
SpanContext span_context = detail::GetCurrentSpan(context);
if (!span_context.IsValid())
{
return;
Expand All @@ -82,17 +83,6 @@ class HttpTraceContext : public TextMapPropagator<T>
return context.SetValue(kSpanKey, sp);
}

static SpanContext GetCurrentSpan(const context::Context &context)
{
context::Context ctx(context);
context::ContextValue span = ctx.GetValue(kSpanKey);
if (nostd::holds_alternative<nostd::shared_ptr<Span>>(span))
{
return nostd::get<nostd::shared_ptr<Span>>(span).get()->GetContext();
}
return SpanContext::GetInvalid();
}

static TraceId GenerateTraceIdFromString(nostd::string_view trace_id)
{
int trace_id_len = kHeaderElementLengths[1];
Expand Down
131 changes: 131 additions & 0 deletions api/include/opentelemetry/trace/propagation/jaeger.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#pragma once

// Copyright 2021, OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "detail/context.h"
#include "detail/hex.h"
#include "detail/string.h"
#include "opentelemetry/trace/default_span.h"
#include "opentelemetry/trace/propagation/text_map_propagator.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace trace
{
namespace propagation
{

static const nostd::string_view kTraceHeader = "uber-trace-id";

template <typename T>
class JaegerPropagator : public TextMapPropagator<T>
{
public:
using Getter = nostd::string_view (*)(const T &carrier, nostd::string_view trace_type);

using Setter = void (*)(T &carrier,
nostd::string_view trace_type,
nostd::string_view trace_description);

void Inject(Setter setter, T &carrier, const context::Context &context) noexcept override
{
SpanContext span_context = detail::GetCurrentSpan(context);
if (!span_context.IsValid())
{
return;
}

const size_t trace_id_length = 32;
const size_t span_id_length = 16;

// trace-id(32):span-id(16):0:debug(2)
char trace_identity[trace_id_length + span_id_length + 6];
span_context.trace_id().ToLowerBase16({&trace_identity[0], trace_id_length});
trace_identity[trace_id_length] = ':';
span_context.span_id().ToLowerBase16({&trace_identity[trace_id_length + 1], span_id_length});
trace_identity[trace_id_length + span_id_length + 1] = ':';
trace_identity[trace_id_length + span_id_length + 2] = '0';
trace_identity[trace_id_length + span_id_length + 3] = ':';
trace_identity[trace_id_length + span_id_length + 4] = '0';
trace_identity[trace_id_length + span_id_length + 5] = span_context.IsSampled() ? '1' : '0';

setter(carrier, kTraceHeader, nostd::string_view(trace_identity, sizeof(trace_identity)));
}

context::Context Extract(Getter getter,
const T &carrier,
context::Context &context) noexcept override
{
SpanContext span_context = ExtractImpl(getter, carrier);
nostd::shared_ptr<Span> sp{new DefaultSpan(span_context)};
return context.SetValue(kSpanKey, sp);
}

private:
static constexpr uint8_t kIsSampled = 0x01;

static TraceFlags GetTraceFlags(uint8_t jaeger_flags)
{
uint8_t sampled = jaeger_flags & kIsSampled;
return TraceFlags(sampled);
}

static SpanContext ExtractImpl(Getter getter, const T &carrier)
{
nostd::string_view trace_identity = getter(carrier, kTraceHeader);

const size_t trace_field_count = 4;
nostd::string_view trace_fields[trace_field_count];

if (detail::SplitString(trace_identity, ':', trace_fields, trace_field_count) !=
trace_field_count)
{
return SpanContext::GetInvalid();
}

nostd::string_view trace_id_hex = trace_fields[0];
nostd::string_view span_id_hex = trace_fields[1];
nostd::string_view flags_hex = trace_fields[3];

if (!detail::IsValidHex(trace_id_hex) || !detail::IsValidHex(span_id_hex) ||
!detail::IsValidHex(flags_hex))
{
return SpanContext::GetInvalid();
}

uint8_t trace_id[16];
if (!detail::HexToBinary(trace_id_hex, trace_id, sizeof(trace_id)))
{
return SpanContext::GetInvalid();
}

uint8_t span_id[8];
if (!detail::HexToBinary(span_id_hex, span_id, sizeof(span_id)))
{
return SpanContext::GetInvalid();
}

uint8_t flags;
if (!detail::HexToBinary(flags_hex, &flags, sizeof(flags)))
{
return SpanContext::GetInvalid();
}

return SpanContext(TraceId(trace_id), SpanId(span_id), GetTraceFlags(flags), true);
}
};

} // namespace propagation
} // namespace trace
OPENTELEMETRY_END_NAMESPACE
Loading

0 comments on commit 1e5751e

Please sign in to comment.