Skip to content

Commit

Permalink
ETW Exporter - Add support for Sampler and ID Generator (#1547)
Browse files Browse the repository at this point in the history
  • Loading branch information
lalitb authored Aug 24, 2022
1 parent 39d6a22 commit 4535347
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 36 deletions.
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
98 changes: 62 additions & 36 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,20 @@

#include "opentelemetry/common/key_value_iterable_view.h"

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

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

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

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

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

/**
Expand Down Expand Up @@ -353,14 +357,7 @@ class Tracer : public opentelemetry::trace::Tracer
encoding(encoding),
provHandle(initProvHandle())
{
// Generate random GUID
GUID trace_id;
CoCreateGuid(&trace_id);
// Populate TraceId of the Tracer with the above GUID
const auto *traceIdPtr = reinterpret_cast<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 +374,30 @@ class Tracer : public opentelemetry::trace::Tracer
const opentelemetry::trace::SpanContextKeyValueIterable &links,
const opentelemetry::trace::StartSpanOptions &options = {}) noexcept override
{
const auto &cfg = GetConfiguration(tracerProvider_);

// Parent Context:
// - either use current span
// - or attach to parent SpanContext specified in options
opentelemetry::trace::SpanContext parentContext = GetCurrentSpan()->GetContext();
if (nostd::holds_alternative<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 +707,6 @@ class Span : public opentelemetry::trace::Span

opentelemetry::trace::SpanContext context_;

const opentelemetry::trace::SpanContext CreateContext()
{
GUID activity_id;
// Generate random GUID
CoCreateGuid(&activity_id);
const auto *activityIdPtr = reinterpret_cast<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 +764,8 @@ class Span : public opentelemetry::trace::Span
start_time_(std::chrono::system_clock::now()),
owner_(owner),
parent_(parent),
context_(CreateContext())
context_{owner.traceId_, GetIdGenerator(owner.tracerProvider_).GenerateSpanId(),
opentelemetry::trace::TraceFlags{0}, false}
{
name_ = name;
UNREFERENCED_PARAMETER(options);
Expand Down Expand Up @@ -920,11 +921,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 +976,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 +1010,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)
Tracer(*this, name, evtFmt)};
std::shared_ptr<opentelemetry::trace::Tracer> tracer{new (std::nothrow)
Tracer(*this, name, evtFmt)};
return nostd::shared_ptr<opentelemetry::trace::Tracer>{tracer};
}
};

Expand Down
57 changes: 57 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 All @@ -21,6 +22,24 @@ std::string getTemporaryValue()
return std::string("Value from Temporary std::string");
}

/**
* A Mock Custom Id Generator
*/
class MockIdGenerator : public sdk::trace::IdGenerator
{
opentelemetry::trace::SpanId GenerateSpanId() noexcept override
{
return opentelemetry::trace::SpanId(buf_span);
}

opentelemetry::trace::TraceId GenerateTraceId() noexcept override
{
return opentelemetry::trace::TraceId(buf_trace);
}
uint8_t buf_span[8] = {1, 2, 3, 4, 5, 6, 7, 8};
uint8_t buf_trace[16] = {1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1};
};

/* clang-format off */
TEST(ETWTracer, TracerCheck)
{
Expand Down Expand Up @@ -383,6 +402,44 @@ TEST(ETWTracer, GlobalSingletonTracer)
globalTracer.CloseWithMicroseconds(0);
}

TEST(ETWTracer, AlwayOffSampler)
{
std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
std::unique_ptr<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);
}

TEST(ETWTracer, CustomIdGenerator)
{
std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
sdk::trace::IdGenerator *id_generator = new MockIdGenerator();
std::unique_ptr<sdk::trace::Sampler> always_on{new sdk::trace::AlwaysOnSampler()};
exporter::etw::TracerProvider tp
({
{"enableTraceId", true},
{"enableSpanId", true},
{"enableActivityId", true},
{"enableRelatedActivityId", true},
{"enableAutoParent", true}
},
std::move(always_on),
std::unique_ptr<sdk::trace::IdGenerator>(id_generator));
auto tracer = tp.GetTracer(providerName);
auto span = tracer->StartSpan("span_on");
EXPECT_EQ(span->GetContext().trace_id(), id_generator->GenerateTraceId());
}

/* clang-format on */

#endif

0 comments on commit 4535347

Please sign in to comment.