diff --git a/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h b/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h new file mode 100644 index 0000000000..a8f596696c --- /dev/null +++ b/ext/include/opentelemetry/ext/zpages/threadsafe_span_data.h @@ -0,0 +1,189 @@ +#pragma once + +#include +#include +#include +#include + +#include "opentelemetry/core/timestamp.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/trace/recordable.h" +#include "opentelemetry/trace/canonical_code.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/sdk/trace/span_data.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_id.h" + +using opentelemetry::sdk::trace::AttributeConverter; +using opentelemetry::sdk::trace::SpanDataAttributeValue; +using opentelemetry::sdk::trace::SpanDataEvent; +namespace trace_api = opentelemetry::trace; + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace ext { +namespace zpages { + +/** + * This class is a threadsafe version of span data used for zpages in OT + */ +class ThreadsafeSpanData final : public opentelemetry::sdk::trace::Recordable { + public: + /** + * Get the trace id for this span + * @return the trace id for this span + */ + opentelemetry::trace::TraceId GetTraceId() const noexcept { + std::lock_guard lock(mutex_); + return trace_id_; + } + + /** + * Get the span id for this span + * @return the span id for this span + */ + opentelemetry::trace::SpanId GetSpanId() const noexcept { + std::lock_guard lock(mutex_); + return span_id_; + } + + /** + * Get the parent span id for this span + * @return the span id for this span's parent + */ + opentelemetry::trace::SpanId GetParentSpanId() const noexcept { + std::lock_guard lock(mutex_); + return parent_span_id_; + } + + /** + * Get the name for this span + * @return the name for this span + */ + opentelemetry::nostd::string_view GetName() const noexcept { + std::lock_guard lock(mutex_); + return name_; + } + + /** + * Get the status for this span + * @return the status for this span + */ + opentelemetry::trace::CanonicalCode GetStatus() const noexcept { + std::lock_guard lock(mutex_); + return status_code_; + } + + /** + * Get the status description for this span + * @return the description of the the status of this span + */ + opentelemetry::nostd::string_view GetDescription() const noexcept { + std::lock_guard lock(mutex_); + return status_desc_; + } + + /** + * Get the start time for this span + * @return the start time for this span + */ + opentelemetry::core::SystemTimestamp GetStartTime() const noexcept { + std::lock_guard lock(mutex_); + return start_time_; + } + + /** + * Get the duration for this span + * @return the duration for this span + */ + std::chrono::nanoseconds GetDuration() const noexcept { + std::lock_guard lock(mutex_); + return duration_; + } + + /** + * Get the attributes for this span + * @return the attributes for this span + */ + const std::unordered_map GetAttributes() + const noexcept { + std::lock_guard lock(mutex_); + return attributes_; + } + + void SetIds(opentelemetry::trace::TraceId trace_id, + opentelemetry::trace::SpanId span_id, + opentelemetry::trace::SpanId parent_span_id) noexcept override { + std::lock_guard lock(mutex_); + trace_id_ = trace_id; + span_id_ = span_id; + parent_span_id_ = parent_span_id; + } + + void SetAttribute(nostd::string_view key, + const common::AttributeValue &value) noexcept override { + std::lock_guard lock(mutex_); + attributes_[std::string(key)] = nostd::visit(converter_, value); + } + + void SetStatus(trace_api::CanonicalCode code, + nostd::string_view description) noexcept override { + std::lock_guard lock(mutex_); + status_code_ = code; + status_desc_ = std::string(description); + } + + void SetName(nostd::string_view name) noexcept override { + std::lock_guard lock(mutex_); + name_ = std::string(name); + } + + void SetStartTime( + opentelemetry::core::SystemTimestamp start_time) noexcept override { + std::lock_guard lock(mutex_); + start_time_ = start_time; + } + + void SetDuration(std::chrono::nanoseconds duration) noexcept override { + std::lock_guard lock(mutex_); + duration_ = duration; + } + + void AddLink( + opentelemetry::trace::SpanContext span_context, + const trace_api::KeyValueIterable &attributes = + trace_api::KeyValueIterableView>({})) noexcept override + { + std::lock_guard lock(mutex_); + (void)span_context; + (void)attributes; + } + + void AddEvent( + nostd::string_view name, + core::SystemTimestamp timestamp = core::SystemTimestamp(std::chrono::system_clock::now()), + const trace_api::KeyValueIterable &attributes = + trace_api::KeyValueIterableView>({})) noexcept override + { + std::lock_guard lock(mutex_); + events_.push_back(SpanDataEvent(std::string(name), timestamp)); + // TODO: handle attributes + } + + private: + mutable std::mutex mutex_; + opentelemetry::trace::TraceId trace_id_; + opentelemetry::trace::SpanId span_id_; + opentelemetry::trace::SpanId parent_span_id_; + core::SystemTimestamp start_time_; + std::chrono::nanoseconds duration_{0}; + std::string name_; + opentelemetry::trace::CanonicalCode status_code_{ + opentelemetry::trace::CanonicalCode::OK}; + std::string status_desc_; + std::unordered_map attributes_; + std::vector events_; + AttributeConverter converter_; +}; +} // namespace zpages +} // namespace ext +OPENTELEMETRY_END_NAMESPACE diff --git a/ext/test/zpages/BUILD b/ext/test/zpages/BUILD index 50d027dbbd..3d6dfbc69a 100644 --- a/ext/test/zpages/BUILD +++ b/ext/test/zpages/BUILD @@ -1,3 +1,15 @@ +cc_test( + name = "threadsafe_span_data_tests", + srcs = [ + "threadsafe_span_data_test.cc", + ], + deps = [ + "//sdk/src/trace", + "//ext/src/zpages", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "tracez_processor_tests", srcs = [ diff --git a/ext/test/zpages/CMakeLists.txt b/ext/test/zpages/CMakeLists.txt index c0e1ddc15b..2fb33cde27 100644 --- a/ext/test/zpages/CMakeLists.txt +++ b/ext/test/zpages/CMakeLists.txt @@ -1,5 +1,5 @@ foreach(testname - tracez_processor_test) + tracez_processor_test threadsafe_span_data_test) add_executable(${testname} "${testname}.cc") target_link_libraries( ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} diff --git a/ext/test/zpages/threadsafe_span_data_test.cc b/ext/test/zpages/threadsafe_span_data_test.cc new file mode 100644 index 0000000000..51f6765eb0 --- /dev/null +++ b/ext/test/zpages/threadsafe_span_data_test.cc @@ -0,0 +1,55 @@ +#include "opentelemetry/ext/zpages/threadsafe_span_data.h" +#include "opentelemetry/nostd/variant.h" +#include "opentelemetry/trace/span_id.h" +#include "opentelemetry/trace/trace_id.h" + +#include +#include + +using opentelemetry::sdk::trace::AttributeConverter; +using opentelemetry::sdk::trace::SpanDataAttributeValue; +using opentelemetry::ext::zpages::ThreadsafeSpanData; + +TEST(ThreadsafeSpanData, DefaultValues) +{ + opentelemetry::trace::TraceId zero_trace_id; + opentelemetry::trace::SpanId zero_span_id; + ThreadsafeSpanData data; + + ASSERT_EQ(data.GetTraceId(), zero_trace_id); + ASSERT_EQ(data.GetSpanId(), zero_span_id); + ASSERT_EQ(data.GetParentSpanId(), zero_span_id); + ASSERT_EQ(data.GetName(), ""); + ASSERT_EQ(data.GetStatus(), opentelemetry::trace::CanonicalCode::OK); + ASSERT_EQ(data.GetDescription(), ""); + ASSERT_EQ(data.GetStartTime().time_since_epoch(), std::chrono::nanoseconds(0)); + ASSERT_EQ(data.GetDuration(), std::chrono::nanoseconds(0)); + ASSERT_EQ(data.GetAttributes().size(), 0); +} + +TEST(ThreadsafeSpanData, Set) +{ + opentelemetry::trace::TraceId trace_id; + opentelemetry::trace::SpanId span_id; + opentelemetry::trace::SpanId parent_span_id; + opentelemetry::core::SystemTimestamp now(std::chrono::system_clock::now()); + + ThreadsafeSpanData data; + data.SetIds(trace_id, span_id, parent_span_id); + data.SetName("span name"); + data.SetStatus(opentelemetry::trace::CanonicalCode::UNKNOWN, "description"); + data.SetStartTime(now); + data.SetDuration(std::chrono::nanoseconds(1000000)); + data.SetAttribute("attr1", 314159); + data.AddEvent("event1", now); + + ASSERT_EQ(data.GetTraceId(), trace_id); + ASSERT_EQ(data.GetSpanId(), span_id); + ASSERT_EQ(data.GetParentSpanId(), parent_span_id); + ASSERT_EQ(data.GetName(), "span name"); + ASSERT_EQ(data.GetStatus(), opentelemetry::trace::CanonicalCode::UNKNOWN); + ASSERT_EQ(data.GetDescription(), "description"); + ASSERT_EQ(data.GetStartTime().time_since_epoch(), now.time_since_epoch()); + ASSERT_EQ(data.GetDuration(), std::chrono::nanoseconds(1000000)); + ASSERT_EQ(opentelemetry::nostd::get(data.GetAttributes().at("attr1")), 314159); +}