diff --git a/api/include/opentelemetry/core/timestamp.h b/api/include/opentelemetry/core/timestamp.h index 5fea58d535b..7ab7da6f5d6 100644 --- a/api/include/opentelemetry/core/timestamp.h +++ b/api/include/opentelemetry/core/timestamp.h @@ -42,6 +42,11 @@ class SystemTimestamp return std::chrono::nanoseconds{nanos_since_epoch_}; } + bool operator==(const SystemTimestamp &other) const noexcept + { + return nanos_since_epoch_ == other.nanos_since_epoch_; + } + private: int64_t nanos_since_epoch_; }; @@ -79,6 +84,11 @@ class SteadyTimestamp return std::chrono::nanoseconds{nanos_since_epoch_}; } + bool operator==(const SteadyTimestamp &other) const noexcept + { + return nanos_since_epoch_ == other.nanos_since_epoch_; + } + private: int64_t nanos_since_epoch_; }; diff --git a/api/include/opentelemetry/plugin/tracer.h b/api/include/opentelemetry/plugin/tracer.h index b2199a07bf9..4f00be0c32c 100644 --- a/api/include/opentelemetry/plugin/tracer.h +++ b/api/include/opentelemetry/plugin/tracer.h @@ -39,7 +39,7 @@ class Span final : public trace::Span void UpdateName(nostd::string_view name) noexcept override { span_->UpdateName(name); } - void End() noexcept override { span_->End(); } + void End(const trace::EndSpanOptions &options = {}) noexcept override { span_->End(); } bool IsRecording() const noexcept override { return span_->IsRecording(); } diff --git a/api/include/opentelemetry/trace/noop.h b/api/include/opentelemetry/trace/noop.h index 0c3ef15c69f..82b10b9a71e 100644 --- a/api/include/opentelemetry/trace/noop.h +++ b/api/include/opentelemetry/trace/noop.h @@ -38,7 +38,7 @@ class NoopSpan final : public Span void UpdateName(nostd::string_view /*name*/) noexcept override {} - void End() noexcept override {} + void End(const EndSpanOptions &options = {}) noexcept override {} bool IsRecording() const noexcept override { return false; } diff --git a/api/include/opentelemetry/trace/span.h b/api/include/opentelemetry/trace/span.h index 7d323b72071..f7b0aebd945 100644 --- a/api/include/opentelemetry/trace/span.h +++ b/api/include/opentelemetry/trace/span.h @@ -44,6 +44,11 @@ struct StartSpanOptions SpanKind kind = SpanKind::kInternal; }; +struct EndSpanOptions +{ + core::SteadyTimestamp end_steady_time; +}; + class Tracer; /** @@ -132,10 +137,7 @@ class Span // Mark the end of the Span. Only the timing of the first End call for a given Span will // be recorded, and implementations are free to ignore all further calls. - virtual void End() noexcept = 0; - - // TODO - // virtual void End(EndSpanOptions&& opts) noexcept = 0; + virtual void End(const EndSpanOptions &options = {}) noexcept = 0; // TODO // SpanContext context() const noexcept = 0; diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7d5453f66d6..dcb04ccf4e4 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(plugin) +add_subdirectory(simple) diff --git a/examples/plugin/plugin/tracer.cc b/examples/plugin/plugin/tracer.cc index 6cf63af1184..18ab36a03b3 100644 --- a/examples/plugin/plugin/tracer.cc +++ b/examples/plugin/plugin/tracer.cc @@ -38,7 +38,7 @@ class Span final : public trace::Span void UpdateName(nostd::string_view /*name*/) noexcept override {} - void End() noexcept override {} + void End(const trace::EndSpanOptions &options) noexcept override {} bool IsRecording() const noexcept override { return true; } diff --git a/examples/simple/BUILD b/examples/simple/BUILD new file mode 100644 index 00000000000..34583e2eace --- /dev/null +++ b/examples/simple/BUILD @@ -0,0 +1,25 @@ +cc_library( + name = "foo_library", + srcs = [ + "foo_library/foo_library.cc", + ], + hdrs = [ + "foo_library/foo_library.h", + ], + deps = [ + "//api", + ], +) + +cc_binary( + name = "example_simple", + srcs = [ + "main.cc", + "stdout_exporter.h", + ], + deps = [ + ":foo_library", + "//api", + "//sdk/src/trace", + ], +) diff --git a/examples/simple/CMakeLists.txt b/examples/simple/CMakeLists.txt new file mode 100644 index 00000000000..4b0ab35f74f --- /dev/null +++ b/examples/simple/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(foo_library foo_library/foo_library.cc) +target_link_libraries(foo_library ${CMAKE_THREAD_LIBS_INIT} opentelemetry_api) + +add_executable(example_simple main.cc) +target_link_libraries(example_simple ${CMAKE_THREAD_LIBS_INIT} foo_library + opentelemetry_trace) diff --git a/examples/simple/foo_library/foo_library.cc b/examples/simple/foo_library/foo_library.cc new file mode 100644 index 00000000000..0990b85cede --- /dev/null +++ b/examples/simple/foo_library/foo_library.cc @@ -0,0 +1,33 @@ +#include "opentelemetry/trace/provider.h" + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; + +namespace +{ +nostd::shared_ptr get_tracer() +{ + auto provider = trace::Provider::GetTracerProvider(); + return provider->GetTracer("foo_library"); +} + +void f1() +{ + auto span = get_tracer()->StartSpan("f1"); +} + +void f2() +{ + auto span = get_tracer()->StartSpan("f2"); + + f1(); + f1(); +} +} // namespace + +void foo_library() +{ + auto span = get_tracer()->StartSpan("library"); + + f2(); +} diff --git a/examples/simple/foo_library/foo_library.h b/examples/simple/foo_library/foo_library.h new file mode 100644 index 00000000000..7ac75c0e50a --- /dev/null +++ b/examples/simple/foo_library/foo_library.h @@ -0,0 +1,3 @@ +#pragma once + +void foo_library(); diff --git a/examples/simple/main.cc b/examples/simple/main.cc new file mode 100644 index 00000000000..af03d2e6db0 --- /dev/null +++ b/examples/simple/main.cc @@ -0,0 +1,29 @@ +#include "opentelemetry/sdk/trace/simple_processor.h" +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/trace/provider.h" + +// Using an exporter that simply dumps span data to stdout. +#include "stdout_exporter.h" + +#include "foo_library/foo_library.h" + +namespace +{ +void initTracer() +{ + auto exporter = std::unique_ptr(new StdoutExporter); + auto processor = std::shared_ptr( + new sdktrace::SimpleSpanProcessor(std::move(exporter))); + auto provider = nostd::shared_ptr(new sdktrace::TracerProvider(processor)); + trace::Provider::SetTracerProvider(provider); +} +} // namespace + +int main() +{ + // Removing this line will leave OT initialized with the default noop + // tracer, thus being effectively deactivated. + initTracer(); + + foo_library(); +} diff --git a/examples/simple/stdout_exporter.h b/examples/simple/stdout_exporter.h new file mode 100644 index 00000000000..6a58c5b8d66 --- /dev/null +++ b/examples/simple/stdout_exporter.h @@ -0,0 +1,52 @@ +#pragma once + +#include "opentelemetry/sdk/trace/exporter.h" +#include "opentelemetry/sdk/trace/span_data.h" + +#include + +namespace trace = opentelemetry::trace; +namespace nostd = opentelemetry::nostd; +namespace sdktrace = opentelemetry::sdk::trace; + +class StdoutExporter final : public sdktrace::SpanExporter +{ + std::unique_ptr MakeRecordable() noexcept + { + return std::unique_ptr(new sdktrace::SpanData); + } + + sdktrace::ExportResult Export( + const nostd::span> &spans) noexcept + { + for (auto &recordable : spans) + { + auto span = std::unique_ptr( + static_cast(recordable.release())); + + if (span != nullptr) + { + char trace_id[32] = {0}; + char span_id[16] = {0}; + char parent_span_id[16] = {0}; + + span->GetTraceId().ToLowerBase16(trace_id); + span->GetSpanId().ToLowerBase16(span_id); + span->GetParentSpanId().ToLowerBase16(parent_span_id); + + std::cout << "{" + << "\n name : " << span->GetName() + << "\n trace_id : " << std::string(trace_id, 32) + << "\n span_id : " << std::string(span_id, 16) + << "\n parent_span_id: " << std::string(parent_span_id, 16) + << "\n start : " << span->GetStartTime().time_since_epoch().count() + << "\n duration : " << span->GetDuration().count() << "\n}" + << "\n"; + } + } + + return sdktrace::ExportResult::kSuccess; + } + + void Shutdown(std::chrono::microseconds timeout = std::chrono::microseconds(0)) noexcept {} +}; diff --git a/sdk/include/opentelemetry/sdk/trace/exporter.h b/sdk/include/opentelemetry/sdk/trace/exporter.h index d517c572b2a..ec5473edb80 100644 --- a/sdk/include/opentelemetry/sdk/trace/exporter.h +++ b/sdk/include/opentelemetry/sdk/trace/exporter.h @@ -48,7 +48,8 @@ class SpanExporter * @param spans a span of unique pointers to span recordables */ virtual ExportResult Export( - nostd::span> &spans) noexcept = 0; + const nostd::span> + &spans) noexcept = 0; /** * Shut down the exporter. diff --git a/sdk/src/trace/simple_processor.h b/sdk/include/opentelemetry/sdk/trace/simple_processor.h similarity index 100% rename from sdk/src/trace/simple_processor.h rename to sdk/include/opentelemetry/sdk/trace/simple_processor.h diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h index 322d2fcb59d..d78fed02fc5 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -43,7 +43,7 @@ class TracerProvider final : public opentelemetry::trace::TracerProvider private: opentelemetry::sdk::AtomicSharedPtr processor_; - opentelemetry::nostd::shared_ptr tracer_; + std::shared_ptr tracer_; }; } // namespace trace } // namespace sdk diff --git a/sdk/src/trace/.tracer.cc.swp b/sdk/src/trace/.tracer.cc.swp new file mode 100644 index 00000000000..6bb638cf102 Binary files /dev/null and b/sdk/src/trace/.tracer.cc.swp differ diff --git a/sdk/src/trace/span.cc b/sdk/src/trace/span.cc index 7b4937bb86f..fbe2923dcf3 100644 --- a/sdk/src/trace/span.cc +++ b/sdk/src/trace/span.cc @@ -7,11 +7,45 @@ namespace sdk { namespace trace { + +using opentelemetry::core::SteadyTimestamp; +using opentelemetry::core::SystemTimestamp; + +namespace +{ +SystemTimestamp NowOrGiven(const SystemTimestamp &system) +{ + if (system == SystemTimestamp()) + { + return SystemTimestamp(std::chrono::system_clock::now()); + } + else + { + return system; + } +} + +SteadyTimestamp NowOrGiven(const SteadyTimestamp &steady) +{ + if (steady == SteadyTimestamp()) + { + return SteadyTimestamp(std::chrono::steady_clock::now()); + } + else + { + return steady; + } +} +} // namespace + Span::Span(std::shared_ptr &&tracer, std::shared_ptr processor, nostd::string_view name, const trace_api::StartSpanOptions &options) noexcept - : tracer_{std::move(tracer)}, processor_{processor}, recordable_{processor_->MakeRecordable()} + : tracer_{std::move(tracer)}, + processor_{processor}, + recordable_{processor_->MakeRecordable()}, + start_steady_time{options.start_steady_time} { (void)options; if (recordable_ == nullptr) @@ -20,6 +54,9 @@ Span::Span(std::shared_ptr &&tracer, } processor_->OnStart(*recordable_); recordable_->SetName(name); + + recordable_->SetStartTime(NowOrGiven(options.start_system_time)); + start_steady_time = NowOrGiven(options.start_steady_time); } Span::~Span() @@ -67,13 +104,17 @@ void Span::UpdateName(nostd::string_view name) noexcept recordable_->SetName(name); } -void Span::End() noexcept +void Span::End(const trace_api::EndSpanOptions &options) noexcept { std::lock_guard lock_guard{mu_}; if (recordable_ == nullptr) { return; } + + recordable_->SetDuration( + std::chrono::steady_clock::time_point(NowOrGiven(options.end_steady_time)) - + std::chrono::steady_clock::time_point(start_steady_time)); processor_->OnEnd(std::move(recordable_)); recordable_.reset(); } diff --git a/sdk/src/trace/span.h b/sdk/src/trace/span.h index 3fd1bb4af7f..e1a6f2084a9 100644 --- a/sdk/src/trace/span.h +++ b/sdk/src/trace/span.h @@ -35,7 +35,7 @@ class Span final : public trace_api::Span void UpdateName(nostd::string_view name) noexcept override; - void End() noexcept override; + void End(const trace_api::EndSpanOptions &options = {}) noexcept override; bool IsRecording() const noexcept override; @@ -46,6 +46,7 @@ class Span final : public trace_api::Span std::shared_ptr processor_; mutable std::mutex mu_; std::unique_ptr recordable_; + opentelemetry::core::SteadyTimestamp start_steady_time; }; } // namespace trace } // namespace sdk diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index 18e3dc99f94..3268207510d 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -13,7 +13,7 @@ opentelemetry::nostd::shared_ptr TracerProvider::G nostd::string_view library_name, nostd::string_view library_version) noexcept { - return tracer_; + return opentelemetry::nostd::shared_ptr(tracer_); } void TracerProvider::SetProcessor(std::shared_ptr processor) noexcept diff --git a/sdk/test/trace/simple_processor_test.cc b/sdk/test/trace/simple_processor_test.cc index ddeba25fe6d..b4819aa0ae2 100644 --- a/sdk/test/trace/simple_processor_test.cc +++ b/sdk/test/trace/simple_processor_test.cc @@ -1,4 +1,4 @@ -#include "src/trace/simple_processor.h" +#include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/sdk/trace/span_data.h" @@ -23,7 +23,7 @@ class MockSpanExporter final : public SpanExporter } ExportResult Export( - opentelemetry::nostd::span> &spans) noexcept override + const opentelemetry::nostd::span> &spans) noexcept override { for (auto &span : spans) { diff --git a/sdk/test/trace/tracer_provider_test.cc b/sdk/test/trace/tracer_provider_test.cc index 8168aeb6bae..90dbe7c7470 100644 --- a/sdk/test/trace/tracer_provider_test.cc +++ b/sdk/test/trace/tracer_provider_test.cc @@ -1,6 +1,6 @@ #include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/tracer.h" -#include "src/trace/simple_processor.h" #include diff --git a/sdk/test/trace/tracer_test.cc b/sdk/test/trace/tracer_test.cc index 99a18ba99df..92167fe3b1d 100644 --- a/sdk/test/trace/tracer_test.cc +++ b/sdk/test/trace/tracer_test.cc @@ -1,10 +1,12 @@ #include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/span_data.h" -#include "src/trace/simple_processor.h" #include using namespace opentelemetry::sdk::trace; +using opentelemetry::core::SteadyTimestamp; +using opentelemetry::core::SystemTimestamp; /** * A mock exporter that switches a flag once a valid recordable was received. @@ -12,7 +14,7 @@ using namespace opentelemetry::sdk::trace; class MockSpanExporter final : public SpanExporter { public: - MockSpanExporter(std::shared_ptr> spans_received) noexcept + MockSpanExporter(std::shared_ptr>> spans_received) noexcept : spans_received_(spans_received) {} @@ -22,14 +24,14 @@ class MockSpanExporter final : public SpanExporter } ExportResult Export( - opentelemetry::nostd::span> &recordables) noexcept override + const 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())); + spans_received_->push_back(std::move(span)); } } @@ -40,15 +42,25 @@ class MockSpanExporter final : public SpanExporter {} private: - std::shared_ptr> spans_received_; + std::shared_ptr>> spans_received_; }; -TEST(Tracer, ToMockSpanExporter) +namespace +{ +std::shared_ptr initTracer( + std::shared_ptr>> &received) { - std::shared_ptr> spans_received(new std::vector); - std::unique_ptr exporter(new MockSpanExporter(spans_received)); + std::unique_ptr exporter(new MockSpanExporter(received)); std::shared_ptr processor(new SimpleSpanProcessor(std::move(exporter))); - std::shared_ptr tracer(new Tracer(processor)); + return std::shared_ptr(new Tracer(processor)); +} +} // namespace + +TEST(Tracer, ToMockSpanExporter) +{ + std::shared_ptr>> spans_received( + new std::vector>); + auto tracer = initTracer(spans_received); auto span_first = tracer->StartSpan("span 1"); auto span_second = tracer->StartSpan("span 2"); @@ -57,9 +69,46 @@ TEST(Tracer, ToMockSpanExporter) span_second->End(); ASSERT_EQ(1, spans_received->size()); - ASSERT_EQ("span 2", spans_received->at(0)); + ASSERT_EQ("span 2", spans_received->at(0)->GetName()); span_first->End(); ASSERT_EQ(2, spans_received->size()); - ASSERT_EQ("span 1", spans_received->at(1)); + ASSERT_EQ("span 1", spans_received->at(1)->GetName()); +} + +TEST(Tracer, StartSpan) +{ + std::shared_ptr>> spans_received( + new std::vector>); + auto tracer = initTracer(spans_received); + + tracer->StartSpan("span 1")->End(); + + ASSERT_EQ(1, spans_received->size()); + + auto &span_data = spans_received->at(0); + ASSERT_LT(std::chrono::nanoseconds(0), span_data->GetStartTime().time_since_epoch()); + ASSERT_LT(std::chrono::nanoseconds(0), span_data->GetDuration()); +} + +TEST(Tracer, StartSpanWithTime) +{ + std::shared_ptr>> spans_received( + new std::vector>); + auto tracer = initTracer(spans_received); + + opentelemetry::trace::StartSpanOptions start; + start.start_system_time = SystemTimestamp(std::chrono::nanoseconds(300)); + start.start_steady_time = SteadyTimestamp(std::chrono::nanoseconds(10)); + + opentelemetry::trace::EndSpanOptions end; + end.end_steady_time = SteadyTimestamp(std::chrono::nanoseconds(40)); + + tracer->StartSpan("span 1", start)->End(end); + + ASSERT_EQ(1, spans_received->size()); + + auto &span_data = spans_received->at(0); + ASSERT_EQ(std::chrono::nanoseconds(300), span_data->GetStartTime().time_since_epoch()); + ASSERT_EQ(std::chrono::nanoseconds(30), span_data->GetDuration()); }