Skip to content

Commit 2142d00

Browse files
authored
[ETW Exporter] - Add Trace flags in SpanContext (#1618)
1 parent ed9f583 commit 2142d00

File tree

2 files changed

+113
-40
lines changed

2 files changed

+113
-40
lines changed

exporters/etw/include/opentelemetry/exporters/etw/etw_tracer.h

+44-39
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ class Span;
6464
template <class SpanType, class TracerType>
6565
SpanType *new_span(TracerType *objPtr,
6666
nostd::string_view name,
67-
const opentelemetry::trace::StartSpanOptions &options)
67+
const opentelemetry::trace::StartSpanOptions &options,
68+
std::unique_ptr<opentelemetry::trace::SpanContext> spanContext)
6869
{
69-
return new (std::nothrow) SpanType{*objPtr, name, options};
70+
return new (std::nothrow) SpanType{*objPtr, name, options, std::move(spanContext)};
7071
}
7172

7273
/**
@@ -374,30 +375,6 @@ class Tracer : public opentelemetry::trace::Tracer,
374375
const opentelemetry::trace::SpanContextKeyValueIterable &links,
375376
const opentelemetry::trace::StartSpanOptions &options = {}) noexcept override
376377
{
377-
const auto &cfg = GetConfiguration(tracerProvider_);
378-
379-
// Parent Context:
380-
// - either use current span
381-
// - or attach to parent SpanContext specified in options
382-
opentelemetry::trace::SpanContext parentContext = GetCurrentSpan()->GetContext();
383-
if (nostd::holds_alternative<opentelemetry::trace::SpanContext>(options.parent))
384-
{
385-
auto span_context = nostd::get<opentelemetry::trace::SpanContext>(options.parent);
386-
if (span_context.IsValid())
387-
{
388-
parentContext = span_context;
389-
}
390-
}
391-
auto sampling_result =
392-
GetSampler(tracerProvider_)
393-
.ShouldSample(parentContext, traceId_, name, options.kind, attributes, links);
394-
if (sampling_result.decision == sdk::trace::Decision::DROP)
395-
{
396-
static nostd::shared_ptr<trace::Span> noop_span(
397-
new trace::NoopSpan{this->shared_from_this()});
398-
return noop_span;
399-
}
400-
401378
#ifdef OPENTELEMETRY_RTTI_ENABLED
402379
common::KeyValueIterable &attribs = const_cast<common::KeyValueIterable &>(attributes);
403380
Properties *evt = dynamic_cast<Properties *>(&attribs);
@@ -433,12 +410,42 @@ class Tracer : public opentelemetry::trace::Tracer,
433410
opentelemetry::trace::SpanContext parentContext = GetCurrentSpan()->GetContext();
434411
if (nostd::holds_alternative<opentelemetry::trace::SpanContext>(options.parent))
435412
{
436-
auto span_context = nostd::get<opentelemetry::trace::SpanContext>(options.parent);
437-
if (span_context.IsValid())
413+
auto spanContext = nostd::get<opentelemetry::trace::SpanContext>(options.parent);
414+
if (spanContext.IsValid())
438415
{
439-
parentContext = span_context;
416+
parentContext = spanContext;
440417
}
441418
}
419+
auto traceId = parentContext.IsValid() ? parentContext.trace_id() : traceId_;
420+
421+
// Sampling based on attributes is not supported for now, so passing empty below.
422+
std::map<std::string, int> emptyAttributes = {{}};
423+
opentelemetry::sdk::trace::SamplingResult sampling_result =
424+
GetSampler(tracerProvider_)
425+
.ShouldSample(parentContext, traceId, name, options.kind,
426+
opentelemetry::common::KeyValueIterableView<std::map<std::string, int>>(
427+
emptyAttributes),
428+
links);
429+
430+
opentelemetry::trace::TraceFlags traceFlags =
431+
sampling_result.decision == opentelemetry::sdk::trace::Decision::DROP
432+
? opentelemetry::trace::TraceFlags{}
433+
: opentelemetry::trace::TraceFlags{opentelemetry::trace::TraceFlags::kIsSampled};
434+
435+
auto spanContext =
436+
std::unique_ptr<opentelemetry::trace::SpanContext>(new opentelemetry::trace::SpanContext(
437+
traceId, GetIdGenerator(tracerProvider_).GenerateSpanId(), traceFlags, false,
438+
sampling_result.trace_state
439+
? sampling_result.trace_state
440+
: parentContext.IsValid() ? parentContext.trace_state()
441+
: opentelemetry::trace::TraceState::GetDefault()));
442+
443+
if (sampling_result.decision == sdk::trace::Decision::DROP)
444+
{
445+
auto noopSpan = nostd::shared_ptr<trace::Span>{
446+
new (std::nothrow) trace::NoopSpan(this->shared_from_this(), std::move(spanContext))};
447+
return noopSpan;
448+
}
442449

443450
// Populate Etw.RelatedActivityId at envelope level if enabled
444451
GUID RelatedActivityId;
@@ -456,11 +463,9 @@ class Tracer : public opentelemetry::trace::Tracer,
456463

457464
// This template pattern allows us to forward-declare the etw::Span,
458465
// create an instance of it, then assign it to tracer::Span result.
459-
auto currentSpan = new_span<Span, Tracer>(this, name, options);
466+
auto currentSpan = new_span<Span, Tracer>(this, name, options, std::move(spanContext));
460467
nostd::shared_ptr<opentelemetry::trace::Span> result = to_span_ptr<Span>(currentSpan);
461468

462-
auto spanContext = result->GetContext();
463-
464469
// Decorate with additional standard fields
465470
std::string eventName = name.data();
466471

@@ -475,13 +480,13 @@ class Tracer : public opentelemetry::trace::Tracer,
475480
{
476481
evt[ETW_FIELD_SPAN_PARENTID] = ToLowerBase16(parentContext.span_id());
477482
}
478-
evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(spanContext.span_id());
483+
evt[ETW_FIELD_SPAN_ID] = ToLowerBase16(result.get()->GetContext().span_id());
479484
}
480485

481486
// Populate Etw.Payload["TraceId"] attribute
482487
if (cfg.enableTraceId)
483488
{
484-
evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(spanContext.trace_id());
489+
evt[ETW_FIELD_TRACE_ID] = ToLowerBase16(result.get()->GetContext().trace_id());
485490
}
486491

487492
// Populate Etw.ActivityId at envelope level if enabled
@@ -705,7 +710,7 @@ class Span : public opentelemetry::trace::Span
705710
*/
706711
Span *GetParent() const { return parent_; }
707712

708-
opentelemetry::trace::SpanContext context_;
713+
std::unique_ptr<opentelemetry::trace::SpanContext> context_;
709714

710715
public:
711716
/**
@@ -759,13 +764,13 @@ class Span : public opentelemetry::trace::Span
759764
Span(Tracer &owner,
760765
nostd::string_view name,
761766
const opentelemetry::trace::StartSpanOptions &options,
767+
std::unique_ptr<opentelemetry::trace::SpanContext> spanContext,
762768
Span *parent = nullptr) noexcept
763769
: opentelemetry::trace::Span(),
764770
start_time_(std::chrono::system_clock::now()),
765771
owner_(owner),
766-
parent_(parent),
767-
context_{owner.traceId_, GetIdGenerator(owner.tracerProvider_).GenerateSpanId(),
768-
opentelemetry::trace::TraceFlags{0}, false}
772+
context_(std::move(spanContext)),
773+
parent_(parent)
769774
{
770775
name_ = name;
771776
UNREFERENCED_PARAMETER(options);
@@ -883,7 +888,7 @@ class Span : public opentelemetry::trace::Span
883888
* @brief Obtain SpanContext
884889
* @return
885890
*/
886-
opentelemetry::trace::SpanContext GetContext() const noexcept override { return context_; }
891+
opentelemetry::trace::SpanContext GetContext() const noexcept override { return *context_.get(); }
887892

888893
/**
889894
* @brief Check if Span is recording data.

exporters/etw/test/etw_tracer_test.cc

+69-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
# include <map>
88
# include <string>
99

10+
# include "opentelemetry//sdk/trace/sampler.h"
1011
# include "opentelemetry/exporters/etw/etw_tracer_exporter.h"
1112
# include "opentelemetry/sdk/trace/samplers/always_off.h"
1213
# include "opentelemetry/sdk/trace/simple_processor.h"
1314

1415
using namespace OPENTELEMETRY_NAMESPACE;
1516

1617
using namespace opentelemetry::exporter::etw;
18+
using namespace opentelemetry::sdk::trace;
1719

1820
const char *kGlobalProviderName = "OpenTelemetry-ETW-TLD";
1921

@@ -40,6 +42,42 @@ class MockIdGenerator : public sdk::trace::IdGenerator
4042
uint8_t buf_trace[16] = {1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1};
4143
};
4244

45+
/* A Custom Sampler, implementing parent based sampler*/
46+
class MockSampler : public sdk::trace::Sampler
47+
{
48+
public:
49+
MockSampler(std::shared_ptr<Sampler> delegate_sampler) noexcept
50+
: delegate_sampler_(delegate_sampler)
51+
{}
52+
sdk::trace::SamplingResult ShouldSample(
53+
const trace_api::SpanContext &parent_context,
54+
trace_api::TraceId trace_id,
55+
nostd::string_view name,
56+
trace_api::SpanKind span_kind,
57+
const opentelemetry::common::KeyValueIterable &attributes,
58+
const trace_api::SpanContextKeyValueIterable &links) noexcept
59+
{
60+
if (!parent_context.IsValid())
61+
{
62+
// If no parent (root span) exists returns the result of the delegateSampler
63+
return delegate_sampler_->ShouldSample(parent_context, trace_id, name, span_kind, attributes,
64+
links);
65+
}
66+
67+
// If parent exists:
68+
if (parent_context.IsSampled())
69+
{
70+
return {Decision::RECORD_AND_SAMPLE, nullptr, parent_context.trace_state()};
71+
}
72+
return {Decision::DROP, nullptr, parent_context.trace_state()};
73+
}
74+
75+
nostd::string_view GetDescription() const noexcept { return "Custom Sampler"; }
76+
77+
private:
78+
std::shared_ptr<Sampler> delegate_sampler_;
79+
};
80+
4381
/* clang-format off */
4482
TEST(ETWTracer, TracerCheck)
4583
{
@@ -417,7 +455,8 @@ TEST(ETWTracer, AlwayOffSampler)
417455
std::move(always_off));
418456
auto tracer = tp.GetTracer(providerName);
419457
auto span = tracer->StartSpan("span_off");
420-
EXPECT_EQ(span->GetContext().IsValid(), false);
458+
EXPECT_EQ(span->GetContext().IsValid(), true);
459+
EXPECT_EQ(span->GetContext().IsSampled(), false);
421460
}
422461

423462
TEST(ETWTracer, CustomIdGenerator)
@@ -440,6 +479,35 @@ TEST(ETWTracer, CustomIdGenerator)
440479
EXPECT_EQ(span->GetContext().trace_id(), id_generator->GenerateTraceId());
441480
}
442481

482+
TEST(ETWTracer, CustomSampler)
483+
{
484+
std::string providerName = kGlobalProviderName; // supply unique instrumentation name here
485+
auto parent_off = std::unique_ptr<Sampler>(new MockSampler(std::make_shared<AlwaysOnSampler>()));
486+
exporter::etw::TracerProvider tp
487+
({
488+
{"enableTraceId", true},
489+
{"enableSpanId", true},
490+
{"enableActivityId", true},
491+
{"enableRelatedActivityId", true},
492+
{"enableAutoParent", true}
493+
},
494+
std::move(parent_off));
495+
auto tracer = tp.GetTracer(providerName);
496+
{
497+
auto span = tracer->StartSpan("span_off");
498+
EXPECT_EQ(span->GetContext().IsValid(), true);
499+
EXPECT_EQ(span->GetContext().IsSampled(), true);
500+
auto scope = tracer->WithActiveSpan(span);
501+
auto trace_id = span->GetContext().trace_id();
502+
{
503+
auto child_span = tracer->StartSpan("span on");
504+
EXPECT_EQ(child_span->GetContext().IsValid(), true);
505+
EXPECT_EQ(child_span->GetContext().IsSampled(), true);
506+
EXPECT_EQ(child_span->GetContext().trace_id(), trace_id);
507+
}
508+
}
509+
}
510+
443511
/* clang-format on */
444512

445513
#endif

0 commit comments

Comments
 (0)