Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ETW Exporter - Add support for Sampler and ID Generator #1547

Merged
merged 18 commits into from
Aug 24, 2022
20 changes: 20 additions & 0 deletions exporters/etw/include/opentelemetry/exporters/etw/etw_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/trace/span_id.h"

#include "opentelemetry//sdk/trace/sampler.h"
#include "opentelemetry/exporters/etw/etw_provider.h"
#include "opentelemetry/sdk/trace/id_generator.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace exporter
Expand Down Expand Up @@ -146,6 +148,24 @@ TelemetryProviderConfiguration &GetConfiguration(T &t)
return t.config_;
}

/**
* @brief Utility function to obtain etw::TracerProvider.id_generator_
*/
template <class T>
sdk::trace::IdGenerator &GetIdGenerator(T &t)
{
return *t.id_generator_;
}

/**
* @brief Utility function to obtain etw::TracerProvider.sampler_
*/
template <class T>
sdk::trace::Sampler &GetSampler(T &t)
{
return *t.sampler_;
}

/**
* @brief Utility template to convert SpanId or TraceId to hex.
* @param id - value of SpanId or TraceId
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once
#include "opentelemetry/sdk/trace/id_generator.h"
#include "opentelemetry/version.h"

#ifdef _WIN32
# include "Windows.h"
#endif

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace trace
{

class ETWRandomIdGenerator : public IdGenerator
{

opentelemetry::trace::SpanId GenerateSpanId() noexcept override
{
GUID span_id;
// Generate random GUID
CoCreateGuid(&span_id);
const auto *spanIdPtr = reinterpret_cast<const uint8_t *>(std::addressof(span_id));

// Populate SpanId with that GUID
nostd::span<const uint8_t, opentelemetry::trace::SpanId::kSize> spanIdBytes(
spanIdPtr, spanIdPtr + opentelemetry::trace::SpanId::kSize);
return opentelemetry::trace::SpanId(spanIdBytes);
}

opentelemetry::trace::TraceId GenerateTraceId() noexcept override
{
GUID trace_id;
CoCreateGuid(&trace_id);
// Populate TraceId of the Tracer with the above GUID
const auto *traceIdPtr = reinterpret_cast<const uint8_t *>(std::addressof(trace_id));
nostd::span<const uint8_t, opentelemetry::trace::TraceId::kSize> traceIdBytes(
traceIdPtr, traceIdPtr + opentelemetry::trace::TraceId::kSize);
return opentelemetry::trace::TraceId(traceIdBytes);
}
};
} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
97 changes: 62 additions & 35 deletions exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,21 @@

#include "opentelemetry/common/key_value_iterable_view.h"

#include "opentelemetry/trace/span.h"
#include "opentelemetry/trace/noop.h"
//#include "opentelemetry/trace/span.h"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this neede?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not required. Removed now.

#include "opentelemetry/trace/span_context_kv_iterable_view.h"
#include "opentelemetry/trace/span_id.h"
#include "opentelemetry/trace/trace_id.h"
#include "opentelemetry/trace/tracer_provider.h"

#include "opentelemetry/sdk/trace/exporter.h"
#include "opentelemetry/sdk/trace/samplers/always_on.h"

#include "opentelemetry/exporters/etw/etw_config.h"
#include "opentelemetry/exporters/etw/etw_fields.h"
#include "opentelemetry/exporters/etw/etw_properties.h"
#include "opentelemetry/exporters/etw/etw_provider.h"
#include "opentelemetry/exporters/etw/etw_random_id_generator.h"
#include "opentelemetry/exporters/etw/utils.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand Down Expand Up @@ -154,7 +157,9 @@ void UpdateStatus(T &t, Properties &props)
/**
* @brief Tracer class that allows to send spans to ETW Provider.
*/
class Tracer : public opentelemetry::trace::Tracer

class Tracer : public opentelemetry::trace::Tracer,
public std::enable_shared_from_this<trace::Tracer>
{

/**
Expand Down Expand Up @@ -353,14 +358,7 @@ class Tracer : public opentelemetry::trace::Tracer
encoding(encoding),
provHandle(initProvHandle())
{
// Generate random GUID
GUID trace_id;
CoCreateGuid(&trace_id);
// Populate TraceId of the Tracer with the above GUID
const auto *traceIdPtr = reinterpret_cast<const uint8_t *>(std::addressof(trace_id));
nostd::span<const uint8_t, opentelemetry::trace::TraceId::kSize> traceIdBytes(
traceIdPtr, traceIdPtr + opentelemetry::trace::TraceId::kSize);
traceId_ = opentelemetry::trace::TraceId(traceIdBytes);
traceId_ = GetIdGenerator(tracerProvider_).GenerateTraceId();
}

/**
Expand All @@ -377,6 +375,30 @@ class Tracer : public opentelemetry::trace::Tracer
const opentelemetry::trace::SpanContextKeyValueIterable &links,
const opentelemetry::trace::StartSpanOptions &options = {}) noexcept override
{
const auto &cfg = GetConfiguration(tracerProvider_);

// Parent Context:
// - either use current span
// - or attach to parent SpanContext specified in options
opentelemetry::trace::SpanContext parentContext = GetCurrentSpan()->GetContext();
if (nostd::holds_alternative<opentelemetry::trace::SpanContext>(options.parent))
{
auto span_context = nostd::get<opentelemetry::trace::SpanContext>(options.parent);
if (span_context.IsValid())
{
parentContext = span_context;
}
}
auto sampling_result =
GetSampler(tracerProvider_)
.ShouldSample(parentContext, traceId_, name, options.kind, attributes, links);
if (sampling_result.decision == sdk::trace::Decision::DROP)
{
static nostd::shared_ptr<trace::Span> noop_span(
new trace::NoopSpan{this->shared_from_this()});
return noop_span;
}

#ifdef OPENTELEMETRY_RTTI_ENABLED
common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
Properties *evt = dynamic_cast<Properties *>(&attribs);
Expand Down Expand Up @@ -686,27 +708,6 @@ class Span : public opentelemetry::trace::Span

opentelemetry::trace::SpanContext context_;

const opentelemetry::trace::SpanContext CreateContext()
{
GUID activity_id;
// Generate random GUID
CoCreateGuid(&activity_id);
const auto *activityIdPtr = reinterpret_cast<const uint8_t *>(std::addressof(activity_id));

// Populate SpanId with that GUID
nostd::span<const uint8_t, opentelemetry::trace::SpanId::kSize> spanIdBytes(
activityIdPtr, activityIdPtr + opentelemetry::trace::SpanId::kSize);
const opentelemetry::trace::SpanId spanId(spanIdBytes);

// Inherit trace_id from Tracer
const opentelemetry::trace::TraceId traceId{owner_.trace_id()};
// TODO: TraceFlags are not supported by ETW exporter.
const opentelemetry::trace::TraceFlags flags{0};
// TODO: Remote parent is not supported by ETW exporter.
const bool hasRemoteParent = false;
return opentelemetry::trace::SpanContext{traceId, spanId, flags, hasRemoteParent};
}

public:
/**
* @brief Update Properties object with current Span status
Expand Down Expand Up @@ -764,7 +765,8 @@ class Span : public opentelemetry::trace::Span
start_time_(std::chrono::system_clock::now()),
owner_(owner),
parent_(parent),
context_(CreateContext())
context_{owner.traceId_, GetIdGenerator(owner.tracerProvider_).GenerateSpanId(),
opentelemetry::trace::TraceFlags{0}, false}
{
name_ = name;
UNREFERENCED_PARAMETER(options);
Expand Down Expand Up @@ -920,11 +922,31 @@ class TracerProvider : public opentelemetry::trace::TracerProvider
*/
TelemetryProviderConfiguration config_;

/**
* @brief Sampler configured
*
*/
std::unique_ptr<sdk::trace::Sampler> sampler_;

/**
* @brief IdGenerator for trace_id and span_id
*
*/
std::unique_ptr<sdk::trace::IdGenerator> id_generator_;

/**
* @brief Construct instance of TracerProvider with given options
* @param options Configuration options
*/
TracerProvider(TelemetryProviderOptions options) : opentelemetry::trace::TracerProvider()
TracerProvider(TelemetryProviderOptions options,
std::unique_ptr<sdk::trace::Sampler> sampler =
std::unique_ptr<sdk::trace::AlwaysOnSampler>(new sdk::trace::AlwaysOnSampler),
std::unique_ptr<sdk::trace::IdGenerator> id_generator =
std::unique_ptr<opentelemetry::sdk::trace::IdGenerator>(
new sdk::trace::ETWRandomIdGenerator()))
: opentelemetry::trace::TracerProvider(),
sampler_{std::move(sampler)},
id_generator_{std::move(id_generator)}
{
// By default we ensure that all events carry their with TraceId and SpanId
GetOption(options, "enableTraceId", config_.enableTraceId, true);
Expand Down Expand Up @@ -955,7 +977,11 @@ class TracerProvider : public opentelemetry::trace::TracerProvider
config_.encoding = GetEncoding(options);
}

TracerProvider() : opentelemetry::trace::TracerProvider()
TracerProvider()
: opentelemetry::trace::TracerProvider(),
sampler_{std::unique_ptr<sdk::trace::AlwaysOnSampler>(new sdk::trace::AlwaysOnSampler)},
id_generator_{std::unique_ptr<opentelemetry::sdk::trace::IdGenerator>(
new sdk::trace::ETWRandomIdGenerator())}
{
config_.enableTraceId = true;
config_.enableSpanId = true;
Expand Down Expand Up @@ -985,8 +1011,9 @@ class TracerProvider : public opentelemetry::trace::TracerProvider
UNREFERENCED_PARAMETER(args);
UNREFERENCED_PARAMETER(schema_url);
ETWProvider::EventFormat evtFmt = config_.encoding;
return nostd::shared_ptr<opentelemetry::trace::Tracer>{new (std::nothrow)
std::shared_ptr<opentelemetry::trace::Tracer> tracer{new (std::nothrow)
Tracer(*this, name, evtFmt)};
return nostd::shared_ptr<opentelemetry::trace::Tracer>{tracer};
Copy link
Member Author

@lalitb lalitb Aug 10, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ThomsonTan - Can you please review this PR when you have time? The changes were crashing earlier while creating Spans that are not sampled. The fix is to create the Tracer using std::shared_ptr, and then move/encapsulate it to nostd::shared_ptr (as done in line 1014 and 1016 above).

}
};

Expand Down
19 changes: 19 additions & 0 deletions exporters/etw/test/etw_tracer_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
# include <string>

# include "opentelemetry/exporters/etw/etw_tracer_exporter.h"
#include "opentelemetry/sdk/trace/samplers/always_off.h"
# include "opentelemetry/sdk/trace/simple_processor.h"

using namespace OPENTELEMETRY_NAMESPACE;
Expand Down Expand Up @@ -383,6 +384,24 @@ TEST(ETWTracer, GlobalSingletonTracer)
globalTracer.CloseWithMicroseconds(0);
}

TEST(ETWTracer, AlwayOffSampler)
{
std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
std::unique_ptr<sdk::trace::Sampler> always_off{new sdk::trace::AlwaysOffSampler()};
exporter::etw::TracerProvider tp
({
{"enableTraceId", true},
{"enableSpanId", true},
{"enableActivityId", true},
{"enableRelatedActivityId", true},
{"enableAutoParent", true}
},
std::move(always_off));
auto tracer = tp.GetTracer(providerName);
auto span = tracer->StartSpan("span_off");
EXPECT_EQ(span->GetContext().IsValid(), false);
}

/* clang-format on */

#endif