Skip to content

Commit

Permalink
[ETW Exporter] Tail based sampling support (open-telemetry#1780)
Browse files Browse the repository at this point in the history
  • Loading branch information
lalitb authored and yxue committed Dec 5, 2022
1 parent a4c5e0b commit 5d8e1bc
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 15 deletions.
10 changes: 10 additions & 0 deletions exporters/etw/include/opentelemetry/exporters/etw/etw_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

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

OPENTELEMETRY_BEGIN_NAMESPACE
Expand Down Expand Up @@ -166,6 +167,15 @@ sdk::trace::Sampler &GetSampler(T &t)
return *t.sampler_;
}

/**
* @brief Utility function to obtain etw::TracerProvider.tail_sampler_
*/
template <class T>
TailSampler &GetTailSampler(T &t)
{
return *t.tail_sampler_;
}

/**
* @brief Utility template to convert SpanId or TraceId to hex.
* @param id - value of SpanId or TraceId
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/trace/span.h"

#pragma once
OPENTELEMETRY_BEGIN_NAMESPACE
namespace exporter
{
namespace etw
{

class TailSampler
{
public:
// convert to etw span if required for getters on span.
// auto etw_span = static_cast<const opentelemetry::exporter::etw::Span*>(&span);
// Decision based on
// Span::GetStatus()
// Span::GetProperties()
// Span::GetContext()
virtual opentelemetry::sdk::trace::SamplingResult ShouldSample(
const opentelemetry::trace::Span &span) noexcept = 0;
};

class AlwaysOnTailSampler : public TailSampler
{
public:
opentelemetry::sdk::trace::SamplingResult ShouldSample(
const opentelemetry::trace::Span &span) noexcept override
{
return {opentelemetry::sdk::trace::Decision::RECORD_AND_SAMPLE};
}
};

} // namespace etw
} // namespace exporter
OPENTELEMETRY_END_NAMESPACE
63 changes: 49 additions & 14 deletions exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "opentelemetry/exporters/etw/etw_properties.h"
#include "opentelemetry/exporters/etw/etw_provider.h"
#include "opentelemetry/exporters/etw/etw_random_id_generator.h"
#include "opentelemetry/exporters/etw/etw_tail_sampler.h"
#include "opentelemetry/exporters/etw/utils.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand Down Expand Up @@ -235,9 +236,17 @@ class Tracer : public opentelemetry::trace::Tracer,
const opentelemetry::trace::Span *parentSpan = nullptr,
const opentelemetry::trace::EndSpanOptions & = {})
{
const auto &cfg = GetConfiguration(tracerProvider_);
const auto &cfg = GetConfiguration(tracerProvider_);
const auto &tail_sampler = GetTailSampler(tracerProvider_);
const opentelemetry::trace::Span &spanBase =
reinterpret_cast<const opentelemetry::trace::Span &>(span);

// Sample span based on the decision of tail based sampler
auto sampling_result = const_cast<TailSampler *>(&tail_sampler)->ShouldSample(spanBase);
if (!sampling_result.IsRecording() || !sampling_result.IsSampled())
{
return;
}
auto spanContext = spanBase.GetContext();

// Populate Span with presaved attributes
Expand Down Expand Up @@ -375,17 +384,23 @@ class Tracer : public opentelemetry::trace::Tracer,
const opentelemetry::trace::SpanContextKeyValueIterable &links,
const opentelemetry::trace::StartSpanOptions &options = {}) noexcept override
{
#ifdef OPENTELEMETRY_RTTI_ENABLED
// If RTTI is enabled by compiler, the below code modifies the attributes object passed as arg,
// which is sometime not desirable, set OPENTELEMETRY_NOT_USE_RTTI in application
// to avoid using RTTI in that case in below set of code.
#if !defined OPENTELEMETRY_RTTI_ENABLED || defined OPENTELEMETRY_NOT_USE_RTTI
Properties evtCopy = attributes;
return StartSpan(name, evtCopy, links, options);
#else // OPENTELEMETRY_RTTI_ENABLED is defined
common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
Properties *evt = dynamic_cast<Properties *>(&attribs);
if (evt != nullptr)
{
// Pass as a reference to original modifyable collection without creating a copy
return StartSpan(name, *evt, links, options);
}
#endif
Properties evtCopy = attributes;
return StartSpan(name, evtCopy, links, options);
#endif
}

/**
Expand Down Expand Up @@ -557,18 +572,24 @@ class Tracer : public opentelemetry::trace::Tracer,
common::SystemTimestamp timestamp,
const common::KeyValueIterable &attributes) noexcept
{
#ifdef OPENTELEMETRY_RTTI_ENABLED
// If RTTI is enabled by compiler, the below code modifies the attributes object passed as arg,
// which is sometime not desirable, set OPENTELEMETRY_NOT_USE_RTTI in application
// to avoid using RTTI in that case in below set of code.
#if !defined OPENTELEMETRY_RTTI_ENABLED || defined OPENTELEMETRY_NOT_USE_RTTI
// Pass a copy converted to Properties object on stack
Properties evtCopy = attributes;
return AddEvent(span, name, timestamp, evtCopy);
#else // OPENTELEMETRY_RTTI_ENABLED is defined
common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
Properties *evt = dynamic_cast<Properties *>(&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);
#endif
}

/**
Expand Down Expand Up @@ -826,6 +847,8 @@ class Span : public opentelemetry::trace::Span
status_description_ = description.data();
}

opentelemetry::trace::StatusCode GetStatus() { return status_code_; }

void SetAttributes(Properties attributes) { attributes_ = attributes; }

/**
Expand Down Expand Up @@ -932,6 +955,12 @@ class TracerProvider : public opentelemetry::trace::TracerProvider
*/
std::unique_ptr<sdk::trace::Sampler> sampler_;

/**
* @brief Sampler configured
*
*/
std::unique_ptr<opentelemetry::exporter::etw::TailSampler> tail_sampler_;

/**
* @brief IdGenerator for trace_id and span_id
*
Expand All @@ -942,15 +971,19 @@ class TracerProvider : public opentelemetry::trace::TracerProvider
* @brief Construct instance of TracerProvider with given options
* @param options Configuration options
*/
TracerProvider(TelemetryProviderOptions options,
std::unique_ptr<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()))
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()),
std::unique_ptr<opentelemetry::exporter::etw::TailSampler> tail_sampler =
std::unique_ptr<opentelemetry::exporter::etw::TailSampler>(new AlwaysOnTailSampler()))
: opentelemetry::trace::TracerProvider(),
sampler_{std::move(sampler)},
id_generator_{std::move(id_generator)}
id_generator_{std::move(id_generator)},
tail_sampler_{std::move(tail_sampler)}
{
// By default we ensure that all events carry their with TraceId and SpanId
GetOption(options, "enableTraceId", config_.enableTraceId, true);
Expand Down Expand Up @@ -985,7 +1018,9 @@ class TracerProvider : public opentelemetry::trace::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())}
new sdk::trace::ETWRandomIdGenerator())},
tail_sampler_{
std::unique_ptr<opentelemetry::exporter::etw::TailSampler>(new AlwaysOnTailSampler())}
{
config_.enableTraceId = true;
config_.enableSpanId = true;
Expand Down
31 changes: 30 additions & 1 deletion exporters/etw/test/etw_tracer_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
# include <gtest/gtest.h>
# include <map>
# include <string>

# include "opentelemetry//sdk/trace/sampler.h"
# include "opentelemetry/exporters/etw/etw_tracer_exporter.h"
# include "opentelemetry/sdk/trace/samplers/always_off.h"
Expand Down Expand Up @@ -78,6 +77,16 @@ class MockSampler : public sdk::trace::Sampler
std::shared_ptr<Sampler> delegate_sampler_;
};

class AlwaysOffTailSampler : public TailSampler
{
public:
opentelemetry::sdk::trace::SamplingResult ShouldSample(
const opentelemetry::trace::Span &span) noexcept override
{
return {opentelemetry::sdk::trace::Decision::DROP};
}
};

/* clang-format off */
TEST(ETWTracer, TracerCheck)
{
Expand Down Expand Up @@ -459,6 +468,26 @@ TEST(ETWTracer, AlwayOffSampler)
EXPECT_EQ(span->GetContext().IsSampled(), false);
}

TEST(ETWTracer, AlwayOffTailSampler)
{
std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
std::unique_ptr<sdk::trace::Sampler> always_on{new sdk::trace::AlwaysOnSampler()};
sdk::trace::IdGenerator *id_generator = new MockIdGenerator();
std::unique_ptr<TailSampler> always_off_tail{new AlwaysOffTailSampler()};
exporter::etw::TracerProvider tp
({
{"enableTraceId", true},
{"enableSpanId", true},
{"enableActivityId", true},
{"enableRelatedActivityId", true},
{"enableAutoParent", true}
},
std::move(always_on),
std::unique_ptr<sdk::trace::IdGenerator>(id_generator),
std::move(always_off_tail));
auto tracer = tp.GetTracer(providerName);
}

TEST(ETWTracer, CustomIdGenerator)
{
std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
Expand Down

0 comments on commit 5d8e1bc

Please sign in to comment.