From 788075a69d2d35e667df3f2eb9fec74840371cb9 Mon Sep 17 00:00:00 2001 From: Lalit Date: Thu, 2 Sep 2021 16:45:57 -0700 Subject: [PATCH 1/7] suport parent span from context --- api/include/opentelemetry/trace/span.h | 65 +------------------ .../opentelemetry/trace/span_metadata.h | 43 ++++++++++++ .../opentelemetry/trace/span_startoptions.h | 45 +++++++++++++ api/include/opentelemetry/trace/tracer.h | 3 +- sdk/src/trace/tracer.cc | 22 ++++++- sdk/test/trace/tracer_test.cc | 39 +++++++++++ 6 files changed, 152 insertions(+), 65 deletions(-) create mode 100644 api/include/opentelemetry/trace/span_metadata.h create mode 100644 api/include/opentelemetry/trace/span_startoptions.h diff --git a/api/include/opentelemetry/trace/span.h b/api/include/opentelemetry/trace/span.h index 87b64747cf..77de4050d4 100644 --- a/api/include/opentelemetry/trace/span.h +++ b/api/include/opentelemetry/trace/span.h @@ -7,7 +7,6 @@ #include "opentelemetry/common/attribute_value.h" #include "opentelemetry/common/key_value_iterable_view.h" -#include "opentelemetry/common/timestamp.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" @@ -15,72 +14,14 @@ #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/trace/canonical_code.h" #include "opentelemetry/trace/span_context.h" +#include "opentelemetry/trace/span_metadata.h" + #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace trace { -// The key identifies the active span in the current context. -constexpr char kSpanKey[] = "active_span"; - -enum class SpanKind -{ - kInternal, - kServer, - kClient, - kProducer, - kConsumer, -}; - -// StatusCode - Represents the canonical set of status codes of a finished Span. - -enum class StatusCode -{ - kUnset, // default status - kOk, // Operation has completed successfully. - kError // The operation contains an error -}; - -/** - * StartSpanOptions provides options to set properties of a Span at the time of - * its creation - */ -struct StartSpanOptions -{ - // Optionally sets the start time of a Span. - // - // If the start time of a Span is set, timestamps from both the system clock - // and steady clock must be provided. - // - // Timestamps from the steady clock can be used to most accurately measure a - // Span's duration, while timestamps from the system clock can be used to most - // accurately place a Span's - // time point relative to other Spans collected across a distributed system. - common::SystemTimestamp start_system_time; - common::SteadyTimestamp start_steady_time; - - // Explicitly set the parent of a Span. - // - // This defaults to an invalid span context. In this case, the Span is - // automatically parented to the currently active span. - SpanContext parent = SpanContext::GetInvalid(); - - // TODO: - // SpanContext remote_parent; - // Links - SpanKind kind = SpanKind::kInternal; -}; -/** - * StartEndOptions provides options to set properties of a Span when it is - * ended. - */ -struct EndSpanOptions -{ - // Optionally sets the end time of a Span. - common::SteadyTimestamp end_steady_time; -}; - class Tracer; /** @@ -176,7 +117,7 @@ class Span * @param options can be used to manually define span properties like the end * timestamp */ - virtual void End(const EndSpanOptions &options = {}) noexcept = 0; + virtual void End(const trace::EndSpanOptions &options = {}) noexcept = 0; virtual trace::SpanContext GetContext() const noexcept = 0; diff --git a/api/include/opentelemetry/trace/span_metadata.h b/api/include/opentelemetry/trace/span_metadata.h new file mode 100644 index 0000000000..977329a151 --- /dev/null +++ b/api/include/opentelemetry/trace/span_metadata.h @@ -0,0 +1,43 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/common/timestamp.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace trace +{ + +enum class SpanKind +{ + kInternal, + kServer, + kClient, + kProducer, + kConsumer, +}; + +// The key identifies the active span in the current context. +constexpr char kSpanKey[] = "active_span"; + +// StatusCode - Represents the canonical set of status codes of a finished Span. +enum class StatusCode +{ + kUnset, // default status + kOk, // Operation has completed successfully. + kError // The operation contains an error +}; + +/** + * EndSpanOptions provides options to set properties of a Span when it is + * ended. + */ +struct EndSpanOptions +{ + // Optionally sets the end time of a Span. + common::SteadyTimestamp end_steady_time; +}; + +} // namespace trace +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/api/include/opentelemetry/trace/span_startoptions.h b/api/include/opentelemetry/trace/span_startoptions.h new file mode 100644 index 0000000000..688b768bd0 --- /dev/null +++ b/api/include/opentelemetry/trace/span_startoptions.h @@ -0,0 +1,45 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/context/context.h" +#include "opentelemetry/trace/span_context.h" +#include "opentelemetry/trace/span_metadata.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace trace +{ + +/** + * StartSpanOptions provides options to set properties of a Span at the time of + * its creation + */ +struct StartSpanOptions +{ + // Optionally sets the start time of a Span. + // + // If the start time of a Span is set, timestamps from both the system clock + // and steady clock must be provided. + // + // Timestamps from the steady clock can be used to most accurately measure a + // Span's duration, while timestamps from the system clock can be used to most + // accurately place a Span's + // time point relative to other Spans collected across a distributed system. + common::SystemTimestamp start_system_time; + common::SteadyTimestamp start_steady_time; + + // Explicitly set the parent of a Span. + // + // This defaults to an invalid span context. In this case, the Span is + // automatically parented to the currently active span. + nostd::variant parent = SpanContext::GetInvalid(); + + // TODO: + // SpanContext remote_parent; + // Links + SpanKind kind = SpanKind::kInternal; +}; + +} // namespace trace +OPENTELEMETRY_END_NAMESPACE \ No newline at end of file diff --git a/api/include/opentelemetry/trace/tracer.h b/api/include/opentelemetry/trace/tracer.h index 425e085116..b60336a48d 100644 --- a/api/include/opentelemetry/trace/tracer.h +++ b/api/include/opentelemetry/trace/tracer.h @@ -3,6 +3,7 @@ #pragma once +#include "opentelemetry/context/context.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" @@ -10,6 +11,7 @@ #include "opentelemetry/trace/scope.h" #include "opentelemetry/trace/span.h" #include "opentelemetry/trace/span_context_kv_iterable_view.h" +#include "opentelemetry/trace/span_startoptions.h" #include "opentelemetry/version.h" #include @@ -17,7 +19,6 @@ OPENTELEMETRY_BEGIN_NAMESPACE namespace trace { - /** * Handles span creation and in-process context propagation. * diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index 977722d0ca..6aa5c37d86 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -5,6 +5,7 @@ #include "opentelemetry/context/runtime_context.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/sdk/common/atomic_shared_ptr.h" +#include "opentelemetry/trace/propagation/detail/context.h" #include "opentelemetry/version.h" #include "src/trace/span.h" @@ -27,8 +28,25 @@ nostd::shared_ptr Tracer::StartSpan( const trace_api::SpanContextKeyValueIterable &links, const trace_api::StartSpanOptions &options) noexcept { - trace_api::SpanContext parent_context = - options.parent.IsValid() ? options.parent : GetCurrentSpan()->GetContext(); + trace_api::SpanContext parent_context = GetCurrentSpan()->GetContext(); + if (nostd::holds_alternative(options.parent)) + { + auto span_context = nostd::get(options.parent); + if (span_context.IsValid()) + { + parent_context = span_context; + } + } + else if (nostd::holds_alternative(options.parent)) + { + auto context = nostd::get(options.parent); + // fetch span context from parent span stored in the context + auto span_context = opentelemetry::trace::propagation::GetSpan(context)->GetContext(); + if (span_context.IsValid()) + { + parent_context = span_context; + } + } trace_api::TraceId trace_id; trace_api::SpanId span_id = GetIdGenerator().GenerateSpanId(); diff --git a/sdk/test/trace/tracer_test.cc b/sdk/test/trace/tracer_test.cc index 92659a8a5a..dbf364fe5c 100644 --- a/sdk/test/trace/tracer_test.cc +++ b/sdk/test/trace/tracer_test.cc @@ -9,6 +9,7 @@ #include "opentelemetry/sdk/trace/samplers/parent.h" #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/span_data.h" +#include "opentelemetry/trace/propagation/detail/context.h" #include @@ -621,6 +622,44 @@ TEST(Tracer, ExpectParent) EXPECT_EQ(spandata_second->GetSpanId(), spandata_third->GetParentSpanId()); } +TEST(Tracer, ExpectParentAsContext) +{ + std::unique_ptr exporter(new InMemorySpanExporter()); + std::shared_ptr span_data = exporter->GetData(); + auto tracer = initTracer(std::move(exporter)); + auto spans = span_data.get()->GetSpans(); + + ASSERT_EQ(0, spans.size()); + + auto span_first = tracer->StartSpan("span 1"); + + opentelemetry::context::Context c1; + auto c2 = trace_api::propagation::SetSpan(c1, span_first); + trace_api::StartSpanOptions options; + options.parent = c2; + auto span_second = tracer->StartSpan("span 2", options); + + auto c3 = trace_api::propagation::SetSpan(c2, span_second); + options.parent = c3; + auto span_third = tracer->StartSpan("span 3", options); + + span_third->End(); + span_second->End(); + span_first->End(); + + spans = span_data->GetSpans(); + ASSERT_EQ(3, spans.size()); + auto spandata_first = std::move(spans.at(2)); + auto spandata_second = std::move(spans.at(1)); + auto spandata_third = std::move(spans.at(0)); + EXPECT_EQ("span 1", spandata_first->GetName()); + EXPECT_EQ("span 2", spandata_second->GetName()); + EXPECT_EQ("span 3", spandata_third->GetName()); + + EXPECT_EQ(spandata_first->GetSpanId(), spandata_second->GetParentSpanId()); + EXPECT_EQ(spandata_second->GetSpanId(), spandata_third->GetParentSpanId()); +} + TEST(Tracer, ValidTraceIdToSampler) { std::unique_ptr exporter(new InMemorySpanExporter()); From 60a9326edc70b74c761ae6c261b5ecdda1e3850b Mon Sep 17 00:00:00 2001 From: Lalit Date: Thu, 2 Sep 2021 17:34:35 -0700 Subject: [PATCH 2/7] fix etw exporter, and otlp http test --- .../opentelemetry/exporters/etw/etw_tracer.h | 980 +++++++++--------- .../otlp/test/otlp_http_exporter_test.cc | 8 +- 2 files changed, 497 insertions(+), 491 deletions(-) diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index 2806bfa593..1fb2fdf627 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -569,568 +569,570 @@ class Tracer : public trace::Tracer // Parent Context: // - either use current span // - or attach to parent SpanContext specified in options - const auto parentContext = - (options.parent.IsValid()) ? options.parent : GetCurrentSpan()->GetContext(); - - // Populate Etw.RelatedActivityId at envelope level if enabled - GUID RelatedActivityId; - LPCGUID RelatedActivityIdPtr = nullptr; - if (cfg.enableAutoParent) + trace_api::SpanContext parentContext = GetCurrentSpan()->GetContext(); + if (nostd::holds_alternative(options.parent)) { - if (cfg.enableRelatedActivityId) + auto span_context = nostd::get(options.parent); + if (span_context.IsValid()) + { + parentContext = span_context; + } + + // Populate Etw.RelatedActivityId at envelope level if enabled + GUID RelatedActivityId; + LPCGUID RelatedActivityIdPtr = nullptr; + if (cfg.enableAutoParent) { - if (CopySpanIdToActivityId(parentContext, RelatedActivityId)) + if (cfg.enableRelatedActivityId) { - RelatedActivityIdPtr = &RelatedActivityId; + if (CopySpanIdToActivityId(parentContext, RelatedActivityId)) + { + RelatedActivityIdPtr = &RelatedActivityId; + } } } - } - // 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); - nostd::shared_ptr result = to_span_ptr(currentSpan); + // 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); + nostd::shared_ptr result = to_span_ptr(currentSpan); - auto spanContext = result->GetContext(); + auto spanContext = result->GetContext(); - // Decorate with additional standard fields - std::string eventName = name.data(); + // Decorate with additional standard fields + std::string eventName = name.data(); - // Populate Etw.EventName attribute at envelope level - evt[ETW_FIELD_NAME] = eventName; + // Populate Etw.EventName attribute at envelope level + evt[ETW_FIELD_NAME] = eventName; - // Populate Payload["SpanId"] attribute - // Populate Payload["ParentSpanId"] attribute if parent Span is valid - if (cfg.enableSpanId) + // Populate Payload["SpanId"] attribute + // Populate Payload["ParentSpanId"] attribute if parent Span is valid + if (cfg.enableSpanId) + { + if (parentContext.IsValid()) + { + evt[ETW_FIELD_SPAN_PARENTID] = ToLowerBase16(parentContext.span_id()); + } + evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id()); + } + + // Populate Etw.Payload["TraceId"] attribute + if (cfg.enableTraceId) + { + evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id()); + } + + // Populate Etw.ActivityId at envelope level if enabled + GUID ActivityId; + LPCGUID ActivityIdPtr = nullptr; + if (cfg.enableActivityId) + { + if (CopySpanIdToActivityId(result.get()->GetContext(), ActivityId)) + { + ActivityIdPtr = &ActivityId; + } + } + + // Links + DecorateLinks(evt, links); + + // Remember Span attributes to be passed down to ETW on Span end + SetSpanAttributes(*currentSpan, evt); + + if (cfg.enableActivityTracking) + { + // TODO: add support for options that are presently ignored : + // - options.kind + // - options.start_steady_time + // - options.start_system_time + etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 1, encoding); + }; + + return result; + }; + + /** + * @brief Force flush data to Tracer, spending up to given amount of microseconds to flush. + * NOTE: this method has no effect for the realtime streaming Tracer. + * + * @param timeout Allow Tracer to drop data if timeout is reached + * @return + */ + void ForceFlushWithMicroseconds(uint64_t) noexcept override{}; + + /** + * @brief Close tracer, spending up to given amount of microseconds to flush and close. + * NOTE: This method decrements the reference count on current ETW Provider Handle and + * closes it if reference count on that provider handle is zero. + * + * @param timeout Allow Tracer to drop data if timeout is reached. + * @return + */ + void CloseWithMicroseconds(uint64_t) noexcept override { - if (parentContext.IsValid()) + // Close once only + if (!isClosed_.exchange(true)) { - evt[ETW_FIELD_SPAN_PARENTID] = ToLowerBase16(parentContext.span_id()); + etwProvider().close(provHandle); } - evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id()); - } + }; - // Populate Etw.Payload["TraceId"] attribute - if (cfg.enableTraceId) + /** + * @brief Add event data to span associated with tracer. + * @param span Parent span. + * @param name Event name. + * @param timestamp Event timestamp. + * @param attributes Event attributes. + * @return + */ + void AddEvent(trace::Span & span, nostd::string_view name, common::SystemTimestamp timestamp, + const common::KeyValueIterable &attributes) noexcept { - evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id()); +#ifdef RTTI_ENABLED + common::KeyValueIterable &attribs = const_cast(attributes); + Properties *evt = dynamic_cast(&attribs); + if (evt != nullptr) + { + // 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); } - // Populate Etw.ActivityId at envelope level if enabled - GUID ActivityId; - LPCGUID ActivityIdPtr = nullptr; - if (cfg.enableActivityId) + /** + * @brief Add event data to span associated with tracer. + * @param span Parent span. + * @param name Event name. + * @param timestamp Event timestamp. + * @param attributes Event attributes. + * @return + */ + void AddEvent(trace::Span & span, nostd::string_view name, common::SystemTimestamp timestamp, + Properties & evt) noexcept { - if (CopySpanIdToActivityId(result.get()->GetContext(), ActivityId)) + // TODO: respect originating timestamp. Do we need to reserve + // a special 'Timestamp' field or is it an overkill? The delta + // between when `AddEvent` API is called and when ETW layer + // timestamp is appended is nanos- to micros-, thus handling + // the explicitly provided timestamp is only necessary in case + // if a process wants to submit back-dated or future-dated + // timestamp. Unless there is a strong ask from any ETW customer + // to have it, this feature (custom timestamp) remains unimplemented. + (void)timestamp; + + const auto &cfg = GetConfiguration(tracerProvider_); + + evt[ETW_FIELD_NAME] = name.data(); + + const auto &spanContext = span.GetContext(); + if (cfg.enableSpanId) { + evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id()); + } + + if (cfg.enableTraceId) + { + evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id()); + } + + LPGUID ActivityIdPtr = nullptr; + GUID ActivityId; + if (cfg.enableActivityId) + { + CopySpanIdToActivityId(spanContext, ActivityId); ActivityIdPtr = &ActivityId; } - } - // Links - DecorateLinks(evt, links); +#ifdef HAVE_FIELD_TIME + { + auto timeNow = std::chrono::system_clock::now().time_since_epoch(); + auto millis = std::chrono::duration_cast(timeNow).count(); + evt[ETW_FIELD_TIME] = utils::formatUtcTimestampMsAsISO8601(millis); + } +#endif - // Remember Span attributes to be passed down to ETW on Span end - SetSpanAttributes(*currentSpan, evt); + etwProvider().write(provHandle, evt, ActivityIdPtr, nullptr, 0, encoding); + }; - if (cfg.enableActivityTracking) + /** + * @brief Add event data to span associated with tracer. + * @param span Span. + * @param name Event name. + * @param timestamp Event timestamp. + * @return + */ + void AddEvent(trace::Span & span, nostd::string_view name, + common::SystemTimestamp timestamp) noexcept { - // TODO: add support for options that are presently ignored : - // - options.kind - // - options.start_steady_time - // - options.start_system_time - etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 1, encoding); + AddEvent(span, name, timestamp, sdk::GetEmptyAttributes()); }; - return result; - }; + /** + * @brief Add event data to span associated with tracer. + * @param span Spab. + * @param name Event name. + */ + void AddEvent(trace::Span & span, nostd::string_view name) + { + AddEvent(span, name, std::chrono::system_clock::now(), sdk::GetEmptyAttributes()); + }; - /** - * @brief Force flush data to Tracer, spending up to given amount of microseconds to flush. - * NOTE: this method has no effect for the realtime streaming Tracer. - * - * @param timeout Allow Tracer to drop data if timeout is reached - * @return - */ - void ForceFlushWithMicroseconds(uint64_t) noexcept override{}; + /** + * @brief Tracer destructor. + */ + virtual ~Tracer() { CloseWithMicroseconds(0); }; + }; /** - * @brief Close tracer, spending up to given amount of microseconds to flush and close. - * NOTE: This method decrements the reference count on current ETW Provider Handle and - * closes it if reference count on that provider handle is zero. - * - * @param timeout Allow Tracer to drop data if timeout is reached. - * @return + * @brief etw::Span allows to send event data to ETW listener. */ - void CloseWithMicroseconds(uint64_t) noexcept override + class Span : public trace::Span { - // Close once only - if (!isClosed_.exchange(true)) + protected: + friend class Tracer; + + /** + * @brief Span properties are attached on "Span" event on end of Span. + */ + Properties attributes_; + + common::SystemTimestamp start_time_; + common::SystemTimestamp end_time_; + + trace::StatusCode status_code_{trace::StatusCode::kUnset}; + std::string status_description_; + + /** + * @brief Owner Tracer of this Span + */ + Tracer &owner_; + + /** + * @brief Span name. + */ + nostd::string_view name_; + + /** + * @brief Attribute indicating that the span has ended. + */ + std::atomic has_ended_{false}; + + /** + * @brief Attribute indicating that the span has started. + */ + std::atomic has_started_{false}; + + /** + * @brief Parent Span of this nested Span (optional) + */ + Span *parent_{nullptr}; + + /** + * @brief Get Parent Span of this nested Span. + * @return Pointer to Parent or nullptr if no Parent. + */ + Span *GetParent() const { return parent_; } + + trace::SpanContext context_; + + const trace::SpanContext CreateContext() { - etwProvider().close(provHandle); + 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 + trace::SpanId::kSize); + const trace::SpanId spanId(spanIdBytes); + + // Inherit trace_id from Tracer + const trace::TraceId traceId{owner_.trace_id()}; + // TODO: TraceFlags are not supported by ETW exporter. + const trace::TraceFlags flags{0}; + // TODO: Remote parent is not supported by ETW exporter. + const bool hasRemoteParent = false; + return trace::SpanContext{traceId, spanId, flags, hasRemoteParent}; } - }; - /** - * @brief Add event data to span associated with tracer. - * @param span Parent span. - * @param name Event name. - * @param timestamp Event timestamp. - * @param attributes Event attributes. - * @return - */ - void AddEvent(trace::Span &span, - nostd::string_view name, - common::SystemTimestamp timestamp, - const common::KeyValueIterable &attributes) noexcept - { -#ifdef RTTI_ENABLED - common::KeyValueIterable &attribs = const_cast(attributes); - Properties *evt = dynamic_cast(&attribs); - if (evt != nullptr) + public: + /** + * @brief Update Properties object with current Span status + * @param evt + */ + void UpdateStatus(Properties &evt) { - // Pass as a reference to original modifyable collection without creating a copy - return AddEvent(span, name, timestamp, *evt); + /* Should we avoid populating this extra field if status is unset? */ + if ((status_code_ == trace::StatusCode::kUnset) || (status_code_ == trace::StatusCode::kOk)) + { + evt[ETW_FIELD_SUCCESS] = "True"; + evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_); + evt[ETW_FIELD_STATUSMESSAGE] = status_description_; + } + else + { + evt[ETW_FIELD_SUCCESS] = "False"; + evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_); + evt[ETW_FIELD_STATUSMESSAGE] = status_description_; + } } -#endif - // Pass a copy converted to Properties object on stack - Properties evtCopy = attributes; - return AddEvent(span, name, timestamp, evtCopy); - } - - /** - * @brief Add event data to span associated with tracer. - * @param span Parent span. - * @param name Event name. - * @param timestamp Event timestamp. - * @param attributes Event attributes. - * @return - */ - void AddEvent(trace::Span &span, - nostd::string_view name, - common::SystemTimestamp timestamp, - Properties &evt) noexcept - { - // TODO: respect originating timestamp. Do we need to reserve - // a special 'Timestamp' field or is it an overkill? The delta - // between when `AddEvent` API is called and when ETW layer - // timestamp is appended is nanos- to micros-, thus handling - // the explicitly provided timestamp is only necessary in case - // if a process wants to submit back-dated or future-dated - // timestamp. Unless there is a strong ask from any ETW customer - // to have it, this feature (custom timestamp) remains unimplemented. - (void)timestamp; - const auto &cfg = GetConfiguration(tracerProvider_); - - evt[ETW_FIELD_NAME] = name.data(); - - const auto &spanContext = span.GetContext(); - if (cfg.enableSpanId) + /** + * @brief Get start time of this Span. + * @return + */ + common::SystemTimestamp GetStartTime() { return start_time_; } + + /** + * @brief Get end time of this Span. + * @return + */ + common::SystemTimestamp GetEndTime() { return end_time_; } + + /** + * @brief Get Span Name. + * @return Span Name. + */ + nostd::string_view GetName() const { return name_; } + + /** + * @brief Span constructor + * @param owner Owner Tracer + * @param name Span name + * @param options Span options + * @param parent Parent Span (optional) + * @return + */ + Span(Tracer &owner, + nostd::string_view name, + const trace::StartSpanOptions &options, + Span *parent = nullptr) noexcept + : trace::Span(), + owner_(owner), + parent_(parent), + context_(CreateContext()), + start_time_(std::chrono::system_clock::now()) { - evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id()); - } + name_ = name; + UNREFERENCED_PARAMETER(options); + }; - if (cfg.enableTraceId) + /** + * @brief Span Destructor + */ + ~Span() { End(); } + + /** + * @brief Add named event with no attributes. + * @param name Event name. + * @return + */ + void AddEvent(nostd::string_view name) noexcept override { owner_.AddEvent(*this, name); } + + /** + * @brief Add named event with custom timestamp. + * @param name + * @param timestamp + * @return + */ + void AddEvent(nostd::string_view name, common::SystemTimestamp timestamp) noexcept override { - evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id()); + owner_.AddEvent(*this, name, timestamp); } - LPGUID ActivityIdPtr = nullptr; - GUID ActivityId; - if (cfg.enableActivityId) + /** + * @brief Add named event with custom timestamp and attributes. + * @param name Event name. + * @param timestamp Event timestamp. + * @param attributes Event attributes. + * @return + */ + void AddEvent(nostd::string_view name, + common::SystemTimestamp timestamp, + const common::KeyValueIterable &attributes) noexcept override { - CopySpanIdToActivityId(spanContext, ActivityId); - ActivityIdPtr = &ActivityId; + owner_.AddEvent(*this, name, timestamp, attributes); } -#ifdef HAVE_FIELD_TIME + /** + * @brief Set Span status + * @param code Span status code. + * @param description Span description. + * @return + */ + void SetStatus(trace::StatusCode code, nostd::string_view description) noexcept override { - auto timeNow = std::chrono::system_clock::now().time_since_epoch(); - auto millis = std::chrono::duration_cast(timeNow).count(); - evt[ETW_FIELD_TIME] = utils::formatUtcTimestampMsAsISO8601(millis); + status_code_ = code; + status_description_ = description.data(); } -#endif - - etwProvider().write(provHandle, evt, ActivityIdPtr, nullptr, 0, encoding); - }; - - /** - * @brief Add event data to span associated with tracer. - * @param span Span. - * @param name Event name. - * @param timestamp Event timestamp. - * @return - */ - void AddEvent(trace::Span &span, - nostd::string_view name, - common::SystemTimestamp timestamp) noexcept - { - AddEvent(span, name, timestamp, sdk::GetEmptyAttributes()); - }; - - /** - * @brief Add event data to span associated with tracer. - * @param span Spab. - * @param name Event name. - */ - void AddEvent(trace::Span &span, nostd::string_view name) - { - AddEvent(span, name, std::chrono::system_clock::now(), sdk::GetEmptyAttributes()); - }; - - /** - * @brief Tracer destructor. - */ - virtual ~Tracer() { CloseWithMicroseconds(0); }; -}; - -/** - * @brief etw::Span allows to send event data to ETW listener. - */ -class Span : public trace::Span -{ -protected: - friend class Tracer; - - /** - * @brief Span properties are attached on "Span" event on end of Span. - */ - Properties attributes_; - - common::SystemTimestamp start_time_; - common::SystemTimestamp end_time_; - - trace::StatusCode status_code_{trace::StatusCode::kUnset}; - std::string status_description_; - - /** - * @brief Owner Tracer of this Span - */ - Tracer &owner_; - - /** - * @brief Span name. - */ - nostd::string_view name_; - - /** - * @brief Attribute indicating that the span has ended. - */ - std::atomic has_ended_{false}; - /** - * @brief Attribute indicating that the span has started. - */ - std::atomic has_started_{false}; - - /** - * @brief Parent Span of this nested Span (optional) - */ - Span *parent_{nullptr}; + void SetAttributes(Properties attributes) { attributes_ = attributes; } + + /** + * @brief Obtain span attributes specified at Span start. + * NOTE: please consider that this method is NOT thread-safe. + * + * @return ref to Properties collection + */ + Properties &GetAttributes() { return attributes_; } + + /** + * @brief Sets an attribute on the Span. If the Span previously contained a mapping + * for the key, the old value is replaced. + * + * @param key + * @param value + * @return + */ + void SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept override + { + // TODO: not implemented + UNREFERENCED_PARAMETER(key); + UNREFERENCED_PARAMETER(value); + }; - /** - * @brief Get Parent Span of this nested Span. - * @return Pointer to Parent or nullptr if no Parent. - */ - Span *GetParent() const { return parent_; } + /** + * @brief Update Span name. + * + * NOTE: this method is a no-op for streaming implementation. + * We cannot change the Span name after it started streaming. + * + * @param name + * @return + */ + void UpdateName(nostd::string_view) noexcept override + { + // We can't do that! + // name_ = name; + } - trace::SpanContext context_; + /** + * @brief End Span. + * @param EndSpanOptions + * @return + */ + void End(const trace::EndSpanOptions &options = {}) noexcept override + { + end_time_ = std::chrono::system_clock::now(); - const 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 + trace::SpanId::kSize); - const trace::SpanId spanId(spanIdBytes); - - // Inherit trace_id from Tracer - const trace::TraceId traceId{owner_.trace_id()}; - // TODO: TraceFlags are not supported by ETW exporter. - const trace::TraceFlags flags{0}; - // TODO: Remote parent is not supported by ETW exporter. - const bool hasRemoteParent = false; - return trace::SpanContext{traceId, spanId, flags, hasRemoteParent}; - } + if (!has_ended_.exchange(true)) + { + owner_.EndSpan(*this, parent_, options); + } + } -public: - /** - * @brief Update Properties object with current Span status - * @param evt - */ - void UpdateStatus(Properties &evt) - { - /* Should we avoid populating this extra field if status is unset? */ - if ((status_code_ == trace::StatusCode::kUnset) || (status_code_ == trace::StatusCode::kOk)) + /** + * @brief Obtain SpanContext + * @return + */ + trace::SpanContext GetContext() const noexcept override { return context_; } + + /** + * @brief Check if Span is recording data. + * @return + */ + bool IsRecording() const noexcept override { - evt[ETW_FIELD_SUCCESS] = "True"; - evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_); - evt[ETW_FIELD_STATUSMESSAGE] = status_description_; + // For streaming implementation this should return the state of ETW Listener. + // In certain unprivileged environments, ex. containers, it is impossible + // to determine if a listener is registered. Thus, we always return true. + return true; } - else + + virtual void SetToken(nostd::unique_ptr &&token) noexcept { - evt[ETW_FIELD_SUCCESS] = "False"; - evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_); - evt[ETW_FIELD_STATUSMESSAGE] = status_description_; + // TODO: not implemented + UNREFERENCED_PARAMETER(token); } - } - - /** - * @brief Get start time of this Span. - * @return - */ - common::SystemTimestamp GetStartTime() { return start_time_; } - - /** - * @brief Get end time of this Span. - * @return - */ - common::SystemTimestamp GetEndTime() { return end_time_; } - - /** - * @brief Get Span Name. - * @return Span Name. - */ - nostd::string_view GetName() const { return name_; } - /** - * @brief Span constructor - * @param owner Owner Tracer - * @param name Span name - * @param options Span options - * @param parent Parent Span (optional) - * @return - */ - Span(Tracer &owner, - nostd::string_view name, - const trace::StartSpanOptions &options, - Span *parent = nullptr) noexcept - : trace::Span(), - owner_(owner), - parent_(parent), - context_(CreateContext()), - start_time_(std::chrono::system_clock::now()) - { - name_ = name; - UNREFERENCED_PARAMETER(options); + /// + /// Get Owner tracer of this Span + /// + /// + trace::Tracer &tracer() const noexcept { return this->owner_; }; }; /** - * @brief Span Destructor + * @brief ETW TracerProvider */ - ~Span() { End(); } - - /** - * @brief Add named event with no attributes. - * @param name Event name. - * @return - */ - void AddEvent(nostd::string_view name) noexcept override { owner_.AddEvent(*this, name); } - - /** - * @brief Add named event with custom timestamp. - * @param name - * @param timestamp - * @return - */ - void AddEvent(nostd::string_view name, common::SystemTimestamp timestamp) noexcept override + class TracerProvider : public trace::TracerProvider { - owner_.AddEvent(*this, name, timestamp); - } + public: + /** + * @brief TracerProvider options supplied during initialization. + */ + TracerProviderConfiguration config_; + + /** + * @brief Construct instance of TracerProvider with given options + * @param options Configuration options + */ + TracerProvider(TracerProviderOptions options) : trace::TracerProvider() + { + // 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); - /** - * @brief Add named event with custom timestamp and attributes. - * @param name Event name. - * @param timestamp Event timestamp. - * @param attributes Event attributes. - * @return - */ - void AddEvent(nostd::string_view name, - common::SystemTimestamp timestamp, - const common::KeyValueIterable &attributes) noexcept override - { - owner_.AddEvent(*this, name, timestamp, attributes); - } + // Backwards-compatibility option that allows to reuse ETW-specific parenting described here: + // https://docs.microsoft.com/en-us/uwp/api/windows.foundation.diagnostics.loggingoptions.relatedactivityid + // https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventwritetransfer - /** - * @brief Set Span status - * @param code Span status code. - * @param description Span description. - * @return - */ - void SetStatus(trace::StatusCode code, nostd::string_view description) noexcept override - { - status_code_ = code; - status_description_ = description.data(); - } + // Emit separate events compatible with TraceLogging Activity/Start and Activity/Stop + // format for every Span emitted. + GetOption(options, "enableActivityTracking", config_.enableActivityTracking, false); - void SetAttributes(Properties attributes) { attributes_ = attributes; } + // Map current `SpanId` to ActivityId - GUID that uniquely identifies this activity. If NULL, + // ETW gets the identifier from the thread local storage. For details on getting this + // identifier, see EventActivityIdControl. + GetOption(options, "enableActivityId", config_.enableActivityId, false); - /** - * @brief Obtain span attributes specified at Span start. - * NOTE: please consider that this method is NOT thread-safe. - * - * @return ref to Properties collection - */ - Properties &GetAttributes() { return attributes_; } + // Map parent `SpanId` to RelatedActivityId - Activity identifier from the previous + // component. Use this parameter to link your component's events to the previous component's + // events. + GetOption(options, "enableRelatedActivityId", config_.enableRelatedActivityId, false); - /** - * @brief Sets an attribute on the Span. If the Span previously contained a mapping - * for the key, the old value is replaced. - * - * @param key - * @param value - * @return - */ - void SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept override - { - // TODO: not implemented - UNREFERENCED_PARAMETER(key); - UNREFERENCED_PARAMETER(value); - }; + // When a new Span is started, the current span automatically becomes its parent. + GetOption(options, "enableAutoParent", config_.enableAutoParent, false); - /** - * @brief Update Span name. - * - * NOTE: this method is a no-op for streaming implementation. - * We cannot change the Span name after it started streaming. - * - * @param name - * @return - */ - void UpdateName(nostd::string_view) noexcept override - { - // We can't do that! - // name_ = name; - } - - /** - * @brief End Span. - * @param EndSpanOptions - * @return - */ - void End(const trace::EndSpanOptions &options = {}) noexcept override - { - end_time_ = std::chrono::system_clock::now(); + // Determines what encoding to use for ETW events: TraceLogging Dynamic, MsgPack, XML, etc. + config_.encoding = GetEncoding(options); + } - if (!has_ended_.exchange(true)) + TracerProvider() : trace::TracerProvider() { - owner_.EndSpan(*this, parent_, options); + config_.enableTraceId = true; + config_.enableSpanId = true; + config_.enableActivityId = false; + config_.enableActivityTracking = false; + config_.enableRelatedActivityId = false; + config_.enableAutoParent = false; + config_.encoding = ETWProvider::EventFormat::ETW_MANIFEST; } - } - - /** - * @brief Obtain SpanContext - * @return - */ - trace::SpanContext GetContext() const noexcept override { return context_; } - - /** - * @brief Check if Span is recording data. - * @return - */ - bool IsRecording() const noexcept override - { - // For streaming implementation this should return the state of ETW Listener. - // In certain unprivileged environments, ex. containers, it is impossible - // to determine if a listener is registered. Thus, we always return true. - return true; - } - - virtual void SetToken(nostd::unique_ptr &&token) noexcept - { - // TODO: not implemented - UNREFERENCED_PARAMETER(token); - } - - /// - /// Get Owner tracer of this Span - /// - /// - trace::Tracer &tracer() const noexcept { return this->owner_; }; -}; -/** - * @brief ETW TracerProvider - */ -class TracerProvider : public trace::TracerProvider -{ -public: - /** - * @brief TracerProvider options supplied during initialization. - */ - TracerProviderConfiguration config_; - - /** - * @brief Construct instance of TracerProvider with given options - * @param options Configuration options - */ - TracerProvider(TracerProviderOptions options) : trace::TracerProvider() - { - // 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); - - // Backwards-compatibility option that allows to reuse ETW-specific parenting described here: - // https://docs.microsoft.com/en-us/uwp/api/windows.foundation.diagnostics.loggingoptions.relatedactivityid - // https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventwritetransfer - - // Emit separate events compatible with TraceLogging Activity/Start and Activity/Stop - // format for every Span emitted. - GetOption(options, "enableActivityTracking", config_.enableActivityTracking, false); - - // Map current `SpanId` to ActivityId - GUID that uniquely identifies this activity. If NULL, - // ETW gets the identifier from the thread local storage. For details on getting this - // identifier, see EventActivityIdControl. - GetOption(options, "enableActivityId", config_.enableActivityId, false); - - // Map parent `SpanId` to RelatedActivityId - Activity identifier from the previous component. - // Use this parameter to link your component's events to the previous component's events. - GetOption(options, "enableRelatedActivityId", config_.enableRelatedActivityId, false); - - // When a new Span is started, the current span automatically becomes its parent. - GetOption(options, "enableAutoParent", config_.enableAutoParent, false); - - // Determines what encoding to use for ETW events: TraceLogging Dynamic, MsgPack, XML, etc. - config_.encoding = GetEncoding(options); - } - - TracerProvider() : trace::TracerProvider() - { - config_.enableTraceId = true; - config_.enableSpanId = true; - config_.enableActivityId = false; - config_.enableActivityTracking = false; - config_.enableRelatedActivityId = false; - config_.enableAutoParent = false; - config_.encoding = ETWProvider::EventFormat::ETW_MANIFEST; - } - - /** - * @brief Obtain ETW Tracer. - * @param name ProviderId (instrumentation name) - Name or GUID - * - * @param args Additional arguments that controls `codec` of the provider. - * Possible values are: - * - "ETW" - 'classic' Trace Logging Dynamic manifest ETW events. - * - "MSGPACK" - MessagePack-encoded binary payload ETW events. - * - "XML" - XML events (reserved for future use) - * @return - */ - nostd::shared_ptr GetTracer(nostd::string_view name, - nostd::string_view args = "") override - { - UNREFERENCED_PARAMETER(args); - ETWProvider::EventFormat evtFmt = config_.encoding; - return nostd::shared_ptr{new (std::nothrow) Tracer(*this, name, evtFmt)}; - } -}; + /** + * @brief Obtain ETW Tracer. + * @param name ProviderId (instrumentation name) - Name or GUID + * + * @param args Additional arguments that controls `codec` of the provider. + * Possible values are: + * - "ETW" - 'classic' Trace Logging Dynamic manifest ETW events. + * - "MSGPACK" - MessagePack-encoded binary payload ETW events. + * - "XML" - XML events (reserved for future use) + * @return + */ + nostd::shared_ptr GetTracer(nostd::string_view name, + nostd::string_view args = "") override + { + UNREFERENCED_PARAMETER(args); + ETWProvider::EventFormat evtFmt = config_.encoding; + return nostd::shared_ptr{new (std::nothrow) Tracer(*this, name, evtFmt)}; + } + }; } // namespace etw -} // namespace exporter +} // namespace etw OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/otlp/test/otlp_http_exporter_test.cc b/exporters/otlp/test/otlp_http_exporter_test.cc index 2dfd32ce05..845520db68 100644 --- a/exporters/otlp/test/otlp_http_exporter_test.cc +++ b/exporters/otlp/test/otlp_http_exporter_test.cc @@ -222,7 +222,9 @@ TEST_F(OtlpHttpExporterTestPeer, ExportJsonIntegrationTest) child_span->End(); parent_span->End(); - child_span_opts.parent.trace_id().ToLowerBase16(MakeSpan(trace_id_hex)); + nostd::get(child_span_opts.parent) + .trace_id() + .ToLowerBase16(MakeSpan(trace_id_hex)); report_trace_id.assign(trace_id_hex, sizeof(trace_id_hex)); } @@ -282,7 +284,9 @@ TEST_F(OtlpHttpExporterTestPeer, ExportBinaryIntegrationTest) child_span->End(); parent_span->End(); - child_span_opts.parent.trace_id().CopyBytesTo(MakeSpan(trace_id_binary)); + nostd::get(child_span_opts.parent) + .trace_id() + .CopyBytesTo(MakeSpan(trace_id_binary)); report_trace_id.assign(reinterpret_cast(trace_id_binary), sizeof(trace_id_binary)); } From 4d1b1213145b42eb77469c88e6be368344005fdb Mon Sep 17 00:00:00 2001 From: Lalit Date: Thu, 2 Sep 2021 17:48:12 -0700 Subject: [PATCH 3/7] fix mising braces --- .../opentelemetry/exporters/etw/etw_tracer.h | 972 +++++++++--------- 1 file changed, 489 insertions(+), 483 deletions(-) diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index 1fb2fdf627..d4229486d6 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -577,562 +577,568 @@ class Tracer : public trace::Tracer { parentContext = span_context; } + } - // Populate Etw.RelatedActivityId at envelope level if enabled - GUID RelatedActivityId; - LPCGUID RelatedActivityIdPtr = nullptr; - if (cfg.enableAutoParent) + // Populate Etw.RelatedActivityId at envelope level if enabled + GUID RelatedActivityId; + LPCGUID RelatedActivityIdPtr = nullptr; + if (cfg.enableAutoParent) + { + if (cfg.enableRelatedActivityId) { - if (cfg.enableRelatedActivityId) + if (CopySpanIdToActivityId(parentContext, RelatedActivityId)) { - if (CopySpanIdToActivityId(parentContext, RelatedActivityId)) - { - RelatedActivityIdPtr = &RelatedActivityId; - } + RelatedActivityIdPtr = &RelatedActivityId; } } + } - // 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); - nostd::shared_ptr result = to_span_ptr(currentSpan); + // 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); + nostd::shared_ptr result = to_span_ptr(currentSpan); - auto spanContext = result->GetContext(); + auto spanContext = result->GetContext(); - // Decorate with additional standard fields - std::string eventName = name.data(); + // Decorate with additional standard fields + std::string eventName = name.data(); - // Populate Etw.EventName attribute at envelope level - evt[ETW_FIELD_NAME] = eventName; + // Populate Etw.EventName attribute at envelope level + evt[ETW_FIELD_NAME] = eventName; - // Populate Payload["SpanId"] attribute - // Populate Payload["ParentSpanId"] attribute if parent Span is valid - if (cfg.enableSpanId) - { - if (parentContext.IsValid()) - { - evt[ETW_FIELD_SPAN_PARENTID] = ToLowerBase16(parentContext.span_id()); - } - evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id()); - } - - // Populate Etw.Payload["TraceId"] attribute - if (cfg.enableTraceId) - { - evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id()); - } - - // Populate Etw.ActivityId at envelope level if enabled - GUID ActivityId; - LPCGUID ActivityIdPtr = nullptr; - if (cfg.enableActivityId) - { - if (CopySpanIdToActivityId(result.get()->GetContext(), ActivityId)) - { - ActivityIdPtr = &ActivityId; - } - } - - // Links - DecorateLinks(evt, links); - - // Remember Span attributes to be passed down to ETW on Span end - SetSpanAttributes(*currentSpan, evt); - - if (cfg.enableActivityTracking) - { - // TODO: add support for options that are presently ignored : - // - options.kind - // - options.start_steady_time - // - options.start_system_time - etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 1, encoding); - }; - - return result; - }; - - /** - * @brief Force flush data to Tracer, spending up to given amount of microseconds to flush. - * NOTE: this method has no effect for the realtime streaming Tracer. - * - * @param timeout Allow Tracer to drop data if timeout is reached - * @return - */ - void ForceFlushWithMicroseconds(uint64_t) noexcept override{}; - - /** - * @brief Close tracer, spending up to given amount of microseconds to flush and close. - * NOTE: This method decrements the reference count on current ETW Provider Handle and - * closes it if reference count on that provider handle is zero. - * - * @param timeout Allow Tracer to drop data if timeout is reached. - * @return - */ - void CloseWithMicroseconds(uint64_t) noexcept override + // Populate Payload["SpanId"] attribute + // Populate Payload["ParentSpanId"] attribute if parent Span is valid + if (cfg.enableSpanId) { - // Close once only - if (!isClosed_.exchange(true)) + if (parentContext.IsValid()) { - etwProvider().close(provHandle); + evt[ETW_FIELD_SPAN_PARENTID] = ToLowerBase16(parentContext.span_id()); } - }; + evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id()); + } - /** - * @brief Add event data to span associated with tracer. - * @param span Parent span. - * @param name Event name. - * @param timestamp Event timestamp. - * @param attributes Event attributes. - * @return - */ - void AddEvent(trace::Span & span, nostd::string_view name, common::SystemTimestamp timestamp, - const common::KeyValueIterable &attributes) noexcept + // Populate Etw.Payload["TraceId"] attribute + if (cfg.enableTraceId) { -#ifdef RTTI_ENABLED - common::KeyValueIterable &attribs = const_cast(attributes); - Properties *evt = dynamic_cast(&attribs); - if (evt != nullptr) - { - // 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); + evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id()); } - /** - * @brief Add event data to span associated with tracer. - * @param span Parent span. - * @param name Event name. - * @param timestamp Event timestamp. - * @param attributes Event attributes. - * @return - */ - void AddEvent(trace::Span & span, nostd::string_view name, common::SystemTimestamp timestamp, - Properties & evt) noexcept + // Populate Etw.ActivityId at envelope level if enabled + GUID ActivityId; + LPCGUID ActivityIdPtr = nullptr; + if (cfg.enableActivityId) { - // TODO: respect originating timestamp. Do we need to reserve - // a special 'Timestamp' field or is it an overkill? The delta - // between when `AddEvent` API is called and when ETW layer - // timestamp is appended is nanos- to micros-, thus handling - // the explicitly provided timestamp is only necessary in case - // if a process wants to submit back-dated or future-dated - // timestamp. Unless there is a strong ask from any ETW customer - // to have it, this feature (custom timestamp) remains unimplemented. - (void)timestamp; - - const auto &cfg = GetConfiguration(tracerProvider_); - - evt[ETW_FIELD_NAME] = name.data(); - - const auto &spanContext = span.GetContext(); - if (cfg.enableSpanId) - { - evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id()); - } - - if (cfg.enableTraceId) - { - evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id()); - } - - LPGUID ActivityIdPtr = nullptr; - GUID ActivityId; - if (cfg.enableActivityId) + if (CopySpanIdToActivityId(result.get()->GetContext(), ActivityId)) { - CopySpanIdToActivityId(spanContext, ActivityId); ActivityIdPtr = &ActivityId; } + } -#ifdef HAVE_FIELD_TIME - { - auto timeNow = std::chrono::system_clock::now().time_since_epoch(); - auto millis = std::chrono::duration_cast(timeNow).count(); - evt[ETW_FIELD_TIME] = utils::formatUtcTimestampMsAsISO8601(millis); - } -#endif - - etwProvider().write(provHandle, evt, ActivityIdPtr, nullptr, 0, encoding); - }; + // Links + DecorateLinks(evt, links); - /** - * @brief Add event data to span associated with tracer. - * @param span Span. - * @param name Event name. - * @param timestamp Event timestamp. - * @return - */ - void AddEvent(trace::Span & span, nostd::string_view name, - common::SystemTimestamp timestamp) noexcept - { - AddEvent(span, name, timestamp, sdk::GetEmptyAttributes()); - }; + // Remember Span attributes to be passed down to ETW on Span end + SetSpanAttributes(*currentSpan, evt); - /** - * @brief Add event data to span associated with tracer. - * @param span Spab. - * @param name Event name. - */ - void AddEvent(trace::Span & span, nostd::string_view name) + if (cfg.enableActivityTracking) { - AddEvent(span, name, std::chrono::system_clock::now(), sdk::GetEmptyAttributes()); + // TODO: add support for options that are presently ignored : + // - options.kind + // - options.start_steady_time + // - options.start_system_time + etwProvider().write(provHandle, evt, ActivityIdPtr, RelatedActivityIdPtr, 1, encoding); }; - /** - * @brief Tracer destructor. - */ - virtual ~Tracer() { CloseWithMicroseconds(0); }; + return result; }; /** - * @brief etw::Span allows to send event data to ETW listener. + * @brief Force flush data to Tracer, spending up to given amount of microseconds to flush. + * NOTE: this method has no effect for the realtime streaming Tracer. + * + * @param timeout Allow Tracer to drop data if timeout is reached + * @return + */ + void ForceFlushWithMicroseconds(uint64_t) noexcept override{}; + + /** + * @brief Close tracer, spending up to given amount of microseconds to flush and close. + * NOTE: This method decrements the reference count on current ETW Provider Handle and + * closes it if reference count on that provider handle is zero. + * + * @param timeout Allow Tracer to drop data if timeout is reached. + * @return */ - class Span : public trace::Span + void CloseWithMicroseconds(uint64_t) noexcept override { - protected: - friend class Tracer; - - /** - * @brief Span properties are attached on "Span" event on end of Span. - */ - Properties attributes_; - - common::SystemTimestamp start_time_; - common::SystemTimestamp end_time_; - - trace::StatusCode status_code_{trace::StatusCode::kUnset}; - std::string status_description_; - - /** - * @brief Owner Tracer of this Span - */ - Tracer &owner_; - - /** - * @brief Span name. - */ - nostd::string_view name_; - - /** - * @brief Attribute indicating that the span has ended. - */ - std::atomic has_ended_{false}; - - /** - * @brief Attribute indicating that the span has started. - */ - std::atomic has_started_{false}; - - /** - * @brief Parent Span of this nested Span (optional) - */ - Span *parent_{nullptr}; - - /** - * @brief Get Parent Span of this nested Span. - * @return Pointer to Parent or nullptr if no Parent. - */ - Span *GetParent() const { return parent_; } - - trace::SpanContext context_; - - const trace::SpanContext CreateContext() + // Close once only + if (!isClosed_.exchange(true)) { - 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 + trace::SpanId::kSize); - const trace::SpanId spanId(spanIdBytes); - - // Inherit trace_id from Tracer - const trace::TraceId traceId{owner_.trace_id()}; - // TODO: TraceFlags are not supported by ETW exporter. - const trace::TraceFlags flags{0}; - // TODO: Remote parent is not supported by ETW exporter. - const bool hasRemoteParent = false; - return trace::SpanContext{traceId, spanId, flags, hasRemoteParent}; + etwProvider().close(provHandle); } + }; - public: - /** - * @brief Update Properties object with current Span status - * @param evt - */ - void UpdateStatus(Properties &evt) + /** + * @brief Add event data to span associated with tracer. + * @param span Parent span. + * @param name Event name. + * @param timestamp Event timestamp. + * @param attributes Event attributes. + * @return + */ + void AddEvent(trace::Span &span, + nostd::string_view name, + common::SystemTimestamp timestamp, + const common::KeyValueIterable &attributes) noexcept + { +#ifdef RTTI_ENABLED + common::KeyValueIterable &attribs = const_cast(attributes); + Properties *evt = dynamic_cast(&attribs); + if (evt != nullptr) { - /* Should we avoid populating this extra field if status is unset? */ - if ((status_code_ == trace::StatusCode::kUnset) || (status_code_ == trace::StatusCode::kOk)) - { - evt[ETW_FIELD_SUCCESS] = "True"; - evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_); - evt[ETW_FIELD_STATUSMESSAGE] = status_description_; - } - else - { - evt[ETW_FIELD_SUCCESS] = "False"; - evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_); - evt[ETW_FIELD_STATUSMESSAGE] = status_description_; - } + // 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); + } - /** - * @brief Get start time of this Span. - * @return - */ - common::SystemTimestamp GetStartTime() { return start_time_; } - - /** - * @brief Get end time of this Span. - * @return - */ - common::SystemTimestamp GetEndTime() { return end_time_; } - - /** - * @brief Get Span Name. - * @return Span Name. - */ - nostd::string_view GetName() const { return name_; } - - /** - * @brief Span constructor - * @param owner Owner Tracer - * @param name Span name - * @param options Span options - * @param parent Parent Span (optional) - * @return - */ - Span(Tracer &owner, - nostd::string_view name, - const trace::StartSpanOptions &options, - Span *parent = nullptr) noexcept - : trace::Span(), - owner_(owner), - parent_(parent), - context_(CreateContext()), - start_time_(std::chrono::system_clock::now()) - { - name_ = name; - UNREFERENCED_PARAMETER(options); - }; + /** + * @brief Add event data to span associated with tracer. + * @param span Parent span. + * @param name Event name. + * @param timestamp Event timestamp. + * @param attributes Event attributes. + * @return + */ + void AddEvent(trace::Span &span, + nostd::string_view name, + common::SystemTimestamp timestamp, + Properties &evt) noexcept + { + // TODO: respect originating timestamp. Do we need to reserve + // a special 'Timestamp' field or is it an overkill? The delta + // between when `AddEvent` API is called and when ETW layer + // timestamp is appended is nanos- to micros-, thus handling + // the explicitly provided timestamp is only necessary in case + // if a process wants to submit back-dated or future-dated + // timestamp. Unless there is a strong ask from any ETW customer + // to have it, this feature (custom timestamp) remains unimplemented. + (void)timestamp; - /** - * @brief Span Destructor - */ - ~Span() { End(); } - - /** - * @brief Add named event with no attributes. - * @param name Event name. - * @return - */ - void AddEvent(nostd::string_view name) noexcept override { owner_.AddEvent(*this, name); } - - /** - * @brief Add named event with custom timestamp. - * @param name - * @param timestamp - * @return - */ - void AddEvent(nostd::string_view name, common::SystemTimestamp timestamp) noexcept override - { - owner_.AddEvent(*this, name, timestamp); - } + const auto &cfg = GetConfiguration(tracerProvider_); - /** - * @brief Add named event with custom timestamp and attributes. - * @param name Event name. - * @param timestamp Event timestamp. - * @param attributes Event attributes. - * @return - */ - void AddEvent(nostd::string_view name, - common::SystemTimestamp timestamp, - const common::KeyValueIterable &attributes) noexcept override + evt[ETW_FIELD_NAME] = name.data(); + + const auto &spanContext = span.GetContext(); + if (cfg.enableSpanId) { - owner_.AddEvent(*this, name, timestamp, attributes); + evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id()); } - /** - * @brief Set Span status - * @param code Span status code. - * @param description Span description. - * @return - */ - void SetStatus(trace::StatusCode code, nostd::string_view description) noexcept override + if (cfg.enableTraceId) { - status_code_ = code; - status_description_ = description.data(); + evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id()); } - void SetAttributes(Properties attributes) { attributes_ = attributes; } - - /** - * @brief Obtain span attributes specified at Span start. - * NOTE: please consider that this method is NOT thread-safe. - * - * @return ref to Properties collection - */ - Properties &GetAttributes() { return attributes_; } - - /** - * @brief Sets an attribute on the Span. If the Span previously contained a mapping - * for the key, the old value is replaced. - * - * @param key - * @param value - * @return - */ - void SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept override + LPGUID ActivityIdPtr = nullptr; + GUID ActivityId; + if (cfg.enableActivityId) { - // TODO: not implemented - UNREFERENCED_PARAMETER(key); - UNREFERENCED_PARAMETER(value); - }; + CopySpanIdToActivityId(spanContext, ActivityId); + ActivityIdPtr = &ActivityId; + } - /** - * @brief Update Span name. - * - * NOTE: this method is a no-op for streaming implementation. - * We cannot change the Span name after it started streaming. - * - * @param name - * @return - */ - void UpdateName(nostd::string_view) noexcept override +#ifdef HAVE_FIELD_TIME { - // We can't do that! - // name_ = name; + auto timeNow = std::chrono::system_clock::now().time_since_epoch(); + auto millis = std::chrono::duration_cast(timeNow).count(); + evt[ETW_FIELD_TIME] = utils::formatUtcTimestampMsAsISO8601(millis); } +#endif - /** - * @brief End Span. - * @param EndSpanOptions - * @return - */ - void End(const trace::EndSpanOptions &options = {}) noexcept override - { - end_time_ = std::chrono::system_clock::now(); + etwProvider().write(provHandle, evt, ActivityIdPtr, nullptr, 0, encoding); + }; - if (!has_ended_.exchange(true)) - { - owner_.EndSpan(*this, parent_, options); - } - } + /** + * @brief Add event data to span associated with tracer. + * @param span Span. + * @param name Event name. + * @param timestamp Event timestamp. + * @return + */ + void AddEvent(trace::Span &span, + nostd::string_view name, + common::SystemTimestamp timestamp) noexcept + { + AddEvent(span, name, timestamp, sdk::GetEmptyAttributes()); + }; + + /** + * @brief Add event data to span associated with tracer. + * @param span Spab. + * @param name Event name. + */ + void AddEvent(trace::Span &span, nostd::string_view name) + { + AddEvent(span, name, std::chrono::system_clock::now(), sdk::GetEmptyAttributes()); + }; + + /** + * @brief Tracer destructor. + */ + virtual ~Tracer() { CloseWithMicroseconds(0); }; +}; + +/** + * @brief etw::Span allows to send event data to ETW listener. + */ +class Span : public trace::Span +{ +protected: + friend class Tracer; + + /** + * @brief Span properties are attached on "Span" event on end of Span. + */ + Properties attributes_; + + common::SystemTimestamp start_time_; + common::SystemTimestamp end_time_; + + trace::StatusCode status_code_{trace::StatusCode::kUnset}; + std::string status_description_; + + /** + * @brief Owner Tracer of this Span + */ + Tracer &owner_; + + /** + * @brief Span name. + */ + nostd::string_view name_; - /** - * @brief Obtain SpanContext - * @return - */ - trace::SpanContext GetContext() const noexcept override { return context_; } - - /** - * @brief Check if Span is recording data. - * @return - */ - bool IsRecording() const noexcept override + /** + * @brief Attribute indicating that the span has ended. + */ + std::atomic has_ended_{false}; + + /** + * @brief Attribute indicating that the span has started. + */ + std::atomic has_started_{false}; + + /** + * @brief Parent Span of this nested Span (optional) + */ + Span *parent_{nullptr}; + + /** + * @brief Get Parent Span of this nested Span. + * @return Pointer to Parent or nullptr if no Parent. + */ + Span *GetParent() const { return parent_; } + + trace::SpanContext context_; + + const 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 + trace::SpanId::kSize); + const trace::SpanId spanId(spanIdBytes); + + // Inherit trace_id from Tracer + const trace::TraceId traceId{owner_.trace_id()}; + // TODO: TraceFlags are not supported by ETW exporter. + const trace::TraceFlags flags{0}; + // TODO: Remote parent is not supported by ETW exporter. + const bool hasRemoteParent = false; + return trace::SpanContext{traceId, spanId, flags, hasRemoteParent}; + } + +public: + /** + * @brief Update Properties object with current Span status + * @param evt + */ + void UpdateStatus(Properties &evt) + { + /* Should we avoid populating this extra field if status is unset? */ + if ((status_code_ == trace::StatusCode::kUnset) || (status_code_ == trace::StatusCode::kOk)) { - // For streaming implementation this should return the state of ETW Listener. - // In certain unprivileged environments, ex. containers, it is impossible - // to determine if a listener is registered. Thus, we always return true. - return true; + evt[ETW_FIELD_SUCCESS] = "True"; + evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_); + evt[ETW_FIELD_STATUSMESSAGE] = status_description_; } - - virtual void SetToken(nostd::unique_ptr &&token) noexcept + else { - // TODO: not implemented - UNREFERENCED_PARAMETER(token); + evt[ETW_FIELD_SUCCESS] = "False"; + evt[ETW_FIELD_STATUSCODE] = uint32_t(status_code_); + evt[ETW_FIELD_STATUSMESSAGE] = status_description_; } + } + + /** + * @brief Get start time of this Span. + * @return + */ + common::SystemTimestamp GetStartTime() { return start_time_; } + + /** + * @brief Get end time of this Span. + * @return + */ + common::SystemTimestamp GetEndTime() { return end_time_; } - /// - /// Get Owner tracer of this Span - /// - /// - trace::Tracer &tracer() const noexcept { return this->owner_; }; + /** + * @brief Get Span Name. + * @return Span Name. + */ + nostd::string_view GetName() const { return name_; } + + /** + * @brief Span constructor + * @param owner Owner Tracer + * @param name Span name + * @param options Span options + * @param parent Parent Span (optional) + * @return + */ + Span(Tracer &owner, + nostd::string_view name, + const trace::StartSpanOptions &options, + Span *parent = nullptr) noexcept + : trace::Span(), + owner_(owner), + parent_(parent), + context_(CreateContext()), + start_time_(std::chrono::system_clock::now()) + { + name_ = name; + UNREFERENCED_PARAMETER(options); }; /** - * @brief ETW TracerProvider + * @brief Span Destructor + */ + ~Span() { End(); } + + /** + * @brief Add named event with no attributes. + * @param name Event name. + * @return + */ + void AddEvent(nostd::string_view name) noexcept override { owner_.AddEvent(*this, name); } + + /** + * @brief Add named event with custom timestamp. + * @param name + * @param timestamp + * @return */ - class TracerProvider : public trace::TracerProvider + void AddEvent(nostd::string_view name, common::SystemTimestamp timestamp) noexcept override { - public: - /** - * @brief TracerProvider options supplied during initialization. - */ - TracerProviderConfiguration config_; - - /** - * @brief Construct instance of TracerProvider with given options - * @param options Configuration options - */ - TracerProvider(TracerProviderOptions options) : trace::TracerProvider() - { - // 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); + owner_.AddEvent(*this, name, timestamp); + } + + /** + * @brief Add named event with custom timestamp and attributes. + * @param name Event name. + * @param timestamp Event timestamp. + * @param attributes Event attributes. + * @return + */ + void AddEvent(nostd::string_view name, + common::SystemTimestamp timestamp, + const common::KeyValueIterable &attributes) noexcept override + { + owner_.AddEvent(*this, name, timestamp, attributes); + } - // Backwards-compatibility option that allows to reuse ETW-specific parenting described here: - // https://docs.microsoft.com/en-us/uwp/api/windows.foundation.diagnostics.loggingoptions.relatedactivityid - // https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventwritetransfer + /** + * @brief Set Span status + * @param code Span status code. + * @param description Span description. + * @return + */ + void SetStatus(trace::StatusCode code, nostd::string_view description) noexcept override + { + status_code_ = code; + status_description_ = description.data(); + } - // Emit separate events compatible with TraceLogging Activity/Start and Activity/Stop - // format for every Span emitted. - GetOption(options, "enableActivityTracking", config_.enableActivityTracking, false); + void SetAttributes(Properties attributes) { attributes_ = attributes; } - // Map current `SpanId` to ActivityId - GUID that uniquely identifies this activity. If NULL, - // ETW gets the identifier from the thread local storage. For details on getting this - // identifier, see EventActivityIdControl. - GetOption(options, "enableActivityId", config_.enableActivityId, false); + /** + * @brief Obtain span attributes specified at Span start. + * NOTE: please consider that this method is NOT thread-safe. + * + * @return ref to Properties collection + */ + Properties &GetAttributes() { return attributes_; } - // Map parent `SpanId` to RelatedActivityId - Activity identifier from the previous - // component. Use this parameter to link your component's events to the previous component's - // events. - GetOption(options, "enableRelatedActivityId", config_.enableRelatedActivityId, false); + /** + * @brief Sets an attribute on the Span. If the Span previously contained a mapping + * for the key, the old value is replaced. + * + * @param key + * @param value + * @return + */ + void SetAttribute(nostd::string_view key, const common::AttributeValue &value) noexcept override + { + // TODO: not implemented + UNREFERENCED_PARAMETER(key); + UNREFERENCED_PARAMETER(value); + }; - // When a new Span is started, the current span automatically becomes its parent. - GetOption(options, "enableAutoParent", config_.enableAutoParent, false); + /** + * @brief Update Span name. + * + * NOTE: this method is a no-op for streaming implementation. + * We cannot change the Span name after it started streaming. + * + * @param name + * @return + */ + void UpdateName(nostd::string_view) noexcept override + { + // We can't do that! + // name_ = name; + } - // Determines what encoding to use for ETW events: TraceLogging Dynamic, MsgPack, XML, etc. - config_.encoding = GetEncoding(options); - } + /** + * @brief End Span. + * @param EndSpanOptions + * @return + */ + void End(const trace::EndSpanOptions &options = {}) noexcept override + { + end_time_ = std::chrono::system_clock::now(); - TracerProvider() : trace::TracerProvider() + if (!has_ended_.exchange(true)) { - config_.enableTraceId = true; - config_.enableSpanId = true; - config_.enableActivityId = false; - config_.enableActivityTracking = false; - config_.enableRelatedActivityId = false; - config_.enableAutoParent = false; - config_.encoding = ETWProvider::EventFormat::ETW_MANIFEST; + owner_.EndSpan(*this, parent_, options); } + } - /** - * @brief Obtain ETW Tracer. - * @param name ProviderId (instrumentation name) - Name or GUID - * - * @param args Additional arguments that controls `codec` of the provider. - * Possible values are: - * - "ETW" - 'classic' Trace Logging Dynamic manifest ETW events. - * - "MSGPACK" - MessagePack-encoded binary payload ETW events. - * - "XML" - XML events (reserved for future use) - * @return - */ - nostd::shared_ptr GetTracer(nostd::string_view name, - nostd::string_view args = "") override - { - UNREFERENCED_PARAMETER(args); - ETWProvider::EventFormat evtFmt = config_.encoding; - return nostd::shared_ptr{new (std::nothrow) Tracer(*this, name, evtFmt)}; - } - }; + /** + * @brief Obtain SpanContext + * @return + */ + trace::SpanContext GetContext() const noexcept override { return context_; } + + /** + * @brief Check if Span is recording data. + * @return + */ + bool IsRecording() const noexcept override + { + // For streaming implementation this should return the state of ETW Listener. + // In certain unprivileged environments, ex. containers, it is impossible + // to determine if a listener is registered. Thus, we always return true. + return true; + } + + virtual void SetToken(nostd::unique_ptr &&token) noexcept + { + // TODO: not implemented + UNREFERENCED_PARAMETER(token); + } + + /// + /// Get Owner tracer of this Span + /// + /// + trace::Tracer &tracer() const noexcept { return this->owner_; }; +}; + +/** + * @brief ETW TracerProvider + */ +class TracerProvider : public trace::TracerProvider +{ +public: + /** + * @brief TracerProvider options supplied during initialization. + */ + TracerProviderConfiguration config_; + + /** + * @brief Construct instance of TracerProvider with given options + * @param options Configuration options + */ + TracerProvider(TracerProviderOptions options) : trace::TracerProvider() + { + // 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); + + // Backwards-compatibility option that allows to reuse ETW-specific parenting described here: + // https://docs.microsoft.com/en-us/uwp/api/windows.foundation.diagnostics.loggingoptions.relatedactivityid + // https://docs.microsoft.com/en-us/windows/win32/api/evntprov/nf-evntprov-eventwritetransfer + + // Emit separate events compatible with TraceLogging Activity/Start and Activity/Stop + // format for every Span emitted. + GetOption(options, "enableActivityTracking", config_.enableActivityTracking, false); + + // Map current `SpanId` to ActivityId - GUID that uniquely identifies this activity. If NULL, + // ETW gets the identifier from the thread local storage. For details on getting this + // identifier, see EventActivityIdControl. + GetOption(options, "enableActivityId", config_.enableActivityId, false); + + // Map parent `SpanId` to RelatedActivityId - Activity identifier from the previous + // component. Use this parameter to link your component's events to the previous component's + // events. + GetOption(options, "enableRelatedActivityId", config_.enableRelatedActivityId, false); + + // When a new Span is started, the current span automatically becomes its parent. + GetOption(options, "enableAutoParent", config_.enableAutoParent, false); + + // Determines what encoding to use for ETW events: TraceLogging Dynamic, MsgPack, XML, etc. + config_.encoding = GetEncoding(options); + } + + TracerProvider() : trace::TracerProvider() + { + config_.enableTraceId = true; + config_.enableSpanId = true; + config_.enableActivityId = false; + config_.enableActivityTracking = false; + config_.enableRelatedActivityId = false; + config_.enableAutoParent = false; + config_.encoding = ETWProvider::EventFormat::ETW_MANIFEST; + } + + /** + * @brief Obtain ETW Tracer. + * @param name ProviderId (instrumentation name) - Name or GUID + * + * @param args Additional arguments that controls `codec` of the provider. + * Possible values are: + * - "ETW" - 'classic' Trace Logging Dynamic manifest ETW events. + * - "MSGPACK" - MessagePack-encoded binary payload ETW events. + * - "XML" - XML events (reserved for future use) + * @return + */ + nostd::shared_ptr GetTracer(nostd::string_view name, + nostd::string_view args = "") override + { + UNREFERENCED_PARAMETER(args); + ETWProvider::EventFormat evtFmt = config_.encoding; + return nostd::shared_ptr{new (std::nothrow) Tracer(*this, name, evtFmt)}; + } +}; } // namespace etw -} // namespace etw +} // namespace exporter OPENTELEMETRY_END_NAMESPACE From 9295cf97170f9931af9279ffc4d522c213d47d0b Mon Sep 17 00:00:00 2001 From: Lalit Date: Thu, 2 Sep 2021 18:01:50 -0700 Subject: [PATCH 4/7] fix namespace --- .../etw/include/opentelemetry/exporters/etw/etw_tracer.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h index d4229486d6..47e6fa79c4 100644 --- a/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h +++ b/exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h @@ -569,10 +569,10 @@ class Tracer : public trace::Tracer // Parent Context: // - either use current span // - or attach to parent SpanContext specified in options - trace_api::SpanContext parentContext = GetCurrentSpan()->GetContext(); - if (nostd::holds_alternative(options.parent)) + trace::SpanContext parentContext = GetCurrentSpan()->GetContext(); + if (nostd::holds_alternative(options.parent)) { - auto span_context = nostd::get(options.parent); + auto span_context = nostd::get(options.parent); if (span_context.IsValid()) { parentContext = span_context; From 19887f941adba88396c8b0a45ad4d8aa7c68aa27 Mon Sep 17 00:00:00 2001 From: Lalit Date: Thu, 2 Sep 2021 18:15:30 -0700 Subject: [PATCH 5/7] namespace issue in otlp-http test --- exporters/otlp/test/otlp_http_exporter_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporters/otlp/test/otlp_http_exporter_test.cc b/exporters/otlp/test/otlp_http_exporter_test.cc index 845520db68..5940390952 100644 --- a/exporters/otlp/test/otlp_http_exporter_test.cc +++ b/exporters/otlp/test/otlp_http_exporter_test.cc @@ -222,7 +222,7 @@ TEST_F(OtlpHttpExporterTestPeer, ExportJsonIntegrationTest) child_span->End(); parent_span->End(); - nostd::get(child_span_opts.parent) + nostd::get(child_span_opts.parent) .trace_id() .ToLowerBase16(MakeSpan(trace_id_hex)); report_trace_id.assign(trace_id_hex, sizeof(trace_id_hex)); From bd0cdefdadbed93160f07cc85322d0707b94bd9b Mon Sep 17 00:00:00 2001 From: Lalit Date: Thu, 2 Sep 2021 18:24:10 -0700 Subject: [PATCH 6/7] namespace issue in otlp-http test - 2 --- exporters/otlp/test/otlp_http_exporter_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/exporters/otlp/test/otlp_http_exporter_test.cc b/exporters/otlp/test/otlp_http_exporter_test.cc index 5940390952..daab046439 100644 --- a/exporters/otlp/test/otlp_http_exporter_test.cc +++ b/exporters/otlp/test/otlp_http_exporter_test.cc @@ -284,7 +284,7 @@ TEST_F(OtlpHttpExporterTestPeer, ExportBinaryIntegrationTest) child_span->End(); parent_span->End(); - nostd::get(child_span_opts.parent) + nostd::get(child_span_opts.parent) .trace_id() .CopyBytesTo(MakeSpan(trace_id_binary)); report_trace_id.assign(reinterpret_cast(trace_id_binary), sizeof(trace_id_binary)); From 1bf59ef38f906e296ef07a61d677720cdce8994c Mon Sep 17 00:00:00 2001 From: Lalit Date: Fri, 3 Sep 2021 09:34:02 -0700 Subject: [PATCH 7/7] resolve merge conflict --- sdk/src/trace/tracer.cc | 4 ++-- sdk/test/trace/tracer_test.cc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index 6aa5c37d86..be5e5f7d96 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -5,7 +5,7 @@ #include "opentelemetry/context/runtime_context.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/sdk/common/atomic_shared_ptr.h" -#include "opentelemetry/trace/propagation/detail/context.h" +#include "opentelemetry/trace/context.h" #include "opentelemetry/version.h" #include "src/trace/span.h" @@ -41,7 +41,7 @@ nostd::shared_ptr Tracer::StartSpan( { auto context = nostd::get(options.parent); // fetch span context from parent span stored in the context - auto span_context = opentelemetry::trace::propagation::GetSpan(context)->GetContext(); + auto span_context = opentelemetry::trace::GetSpan(context)->GetContext(); if (span_context.IsValid()) { parent_context = span_context; diff --git a/sdk/test/trace/tracer_test.cc b/sdk/test/trace/tracer_test.cc index dbf364fe5c..94e57a4cd7 100644 --- a/sdk/test/trace/tracer_test.cc +++ b/sdk/test/trace/tracer_test.cc @@ -9,7 +9,7 @@ #include "opentelemetry/sdk/trace/samplers/parent.h" #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/span_data.h" -#include "opentelemetry/trace/propagation/detail/context.h" +#include "opentelemetry/trace/context.h" #include @@ -634,12 +634,12 @@ TEST(Tracer, ExpectParentAsContext) auto span_first = tracer->StartSpan("span 1"); opentelemetry::context::Context c1; - auto c2 = trace_api::propagation::SetSpan(c1, span_first); + auto c2 = trace_api::SetSpan(c1, span_first); trace_api::StartSpanOptions options; options.parent = c2; auto span_second = tracer->StartSpan("span 2", options); - auto c3 = trace_api::propagation::SetSpan(c2, span_second); + auto c3 = trace_api::SetSpan(c2, span_second); options.parent = c3; auto span_third = tracer->StartSpan("span 3", options);