From 453534726b1342c54c871c331d7f5c7f50c3c2db Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 23 Aug 2022 17:53:16 -0700 Subject: [PATCH] ETW Exporter - Add support for Sampler and ID Generator (#1547) --- .../opentelemetry/exporters/etw/etw_config.h | 20 ++++ .../exporters/etw/etw_random_id_generator.h | 47 +++++++++ .../opentelemetry/exporters/etw/etw_tracer.h | 98 ++++++++++++------- exporters/etw/test/etw_tracer_test.cc | 57 +++++++++++ 4 files changed, 186 insertions(+), 36 deletions(-) create mode 100644 exporters/etw/include/opentelemetry/exporters/etw/etw_random_id_generator.h diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h index 60cac48a5e..d5b69aeabb 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h @@ -10,7 +10,9 @@ #include "opentelemetry/nostd/variant.h" #include "opentelemetry/trace/span_id.h" +#include "opentelemetry//sdk/trace/sampler.h" #include "opentelemetry/exporters/etw/etw_provider.h" +#include "opentelemetry/sdk/trace/id_generator.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace exporter @@ -146,6 +148,24 @@ TelemetryProviderConfiguration &GetConfiguration(T &t) return t.config_; } +/** + * @brief Utility function to obtain etw::TracerProvider.id_generator_ + */ +template +sdk::trace::IdGenerator &GetIdGenerator(T &t) +{ + return *t.id_generator_; +} + +/** + * @brief Utility function to obtain etw::TracerProvider.sampler_ + */ +template +sdk::trace::Sampler &GetSampler(T &t) +{ + return *t.sampler_; +} + /** * @brief Utility template to convert SpanId or TraceId to hex. * @param id - value of SpanId or TraceId diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_random_id_generator.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_random_id_generator.h new file mode 100644 index 0000000000..c347c44f92 --- /dev/null +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_random_id_generator.h @@ -0,0 +1,47 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include "opentelemetry/sdk/trace/id_generator.h" +#include "opentelemetry/version.h" + +#ifdef _WIN32 +# include "Windows.h" +#endif + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +class ETWRandomIdGenerator : public IdGenerator +{ + + opentelemetry::trace::SpanId GenerateSpanId() noexcept override + { + GUID span_id; + // Generate random GUID + CoCreateGuid(&span_id); + const auto *spanIdPtr = reinterpret_cast(std::addressof(span_id)); + + // Populate SpanId with that GUID + nostd::span spanIdBytes( + spanIdPtr, spanIdPtr + opentelemetry::trace::SpanId::kSize); + return opentelemetry::trace::SpanId(spanIdBytes); + } + + opentelemetry::trace::TraceId GenerateTraceId() noexcept override + { + GUID trace_id; + CoCreateGuid(&trace_id); + // Populate TraceId of the Tracer with the above GUID + const auto *traceIdPtr = reinterpret_cast(std::addressof(trace_id)); + nostd::span traceIdBytes( + traceIdPtr, traceIdPtr + opentelemetry::trace::TraceId::kSize); + return opentelemetry::trace::TraceId(traceIdBytes); + } +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index 5db31a7a75..0c828d7241 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -26,18 +26,20 @@ #include "opentelemetry/common/key_value_iterable_view.h" -#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/noop.h" #include "opentelemetry/trace/span_context_kv_iterable_view.h" #include "opentelemetry/trace/span_id.h" #include "opentelemetry/trace/trace_id.h" #include "opentelemetry/trace/tracer_provider.h" #include "opentelemetry/sdk/trace/exporter.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" #include "opentelemetry/exporters/etw/etw_config.h" #include "opentelemetry/exporters/etw/etw_fields.h" #include "opentelemetry/exporters/etw/etw_properties.h" #include "opentelemetry/exporters/etw/etw_provider.h" +#include "opentelemetry/exporters/etw/etw_random_id_generator.h" #include "opentelemetry/exporters/etw/utils.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -154,7 +156,9 @@ void UpdateStatus(T &t, Properties &props) /** * @brief Tracer class that allows to send spans to ETW Provider. */ -class Tracer : public opentelemetry::trace::Tracer + +class Tracer : public opentelemetry::trace::Tracer, + public std::enable_shared_from_this { /** @@ -353,14 +357,7 @@ class Tracer : public opentelemetry::trace::Tracer encoding(encoding), provHandle(initProvHandle()) { - // Generate random GUID - GUID trace_id; - CoCreateGuid(&trace_id); - // Populate TraceId of the Tracer with the above GUID - const auto *traceIdPtr = reinterpret_cast(std::addressof(trace_id)); - nostd::span traceIdBytes( - traceIdPtr, traceIdPtr + opentelemetry::trace::TraceId::kSize); - traceId_ = opentelemetry::trace::TraceId(traceIdBytes); + traceId_ = GetIdGenerator(tracerProvider_).GenerateTraceId(); } /** @@ -377,6 +374,30 @@ class Tracer : public opentelemetry::trace::Tracer const opentelemetry::trace::SpanContextKeyValueIterable &links, const opentelemetry::trace::StartSpanOptions &options = {}) noexcept override { + const auto &cfg = GetConfiguration(tracerProvider_); + + // Parent Context: + // - either use current span + // - or attach to parent SpanContext specified in options + opentelemetry::trace::SpanContext parentContext = GetCurrentSpan()->GetContext(); + if (nostd::holds_alternative(options.parent)) + { + auto span_context = nostd::get(options.parent); + if (span_context.IsValid()) + { + parentContext = span_context; + } + } + auto sampling_result = + GetSampler(tracerProvider_) + .ShouldSample(parentContext, traceId_, name, options.kind, attributes, links); + if (sampling_result.decision == sdk::trace::Decision::DROP) + { + static nostd::shared_ptr noop_span( + new trace::NoopSpan{this->shared_from_this()}); + return noop_span; + } + #ifdef OPENTELEMETRY_RTTI_ENABLED common::KeyValueIterable &attribs = const_cast(attributes); Properties *evt = dynamic_cast(&attribs); @@ -686,27 +707,6 @@ class Span : public opentelemetry::trace::Span opentelemetry::trace::SpanContext context_; - const opentelemetry::trace::SpanContext CreateContext() - { - GUID activity_id; - // Generate random GUID - CoCreateGuid(&activity_id); - const auto *activityIdPtr = reinterpret_cast(std::addressof(activity_id)); - - // Populate SpanId with that GUID - nostd::span spanIdBytes( - activityIdPtr, activityIdPtr + opentelemetry::trace::SpanId::kSize); - const opentelemetry::trace::SpanId spanId(spanIdBytes); - - // Inherit trace_id from Tracer - const opentelemetry::trace::TraceId traceId{owner_.trace_id()}; - // TODO: TraceFlags are not supported by ETW exporter. - const opentelemetry::trace::TraceFlags flags{0}; - // TODO: Remote parent is not supported by ETW exporter. - const bool hasRemoteParent = false; - return opentelemetry::trace::SpanContext{traceId, spanId, flags, hasRemoteParent}; - } - public: /** * @brief Update Properties object with current Span status @@ -764,7 +764,8 @@ class Span : public opentelemetry::trace::Span start_time_(std::chrono::system_clock::now()), owner_(owner), parent_(parent), - context_(CreateContext()) + context_{owner.traceId_, GetIdGenerator(owner.tracerProvider_).GenerateSpanId(), + opentelemetry::trace::TraceFlags{0}, false} { name_ = name; UNREFERENCED_PARAMETER(options); @@ -920,11 +921,31 @@ class TracerProvider : public opentelemetry::trace::TracerProvider */ TelemetryProviderConfiguration config_; + /** + * @brief Sampler configured + * + */ + std::unique_ptr sampler_; + + /** + * @brief IdGenerator for trace_id and span_id + * + */ + std::unique_ptr id_generator_; + /** * @brief Construct instance of TracerProvider with given options * @param options Configuration options */ - TracerProvider(TelemetryProviderOptions options) : opentelemetry::trace::TracerProvider() + TracerProvider(TelemetryProviderOptions options, + std::unique_ptr sampler = + std::unique_ptr(new sdk::trace::AlwaysOnSampler), + std::unique_ptr id_generator = + std::unique_ptr( + new sdk::trace::ETWRandomIdGenerator())) + : opentelemetry::trace::TracerProvider(), + sampler_{std::move(sampler)}, + id_generator_{std::move(id_generator)} { // By default we ensure that all events carry their with TraceId and SpanId GetOption(options, "enableTraceId", config_.enableTraceId, true); @@ -955,7 +976,11 @@ class TracerProvider : public opentelemetry::trace::TracerProvider config_.encoding = GetEncoding(options); } - TracerProvider() : opentelemetry::trace::TracerProvider() + TracerProvider() + : opentelemetry::trace::TracerProvider(), + sampler_{std::unique_ptr(new sdk::trace::AlwaysOnSampler)}, + id_generator_{std::unique_ptr( + new sdk::trace::ETWRandomIdGenerator())} { config_.enableTraceId = true; config_.enableSpanId = true; @@ -985,8 +1010,9 @@ class TracerProvider : public opentelemetry::trace::TracerProvider UNREFERENCED_PARAMETER(args); UNREFERENCED_PARAMETER(schema_url); ETWProvider::EventFormat evtFmt = config_.encoding; - return nostd::shared_ptr{new (std::nothrow) - Tracer(*this, name, evtFmt)}; + std::shared_ptr tracer{new (std::nothrow) + Tracer(*this, name, evtFmt)}; + return nostd::shared_ptr{tracer}; } }; diff --git a/exporters/etw/test/etw_tracer_test.cc b/exporters/etw/test/etw_tracer_test.cc index 1c9c1f09be..43b7d18698 100644 --- a/exporters/etw/test/etw_tracer_test.cc +++ b/exporters/etw/test/etw_tracer_test.cc @@ -8,6 +8,7 @@ # include # include "opentelemetry/exporters/etw/etw_tracer_exporter.h" +# include "opentelemetry/sdk/trace/samplers/always_off.h" # include "opentelemetry/sdk/trace/simple_processor.h" using namespace OPENTELEMETRY_NAMESPACE; @@ -21,6 +22,24 @@ std::string getTemporaryValue() return std::string("Value from Temporary std::string"); } +/** + * A Mock Custom Id Generator + */ +class MockIdGenerator : public sdk::trace::IdGenerator +{ + opentelemetry::trace::SpanId GenerateSpanId() noexcept override + { + return opentelemetry::trace::SpanId(buf_span); + } + + opentelemetry::trace::TraceId GenerateTraceId() noexcept override + { + return opentelemetry::trace::TraceId(buf_trace); + } + uint8_t buf_span[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + uint8_t buf_trace[16] = {1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1}; +}; + /* clang-format off */ TEST(ETWTracer, TracerCheck) { @@ -383,6 +402,44 @@ TEST(ETWTracer, GlobalSingletonTracer) globalTracer.CloseWithMicroseconds(0); } +TEST(ETWTracer, AlwayOffSampler) +{ + std::string providerName = kGlobalProviderName; // supply unique instrumentation name here + std::unique_ptr always_off{new sdk::trace::AlwaysOffSampler()}; + exporter::etw::TracerProvider tp + ({ + {"enableTraceId", true}, + {"enableSpanId", true}, + {"enableActivityId", true}, + {"enableRelatedActivityId", true}, + {"enableAutoParent", true} + }, + std::move(always_off)); + auto tracer = tp.GetTracer(providerName); + auto span = tracer->StartSpan("span_off"); + EXPECT_EQ(span->GetContext().IsValid(), false); +} + +TEST(ETWTracer, CustomIdGenerator) +{ + std::string providerName = kGlobalProviderName; // supply unique instrumentation name here + sdk::trace::IdGenerator *id_generator = new MockIdGenerator(); + std::unique_ptr always_on{new sdk::trace::AlwaysOnSampler()}; + exporter::etw::TracerProvider tp + ({ + {"enableTraceId", true}, + {"enableSpanId", true}, + {"enableActivityId", true}, + {"enableRelatedActivityId", true}, + {"enableAutoParent", true} + }, + std::move(always_on), + std::unique_ptr(id_generator)); + auto tracer = tp.GetTracer(providerName); + auto span = tracer->StartSpan("span_on"); + EXPECT_EQ(span->GetContext().trace_id(), id_generator->GenerateTraceId()); +} + /* clang-format on */ #endif