diff --git a/api/include/opentelemetry/plugin/tracer.h b/api/include/opentelemetry/plugin/tracer.h index 619bc2e405..a1a15e6650 100644 --- a/api/include/opentelemetry/plugin/tracer.h +++ b/api/include/opentelemetry/plugin/tracer.h @@ -48,7 +48,7 @@ class Span final : public trace::Span bool IsRecording() const noexcept override { return span_->IsRecording(); } - trace::Tracer &tracer() const noexcept override { return *tracer_; } + trace::SpanContext GetContext() const noexcept override { return span_->GetContext(); } void SetToken(nostd::unique_ptr &&token) noexcept override {} diff --git a/api/include/opentelemetry/trace/default_span.h b/api/include/opentelemetry/trace/default_span.h new file mode 100644 index 0000000000..ec58437ec2 --- /dev/null +++ b/api/include/opentelemetry/trace/default_span.h @@ -0,0 +1,59 @@ +#pragma once +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/trace/canonical_code.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace trace +{ +class DefaultSpan : public Span +{ +public: + // Returns an invalid span. + static DefaultSpan GetInvalid() { return DefaultSpan(SpanContext::GetInvalid()); } + + trace::SpanContext GetContext() const noexcept { return span_context_; } + + bool IsRecording() const noexcept { return false; } + + void SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept {} + + void AddEvent(nostd::string_view name) noexcept {} + + void AddEvent(nostd::string_view name, core::SystemTimestamp timestamp) noexcept {} + + void AddEvent(nostd::string_view name, + core::SystemTimestamp timestamp, + const KeyValueIterable &attributes) noexcept + {} + + void AddEvent(nostd::string_view name, const KeyValueIterable &attributes) noexcept + { + this->AddEvent(name, std::chrono::system_clock::now(), attributes); + } + + void SetStatus(CanonicalCode status, nostd::string_view description) noexcept {} + + void UpdateName(nostd::string_view name) noexcept {} + + void End(const EndSpanOptions &options = {}) noexcept {} + + nostd::string_view ToString() { return "DefaultSpan"; } + + void SetToken(nostd::unique_ptr &&default_token) noexcept {} + + DefaultSpan() = default; + + DefaultSpan(SpanContext span_context) : span_context_(span_context) {} + + // movable and copiable + DefaultSpan(DefaultSpan &&spn) : span_context_(spn.GetContext()) {} + DefaultSpan(const DefaultSpan &spn) : span_context_(spn.GetContext()) {} + +private: + SpanContext span_context_; +}; + +} // namespace trace +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/default_tracer.h b/api/include/opentelemetry/trace/default_tracer.h new file mode 100644 index 0000000000..f6b24aede0 --- /dev/null +++ b/api/include/opentelemetry/trace/default_tracer.h @@ -0,0 +1,35 @@ +#pragma once +#include "opentelemetry/nostd/unique_ptr.h" +#include "opentelemetry/trace/default_span.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/tracer.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace trace +{ +class DefaultTracer : public Tracer +{ +public: + ~DefaultTracer() = default; + + /** + * Starts a span. + * + * Optionally sets attributes at Span creation from the given key/value pairs. + * + * Attributes will be processed in order, previous attributes with the same + * key will be overwritten. + */ + nostd::unique_ptr StartSpan(nostd::string_view name, + const KeyValueIterable &attributes, + const StartSpanOptions &options = {}) override noexcept + { + return nostd::unique_ptr(new DefaultSpan::GetInvalid()); + } + + void ForceFlushWithMicroseconds(uint64_t timeout) override noexcept {} + + void CloseWithMicroseconds(uint64_t timeout) override noexcept {} +}; +} // namespace trace +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/noop.h b/api/include/opentelemetry/trace/noop.h index 542b0ce382..afb42c1661 100644 --- a/api/include/opentelemetry/trace/noop.h +++ b/api/include/opentelemetry/trace/noop.h @@ -8,6 +8,7 @@ #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/tracer.h" #include "opentelemetry/trace/tracer_provider.h" #include "opentelemetry/version.h" @@ -47,12 +48,13 @@ class NoopSpan final : public Span bool IsRecording() const noexcept override { return false; } - Tracer &tracer() const noexcept override { return *tracer_; } + SpanContext GetContext() const noexcept override { return span_context_; } void SetToken(nostd::unique_ptr && /* token */) noexcept override {} private: std::shared_ptr tracer_; + SpanContext span_context_; }; /** diff --git a/api/include/opentelemetry/trace/propagation/http_text_format.h b/api/include/opentelemetry/trace/propagation/http_text_format.h new file mode 100644 index 0000000000..18cb434632 --- /dev/null +++ b/api/include/opentelemetry/trace/propagation/http_text_format.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include "opentelemetry/context/context.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace trace +{ +namespace propagation +{ + +// The HTTPTextFormat class provides an interface that enables extracting and injecting +// context into headers of HTTP requests. HTTP frameworks and clients +// can integrate with HTTPTextFormat by providing the object containing the +// headers, and a getter and setter function for the extraction and +// injection of values, respectively. +template +class HTTPTextFormat +{ +public: + // Rules that manages how context will be extracted from carrier. + using Getter = nostd::string_view (*)(const T &carrier, nostd::string_view trace_type); + + // Rules that manages how context will be injected to carrier. + using Setter = void (*)(T &carrier, + nostd::string_view trace_type, + nostd::string_view trace_description); + + // Returns the context that is stored in the HTTP header carrier with the getter as extractor. + virtual context::Context Extract(Getter get_from_carrier, + const T &carrier, + context::Context &context) noexcept = 0; + + // Sets the context for a HTTP header carrier with self defined rules. + virtual void Inject(Setter set_from_carrier, + T &carrier, + const context::Context &context) noexcept = 0; +}; +} // namespace propagation +} // namespace trace +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/propagation/http_trace_context.h b/api/include/opentelemetry/trace/propagation/http_trace_context.h new file mode 100644 index 0000000000..259bbcbc3b --- /dev/null +++ b/api/include/opentelemetry/trace/propagation/http_trace_context.h @@ -0,0 +1,272 @@ +// Copyright 2020, 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 +#include +#include +#include +#include "opentelemetry/context/context.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/trace/default_span.h" +#include "opentelemetry/trace/key_value_iterable.h" +#include "opentelemetry/trace/propagation/http_text_format.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace trace +{ +namespace propagation +{ +static const nostd::string_view kTraceParent = "traceparent"; +static const nostd::string_view kTraceState = "tracestate"; +static const int kTraceDelimiterBytes = 3; +static const int kHeaderElementLengths[4] = { + 2, 32, 16, 2}; // 0: version, 1: trace id, 2: span id, 3: trace flags +static const int kHeaderSize = kHeaderElementLengths[0] + kHeaderElementLengths[1] + + kHeaderElementLengths[2] + kHeaderElementLengths[3] + + kTraceDelimiterBytes; +static const int kTraceStateMaxMembers = 32; +static const int kVersionBytes = 2; +static const int kTraceIdBytes = 32; +static const int kSpanIdBytes = 16; +static const int kTraceFlagBytes = 2; + +// The HttpTraceContext provides methods to extract and inject +// context into headers of HTTP requests with traces. +// Example: +// HttpTraceContext.inject(setter,&carrier,&context); +// HttpTraceContext.extract(getter,&carrier,&context); +template +class HttpTraceContext : public HTTPTextFormat +{ +public: + // Rules that manages how context will be extracted from carrier. + using Getter = nostd::string_view (*)(const T &carrier, nostd::string_view trace_type); + + // Rules that manages how context will be injected to carrier. + 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 = GetCurrentSpan(context); + if (!span_context.IsValid()) + { + return; + } + InjectImpl(setter, carrier, span_context); + } + + context::Context Extract(Getter getter, + const T &carrier, + context::Context &context) noexcept override + { + SpanContext span_context = ExtractImpl(getter, carrier); + nostd::string_view span_key = "current-span"; + nostd::shared_ptr sp{new DefaultSpan(span_context)}; + return context.SetValue(span_key, sp); + } + + static SpanContext GetCurrentSpan(const context::Context &context) + { + const nostd::string_view span_key = "current-span"; + context::Context ctx(context); + context::ContextValue span = ctx.GetValue(span_key); + if (nostd::holds_alternative>(span)) + { + return nostd::get>(span).get()->GetContext(); + } + return SpanContext(); + } + + static TraceId GenerateTraceIdFromString(nostd::string_view trace_id) + { + int trace_id_len = kHeaderElementLengths[1]; + uint8_t buf[kTraceIdBytes / 2]; + uint8_t *b_ptr = buf; + GenerateHexFromString(trace_id, trace_id_len, b_ptr); + return TraceId(buf); + } + + static SpanId GenerateSpanIdFromString(nostd::string_view span_id) + { + int span_id_len = kHeaderElementLengths[2]; + uint8_t buf[kSpanIdBytes / 2]; + uint8_t *b_ptr = buf; + GenerateHexFromString(span_id, span_id_len, b_ptr); + return SpanId(buf); + } + + static TraceFlags GenerateTraceFlagsFromString(nostd::string_view trace_flags) + { + if (trace_flags.length() > 2) + { + return TraceFlags(0); // check for invalid length of flags + } + int tmp1 = HexToInt(trace_flags[0]); + int tmp2 = HexToInt(trace_flags[1]); + if (tmp1 < 0 || tmp2 < 0) + return TraceFlags(0); // check for invalid char + uint8_t buf = tmp1 * 16 + tmp2; + return TraceFlags(buf); + } + +private: + // Converts the hex numbers stored as strings into bytes stored in a buffer. + static void GenerateHexFromString(nostd::string_view string, int bytes, uint8_t *buf) + { + const char *str_id = string.begin(); + for (int i = 0; i < bytes; i++) + { + int tmp = HexToInt(str_id[i]); + if (tmp < 0) + { + for (int j = 0; j < bytes / 2; j++) + { + buf[j] = 0; + } + return; + } + if (i % 2 == 0) + { + buf[i / 2] = tmp * 16; + } + else + { + buf[i / 2] += tmp; + } + } + } + + // Converts a single character to a corresponding integer (e.g. '1' to 1), return -1 + // if the character is not a valid number in hex. + static uint8_t HexToInt(char c) + { + if (c >= '0' && c <= '9') + { + return (int)(c - '0'); + } + else if (c >= 'a' && c <= 'f') + { + return (int)(c - 'a' + 10); + } + else if (c >= 'A' && c <= 'F') + { + return (int)(c - 'A' + 10); + } + else + { + return -1; + } + } + + static void InjectTraceParent(const SpanContext &span_context, T &carrier, Setter setter) + { + char trace_id[32]; + TraceId(span_context.trace_id()).ToLowerBase16(trace_id); + char span_id[16]; + SpanId(span_context.span_id()).ToLowerBase16(span_id); + char trace_flags[2]; + TraceFlags(span_context.trace_flags()).ToLowerBase16(trace_flags); + // Note: This is only temporary replacement for appendable string + std::string hex_string = "00-"; + for (int i = 0; i < 32; i++) + { + hex_string.push_back(trace_id[i]); + } + hex_string.push_back('-'); + for (int i = 0; i < 16; i++) + { + hex_string.push_back(span_id[i]); + } + hex_string.push_back('-'); + for (int i = 0; i < 2; i++) + { + hex_string.push_back(trace_flags[i]); + } + setter(carrier, kTraceParent, hex_string); + } + + static void InjectImpl(Setter setter, T &carrier, const SpanContext &span_context) + { + InjectTraceParent(span_context, carrier, setter); + } + + static bool IsValidHex(nostd::string_view string_view) + { + for (int i = 0; i < string_view.length(); i++) + { + if (!(string_view[i] >= '0' && string_view[i] <= '9') && + !(string_view[i] >= 'a' && string_view[i] <= 'f')) + return false; + } + return true; + } + + static SpanContext ExtractContextFromTraceParent(nostd::string_view trace_parent) + { + if (trace_parent.length() != kHeaderSize || trace_parent[kHeaderElementLengths[0]] != '-' || + trace_parent[kHeaderElementLengths[0] + kHeaderElementLengths[1] + 1] != '-' || + trace_parent[kHeaderElementLengths[0] + kHeaderElementLengths[1] + + kHeaderElementLengths[2] + 2] != '-') + { + std::cout << "Unparseable trace_parent header. Returning INVALID span context." << std::endl; + return SpanContext(false, false); + } + nostd::string_view version = trace_parent.substr(0, kHeaderElementLengths[0]); + nostd::string_view trace_id = + trace_parent.substr(kHeaderElementLengths[0] + 1, kHeaderElementLengths[1]); + nostd::string_view span_id = trace_parent.substr( + kHeaderElementLengths[0] + kHeaderElementLengths[1] + 2, kHeaderElementLengths[2]); + nostd::string_view trace_flags = trace_parent.substr( + kHeaderElementLengths[0] + kHeaderElementLengths[1] + kHeaderElementLengths[2] + 3); + + if (version == "ff" || trace_id == "00000000000000000000000000000000" || + span_id == "0000000000000000") + { + return SpanContext(false, false); + } + + // validate ids + if (!IsValidHex(version) || !IsValidHex(trace_id) || !IsValidHex(span_id) || + !IsValidHex(trace_flags)) + { + return SpanContext(false, false); + } + + TraceId trace_id_obj = GenerateTraceIdFromString(trace_id); + SpanId span_id_obj = GenerateSpanIdFromString(span_id); + TraceFlags trace_flags_obj = GenerateTraceFlagsFromString(trace_flags); + return SpanContext(trace_id_obj, span_id_obj, trace_flags_obj, true); + } + + static SpanContext ExtractImpl(Getter getter, const T &carrier) + { + nostd::string_view trace_parent = getter(carrier, kTraceParent); + if (trace_parent == "") + { + return SpanContext(false, false); + } + + return ExtractContextFromTraceParent(trace_parent); + } +}; +} // namespace propagation +} // namespace trace +OPENTELEMETRY_END_NAMESPACE diff --git a/api/include/opentelemetry/trace/span.h b/api/include/opentelemetry/trace/span.h index 01be49f5db..571a7aa2b8 100644 --- a/api/include/opentelemetry/trace/span.h +++ b/api/include/opentelemetry/trace/span.h @@ -9,6 +9,7 @@ #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/trace/canonical_code.h" #include "opentelemetry/trace/key_value_iterable_view.h" +#include "opentelemetry/trace/span_context.h" #include "opentelemetry/version.h" constexpr char SpanKey[] = "span_key"; @@ -156,15 +157,12 @@ class Span */ virtual void End(const EndSpanOptions &options = {}) noexcept = 0; - // TODO - // SpanContext context() const noexcept = 0; + virtual trace::SpanContext GetContext() const noexcept = 0; // Returns true if this Span is recording tracing events (e.g. SetAttribute, // AddEvent). virtual bool IsRecording() const noexcept = 0; - virtual Tracer &tracer() const noexcept = 0; - virtual void SetToken(nostd::unique_ptr &&token) noexcept = 0; }; } // namespace trace diff --git a/api/include/opentelemetry/trace/span_context.h b/api/include/opentelemetry/trace/span_context.h index aed22cfe72..6228bf215c 100644 --- a/api/include/opentelemetry/trace/span_context.h +++ b/api/include/opentelemetry/trace/span_context.h @@ -33,6 +33,10 @@ namespace trace_api = opentelemetry::trace; class SpanContext final { public: + // An invalid SpanContext. + SpanContext() noexcept + : trace_flags_(trace::TraceFlags((uint8_t) false)), remote_parent_(false){}; + /* A temporary constructor for an invalid SpanContext. * Trace id and span id are set to invalid (all zeros). * @@ -53,19 +57,61 @@ class SpanContext final // @returns the trace_flags associated with this span_context const trace_api::TraceFlags &trace_flags() const noexcept { return trace_flags_; } - // @returns whether this context has the sampled flag set or not - bool IsSampled() const noexcept { return trace_flags_.IsSampled(); } + const trace_api::TraceId &trace_id() const noexcept { return trace_id_; } + + const trace_api::SpanId &span_id() const noexcept { return span_id_; } + + SpanContext(TraceId trace_id, + SpanId span_id, + TraceFlags trace_flags, + bool has_remote_parent) noexcept + : trace_id_(trace_id), + span_id_(span_id), + trace_flags_(trace_flags), + remote_parent_(has_remote_parent) + {} + + SpanContext(SpanContext &&ctx) + : trace_id_(ctx.trace_id()), span_id_(ctx.span_id()), trace_flags_(ctx.trace_flags()) + {} + + SpanContext(const SpanContext &ctx) + : trace_id_(ctx.trace_id()), span_id_(ctx.span_id()), trace_flags_(ctx.trace_flags()) + {} + // + // SpanContext &operator=(const SpanContext &ctx) + // { + // SpanContext *spn_ctx = + // new SpanContext(ctx.trace_id(), ctx.span_id(), ctx.trace_flags(), + // ctx.HasRemoteParent()); + // this = spn_ctx; + // return *this; + // }; + // + // SpanContext &operator=(SpanContext &&ctx) + // { + // SpanContext *spn_ctx = + // new SpanContext(ctx.trace_id(), ctx.span_id(), ctx.trace_flags(), + // ctx.HasRemoteParent()); + // this = spn_ctx; + // return *this; + // }; + + bool operator==(const SpanContext &that) const noexcept + { + return trace_id() == that.trace_id() && span_id() == that.span_id() && + trace_flags() == that.trace_flags(); + } - // @returns whether this context has a remote parent or not bool HasRemoteParent() const noexcept { return remote_parent_; } - const TraceId &trace_id() const noexcept { return trace_id_; } + static SpanContext GetInvalid() { return SpanContext(false, false); } - const SpanId &span_id() const noexcept { return span_id_; } + bool IsSampled() const noexcept { return trace_flags_.IsSampled(); } private: - const TraceId trace_id_; - const SpanId span_id_; + const trace_api::TraceId trace_id_; + const trace_api::SpanId span_id_; const trace_api::TraceFlags trace_flags_; const bool remote_parent_ = false; }; diff --git a/api/test/trace/BUILD b/api/test/trace/BUILD index 1fce77d641..b77ae1e2fe 100644 --- a/api/test/trace/BUILD +++ b/api/test/trace/BUILD @@ -1,9 +1,9 @@ load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark") cc_test( - name = "key_value_iterable_view_test", + name = "default_span_test", srcs = [ - "key_value_iterable_view_test.cc", + "default_span_test.cc", ], deps = [ "//api", @@ -22,6 +22,17 @@ cc_test( ], ) +cc_test( + name = "key_value_iterable_view_test", + srcs = [ + "key_value_iterable_view_test.cc", + ], + deps = [ + "//api", + "@com_google_googletest//:gtest_main", + ], +) + otel_cc_benchmark( name = "span_id_benchmark", srcs = ["span_id_benchmark.cc"], diff --git a/api/test/trace/CMakeLists.txt b/api/test/trace/CMakeLists.txt index 16cef9bc6d..558ce2d429 100644 --- a/api/test/trace/CMakeLists.txt +++ b/api/test/trace/CMakeLists.txt @@ -1,12 +1,12 @@ foreach( testname key_value_iterable_view_test - noop_test provider_test span_id_test trace_id_test trace_flags_test - span_context_test) + span_context_test + noop_test) add_executable(${testname} "${testname}.cc") target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) diff --git a/api/test/trace/default_span_test.cc b/api/test/trace/default_span_test.cc new file mode 100644 index 0000000000..f88da3b77f --- /dev/null +++ b/api/test/trace/default_span_test.cc @@ -0,0 +1,21 @@ +#include "opentelemetry/trace/default_span.h" +#include "opentelemetry/trace/span_context.h" + +#include +#include + +#include + +namespace +{ + +using opentelemetry::trace::DefaultSpan; +using opentelemetry::trace::SpanContext; + +TEST(DefaultSpanTest, GetContext) +{ + SpanContext span_context = SpanContext(false, false); + DefaultSpan sp = DefaultSpan(span_context); + EXPECT_EQ(span_context, sp.GetContext()); +} +} // namespace diff --git a/api/test/trace/noop_test.cc b/api/test/trace/noop_test.cc index 945a333f33..989f2acddc 100644 --- a/api/test/trace/noop_test.cc +++ b/api/test/trace/noop_test.cc @@ -1,4 +1,5 @@ #include "opentelemetry/trace/noop.h" +#include "opentelemetry/core/timestamp.h" #include #include @@ -6,14 +7,16 @@ #include +using opentelemetry::context::Token; +using opentelemetry::core::SystemTimestamp; using opentelemetry::trace::NoopTracer; +using opentelemetry::trace::SpanContext; using opentelemetry::trace::Tracer; TEST(NoopTest, UseNoopTracers) { std::shared_ptr tracer{new NoopTracer{}}; auto s1 = tracer->StartSpan("abc"); - EXPECT_EQ(&s1->tracer(), tracer.get()); std::map attributes1; s1->AddEvent("abc", attributes1); @@ -27,4 +30,19 @@ TEST(NoopTest, UseNoopTracers) s1->AddEvent("abc", attributes3); s1->SetAttribute("abc", 4); -} + + s1->AddEvent("abc"); // add Empty + + EXPECT_EQ(s1->IsRecording(), false); + + s1->SetStatus(opentelemetry::trace::CanonicalCode::INVALID_ARGUMENT, "span unavailable"); + + s1->UpdateName("test_name"); + + SystemTimestamp t1; + s1->AddEvent("test_time_stamp", t1); + + s1->SetToken(opentelemetry::nostd::unique_ptr(nullptr)); + + s1->GetContext(); +} \ No newline at end of file diff --git a/api/test/trace/propagation/BUILD b/api/test/trace/propagation/BUILD new file mode 100644 index 0000000000..974c7666d7 --- /dev/null +++ b/api/test/trace/propagation/BUILD @@ -0,0 +1,12 @@ +load("//bazel:otel_cc_benchmark.bzl", "otel_cc_benchmark") + +cc_test( + name = "http_text_format_test", + srcs = [ + "http_text_format_test.cc", + ], + deps = [ + "//api", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/api/test/trace/propagation/CMakeLists.txt b/api/test/trace/propagation/CMakeLists.txt new file mode 100644 index 0000000000..f2a9815426 --- /dev/null +++ b/api/test/trace/propagation/CMakeLists.txt @@ -0,0 +1,6 @@ +foreach(testname http_text_format_test) + add_executable(${testname} "${testname}.cc") + target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + gtest_add_tests(TARGET ${testname} TEST_PREFIX trace. TEST_LIST ${testname}) +endforeach() diff --git a/api/test/trace/propagation/http_text_format_test.cc b/api/test/trace/propagation/http_text_format_test.cc new file mode 100644 index 0000000000..c6262a1606 --- /dev/null +++ b/api/test/trace/propagation/http_text_format_test.cc @@ -0,0 +1,87 @@ +#include "opentelemetry/context/context.h" +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/nostd/span.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/trace/default_span.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/span_context.h" +#include "opentelemetry/trace/trace_id.h" +#include "opentelemetry/trace/tracer.h" + +#include +#include +#include + +#include + +#include "opentelemetry/trace/default_span.h" +#include "opentelemetry/trace/propagation/http_text_format.h" +#include "opentelemetry/trace/propagation/http_trace_context.h" + +using namespace opentelemetry; + +static nostd::string_view Getter(const std::map &carrier, + nostd::string_view trace_type = "traceparent") +{ + auto it = carrier.find(std::string(trace_type)); + if (it != carrier.end()) + { + return nostd::string_view(it->second); + } + return ""; +} + +static void Setter(std::map &carrier, + nostd::string_view trace_type = "traceparent", + nostd::string_view trace_description = "") +{ + carrier[std::string(trace_type)] = std::string(trace_description); +} + +using MapHttpTraceContext = + trace::propagation::HttpTraceContext>; + +static MapHttpTraceContext format = MapHttpTraceContext(); + +TEST(HTTPTextFormatTest, TraceIdBufferGeneration) +{ + constexpr uint8_t buf[] = {1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + EXPECT_EQ(MapHttpTraceContext::GenerateTraceIdFromString("01020304050607080807aabbccddeeff"), + trace::TraceId(buf)); +} + +TEST(HTTPTextFormatTest, SpanIdBufferGeneration) +{ + constexpr uint8_t buf[] = {1, 2, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + EXPECT_EQ(MapHttpTraceContext::GenerateSpanIdFromString("0102aabbccddeeff"), trace::SpanId(buf)); +} + +TEST(HTTPTextFormatTest, TraceFlagsBufferGeneration) +{ + EXPECT_EQ(MapHttpTraceContext::GenerateTraceFlagsFromString("00"), trace::TraceFlags()); +} + +TEST(HTTPTextFormatTest, NoSendEmptyTraceState) +{ + // If the trace state is empty, do not set the header. + const std::map carrier = { + {"traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-0102030405060708-01"}}; + context::Context ctx1 = + context::Context("current-span", nostd::shared_ptr(new trace::DefaultSpan())); + context::Context ctx2 = format.Extract(Getter, carrier, ctx1); + std::map c2 = {}; + format.Inject(Setter, c2, ctx2); + EXPECT_TRUE(carrier.count("traceparent") > 0); + EXPECT_FALSE(carrier.count("tracestate") > 0); +} + +TEST(HTTPTextFormatTest, PropagateInvalidContext) +{ + // Do not propagate invalid trace context. + std::map carrier = {}; + context::Context ctx{ + "current-span", + nostd::shared_ptr(new trace::DefaultSpan(trace::SpanContext::GetInvalid()))}; + format.Inject(Setter, carrier, ctx); + EXPECT_TRUE(carrier.count("traceparent") == 0); +} diff --git a/examples/plugin/plugin/tracer.cc b/examples/plugin/plugin/tracer.cc index db1954a6f3..8226fc1ce0 100644 --- a/examples/plugin/plugin/tracer.cc +++ b/examples/plugin/plugin/tracer.cc @@ -3,6 +3,7 @@ #include "opentelemetry/nostd/unique_ptr.h" #include +#include namespace nostd = opentelemetry::nostd; namespace common = opentelemetry::common; @@ -51,13 +52,14 @@ class Span final : public trace::Span bool IsRecording() const noexcept override { return true; } - Tracer &tracer() const noexcept override { return *tracer_; } + trace::SpanContext GetContext() const noexcept override { return span_context_; } void SetToken(nostd::unique_ptr &&token) noexcept override {} private: std::shared_ptr tracer_; std::string name_; + trace::SpanContext span_context_; }; } // namespace diff --git a/sdk/src/trace/span.h b/sdk/src/trace/span.h index 24af0256e5..03323d6b13 100644 --- a/sdk/src/trace/span.h +++ b/sdk/src/trace/span.h @@ -43,7 +43,7 @@ class Span final : public trace_api::Span bool IsRecording() const noexcept override; - trace_api::Tracer &tracer() const noexcept override { return *tracer_; } + trace_api::SpanContext GetContext() const noexcept override { return *span_context_.get(); } void SetToken(nostd::unique_ptr &&token) noexcept override; @@ -53,6 +53,7 @@ class Span final : public trace_api::Span mutable std::mutex mu_; std::unique_ptr recordable_; opentelemetry::core::SteadyTimestamp start_steady_time; + std::unique_ptr span_context_; bool has_ended_; nostd::unique_ptr token_; };