diff --git a/api/include/opentelemetry/plugin/tracer.h b/api/include/opentelemetry/plugin/tracer.h index aed1d26d28..cb1c4b4e1a 100644 --- a/api/include/opentelemetry/plugin/tracer.h +++ b/api/include/opentelemetry/plugin/tracer.h @@ -4,6 +4,7 @@ #include "opentelemetry/plugin/detail/dynamic_library_handle.h" #include "opentelemetry/plugin/detail/tracer_handle.h" +#include "opentelemetry/trace/link.h" #include "opentelemetry/trace/tracer.h" #include "opentelemetry/version.h" @@ -37,6 +38,18 @@ class Span final : public trace::Span span_->AddEvent(name, timestamp, attributes); } + void AddLink(const trace::Link &link) noexcept override { span_->AddLink(link); } + void AddLink(const trace::SpanContext &span_context, + const trace::KeyValueIterable &attributes) noexcept override + { + span_->AddLink(span_context, attributes); + } + + void AddLink(const trace::SpanContext &span_context) noexcept override + { + span_->AddLink(span_context); + } + void SetStatus(trace::CanonicalCode code, nostd::string_view description) noexcept override { span_->SetStatus(code, description); diff --git a/api/include/opentelemetry/trace/default_span.h b/api/include/opentelemetry/trace/default_span.h index 9dbe682bc7..3b31c67b3d 100644 --- a/api/include/opentelemetry/trace/default_span.h +++ b/api/include/opentelemetry/trace/default_span.h @@ -33,6 +33,14 @@ class DefaultSpan : public Span this->AddEvent(name, std::chrono::system_clock::now(), attributes); } + void AddLink(const trace::Link &link) noexcept override {} + + void AddLink(const trace::SpanContext &span_context, + const trace::KeyValueIterable &attributes) noexcept override + {} + + void AddLink(const trace::SpanContext &span_context) noexcept override {} + void SetStatus(CanonicalCode status, nostd::string_view description) noexcept {} void UpdateName(nostd::string_view name) noexcept {} diff --git a/api/include/opentelemetry/trace/link.h b/api/include/opentelemetry/trace/link.h new file mode 100644 index 0000000000..2ece0200b9 --- /dev/null +++ b/api/include/opentelemetry/trace/link.h @@ -0,0 +1,20 @@ +#pragma once + +#include "opentelemetry/trace/key_value_iterable.h" +#include "opentelemetry/trace/span_context.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace trace +{ +class Link +{ +public: + // returns the Span Context associated with this Link + virtual const SpanContext &GetContext() const = 0; + + // returns the attributes associated with link + virtual const KeyValueIterable &GetAttributes() const = 0; +}; +} // namespace trace +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/api/include/opentelemetry/trace/noop.h b/api/include/opentelemetry/trace/noop.h index b88b1b3093..cc18288a7c 100644 --- a/api/include/opentelemetry/trace/noop.h +++ b/api/include/opentelemetry/trace/noop.h @@ -40,6 +40,14 @@ class NoopSpan final : public Span const trace::KeyValueIterable & /*attributes*/) noexcept override {} + void AddLink(const Link &link) noexcept override {} + + void AddLink(const trace_api::SpanContext &spanContext, + const trace_api::KeyValueIterable &attributes) noexcept override + {} + + void AddLink(const trace_api::SpanContext &span_context) noexcept override {} + void SetStatus(CanonicalCode /*code*/, nostd::string_view /*description*/) noexcept override {} void UpdateName(nostd::string_view /*name*/) noexcept override {} diff --git a/api/include/opentelemetry/trace/span.h b/api/include/opentelemetry/trace/span.h index 922f38fb92..1c575ce5a8 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/link.h" #include "opentelemetry/trace/span_context.h" #include "opentelemetry/version.h" @@ -135,6 +136,37 @@ class Span attributes.begin(), attributes.end()}); } + /** Adds a Link to newly created Span + * @param spanContext the context of the linked span + * @param attributes Link attributes + * + * These methods need to be called immediately after Span + * creation. Specification doesn't allow adding links after + * span creation. + */ + + virtual void AddLink(const trace::Link &link) noexcept = 0; + + virtual void AddLink(const trace::SpanContext &span_context, + const trace::KeyValueIterable &attributes) noexcept = 0; + + virtual void AddLink(const trace::SpanContext &span_context) noexcept = 0; + + template ::value> * = nullptr> + void AddLink(const trace::SpanContext &span_context, const T &attributes) noexcept + { + this->AddLink(span_context, KeyValueIterableView{attributes}); + } + + void AddLink(const trace::SpanContext &span_context, + std::initializer_list> + attributes) noexcept + { + this->AddLink(span_context, + nostd::span>{ + attributes.begin(), attributes.end()}); + } + // Sets the status of the span. The default status is OK. Only the value of // the last call will be // recorded, and implementations are free to ignore previous calls. diff --git a/api/test/trace/noop_test.cc b/api/test/trace/noop_test.cc index 4a069430ef..7de08f6204 100644 --- a/api/test/trace/noop_test.cc +++ b/api/test/trace/noop_test.cc @@ -1,5 +1,6 @@ #include "opentelemetry/trace/noop.h" #include "opentelemetry/core/timestamp.h" +#include "opentelemetry/trace/key_value_iterable_view.h" #include #include @@ -31,6 +32,14 @@ TEST(NoopTest, UseNoopTracers) s1->SetAttribute("abc", 4); s1->AddEvent("abc"); // add Empty + SpanContext sp(false, false); + s1->AddLink(sp); + using M = std::map; + M m1 = {{"abc", "123"}, {"xyz", "456"}}; + opentelemetry::trace::KeyValueIterableView iterable{m1}; + + s1->AddLink(sp, iterable); + s1->AddLink(sp, {{"abc", "123"}, {"xyz", "456"}}); EXPECT_EQ(s1->IsRecording(), false); diff --git a/examples/plugin/plugin/tracer.cc b/examples/plugin/plugin/tracer.cc index e8f324c9e7..dff22598b9 100644 --- a/examples/plugin/plugin/tracer.cc +++ b/examples/plugin/plugin/tracer.cc @@ -42,6 +42,14 @@ class Span final : public trace::Span const trace::KeyValueIterable & /*attributes*/) noexcept override {} + void AddLink(const trace::Link &link) noexcept override {} + + void AddLink(const trace::SpanContext &span_context, + const trace::KeyValueIterable &attributes) noexcept override + {} + + void AddLink(const trace::SpanContext &span_context) noexcept override {} + void SetStatus(trace::CanonicalCode /*code*/, nostd::string_view /*description*/) noexcept override {} diff --git a/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h b/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h index 1c7595aae6..60301b2288 100644 --- a/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h +++ b/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h @@ -163,7 +163,7 @@ class ThreadsafeSpanData final : public opentelemetry::sdk::trace::Recordable } void AddLink( - opentelemetry::trace::SpanContext span_context, + const opentelemetry::trace::SpanContext &span_context, const trace_api::KeyValueIterable &attributes = trace_api::KeyValueIterableView>({})) noexcept override { diff --git a/sdk/include/opentelemetry/sdk/trace/link.h b/sdk/include/opentelemetry/sdk/trace/link.h new file mode 100644 index 0000000000..1488d1501c --- /dev/null +++ b/sdk/include/opentelemetry/sdk/trace/link.h @@ -0,0 +1,47 @@ +#pragma once + +#include "opentelemetry/sdk/trace/attribute_utils.h" +#include "opentelemetry/trace/link.h" +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +namespace trace_api = opentelemetry::trace; + +class Link final : public trace_api::Link +{ +public: + Link(opentelemetry::trace::SpanContext span_context, + const opentelemetry::trace::KeyValueIterableView< + std::unordered_map> + &attributes) noexcept + : span_context_(span_context), attribute_map_{attributes} + {} + + Link(opentelemetry::trace::SpanContext span_context) + : span_context_(span_context), + attribute_map_( + std::unordered_map()) + {} + + const trace_api::SpanContext &GetContext() const noexcept override { return span_context_; } + + const trace_api::KeyValueIterable &GetAttributes() const noexcept override + { + return attribute_map_; + } + +private: + trace_api::SpanContext span_context_; + const trace_api::KeyValueIterableView< + std::unordered_map> + attribute_map_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/recordable.h b/sdk/include/opentelemetry/sdk/trace/recordable.h index 68124db0e6..7653b3a0cb 100644 --- a/sdk/include/opentelemetry/sdk/trace/recordable.h +++ b/sdk/include/opentelemetry/sdk/trace/recordable.h @@ -82,14 +82,14 @@ class Recordable * @param span_context the span context of the linked span * @param attributes the attributes associated with the link */ - virtual void AddLink(opentelemetry::trace::SpanContext span_context, + virtual void AddLink(const opentelemetry::trace::SpanContext &span_context, const trace_api::KeyValueIterable &attributes) noexcept = 0; /** * Add a link to a span with default (empty) attributes. * @param span_context the span context of the linked span */ - void AddLink(opentelemetry::trace::SpanContext span_context) + void AddLink(const opentelemetry::trace::SpanContext &span_context) { AddLink(span_context, opentelemetry::sdk::GetEmptyAttributes()); } diff --git a/sdk/include/opentelemetry/sdk/trace/span_data.h b/sdk/include/opentelemetry/sdk/trace/span_data.h index b745a3f33f..82c3ac95d4 100644 --- a/sdk/include/opentelemetry/sdk/trace/span_data.h +++ b/sdk/include/opentelemetry/sdk/trace/span_data.h @@ -63,7 +63,7 @@ class SpanDataEvent class SpanDataLink { public: - SpanDataLink(opentelemetry::trace::SpanContext span_context, + SpanDataLink(const opentelemetry::trace::SpanContext &span_context, const trace_api::KeyValueIterable &attributes) : span_context_(span_context), attribute_map_(attributes) {} @@ -180,7 +180,7 @@ class SpanData final : public Recordable events_.push_back(event); } - void AddLink(opentelemetry::trace::SpanContext span_context, + void AddLink(const opentelemetry::trace::SpanContext &span_context, const trace_api::KeyValueIterable &attributes) noexcept override { SpanDataLink link(span_context, attributes); diff --git a/sdk/src/trace/span.cc b/sdk/src/trace/span.cc index 0f661d8ef6..79bc3a9264 100644 --- a/sdk/src/trace/span.cc +++ b/sdk/src/trace/span.cc @@ -62,13 +62,15 @@ Span::Span(std::shared_ptr &&tracer, nostd::string_view name, const trace_api::KeyValueIterable &attributes, const trace_api::StartSpanOptions &options, - const trace_api::SpanContext &parent_span_context) noexcept + const trace_api::SpanContext &parent_span_context, + const nostd::span> &links) noexcept : tracer_{std::move(tracer)}, processor_{processor}, recordable_{processor_->MakeRecordable()}, start_steady_time{options.start_steady_time}, has_ended_{false} { + (void)options; if (recordable_ == nullptr) { return; @@ -98,6 +100,11 @@ Span::Span(std::shared_ptr &&tracer, return true; }); + for (auto &link : links) + { + recordable_->AddLink(link->GetContext(), link->GetAttributes()); + } + recordable_->SetStartTime(NowOr(options.start_system_time)); start_steady_time = NowOr(options.start_steady_time); processor_->OnStart(*recordable_); @@ -136,6 +143,34 @@ void Span::AddEvent(nostd::string_view name, (void)attributes; } +void Span::AddLink(const trace_api::Link &link) noexcept +{ + if (recordable_ == nullptr) + { + return; + } + recordable_->AddLink(link.GetContext(), link.GetAttributes()); +} + +void Span::AddLink(const trace_api::SpanContext &span_context, + const trace_api::KeyValueIterable &attributes) noexcept +{ + if (recordable_ == nullptr) + { + return; + } + recordable_->AddLink(span_context, attributes); +} + +void Span::AddLink(const trace_api::SpanContext &span_context) noexcept +{ + if (recordable_ == nullptr) + { + return; + } + recordable_->AddLink(span_context); +} + void Span::SetStatus(trace_api::CanonicalCode code, nostd::string_view description) noexcept { std::lock_guard lock_guard{mu_}; diff --git a/sdk/src/trace/span.h b/sdk/src/trace/span.h index 56baf8838f..09041b1ff1 100644 --- a/sdk/src/trace/span.h +++ b/sdk/src/trace/span.h @@ -2,6 +2,7 @@ #include +#include "opentelemetry/sdk/trace/link.h" #include "opentelemetry/sdk/trace/tracer.h" #include "opentelemetry/version.h" @@ -20,7 +21,8 @@ class Span final : public trace_api::Span nostd::string_view name, const trace_api::KeyValueIterable &attributes, const trace_api::StartSpanOptions &options, - const trace_api::SpanContext &parent_span_context) noexcept; + const trace_api::SpanContext &parent_span_context, + const nostd::span> &links) noexcept; ~Span() override; @@ -35,6 +37,13 @@ class Span final : public trace_api::Span core::SystemTimestamp timestamp, const trace_api::KeyValueIterable &attributes) noexcept override; + void AddLink(const trace_api::Link &link) noexcept override; + + void AddLink(const trace_api::SpanContext &span_context, + const trace_api::KeyValueIterable &attributes) noexcept override; + + void AddLink(const trace_api::SpanContext &span_context) noexcept override; + void SetStatus(trace_api::CanonicalCode code, nostd::string_view description) noexcept override; void UpdateName(nostd::string_view name) noexcept override; diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index 60b00f191c..eaeada03f9 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -61,9 +61,13 @@ nostd::shared_ptr Tracer::StartSpan( } else { - auto span = nostd::shared_ptr{ - new (std::nothrow) Span{this->shared_from_this(), processor_.load(), name, attributes, - options, GetCurrentSpanContext()}}; + auto span = nostd::shared_ptr{new (std::nothrow) Span{this->shared_from_this(), + processor_.load(), + name, + attributes, + options, + GetCurrentSpanContext(), + {}}}; // if the attributes is not nullptr, add attributes to the span. if (sampling_result.attributes) diff --git a/sdk/test/trace/tracer_test.cc b/sdk/test/trace/tracer_test.cc index ba196c67c1..5c5fff3fc7 100644 --- a/sdk/test/trace/tracer_test.cc +++ b/sdk/test/trace/tracer_test.cc @@ -1,4 +1,5 @@ #include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/sdk/trace/link.h" #include "opentelemetry/sdk/trace/samplers/always_off.h" #include "opentelemetry/sdk/trace/samplers/always_on.h" #include "opentelemetry/sdk/trace/samplers/parent_or_else.h" @@ -341,6 +342,24 @@ TEST(Tracer, SpanSetAttribute) ASSERT_EQ(3.1, nostd::get(span_data->GetAttributes().at("abc"))); } +TEST(Tracer, SpanAddLink) +{ + std::shared_ptr>> spans_received( + new std::vector>); + auto tracer = initTracer(spans_received); + + auto span = tracer->StartSpan("span 1"); + + SpanContext sp1(true, true); + span->AddLink(sp1); + + span->End(); + ASSERT_EQ(1, spans_received->size()); + auto &span_data = spans_received->at(0); + + ASSERT_EQ(1, span_data->GetLinks().size()); +} + TEST(Tracer, TestAlwaysOnSampler) { std::shared_ptr>> spans_received(