diff --git a/sdk/include/opentelemetry/sdk/common/atomic_shared_ptr.h b/sdk/include/opentelemetry/sdk/common/atomic_shared_ptr.h new file mode 100644 index 00000000000..99bfd1a30e1 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/common/atomic_shared_ptr.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +/** + * A wrapper to provide atomic shared pointers. + * + * This wrapper relies on std::atomic for gcc 4.8 and C++20, while using + * specializations of std::atomic_store and std::atomic_load in all other + * instances. + */ +#if __cplusplus > 201703L +template +class AtomicSharedPtr +{ +public: + explicit AtomicSharedPtr(std::shared_ptr ptr) noexcept : ptr_{std::move(ptr)} {} + + void store(const std::shared_ptr &other) noexcept + { + ptr_.store(other, std::memory_order_release); + } + + std::shared_ptr load() const noexcept { return ptr_.load(std::memory_order_acquire); } + +private: + std::atomic> ptr_; +}; +#elif (__GNUC__ == 4 && (__GNUC_MINOR__ >= 8)) +template +class AtomicSharedPtr +{ +public: + explicit AtomicSharedPtr(std::shared_ptr ptr) noexcept + : ptr_{new std::shared_ptr(std::move(ptr))} + {} + + ~AtomicSharedPtr() noexcept { delete ptr_.load(std::memory_order_acquire); } + + void store(const std::shared_ptr &other) noexcept + { + ptr_.store(new std::shared_ptr(other), std::memory_order_release); + } + + std::shared_ptr load() const noexcept { return *ptr_.load(std::memory_order_acquire); } + +private: + std::atomic *> ptr_; +}; +#else +template +class AtomicSharedPtr +{ +public: + explicit AtomicSharedPtr(std::shared_ptr ptr) noexcept : ptr_{std::move(ptr)} {} + + void store(const std::shared_ptr &other) noexcept { std::atomic_store(&ptr_, other); } + + std::shared_ptr load() const noexcept { return std::atomic_load(&ptr_); } + +private: + std::shared_ptr ptr_; +}; +#endif +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/default_tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/default_tracer_provider.h deleted file mode 100644 index f566d3e64d8..00000000000 --- a/sdk/include/opentelemetry/sdk/trace/default_tracer_provider.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "opentelemetry/nostd/shared_ptr.h" -#include "opentelemetry/trace/tracer.h" -#include "opentelemetry/trace/tracer_provider.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace sdk -{ -namespace trace -{ -class DefaultTracerProvider final : public opentelemetry::trace::TracerProvider -{ -public: - DefaultTracerProvider() noexcept; - - opentelemetry::nostd::shared_ptr GetTracer( - nostd::string_view library_name, - nostd::string_view library_version = "") noexcept override; - -private: - opentelemetry::nostd::shared_ptr tracer_; -}; -} // namespace trace -} // namespace sdk -OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h new file mode 100644 index 00000000000..06c83b10585 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -0,0 +1,51 @@ +#pragma once + +#include "opentelemetry/sdk/common/atomic_shared_ptr.h" +#include "opentelemetry/sdk/trace/processor.h" +#include "opentelemetry/trace/tracer.h" +#include "opentelemetry/version.h" + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +class Tracer final : public trace_api::Tracer, public std::enable_shared_from_this +{ +public: + /** + * Initialize a new tracer. + * @param processor The span processor for this tracer. This must not be a + * nullptr. + */ + explicit Tracer(std::shared_ptr processor) noexcept : processor_{processor} {} + + /** + * Set the span processor associated with this tracer. + * @param processor The new span processor for this tracer. This must not be + * a nullptr. + */ + void SetProcessor(std::shared_ptr processor) noexcept; + + /** + * Obtain the span processor associated with this tracer. + * @return The span processor for this tracer. + */ + std::shared_ptr GetProcessor() const noexcept; + + nostd::unique_ptr StartSpan( + nostd::string_view name, + const trace_api::StartSpanOptions &options = {}) noexcept override; + + void ForceFlushWithMicroseconds(uint64_t timeout) noexcept override; + + void CloseWithMicroseconds(uint64_t timeout) noexcept override; + +private: + opentelemetry::sdk::AtomicSharedPtr processor_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h new file mode 100644 index 00000000000..322d2fcb59d --- /dev/null +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include + +#include "opentelemetry/nostd/shared_ptr.h" +#include "opentelemetry/sdk/trace/processor.h" +#include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/trace/tracer_provider.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +class TracerProvider final : public opentelemetry::trace::TracerProvider +{ +public: + /** + * Initialize a new tracer provider. + * @param processor The span processor for this tracer provider. This must + * not be a nullptr. + */ + explicit TracerProvider(std::shared_ptr processor) noexcept; + + opentelemetry::nostd::shared_ptr GetTracer( + nostd::string_view library_name, + nostd::string_view library_version = "") noexcept override; + + /** + * Set the span processor associated with this tracer provider. + * @param processor The new span processor for this tracer provider. This + * must not be a nullptr. + */ + void SetProcessor(std::shared_ptr processor) noexcept; + + /** + * Obtain the span processor associated with this tracer provider. + * @return The span processor for this tracer provider. + */ + std::shared_ptr GetProcessor() const noexcept; + +private: + opentelemetry::sdk::AtomicSharedPtr processor_; + opentelemetry::nostd::shared_ptr tracer_; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/trace/CMakeLists.txt b/sdk/src/trace/CMakeLists.txt index c1bb321cf9b..455665be683 100644 --- a/sdk/src/trace/CMakeLists.txt +++ b/sdk/src/trace/CMakeLists.txt @@ -1 +1 @@ -add_library(opentelemetry_trace default_tracer_provider.cc tracer.cc span.cc) +add_library(opentelemetry_trace tracer_provider.cc tracer.cc span.cc) diff --git a/sdk/src/trace/default_tracer_provider.cc b/sdk/src/trace/default_tracer_provider.cc deleted file mode 100644 index 8719c010415..00000000000 --- a/sdk/src/trace/default_tracer_provider.cc +++ /dev/null @@ -1,21 +0,0 @@ -#include "opentelemetry/sdk/trace/default_tracer_provider.h" -#include "src/trace/tracer.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace sdk -{ -namespace trace -{ -DefaultTracerProvider::DefaultTracerProvider() noexcept - : tracer_(new Tracer(std::unique_ptr(nullptr))) -{} - -opentelemetry::nostd::shared_ptr DefaultTracerProvider::GetTracer( - nostd::string_view library_name, - nostd::string_view library_version) noexcept -{ - return tracer_; -} -} // namespace trace -} // namespace sdk -OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/trace/recorder.h b/sdk/src/trace/recorder.h deleted file mode 100644 index 22730a74cb1..00000000000 --- a/sdk/src/trace/recorder.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "opentelemetry/sdk/trace/recordable.h" -#include "opentelemetry/trace/span_id.h" -#include "opentelemetry/trace/trace_id.h" -#include "opentelemetry/version.h" - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace sdk -{ -namespace trace -{ -class Recorder -{ -public: - virtual ~Recorder() = default; - - /** - * @return a Recordable object to maintain a data representation of a span. - */ - virtual std::unique_ptr MakeRecordable() noexcept = 0; - - /** - * Record a span. - * @param recordable the data representation of the span. - */ - virtual void Record(std::unique_ptr &&recordable) noexcept = 0; -}; -} // namespace trace -} // namespace sdk -OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/trace/span.cc b/sdk/src/trace/span.cc index 54fde5d81d3..7be02665952 100644 --- a/sdk/src/trace/span.cc +++ b/sdk/src/trace/span.cc @@ -8,15 +8,17 @@ namespace sdk namespace trace { Span::Span(std::shared_ptr &&tracer, + std::shared_ptr processor, nostd::string_view name, const trace_api::StartSpanOptions &options) noexcept - : tracer_{std::move(tracer)}, recordable_{tracer_->recorder().MakeRecordable()} + : tracer_{std::move(tracer)}, processor_{processor}, recordable_{processor_->MakeRecordable()} { (void)options; if (recordable_ == nullptr) { return; } + processor_->OnStart(*recordable_); recordable_->SetName(name); } @@ -63,7 +65,7 @@ void Span::End() noexcept { return; } - tracer_->recorder().Record(std::move(recordable_)); + processor_->OnEnd(std::move(recordable_)); recordable_.reset(); } diff --git a/sdk/src/trace/span.h b/sdk/src/trace/span.h index 26d74f426a6..d00a5a16512 100644 --- a/sdk/src/trace/span.h +++ b/sdk/src/trace/span.h @@ -2,8 +2,8 @@ #include +#include "opentelemetry/sdk/trace/tracer.h" #include "opentelemetry/version.h" -#include "src/trace/tracer.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk @@ -16,6 +16,7 @@ class Span final : public trace_api::Span { public: explicit Span(std::shared_ptr &&tracer, + std::shared_ptr processor, nostd::string_view name, const trace_api::StartSpanOptions &options) noexcept; @@ -37,7 +38,8 @@ class Span final : public trace_api::Span trace_api::Tracer &tracer() const noexcept override { return *tracer_; } private: - std::shared_ptr tracer_; + std::shared_ptr tracer_; + std::shared_ptr processor_; mutable std::mutex mu_; std::unique_ptr recordable_; }; diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index 73052e93f42..15e89ceffd4 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -1,5 +1,6 @@ -#include "src/trace/tracer.h" +#include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/sdk/common/atomic_shared_ptr.h" #include "opentelemetry/version.h" #include "src/trace/span.h" @@ -8,12 +9,22 @@ namespace sdk { namespace trace { +void Tracer::SetProcessor(std::shared_ptr processor) noexcept +{ + processor_.store(processor); +} + +std::shared_ptr Tracer::GetProcessor() const noexcept +{ + return processor_.load(); +} + nostd::unique_ptr Tracer::StartSpan( nostd::string_view name, const trace_api::StartSpanOptions &options) noexcept { - return nostd::unique_ptr{new (std::nothrow) - Span{this->shared_from_this(), name, options}}; + return nostd::unique_ptr{ + new (std::nothrow) Span{this->shared_from_this(), processor_.load(), name, options}}; } void Tracer::ForceFlushWithMicroseconds(uint64_t timeout) noexcept diff --git a/sdk/src/trace/tracer.h b/sdk/src/trace/tracer.h deleted file mode 100644 index 65d1318e5b4..00000000000 --- a/sdk/src/trace/tracer.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "opentelemetry/trace/tracer.h" -#include "opentelemetry/version.h" -#include "src/trace/recorder.h" - -#include - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace sdk -{ -namespace trace -{ -class Tracer final : public trace_api::Tracer, public std::enable_shared_from_this -{ -public: - // Note: recorder must be non-null - explicit Tracer(std::unique_ptr &&recorder) noexcept : recorder_{std::move(recorder)} {} - - Recorder &recorder() const noexcept { return *recorder_; } - - // trace_api::Tracer - nostd::unique_ptr StartSpan( - nostd::string_view name, - const trace_api::StartSpanOptions &options = {}) noexcept override; - - void ForceFlushWithMicroseconds(uint64_t timeout) noexcept override; - - void CloseWithMicroseconds(uint64_t timeout) noexcept override; - -private: - std::unique_ptr recorder_; -}; -} // namespace trace -} // namespace sdk -OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc new file mode 100644 index 00000000000..18e3dc99f94 --- /dev/null +++ b/sdk/src/trace/tracer_provider.cc @@ -0,0 +1,33 @@ +#include "opentelemetry/sdk/trace/tracer_provider.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +TracerProvider::TracerProvider(std::shared_ptr processor) noexcept + : processor_{processor}, tracer_(new Tracer(std::move(processor))) +{} + +opentelemetry::nostd::shared_ptr TracerProvider::GetTracer( + nostd::string_view library_name, + nostd::string_view library_version) noexcept +{ + return tracer_; +} + +void TracerProvider::SetProcessor(std::shared_ptr processor) noexcept +{ + processor_.store(processor); + + auto sdkTracer = static_cast(tracer_.get()); + sdkTracer->SetProcessor(processor); +} + +std::shared_ptr TracerProvider::GetProcessor() const noexcept +{ + return processor_.load(); +} +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/test/trace/BUILD b/sdk/test/trace/BUILD index 7ba548fd531..6748cde17f8 100644 --- a/sdk/test/trace/BUILD +++ b/sdk/test/trace/BUILD @@ -1,7 +1,7 @@ cc_test( - name = "default_tracer_provider_test", + name = "tracer_provider_test", srcs = [ - "default_tracer_provider_test.cc", + "tracer_provider_test.cc", ], deps = [ "//sdk/src/trace", @@ -30,3 +30,14 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "tracer_test", + srcs = [ + "tracer_test.cc", + ], + deps = [ + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/sdk/test/trace/CMakeLists.txt b/sdk/test/trace/CMakeLists.txt index ec14273050f..5bca38440ab 100644 --- a/sdk/test/trace/CMakeLists.txt +++ b/sdk/test/trace/CMakeLists.txt @@ -1,5 +1,5 @@ -foreach(testname default_tracer_provider_test span_data_test - simple_processor_test) +foreach(testname tracer_provider_test span_data_test simple_processor_test + tracer_test) add_executable(${testname} "${testname}.cc") target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} opentelemetry_trace) diff --git a/sdk/test/trace/default_tracer_provider_test.cc b/sdk/test/trace/default_tracer_provider_test.cc deleted file mode 100644 index e76cf7426cd..00000000000 --- a/sdk/test/trace/default_tracer_provider_test.cc +++ /dev/null @@ -1,20 +0,0 @@ -#include "opentelemetry/sdk/trace/default_tracer_provider.h" - -#include - -using opentelemetry::sdk::trace::DefaultTracerProvider; - -TEST(TracerProvider, GetTracer) -{ - auto tf = DefaultTracerProvider(); - auto t1 = tf.GetTracer("test"); - auto t2 = tf.GetTracer("test"); - auto t3 = tf.GetTracer("different", "1.0.0"); - ASSERT_NE(nullptr, t1); - ASSERT_NE(nullptr, t2); - ASSERT_NE(nullptr, t3); - - // Should return the same instance each time. - ASSERT_EQ(t1, t2); - ASSERT_EQ(t1, t3); -} diff --git a/sdk/test/trace/tracer_provider_test.cc b/sdk/test/trace/tracer_provider_test.cc new file mode 100644 index 00000000000..8168aeb6bae --- /dev/null +++ b/sdk/test/trace/tracer_provider_test.cc @@ -0,0 +1,29 @@ +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/sdk/trace/tracer.h" +#include "src/trace/simple_processor.h" + +#include + +using namespace opentelemetry::sdk::trace; + +TEST(TracerProvider, GetTracer) +{ + std::shared_ptr processor(new SimpleSpanProcessor(nullptr)); + + TracerProvider tf(processor); + auto t1 = tf.GetTracer("test"); + auto t2 = tf.GetTracer("test"); + auto t3 = tf.GetTracer("different", "1.0.0"); + ASSERT_NE(nullptr, t1); + ASSERT_NE(nullptr, t2); + ASSERT_NE(nullptr, t3); + + // Should return the same instance each time. + ASSERT_EQ(t1, t2); + ASSERT_EQ(t1, t3); + + // Should be an sdk::trace::Tracer with the processor attached. + auto sdkTracer = dynamic_cast(t1.get()); + ASSERT_NE(nullptr, sdkTracer); + ASSERT_EQ(processor, sdkTracer->GetProcessor()); +} diff --git a/sdk/test/trace/tracer_test.cc b/sdk/test/trace/tracer_test.cc new file mode 100644 index 00000000000..99a18ba99df --- /dev/null +++ b/sdk/test/trace/tracer_test.cc @@ -0,0 +1,65 @@ +#include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/sdk/trace/span_data.h" +#include "src/trace/simple_processor.h" + +#include + +using namespace opentelemetry::sdk::trace; + +/** + * A mock exporter that switches a flag once a valid recordable was received. + */ +class MockSpanExporter final : public SpanExporter +{ +public: + MockSpanExporter(std::shared_ptr> spans_received) noexcept + : spans_received_(spans_received) + {} + + std::unique_ptr MakeRecordable() noexcept override + { + return std::unique_ptr(new SpanData); + } + + ExportResult Export( + opentelemetry::nostd::span> &recordables) noexcept override + { + for (auto &recordable : recordables) + { + auto span = std::unique_ptr(static_cast(recordable.release())); + if (span != nullptr) + { + spans_received_->push_back(std::string(span->GetName())); + } + } + + return ExportResult::kSuccess; + } + + void Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept override + {} + +private: + std::shared_ptr> spans_received_; +}; + +TEST(Tracer, ToMockSpanExporter) +{ + std::shared_ptr> spans_received(new std::vector); + std::unique_ptr exporter(new MockSpanExporter(spans_received)); + std::shared_ptr processor(new SimpleSpanProcessor(std::move(exporter))); + std::shared_ptr tracer(new Tracer(processor)); + + auto span_first = tracer->StartSpan("span 1"); + auto span_second = tracer->StartSpan("span 2"); + + ASSERT_EQ(0, spans_received->size()); + + span_second->End(); + ASSERT_EQ(1, spans_received->size()); + ASSERT_EQ("span 2", spans_received->at(0)); + + span_first->End(); + ASSERT_EQ(2, spans_received->size()); + ASSERT_EQ("span 1", spans_received->at(1)); +}