From c6738ce7bbbd6ff6fc0f7db39cda42c6b699c97b Mon Sep 17 00:00:00 2001 From: Furkan Mutlu Date: Fri, 8 May 2026 20:47:05 +0300 Subject: [PATCH 1/8] fix: preserve delta start timestamp in fast path --- .../sdk/metrics/state/temporal_metric_storage.h | 2 ++ sdk/src/metrics/state/temporal_metric_storage.cc | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h b/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h index 007b3c74cd..ef0315f3ee 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h @@ -56,6 +56,8 @@ class TemporalMetricStorage // Lock while building metrics mutable opentelemetry::common::SpinLockMutex lock_; const AggregationConfig *aggregation_config_; + opentelemetry::common::SystemTimestamp last_delta_collection_ts_; + bool has_last_delta_collection_ts_ = false; }; } // namespace metrics } // namespace sdk diff --git a/sdk/src/metrics/state/temporal_metric_storage.cc b/sdk/src/metrics/state/temporal_metric_storage.cc index 29be34a8ea..2af533750a 100644 --- a/sdk/src/metrics/state/temporal_metric_storage.cc +++ b/sdk/src/metrics/state/temporal_metric_storage.cc @@ -54,6 +54,11 @@ bool TemporalMetricStorage::buildMetrics(CollectorHandle *collector, // no other reader configured to collect those data. if (collectors.size() == 1 && aggregation_temporarily == AggregationTemporality::kDelta) { + if (!has_last_delta_collection_ts_) + { + last_delta_collection_ts_ = sdk_start_ts; + has_last_delta_collection_ts_ = true; + } // If no metrics, early return if (delta_metrics->Size() == 0) { @@ -63,8 +68,9 @@ bool TemporalMetricStorage::buildMetrics(CollectorHandle *collector, MetricData metric_data; metric_data.instrument_descriptor = instrument_descriptor_; metric_data.aggregation_temporality = AggregationTemporality::kDelta; - metric_data.start_ts = sdk_start_ts; + metric_data.start_ts = last_delta_collection_ts_; metric_data.end_ts = collection_ts; + last_delta_collection_ts_ = collection_ts; // Direct conversion of delta metrics to point data delta_metrics->GetAllEntries( From 62446d845e815795e0eed4c4389b74a5522e2522 Mon Sep 17 00:00:00 2001 From: Furkan Mutlu Date: Fri, 8 May 2026 21:17:28 +0300 Subject: [PATCH 2/8] fix: keep tracer alive for ETW spans --- .../opentelemetry/exporters/etw/etw_tracer.h | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index 2b975e239c..35e5ddfde2 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -64,12 +64,12 @@ class Span; * @return Span instance */ template -SpanType *new_span(TracerType *objPtr, +SpanType *new_span(nostd::shared_ptr objPtr, nostd::string_view name, const opentelemetry::trace::StartSpanOptions &options, std::unique_ptr spanContext) { - return new (std::nothrow) SpanType{*objPtr, name, options, std::move(spanContext)}; + return new (std::nothrow) SpanType{std::move(objPtr), name, options, std::move(spanContext)}; } /** @@ -159,8 +159,7 @@ void UpdateStatus(T &t, Properties &props) /** * @brief Tracer class that allows to send spans to ETW Provider. */ -class Tracer : public opentelemetry::trace::Tracer, - public std::enable_shared_from_this +class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_from_this { public: /** @@ -530,7 +529,9 @@ class Tracer : public opentelemetry::trace::Tracer, // This template pattern allows us to forward-declare the etw::Span, // create an instance of it, then assign it to tracer::Span result. - auto currentSpan = new_span(this, name, options, std::move(spanContext)); + auto owner_ptr = nostd::shared_ptr(this->shared_from_this()); + auto currentSpan = new_span(std::move(owner_ptr), name, options, + std::move(spanContext)); nostd::shared_ptr result = to_span_ptr(currentSpan); // Decorate with additional standard fields @@ -783,7 +784,7 @@ class Span : public opentelemetry::trace::Span /** * @brief Owner Tracer of this Span */ - Tracer &owner_; + nostd::shared_ptr owner_; /** * @brief Span name. @@ -862,14 +863,14 @@ class Span : public opentelemetry::trace::Span * @param parent Parent Span (optional) * @return */ - Span(Tracer &owner, - nostd::string_view name, - const opentelemetry::trace::StartSpanOptions &options, - std::unique_ptr spanContext, - Span *parent = nullptr) noexcept + Span(nostd::shared_ptr owner, + nostd::string_view name, + const opentelemetry::trace::StartSpanOptions &options, + std::unique_ptr spanContext, + Span *parent = nullptr) noexcept : opentelemetry::trace::Span(), start_time_(std::chrono::system_clock::now()), - owner_(owner), + owner_(std::move(owner)), context_(std::move(spanContext)), parent_(parent) { @@ -887,7 +888,7 @@ class Span : public opentelemetry::trace::Span * @param name Event name. * @return */ - void AddEvent(nostd::string_view name) noexcept override { owner_.AddEvent(*this, name); } + void AddEvent(nostd::string_view name) noexcept override { owner_->AddEvent(*this, name); } /** * @brief Add named event with custom timestamp. @@ -898,7 +899,7 @@ class Span : public opentelemetry::trace::Span void AddEvent(nostd::string_view name, opentelemetry::common::SystemTimestamp timestamp) noexcept override { - owner_.AddEvent(*this, name, timestamp); + owner_->AddEvent(*this, name, timestamp); } /** @@ -912,7 +913,7 @@ class Span : public opentelemetry::trace::Span opentelemetry::common::SystemTimestamp timestamp, const opentelemetry::common::KeyValueIterable &attributes) noexcept override { - owner_.AddEvent(*this, name, timestamp, attributes); + owner_->AddEvent(*this, name, timestamp, attributes); } #if OPENTELEMETRY_ABI_VERSION_NO >= 2 @@ -1024,7 +1025,7 @@ class Span : public opentelemetry::trace::Span if (!has_ended_.exchange(true)) { - owner_.EndSpan(*this, parent_, options); + owner_->EndSpan(*this, parent_, options); } } @@ -1056,7 +1057,7 @@ class Span : public opentelemetry::trace::Span /// Get Owner tracer of this Span /// /// - opentelemetry::trace::Tracer &tracer() const noexcept { return this->owner_; } + opentelemetry::trace::Tracer &tracer() const noexcept { return *this->owner_; } }; /** @@ -1177,9 +1178,9 @@ class TracerProvider : public opentelemetry::trace::TracerProvider UNREFERENCED_PARAMETER(args); UNREFERENCED_PARAMETER(schema_url); ETWProvider::EventFormat evtFmt = config_.encoding; - std::shared_ptr tracer{new (std::nothrow) - Tracer(*this, name, evtFmt)}; - return nostd::shared_ptr{tracer}; + std::shared_ptr tracer{new (std::nothrow) Tracer(*this, name, evtFmt)}; + return nostd::shared_ptr{ + std::static_pointer_cast(tracer)}; } }; From 005d87d8910051e196097a6e176266037d087ba5 Mon Sep 17 00:00:00 2001 From: Furkan Mutlu Date: Fri, 8 May 2026 21:26:31 +0300 Subject: [PATCH 3/8] chore: remove unrelated metrics changes --- .../sdk/metrics/state/temporal_metric_storage.h | 2 -- sdk/src/metrics/state/temporal_metric_storage.cc | 8 +------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h b/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h index ef0315f3ee..007b3c74cd 100644 --- a/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h +++ b/sdk/include/opentelemetry/sdk/metrics/state/temporal_metric_storage.h @@ -56,8 +56,6 @@ class TemporalMetricStorage // Lock while building metrics mutable opentelemetry::common::SpinLockMutex lock_; const AggregationConfig *aggregation_config_; - opentelemetry::common::SystemTimestamp last_delta_collection_ts_; - bool has_last_delta_collection_ts_ = false; }; } // namespace metrics } // namespace sdk diff --git a/sdk/src/metrics/state/temporal_metric_storage.cc b/sdk/src/metrics/state/temporal_metric_storage.cc index 2af533750a..29be34a8ea 100644 --- a/sdk/src/metrics/state/temporal_metric_storage.cc +++ b/sdk/src/metrics/state/temporal_metric_storage.cc @@ -54,11 +54,6 @@ bool TemporalMetricStorage::buildMetrics(CollectorHandle *collector, // no other reader configured to collect those data. if (collectors.size() == 1 && aggregation_temporarily == AggregationTemporality::kDelta) { - if (!has_last_delta_collection_ts_) - { - last_delta_collection_ts_ = sdk_start_ts; - has_last_delta_collection_ts_ = true; - } // If no metrics, early return if (delta_metrics->Size() == 0) { @@ -68,9 +63,8 @@ bool TemporalMetricStorage::buildMetrics(CollectorHandle *collector, MetricData metric_data; metric_data.instrument_descriptor = instrument_descriptor_; metric_data.aggregation_temporality = AggregationTemporality::kDelta; - metric_data.start_ts = last_delta_collection_ts_; + metric_data.start_ts = sdk_start_ts; metric_data.end_ts = collection_ts; - last_delta_collection_ts_ = collection_ts; // Direct conversion of delta metrics to point data delta_metrics->GetAllEntries( From 3bc2469337645cb65b02f0e0ad5367a1bfe8c25f Mon Sep 17 00:00:00 2001 From: Furkan Mutlu Date: Mon, 11 May 2026 12:47:32 +0300 Subject: [PATCH 4/8] chore: format etw tracer header --- .../opentelemetry/exporters/etw/etw_tracer.h | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index 35e5ddfde2..a1d8df8749 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -397,7 +397,10 @@ class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_fr } } - const opentelemetry::trace::TraceId &trace_id() { return traceId_; } + const opentelemetry::trace::TraceId &trace_id() + { + return traceId_; + } friend class Span; @@ -530,8 +533,8 @@ class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_fr // This template pattern allows us to forward-declare the etw::Span, // create an instance of it, then assign it to tracer::Span result. auto owner_ptr = nostd::shared_ptr(this->shared_from_this()); - auto currentSpan = new_span(std::move(owner_ptr), name, options, - std::move(spanContext)); + auto currentSpan = + new_span(std::move(owner_ptr), name, options, std::move(spanContext)); nostd::shared_ptr result = to_span_ptr(currentSpan); // Decorate with additional standard fields @@ -597,7 +600,10 @@ class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_fr * * @since ABI_VERSION 2 */ - virtual bool Enabled() const noexcept { return true; } + virtual bool Enabled() const noexcept + { + return true; + } #endif #if OPENTELEMETRY_ABI_VERSION_NO == 1 @@ -784,7 +790,7 @@ class Span : public opentelemetry::trace::Span /** * @brief Owner Tracer of this Span */ - nostd::shared_ptr owner_; + nostd::shared_ptr owner_; /** * @brief Span name. @@ -863,11 +869,11 @@ class Span : public opentelemetry::trace::Span * @param parent Parent Span (optional) * @return */ - Span(nostd::shared_ptr owner, - nostd::string_view name, - const opentelemetry::trace::StartSpanOptions &options, - std::unique_ptr spanContext, - Span *parent = nullptr) noexcept + Span(nostd::shared_ptr owner, + nostd::string_view name, + const opentelemetry::trace::StartSpanOptions &options, + std::unique_ptr spanContext, + Span *parent = nullptr) noexcept : opentelemetry::trace::Span(), start_time_(std::chrono::system_clock::now()), owner_(std::move(owner)), @@ -957,9 +963,15 @@ class Span : public opentelemetry::trace::Span status_description_ = description.data(); } - opentelemetry::trace::StatusCode GetStatus() { return status_code_; } + opentelemetry::trace::StatusCode GetStatus() + { + return status_code_; + } - void SetAttributes(Properties attributes) { attributes_ = attributes; } + void SetAttributes(Properties attributes) + { + attributes_ = attributes; + } /** * @brief Obtain span attributes specified at Span start. @@ -967,7 +979,10 @@ class Span : public opentelemetry::trace::Span * * @return ref to Properties collection */ - Properties &GetAttributes() { return attributes_; } + Properties &GetAttributes() + { + return attributes_; + } /** * @brief Sets an attribute on the Span. If the Span previously contained a mapping @@ -1033,7 +1048,10 @@ class Span : public opentelemetry::trace::Span * @brief Obtain SpanContext * @return */ - opentelemetry::trace::SpanContext GetContext() const noexcept override { return *context_.get(); } + opentelemetry::trace::SpanContext GetContext() const noexcept override + { + return *context_.get(); + } /** * @brief Check if Span is recording data. @@ -1057,7 +1075,10 @@ class Span : public opentelemetry::trace::Span /// Get Owner tracer of this Span /// /// - opentelemetry::trace::Tracer &tracer() const noexcept { return *this->owner_; } + opentelemetry::trace::Tracer &tracer() const noexcept + { + return *this->owner_; + } }; /** From add9b1e0514a0ecfd81b4b2ba7640a43dbf32ec8 Mon Sep 17 00:00:00 2001 From: Furkan Mutlu Date: Mon, 11 May 2026 22:25:57 +0300 Subject: [PATCH 5/8] chore: fix format errors --- .../opentelemetry/exporters/etw/etw_tracer.h | 35 ++++--------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index a1d8df8749..4d2fb1f164 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -397,10 +397,7 @@ class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_fr } } - const opentelemetry::trace::TraceId &trace_id() - { - return traceId_; - } + const opentelemetry::trace::TraceId &trace_id(){return traceId_;} friend class Span; @@ -600,10 +597,7 @@ class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_fr * * @since ABI_VERSION 2 */ - virtual bool Enabled() const noexcept - { - return true; - } + virtual bool Enabled() const noexcept{return true;} #endif #if OPENTELEMETRY_ABI_VERSION_NO == 1 @@ -963,15 +957,9 @@ class Span : public opentelemetry::trace::Span status_description_ = description.data(); } - opentelemetry::trace::StatusCode GetStatus() - { - return status_code_; - } + opentelemetry::trace::StatusCode GetStatus(){return status_code_;} - void SetAttributes(Properties attributes) - { - attributes_ = attributes; - } + void SetAttributes(Properties attributes){attributes_ = attributes;} /** * @brief Obtain span attributes specified at Span start. @@ -979,10 +967,7 @@ class Span : public opentelemetry::trace::Span * * @return ref to Properties collection */ - Properties &GetAttributes() - { - return attributes_; - } + Properties &GetAttributes(){return attributes_;} /** * @brief Sets an attribute on the Span. If the Span previously contained a mapping @@ -1048,10 +1033,7 @@ class Span : public opentelemetry::trace::Span * @brief Obtain SpanContext * @return */ - opentelemetry::trace::SpanContext GetContext() const noexcept override - { - return *context_.get(); - } + opentelemetry::trace::SpanContext GetContext() const noexcept override { return *context_.get(); } /** * @brief Check if Span is recording data. @@ -1075,10 +1057,7 @@ class Span : public opentelemetry::trace::Span /// Get Owner tracer of this Span /// /// - opentelemetry::trace::Tracer &tracer() const noexcept - { - return *this->owner_; - } + opentelemetry::trace::Tracer &tracer() const noexcept{return *this->owner_;} }; /** From 1f76b52c781364b4ba33c5995832ad9cc3a35f35 Mon Sep 17 00:00:00 2001 From: Furkan Mutlu Date: Mon, 11 May 2026 23:33:01 +0300 Subject: [PATCH 6/8] chore: fix format errors --- .../include/opentelemetry/exporters/etw/etw_tracer.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index 4d2fb1f164..cfebd24c02 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -397,7 +397,7 @@ class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_fr } } - const opentelemetry::trace::TraceId &trace_id(){return traceId_;} + const opentelemetry::trace::TraceId &trace_id() { return traceId_; } friend class Span; @@ -597,7 +597,7 @@ class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_fr * * @since ABI_VERSION 2 */ - virtual bool Enabled() const noexcept{return true;} + virtual bool Enabled() const noexcept { return true; } #endif #if OPENTELEMETRY_ABI_VERSION_NO == 1 @@ -957,9 +957,9 @@ class Span : public opentelemetry::trace::Span status_description_ = description.data(); } - opentelemetry::trace::StatusCode GetStatus(){return status_code_;} + opentelemetry::trace::StatusCode GetStatus() { return status_code_; } - void SetAttributes(Properties attributes){attributes_ = attributes;} + void SetAttributes(Properties attributes) { attributes_ = attributes; } /** * @brief Obtain span attributes specified at Span start. @@ -967,7 +967,7 @@ class Span : public opentelemetry::trace::Span * * @return ref to Properties collection */ - Properties &GetAttributes(){return attributes_;} + Properties &GetAttributes() { return attributes_; } /** * @brief Sets an attribute on the Span. If the Span previously contained a mapping @@ -1057,7 +1057,7 @@ class Span : public opentelemetry::trace::Span /// Get Owner tracer of this Span /// /// - opentelemetry::trace::Tracer &tracer() const noexcept{return *this->owner_;} + opentelemetry::trace::Tracer &tracer() const noexcept { return *this->owner_; } }; /** From 7ac1f13a5110ae6b60add7fea0f4f7d5483d74f0 Mon Sep 17 00:00:00 2001 From: Furkan Mutlu Date: Tue, 26 May 2026 18:19:54 +0300 Subject: [PATCH 7/8] fix: cache ETW tracers in provider --- exporters/etw/README.md | 8 ++ .../opentelemetry/exporters/etw/etw_tracer.h | 126 +++++++++++++++--- exporters/etw/test/etw_tracer_test.cc | 15 ++- 3 files changed, 130 insertions(+), 19 deletions(-) diff --git a/exporters/etw/README.md b/exporters/etw/README.md index 372aa5738e..32e99158bd 100644 --- a/exporters/etw/README.md +++ b/exporters/etw/README.md @@ -12,5 +12,13 @@ subsequent data recording or forwarding to alternate pipelines and flows. Windows Event Tracing infrastructure is available to any vendor or application being deployed on Windows. +## Tracer lifetime and reuse + +For ETW, the tracer name maps to the ETW provider name/GUID. Applications +should create one tracer per provider name (and encoding) and reuse it for the +process or component lifetime. `TracerProvider::GetTracer()` may cache tracers +internally, so avoid calling `GetTracer()->StartSpan()` in hot paths. Instead, +obtain the tracer once during setup and reuse it for span creation. + It is recommended to consume this exporter via [vcpkg](https://vcpkg.io/en/package/opentelemetry-cpp). diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index cfebd24c02..9d9edb522e 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -64,12 +66,12 @@ class Span; * @return Span instance */ template -SpanType *new_span(nostd::shared_ptr objPtr, +SpanType *new_span(TracerType *objPtr, nostd::string_view name, const opentelemetry::trace::StartSpanOptions &options, std::unique_ptr spanContext) { - return new (std::nothrow) SpanType{std::move(objPtr), name, options, std::move(spanContext)}; + return new (std::nothrow) SpanType{*objPtr, name, options, std::move(spanContext)}; } /** @@ -159,7 +161,7 @@ void UpdateStatus(T &t, Properties &props) /** * @brief Tracer class that allows to send spans to ETW Provider. */ -class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_from_this +class Tracer : public opentelemetry::trace::Tracer { public: /** @@ -509,7 +511,7 @@ class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_fr { auto noopSpan = nostd::shared_ptr{ new (std::nothrow) - opentelemetry::trace::NoopSpan(this->shared_from_this(), std::move(spanContext))}; + opentelemetry::trace::NoopSpan(std::shared_ptr{}, std::move(spanContext))}; return noopSpan; } @@ -529,9 +531,7 @@ class Tracer : public opentelemetry::trace::Tracer, public std::enable_shared_fr // This template pattern allows us to forward-declare the etw::Span, // create an instance of it, then assign it to tracer::Span result. - auto owner_ptr = nostd::shared_ptr(this->shared_from_this()); - auto currentSpan = - new_span(std::move(owner_ptr), name, options, std::move(spanContext)); + auto currentSpan = new_span(this, name, options, std::move(spanContext)); nostd::shared_ptr result = to_span_ptr(currentSpan); // Decorate with additional standard fields @@ -784,7 +784,7 @@ class Span : public opentelemetry::trace::Span /** * @brief Owner Tracer of this Span */ - nostd::shared_ptr owner_; + Tracer &owner_; /** * @brief Span name. @@ -863,14 +863,14 @@ class Span : public opentelemetry::trace::Span * @param parent Parent Span (optional) * @return */ - Span(nostd::shared_ptr owner, + Span(Tracer &owner, nostd::string_view name, const opentelemetry::trace::StartSpanOptions &options, std::unique_ptr spanContext, Span *parent = nullptr) noexcept : opentelemetry::trace::Span(), start_time_(std::chrono::system_clock::now()), - owner_(std::move(owner)), + owner_(owner), context_(std::move(spanContext)), parent_(parent) { @@ -888,7 +888,7 @@ class Span : public opentelemetry::trace::Span * @param name Event name. * @return */ - void AddEvent(nostd::string_view name) noexcept override { owner_->AddEvent(*this, name); } + void AddEvent(nostd::string_view name) noexcept override { owner_.AddEvent(*this, name); } /** * @brief Add named event with custom timestamp. @@ -899,7 +899,7 @@ class Span : public opentelemetry::trace::Span void AddEvent(nostd::string_view name, opentelemetry::common::SystemTimestamp timestamp) noexcept override { - owner_->AddEvent(*this, name, timestamp); + owner_.AddEvent(*this, name, timestamp); } /** @@ -913,7 +913,7 @@ class Span : public opentelemetry::trace::Span opentelemetry::common::SystemTimestamp timestamp, const opentelemetry::common::KeyValueIterable &attributes) noexcept override { - owner_->AddEvent(*this, name, timestamp, attributes); + owner_.AddEvent(*this, name, timestamp, attributes); } #if OPENTELEMETRY_ABI_VERSION_NO >= 2 @@ -1025,7 +1025,7 @@ class Span : public opentelemetry::trace::Span if (!has_ended_.exchange(true)) { - owner_->EndSpan(*this, parent_, options); + owner_.EndSpan(*this, parent_, options); } } @@ -1057,9 +1057,58 @@ class Span : public opentelemetry::trace::Span /// Get Owner tracer of this Span /// /// - opentelemetry::trace::Tracer &tracer() const noexcept { return *this->owner_; } + opentelemetry::trace::Tracer &tracer() const noexcept { return this->owner_; } }; +static inline std::string TrimWhitespace(std::string input) +{ + auto is_space = [](unsigned char c) { return std::isspace(c) != 0; }; + auto begin = std::find_if_not(input.begin(), input.end(), is_space); + if (begin == input.end()) + { + return std::string{}; + } + auto end = std::find_if_not(input.rbegin(), input.rend(), is_space).base(); + return std::string(begin, end); +} + +static inline ETWProvider::EventFormat ParseEncodingArg(nostd::string_view args, + ETWProvider::EventFormat fallback) +{ + if (args.size() == 0) + { + return fallback; + } + + std::string arg(args.data(), args.size()); + arg = TrimWhitespace(std::move(arg)); + if (arg.empty()) + { + return fallback; + } + + std::transform(arg.begin(), arg.end(), arg.begin(), [](unsigned char c) { + return static_cast(std::toupper(c)); + }); + + if (arg == "MSGPACK" || arg == "MESSAGEPACK") + { + return ETWProvider::EventFormat::ETW_MSGPACK; + } + + if (arg == "XML") + { + return ETWProvider::EventFormat::ETW_XML; + } + + if (arg == "ETW" || arg == "TLD") + { + return ETWProvider::EventFormat::ETW_MANIFEST; + } + + return fallback; +} + /** * @brief ETW TracerProvider */ @@ -1175,13 +1224,56 @@ class TracerProvider : public opentelemetry::trace::TracerProvider #endif ) noexcept override { - UNREFERENCED_PARAMETER(args); UNREFERENCED_PARAMETER(schema_url); - ETWProvider::EventFormat evtFmt = config_.encoding; + ETWProvider::EventFormat evtFmt = ParseEncodingArg(args, config_.encoding); + TracerCacheKey key{name.data(), name.size(), evtFmt}; + + std::lock_guard lock(tracers_mu_); + auto it = tracers_.find(key); + if (it != tracers_.end()) + { + return nostd::shared_ptr{ + std::static_pointer_cast(it->second)}; + } + std::shared_ptr tracer{new (std::nothrow) Tracer(*this, name, evtFmt)}; + tracers_.emplace(std::move(key), tracer); return nostd::shared_ptr{ std::static_pointer_cast(tracer)}; } + +private: + struct TracerCacheKey + { + std::string name; + ETWProvider::EventFormat encoding; + + TracerCacheKey(std::string value, ETWProvider::EventFormat format) + : name(std::move(value)), encoding(format) + {} + + TracerCacheKey(const char *value, size_t size, ETWProvider::EventFormat format) + : name(value, size), encoding(format) + {} + + bool operator==(const TracerCacheKey &other) const + { + return encoding == other.encoding && name == other.name; + } + }; + + struct TracerCacheKeyHash + { + size_t operator()(const TracerCacheKey &key) const noexcept + { + size_t name_hash = std::hash{}(key.name); + size_t fmt_hash = static_cast(key.encoding); + return name_hash ^ (fmt_hash + 0x9e3779b97f4a7c15ULL + (name_hash << 6) + (name_hash >> 2)); + } + }; + + std::mutex tracers_mu_; + std::unordered_map, TracerCacheKeyHash> tracers_; }; } // namespace etw diff --git a/exporters/etw/test/etw_tracer_test.cc b/exporters/etw/test/etw_tracer_test.cc index 7d32a2ffba..07ba575349 100644 --- a/exporters/etw/test/etw_tracer_test.cc +++ b/exporters/etw/test/etw_tracer_test.cc @@ -392,7 +392,7 @@ TEST(ETWTracer, GlobalSingletonTracer) } */ - // Obtain a different tracer withs its own trace-id. + // Obtain the same tracer instance with the same trace-id as before. auto localTracer = GetGlobalTracerProvider().GetTracer(kGlobalProviderName); auto s2 = localTracer->StartSpan("Span2"); auto traceId2 = s2->GetContext().trace_id(); @@ -455,7 +455,7 @@ TEST(ETWTracer, GlobalSingletonTracer) } } */ - EXPECT_NE(traceId1, traceId2); + EXPECT_EQ(traceId1, traceId2); EXPECT_EQ(traceId1, traceId3); # if OPENTELEMETRY_ABI_VERSION_NO == 1 @@ -503,6 +503,17 @@ TEST(ETWTracer, AlwayOffTailSampler) auto tracer = tp.GetTracer(providerName); } +TEST(ETWTracer, SpanSurvivesTracerReassignment) +{ + exporter::etw::TracerProvider tp; + auto tracer = tp.GetTracer("Geneva-Tracer-Foo"); + auto spanFoo = tracer->StartSpan("Span-Foo"); + tracer = tp.GetTracer("Geneva-Tracer-Bar"); + auto spanBar = tracer->StartSpan("Span-Bar"); + EXPECT_NO_THROW(spanFoo->End()); + EXPECT_NO_THROW(spanBar->End()); +} + TEST(ETWTracer, CustomIdGenerator) { std::string providerName = kGlobalProviderName; // supply unique instrumentation name here From b8313f6a0f8539223e7809fbffcc09de18262b51 Mon Sep 17 00:00:00 2001 From: Furkan Mutlu Date: Wed, 27 May 2026 15:56:34 +0300 Subject: [PATCH 8/8] fix: stabilize ETW tracer cache shutdown --- .../opentelemetry/exporters/etw/etw_tracer.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index 9d9edb522e..9f2cb1f980 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -17,8 +17,8 @@ #include #include #include -#include #include +#include #include #include "opentelemetry/nostd/shared_ptr.h" @@ -170,6 +170,8 @@ class Tracer : public opentelemetry::trace::Tracer bool IsClosed() const noexcept { return isClosed_.load(); } private: + friend class TracerProvider; + /** * @brief Parent provider of this Tracer */ @@ -1087,9 +1089,8 @@ static inline ETWProvider::EventFormat ParseEncodingArg(nostd::string_view args, return fallback; } - std::transform(arg.begin(), arg.end(), arg.begin(), [](unsigned char c) { - return static_cast(std::toupper(c)); - }); + std::transform(arg.begin(), arg.end(), arg.begin(), + [](unsigned char c) { return static_cast(std::toupper(c)); }); if (arg == "MSGPACK" || arg == "MESSAGEPACK") { @@ -1156,6 +1157,8 @@ class TracerProvider : public opentelemetry::trace::TracerProvider id_generator_{std::move(id_generator)}, tail_sampler_{std::move(tail_sampler)} { + (void)Tracer::etwProvider().is_registered(std::string{}); + // By default we ensure that all events carry their with TraceId and SpanId GetOption(options, "enableTraceId", config_.enableTraceId, true); GetOption(options, "enableSpanId", config_.enableSpanId, true); @@ -1193,6 +1196,8 @@ class TracerProvider : public opentelemetry::trace::TracerProvider tail_sampler_{ std::unique_ptr(new AlwaysOnTailSampler())} { + (void)Tracer::etwProvider().is_registered(std::string{}); + config_.enableTraceId = true; config_.enableSpanId = true; config_.enableActivityId = false;