diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h index d5b69aeabb..1bf47ded54 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_config.h @@ -12,6 +12,7 @@ #include "opentelemetry//sdk/trace/sampler.h" #include "opentelemetry/exporters/etw/etw_provider.h" +#include "opentelemetry/exporters/etw/etw_tail_sampler.h" #include "opentelemetry/sdk/trace/id_generator.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -166,6 +167,15 @@ sdk::trace::Sampler &GetSampler(T &t) return *t.sampler_; } +/** + * @brief Utility function to obtain etw::TracerProvider.tail_sampler_ + */ +template +TailSampler &GetTailSampler(T &t) +{ + return *t.tail_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_tail_sampler.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tail_sampler.h new file mode 100644 index 0000000000..c4d0a2edd7 --- /dev/null +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tail_sampler.h @@ -0,0 +1,38 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 +#include "opentelemetry/sdk/trace/sampler.h" +#include "opentelemetry/trace/span.h" + +#pragma once +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporter +{ +namespace etw +{ + +class TailSampler +{ +public: + // convert to etw span if required for getters on span. + // auto etw_span = static_cast(&span); + // Decision based on + // Span::GetStatus() + // Span::GetProperties() + // Span::GetContext() + virtual opentelemetry::sdk::trace::SamplingResult ShouldSample( + const opentelemetry::trace::Span &span) noexcept = 0; +}; + +class AlwaysOnTailSampler : public TailSampler +{ +public: + opentelemetry::sdk::trace::SamplingResult ShouldSample( + const opentelemetry::trace::Span &span) noexcept override + { + return {opentelemetry::sdk::trace::Decision::RECORD_AND_SAMPLE}; + } +}; + +} // namespace etw +} // namespace exporter +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 0c26b0a998..3bb5264c92 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -40,6 +40,7 @@ #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/etw_tail_sampler.h" #include "opentelemetry/exporters/etw/utils.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -235,9 +236,17 @@ class Tracer : public opentelemetry::trace::Tracer, const opentelemetry::trace::Span *parentSpan = nullptr, const opentelemetry::trace::EndSpanOptions & = {}) { - const auto &cfg = GetConfiguration(tracerProvider_); + const auto &cfg = GetConfiguration(tracerProvider_); + const auto &tail_sampler = GetTailSampler(tracerProvider_); const opentelemetry::trace::Span &spanBase = reinterpret_cast(span); + + // Sample span based on the decision of tail based sampler + auto sampling_result = const_cast(&tail_sampler)->ShouldSample(spanBase); + if (!sampling_result.IsRecording() || !sampling_result.IsSampled()) + { + return; + } auto spanContext = spanBase.GetContext(); // Populate Span with presaved attributes @@ -375,7 +384,13 @@ class Tracer : public opentelemetry::trace::Tracer, const opentelemetry::trace::SpanContextKeyValueIterable &links, const opentelemetry::trace::StartSpanOptions &options = {}) noexcept override { -#ifdef OPENTELEMETRY_RTTI_ENABLED + // If RTTI is enabled by compiler, the below code modifies the attributes object passed as arg, + // which is sometime not desirable, set OPENTELEMETRY_NOT_USE_RTTI in application + // to avoid using RTTI in that case in below set of code. +#if !defined OPENTELEMETRY_RTTI_ENABLED || defined OPENTELEMETRY_NOT_USE_RTTI + Properties evtCopy = attributes; + return StartSpan(name, evtCopy, links, options); +#else // OPENTELEMETRY_RTTI_ENABLED is defined common::KeyValueIterable &attribs = const_cast(attributes); Properties *evt = dynamic_cast(&attribs); if (evt != nullptr) @@ -383,9 +398,9 @@ class Tracer : public opentelemetry::trace::Tracer, // Pass as a reference to original modifyable collection without creating a copy return StartSpan(name, *evt, links, options); } -#endif Properties evtCopy = attributes; return StartSpan(name, evtCopy, links, options); +#endif } /** @@ -557,7 +572,14 @@ class Tracer : public opentelemetry::trace::Tracer, common::SystemTimestamp timestamp, const common::KeyValueIterable &attributes) noexcept { -#ifdef OPENTELEMETRY_RTTI_ENABLED + // If RTTI is enabled by compiler, the below code modifies the attributes object passed as arg, + // which is sometime not desirable, set OPENTELEMETRY_NOT_USE_RTTI in application + // to avoid using RTTI in that case in below set of code. +#if !defined OPENTELEMETRY_RTTI_ENABLED || defined OPENTELEMETRY_NOT_USE_RTTI + // Pass a copy converted to Properties object on stack + Properties evtCopy = attributes; + return AddEvent(span, name, timestamp, evtCopy); +#else // OPENTELEMETRY_RTTI_ENABLED is defined common::KeyValueIterable &attribs = const_cast(attributes); Properties *evt = dynamic_cast(&attribs); if (evt != nullptr) @@ -565,10 +587,9 @@ class Tracer : public opentelemetry::trace::Tracer, // Pass as a reference to original modifyable collection without creating a copy return AddEvent(span, name, timestamp, *evt); } -#endif - // Pass a copy converted to Properties object on stack Properties evtCopy = attributes; return AddEvent(span, name, timestamp, evtCopy); +#endif } /** @@ -826,6 +847,8 @@ class Span : public opentelemetry::trace::Span status_description_ = description.data(); } + opentelemetry::trace::StatusCode GetStatus() { return status_code_; } + void SetAttributes(Properties attributes) { attributes_ = attributes; } /** @@ -932,6 +955,12 @@ class TracerProvider : public opentelemetry::trace::TracerProvider */ std::unique_ptr sampler_; + /** + * @brief Sampler configured + * + */ + std::unique_ptr tail_sampler_; + /** * @brief IdGenerator for trace_id and span_id * @@ -942,15 +971,19 @@ class TracerProvider : public opentelemetry::trace::TracerProvider * @brief Construct instance of TracerProvider with given options * @param options Configuration options */ - 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())) + 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()), + std::unique_ptr tail_sampler = + std::unique_ptr(new AlwaysOnTailSampler())) : opentelemetry::trace::TracerProvider(), sampler_{std::move(sampler)}, - id_generator_{std::move(id_generator)} + id_generator_{std::move(id_generator)}, + tail_sampler_{std::move(tail_sampler)} { // By default we ensure that all events carry their with TraceId and SpanId GetOption(options, "enableTraceId", config_.enableTraceId, true); @@ -985,7 +1018,9 @@ class TracerProvider : public opentelemetry::trace::TracerProvider : opentelemetry::trace::TracerProvider(), sampler_{std::unique_ptr(new sdk::trace::AlwaysOnSampler)}, id_generator_{std::unique_ptr( - new sdk::trace::ETWRandomIdGenerator())} + new sdk::trace::ETWRandomIdGenerator())}, + tail_sampler_{ + std::unique_ptr(new AlwaysOnTailSampler())} { config_.enableTraceId = true; config_.enableSpanId = true; diff --git a/exporters/etw/test/etw_tracer_test.cc b/exporters/etw/test/etw_tracer_test.cc index a23345ddd9..da2505c4b7 100644 --- a/exporters/etw/test/etw_tracer_test.cc +++ b/exporters/etw/test/etw_tracer_test.cc @@ -6,7 +6,6 @@ # include # include # include - # include "opentelemetry//sdk/trace/sampler.h" # include "opentelemetry/exporters/etw/etw_tracer_exporter.h" # include "opentelemetry/sdk/trace/samplers/always_off.h" @@ -78,6 +77,16 @@ class MockSampler : public sdk::trace::Sampler std::shared_ptr delegate_sampler_; }; +class AlwaysOffTailSampler : public TailSampler +{ +public: + opentelemetry::sdk::trace::SamplingResult ShouldSample( + const opentelemetry::trace::Span &span) noexcept override + { + return {opentelemetry::sdk::trace::Decision::DROP}; + } +}; + /* clang-format off */ TEST(ETWTracer, TracerCheck) { @@ -459,6 +468,26 @@ TEST(ETWTracer, AlwayOffSampler) EXPECT_EQ(span->GetContext().IsSampled(), false); } +TEST(ETWTracer, AlwayOffTailSampler) +{ + std::string providerName = kGlobalProviderName; // supply unique instrumentation name here + std::unique_ptr always_on{new sdk::trace::AlwaysOnSampler()}; + sdk::trace::IdGenerator *id_generator = new MockIdGenerator(); + std::unique_ptr always_off_tail{new AlwaysOffTailSampler()}; + exporter::etw::TracerProvider tp + ({ + {"enableTraceId", true}, + {"enableSpanId", true}, + {"enableActivityId", true}, + {"enableRelatedActivityId", true}, + {"enableAutoParent", true} + }, + std::move(always_on), + std::unique_ptr(id_generator), + std::move(always_off_tail)); + auto tracer = tp.GetTracer(providerName); +} + TEST(ETWTracer, CustomIdGenerator) { std::string providerName = kGlobalProviderName; // supply unique instrumentation name here