Support adding links to span (#351)

This commit is contained in:
Lalit Kumar Bhasin 2020-11-13 01:17:05 +05:30 committed by GitHub
parent c504cfb51e
commit 2bab26e249
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 324 additions and 6 deletions

View File

@ -68,9 +68,10 @@ public:
nostd::shared_ptr<trace::Span> StartSpan( nostd::shared_ptr<trace::Span> StartSpan(
nostd::string_view name, nostd::string_view name,
const common::KeyValueIterable &attributes, const common::KeyValueIterable &attributes,
const trace::SpanContextKeyValueIterable &links,
const trace::StartSpanOptions &options = {}) noexcept override const trace::StartSpanOptions &options = {}) noexcept override
{ {
auto span = tracer_handle_->tracer().StartSpan(name, attributes, options); auto span = tracer_handle_->tracer().StartSpan(name, attributes, links, options);
if (span == nullptr) if (span == nullptr)
{ {
return nostd::shared_ptr<trace::Span>(nullptr); return nostd::shared_ptr<trace::Span>(nullptr);

View File

@ -9,6 +9,7 @@
#include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/nostd/unique_ptr.h"
#include "opentelemetry/trace/span.h" #include "opentelemetry/trace/span.h"
#include "opentelemetry/trace/span_context.h" #include "opentelemetry/trace/span_context.h"
#include "opentelemetry/trace/span_context_kv_iterable.h"
#include "opentelemetry/trace/tracer.h" #include "opentelemetry/trace/tracer.h"
#include "opentelemetry/trace/tracer_provider.h" #include "opentelemetry/trace/tracer_provider.h"
#include "opentelemetry/version.h" #include "opentelemetry/version.h"
@ -66,6 +67,7 @@ public:
// Tracer // Tracer
nostd::shared_ptr<Span> StartSpan(nostd::string_view /*name*/, nostd::shared_ptr<Span> StartSpan(nostd::string_view /*name*/,
const common::KeyValueIterable & /*attributes*/, const common::KeyValueIterable & /*attributes*/,
const SpanContextKeyValueIterable & /*links*/,
const StartSpanOptions & /*options*/) noexcept override const StartSpanOptions & /*options*/) noexcept override
{ {
// Don't allocate a no-op span for every StartSpan call, but use a static // Don't allocate a no-op span for every StartSpan call, but use a static

View File

@ -0,0 +1,34 @@
#pragma once
#include "opentelemetry/common/attribute_value.h"
#include "opentelemetry/common/key_value_iterable_view.h"
#include "opentelemetry/nostd/function_ref.h"
#include "opentelemetry/version.h"
OPENTELEMETRY_BEGIN_NAMESPACE
namespace trace
{
/**
* Supports internal iteration over a collection of SpanContext/key-value pairs.
*/
class SpanContextKeyValueIterable
{
public:
virtual ~SpanContextKeyValueIterable() = default;
/**
* Iterate over SpanContext/key-value pairs
* @param callback a callback to invoke for each key-value for each SpanContext.
* If the callback returns false, the iteration is aborted.
* @return true if every SpanContext/key-value pair was iterated over
*/
virtual bool ForEachKeyValue(
nostd::function_ref<bool(SpanContext, const opentelemetry::common::KeyValueIterable &)>
callback) const noexcept = 0;
/**
* @return the number of key-value pairs
*/
virtual size_t size() const noexcept = 0;
};
} // namespace trace
OPENTELEMETRY_END_NAMESPACE

View File

@ -0,0 +1,113 @@
#pragma once
#include <iterator>
#include <type_traits>
#include <utility>
#include "opentelemetry/common/key_value_iterable_view.h"
#include "opentelemetry/nostd/utility.h"
#include "opentelemetry/trace/span_context_kv_iterable.h"
#include "opentelemetry/version.h"
OPENTELEMETRY_BEGIN_NAMESPACE
namespace trace
{
namespace detail
{
template <class T>
inline void take_span_context_kv(SpanContext, common::KeyValueIterableView<T>)
{}
template <class T, nostd::enable_if_t<common::detail::is_key_value_iterable<T>::value> * = nullptr>
inline void take_span_context_kv(SpanContext, T &)
{}
inline void take_span_context_kv(
SpanContext,
std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>>)
{}
template <class T>
auto is_span_context_kv_iterable_impl(T iterable)
-> decltype(take_span_context_kv(std::begin(iterable)->first, std::begin(iterable)->second),
nostd::size(iterable),
std::true_type{});
std::false_type is_span_context_kv_iterable_impl(...);
template <class T>
struct is_span_context_kv_iterable
{
static const bool value =
decltype(detail::is_span_context_kv_iterable_impl(std::declval<T>()))::value;
};
} // namespace detail
template <class T>
class SpanContextKeyValueIterableView final : public SpanContextKeyValueIterable
{
static_assert(detail::is_span_context_kv_iterable<T>::value,
"Must be a context/key-value iterable");
public:
explicit SpanContextKeyValueIterableView(const T &links) noexcept : container_{&links} {}
bool ForEachKeyValue(
nostd::function_ref<bool(SpanContext, const opentelemetry::common::KeyValueIterable &)>
callback) const noexcept override
{
auto iter = std::begin(*container_);
auto last = std::end(*container_);
for (; iter != last; ++iter)
{
if (!this->do_callback(iter->first, iter->second, callback))
{
return false;
}
}
return true;
}
size_t size() const noexcept override { return nostd::size(*container_); }
private:
const T *container_;
bool do_callback(
SpanContext span_context,
const common::KeyValueIterable &attributes,
nostd::function_ref<bool(SpanContext, const opentelemetry::common::KeyValueIterable &)>
callback) const noexcept
{
if (!callback(span_context, attributes))
{
return false;
}
return true;
}
template <class U,
nostd::enable_if_t<common::detail::is_key_value_iterable<U>::value> * = nullptr>
bool do_callback(
SpanContext span_context,
const U &attributes,
nostd::function_ref<bool(SpanContext, const common::KeyValueIterable &)> callback) const
noexcept
{
return do_callback(span_context, common::KeyValueIterableView<U>(attributes), callback);
}
bool do_callback(
SpanContext span_context,
std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>> attributes,
nostd::function_ref<bool(SpanContext, const common::KeyValueIterable &)> callback) const
noexcept
{
return do_callback(span_context,
nostd::span<const std::pair<nostd::string_view, common::AttributeValue>>{
attributes.begin(), attributes.end()},
callback);
}
};
} // namespace trace
OPENTELEMETRY_END_NAMESPACE

View File

@ -6,6 +6,7 @@
#include "opentelemetry/trace/default_span.h" #include "opentelemetry/trace/default_span.h"
#include "opentelemetry/trace/scope.h" #include "opentelemetry/trace/scope.h"
#include "opentelemetry/trace/span.h" #include "opentelemetry/trace/span.h"
#include "opentelemetry/trace/span_context_kv_iterable_view.h"
#include "opentelemetry/version.h" #include "opentelemetry/version.h"
#include <chrono> #include <chrono>
@ -33,12 +34,13 @@ public:
*/ */
virtual nostd::shared_ptr<Span> StartSpan(nostd::string_view name, virtual nostd::shared_ptr<Span> StartSpan(nostd::string_view name,
const common::KeyValueIterable &attributes, const common::KeyValueIterable &attributes,
const SpanContextKeyValueIterable &links,
const StartSpanOptions &options = {}) noexcept = 0; const StartSpanOptions &options = {}) noexcept = 0;
nostd::shared_ptr<Span> StartSpan(nostd::string_view name, nostd::shared_ptr<Span> StartSpan(nostd::string_view name,
const StartSpanOptions &options = {}) noexcept const StartSpanOptions &options = {}) noexcept
{ {
return this->StartSpan(name, {}, options); return this->StartSpan(name, {}, {}, options);
} }
template <class T, template <class T,
@ -47,7 +49,20 @@ public:
const T &attributes, const T &attributes,
const StartSpanOptions &options = {}) noexcept const StartSpanOptions &options = {}) noexcept
{ {
return this->StartSpan(name, common::KeyValueIterableView<T>(attributes), options); return this->StartSpan(name, attributes, {}, options);
}
template <class T,
class U,
nostd::enable_if_t<common::detail::is_key_value_iterable<T>::value> * = nullptr,
nostd::enable_if_t<detail::is_span_context_kv_iterable<U>::value> * = nullptr>
nostd::shared_ptr<Span> StartSpan(nostd::string_view name,
const T &attributes,
const U &links,
const StartSpanOptions &options = {}) noexcept
{
return this->StartSpan(name, common::KeyValueIterableView<T>(attributes),
SpanContextKeyValueIterableView<U>(links), options);
} }
nostd::shared_ptr<Span> StartSpan( nostd::shared_ptr<Span> StartSpan(
@ -55,10 +70,60 @@ public:
std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>> attributes, std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>> attributes,
const StartSpanOptions &options = {}) noexcept const StartSpanOptions &options = {}) noexcept
{ {
return this->StartSpan(name, attributes, {}, options);
}
template <class T,
nostd::enable_if_t<common::detail::is_key_value_iterable<T>::value> * = nullptr>
nostd::shared_ptr<Span> StartSpan(
nostd::string_view name,
const T &attributes,
std::initializer_list<
std::pair<SpanContext,
std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>>>>
links,
const StartSpanOptions &options = {}) noexcept
{
return this->StartSpan(
name, attributes,
nostd::span<const std::pair<SpanContext, std::initializer_list<std::pair<
nostd::string_view, common::AttributeValue>>>>{
links.begin(), links.end()},
options);
}
template <class T,
nostd::enable_if_t<common::detail::is_key_value_iterable<T>::value> * = nullptr>
nostd::shared_ptr<Span> StartSpan(
nostd::string_view name,
std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>> attributes,
const T &links,
const StartSpanOptions &options = {}) noexcept
{
return this->StartSpan(name, return this->StartSpan(name,
nostd::span<const std::pair<nostd::string_view, common::AttributeValue>>{ nostd::span<const std::pair<nostd::string_view, common::AttributeValue>>{
attributes.begin(), attributes.end()}, attributes.begin(), attributes.end()},
options); links, options);
}
nostd::shared_ptr<Span> StartSpan(
nostd::string_view name,
std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>> attributes,
std::initializer_list<
std::pair<SpanContext,
std::initializer_list<std::pair<nostd::string_view, common::AttributeValue>>>>
links,
const StartSpanOptions &options = {}) noexcept
{
return this->StartSpan(
name,
nostd::span<const std::pair<nostd::string_view, common::AttributeValue>>{attributes.begin(),
attributes.end()},
nostd::span<const std::pair<SpanContext, std::initializer_list<std::pair<
nostd::string_view, common::AttributeValue>>>>{
links.begin(), links.end()},
options);
} }
/** /**

View File

@ -43,3 +43,15 @@ TEST(NoopTest, UseNoopTracers)
s1->GetContext(); s1->GetContext();
} }
TEST(NoopTest, StartSpan)
{
std::shared_ptr<Tracer> tracer{new NoopTracer{}};
std::map<std::string, std::string> attrs = {{"a", "3"}};
std::vector<std::pair<SpanContext, std::map<std::string, std::string>>> links = {
{SpanContext(false, false), attrs}};
auto s1 = tracer->StartSpan("abc", attrs, links);
auto s2 = tracer->StartSpan("efg", {{"a", 3}}, {{SpanContext(false, false), {{"b", 4}}}});
}

View File

@ -19,6 +19,7 @@ public:
Span(std::shared_ptr<Tracer> &&tracer, Span(std::shared_ptr<Tracer> &&tracer,
nostd::string_view name, nostd::string_view name,
const opentelemetry::common::KeyValueIterable & /*attributes*/, const opentelemetry::common::KeyValueIterable & /*attributes*/,
const opentelemetry::trace::SpanContextKeyValueIterable & /*links*/,
const trace::StartSpanOptions & /*options*/) noexcept const trace::StartSpanOptions & /*options*/) noexcept
: tracer_{std::move(tracer)}, name_{name}, span_context_{trace::SpanContext::GetInvalid()} : tracer_{std::move(tracer)}, name_{name}, span_context_{trace::SpanContext::GetInvalid()}
{ {
@ -66,8 +67,9 @@ Tracer::Tracer(nostd::string_view /*output*/) {}
nostd::shared_ptr<trace::Span> Tracer::StartSpan( nostd::shared_ptr<trace::Span> Tracer::StartSpan(
nostd::string_view name, nostd::string_view name,
const opentelemetry::common::KeyValueIterable &attributes, const opentelemetry::common::KeyValueIterable &attributes,
const opentelemetry::trace::SpanContextKeyValueIterable &links,
const trace::StartSpanOptions &options) noexcept const trace::StartSpanOptions &options) noexcept
{ {
return nostd::shared_ptr<opentelemetry::trace::Span>{ return nostd::shared_ptr<opentelemetry::trace::Span>{
new (std::nothrow) Span{this->shared_from_this(), name, attributes, options}}; new (std::nothrow) Span{this->shared_from_this(), name, attributes, links, options}};
} }

View File

@ -14,6 +14,7 @@ public:
opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> StartSpan( opentelemetry::nostd::shared_ptr<opentelemetry::trace::Span> StartSpan(
opentelemetry::nostd::string_view name, opentelemetry::nostd::string_view name,
const opentelemetry::common::KeyValueIterable & /*attributes*/, const opentelemetry::common::KeyValueIterable & /*attributes*/,
const opentelemetry::trace::SpanContextKeyValueIterable & /*links*/,
const opentelemetry::trace::StartSpanOptions & /*options */) noexcept override; const opentelemetry::trace::StartSpanOptions & /*options */) noexcept override;
void ForceFlushWithMicroseconds(uint64_t /*timeout*/) noexcept override {} void ForceFlushWithMicroseconds(uint64_t /*timeout*/) noexcept override {}

View File

@ -47,6 +47,7 @@ public:
nostd::shared_ptr<trace_api::Span> StartSpan( nostd::shared_ptr<trace_api::Span> StartSpan(
nostd::string_view name, nostd::string_view name,
const opentelemetry::common::KeyValueIterable &attributes, const opentelemetry::common::KeyValueIterable &attributes,
const trace_api::SpanContextKeyValueIterable &links,
const trace_api::StartSpanOptions &options = {}) noexcept override; const trace_api::StartSpanOptions &options = {}) noexcept override;
void ForceFlushWithMicroseconds(uint64_t timeout) noexcept override; void ForceFlushWithMicroseconds(uint64_t timeout) noexcept override;

View File

@ -61,6 +61,7 @@ Span::Span(std::shared_ptr<Tracer> &&tracer,
std::shared_ptr<SpanProcessor> processor, std::shared_ptr<SpanProcessor> processor,
nostd::string_view name, nostd::string_view name,
const opentelemetry::common::KeyValueIterable &attributes, const opentelemetry::common::KeyValueIterable &attributes,
const trace_api::SpanContextKeyValueIterable &links,
const trace_api::StartSpanOptions &options, const trace_api::StartSpanOptions &options,
const trace_api::SpanContext &parent_span_context) noexcept const trace_api::SpanContext &parent_span_context) noexcept
: tracer_{std::move(tracer)}, : tracer_{std::move(tracer)},
@ -98,6 +99,12 @@ Span::Span(std::shared_ptr<Tracer> &&tracer,
return true; return true;
}); });
links.ForEachKeyValue([&](opentelemetry::trace::SpanContext span_context,
const opentelemetry::common::KeyValueIterable &attributes) {
recordable_->AddLink(span_context, attributes);
return true;
});
recordable_->SetStartTime(NowOr(options.start_system_time)); recordable_->SetStartTime(NowOr(options.start_system_time));
start_steady_time = NowOr(options.start_steady_time); start_steady_time = NowOr(options.start_steady_time);
processor_->OnStart(*recordable_); processor_->OnStart(*recordable_);

View File

@ -19,6 +19,7 @@ public:
std::shared_ptr<SpanProcessor> processor, std::shared_ptr<SpanProcessor> processor,
nostd::string_view name, nostd::string_view name,
const opentelemetry::common::KeyValueIterable &attributes, const opentelemetry::common::KeyValueIterable &attributes,
const trace_api::SpanContextKeyValueIterable &links,
const trace_api::StartSpanOptions &options, const trace_api::StartSpanOptions &options,
const trace_api::SpanContext &parent_span_context) noexcept; const trace_api::SpanContext &parent_span_context) noexcept;

View File

@ -54,6 +54,7 @@ trace_api::SpanContext GetCurrentSpanContext(const trace_api::SpanContext &expli
nostd::shared_ptr<trace_api::Span> Tracer::StartSpan( nostd::shared_ptr<trace_api::Span> Tracer::StartSpan(
nostd::string_view name, nostd::string_view name,
const opentelemetry::common::KeyValueIterable &attributes, const opentelemetry::common::KeyValueIterable &attributes,
const trace_api::SpanContextKeyValueIterable &links,
const trace_api::StartSpanOptions &options) noexcept const trace_api::StartSpanOptions &options) noexcept
{ {
trace_api::SpanContext parent = GetCurrentSpanContext(options.parent); trace_api::SpanContext parent = GetCurrentSpanContext(options.parent);
@ -72,7 +73,7 @@ nostd::shared_ptr<trace_api::Span> Tracer::StartSpan(
else else
{ {
auto span = nostd::shared_ptr<trace_api::Span>{new (std::nothrow) Span{ auto span = nostd::shared_ptr<trace_api::Span>{new (std::nothrow) Span{
this->shared_from_this(), processor_.load(), name, attributes, options, parent}}; this->shared_from_this(), processor_.load(), name, attributes, links, options, parent}};
// if the attributes is not nullptr, add attributes to the span. // if the attributes is not nullptr, add attributes to the span.
if (sampling_result.attributes) if (sampling_result.attributes)

View File

@ -13,6 +13,7 @@ using opentelemetry::core::SteadyTimestamp;
using opentelemetry::core::SystemTimestamp; using opentelemetry::core::SystemTimestamp;
namespace nostd = opentelemetry::nostd; namespace nostd = opentelemetry::nostd;
namespace common = opentelemetry::common; namespace common = opentelemetry::common;
using opentelemetry::common::KeyValueIterableView;
using opentelemetry::exporter::memory::InMemorySpanData; using opentelemetry::exporter::memory::InMemorySpanData;
using opentelemetry::exporter::memory::InMemorySpanExporter; using opentelemetry::exporter::memory::InMemorySpanExporter;
using opentelemetry::trace::SpanContext; using opentelemetry::trace::SpanContext;
@ -332,6 +333,83 @@ TEST(Tracer, SpanSetEvents)
ASSERT_EQ(1, span_data_events[2].GetAttributes().size()); ASSERT_EQ(1, span_data_events[2].GetAttributes().size());
} }
TEST(Tracer, SpanSetLinks)
{
std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());
std::shared_ptr<InMemorySpanData> span_data = exporter->GetData();
auto tracer = initTracer(std::move(exporter));
{
// Single span link passed through Initialization list
tracer->StartSpan("efg", {{"attr1", 1}}, {{SpanContext(false, false), {{"attr2", 2}}}})->End();
auto spans = span_data->GetSpans();
ASSERT_EQ(1, spans.size());
auto &span_data_links = spans.at(0)->GetLinks();
ASSERT_EQ(1, span_data_links.size());
auto link = span_data_links.at(0);
ASSERT_EQ(nostd::get<int>(link.GetAttributes().at("attr2")), 2);
}
{
// Multiple span links passed through Initialization list
tracer
->StartSpan("efg", {{"attr1", 1}},
{{SpanContext(false, false), {{"attr2", 2}}},
{SpanContext(false, false), {{"attr3", 3}}}})
->End();
auto spans = span_data->GetSpans();
ASSERT_EQ(1, spans.size());
auto &span_data_links = spans.at(0)->GetLinks();
ASSERT_EQ(2, span_data_links.size());
auto link1 = span_data_links.at(0);
ASSERT_EQ(nostd::get<int>(link1.GetAttributes().at("attr2")), 2);
auto link2 = span_data_links.at(1);
ASSERT_EQ(nostd::get<int>(link2.GetAttributes().at("attr3")), 3);
}
{
// Multiple links, each with multiple attributes passed through Initialization list
tracer
->StartSpan("efg", {{"attr1", 1}},
{{SpanContext(false, false), {{"attr2", 2}, {"attr3", 3}}},
{SpanContext(false, false), {{"attr4", 4}}}})
->End();
auto spans = span_data->GetSpans();
ASSERT_EQ(1, spans.size());
auto &span_data_links = spans.at(0)->GetLinks();
ASSERT_EQ(2, span_data_links.size());
auto link1 = span_data_links.at(0);
ASSERT_EQ(nostd::get<int>(link1.GetAttributes().at("attr2")), 2);
ASSERT_EQ(nostd::get<int>(link1.GetAttributes().at("attr3")), 3);
auto link2 = span_data_links.at(1);
ASSERT_EQ(nostd::get<int>(link2.GetAttributes().at("attr4")), 4);
}
{
std::map<std::string, std::string> attrs1 = {{"attr1", "1"}, {"attr2", "2"}};
std::map<std::string, std::string> attrs2 = {{"attr3", "3"}, {"attr4", "4"}};
std::vector<std::pair<SpanContext, std::map<std::string, std::string>>> links = {
{SpanContext(false, false), attrs1}, {SpanContext(false, false), attrs2}};
tracer->StartSpan("efg", attrs1, links)->End();
auto spans = span_data->GetSpans();
auto &span_data_links = spans.at(0)->GetLinks();
ASSERT_EQ(2, span_data_links.size());
auto link1 = span_data_links.at(0);
ASSERT_EQ(nostd::get<std::string>(link1.GetAttributes().at("attr1")), "1");
ASSERT_EQ(nostd::get<std::string>(link1.GetAttributes().at("attr2")), "2");
auto link2 = span_data_links.at(1);
ASSERT_EQ(nostd::get<std::string>(link2.GetAttributes().at("attr3")), "3");
ASSERT_EQ(nostd::get<std::string>(link2.GetAttributes().at("attr4")), "4");
}
}
TEST(Tracer, TestAlwaysOnSampler) TEST(Tracer, TestAlwaysOnSampler)
{ {
std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter()); std::unique_ptr<InMemorySpanExporter> exporter(new InMemorySpanExporter());