diff --git a/opencensus/trace/internal/span.cc b/opencensus/trace/internal/span.cc index b36d66e7..4342c5cc 100644 --- a/opencensus/trace/internal/span.cc +++ b/opencensus/trace/internal/span.cc @@ -94,7 +94,8 @@ class SpanGenerator { SpanContext context(trace_id, span_id, trace_options); SpanImpl* impl = nullptr; if (trace_options.IsSampled()) { - // Only Spans that are sampled are backed by a SpanImpl. + // Only Spans that started off sampled are backed by a SpanImpl. + // They can be abandoned later. impl = new SpanImpl(context, TraceConfigImpl::Get()->current_trace_params(), name, parent_span_id, has_remote_parent); @@ -216,10 +217,20 @@ void Span::End() const { } exporter::RunningSpanStoreImpl::Get()->RemoveSpan(span_impl_); exporter::LocalSpanStoreImpl::Get()->AddSpan(span_impl_); + // SpanExporterImpl checks if span_impl_ is sampled for export. exporter::SpanExporterImpl::Get()->AddSpan(span_impl_); } } +void Span::Abandon() { + if (IsRecording()) { + context_ = SpanContext(context_.trace_id(), context_.span_id(), + context_.trace_options().WithSampling(false)); + span_impl_->MarkAbandoned(); + End(); + } +} + const SpanContext& Span::context() const { return context_; } bool Span::IsSampled() const { return context_.trace_options().IsSampled(); } diff --git a/opencensus/trace/internal/span_exporter_impl.cc b/opencensus/trace/internal/span_exporter_impl.cc index 19ae19f0..ecbafe55 100644 --- a/opencensus/trace/internal/span_exporter_impl.cc +++ b/opencensus/trace/internal/span_exporter_impl.cc @@ -49,6 +49,7 @@ void SpanExporterImpl::AddSpan( const std::shared_ptr& span_impl) { absl::MutexLock l(&span_mu_); if (!collect_spans_) return; + if (!span_impl->IsSampled()) return; spans_.emplace_back(span_impl); } diff --git a/opencensus/trace/internal/span_impl.cc b/opencensus/trace/internal/span_impl.cc index d32b3c8b..7272e109 100644 --- a/opencensus/trace/internal/span_impl.cc +++ b/opencensus/trace/internal/span_impl.cc @@ -144,6 +144,12 @@ void SpanImpl::SetName(absl::string_view name) { } } +void SpanImpl::MarkAbandoned() { + absl::MutexLock l(&mu_); + context_ = SpanContext(context_.trace_id(), context_.span_id(), + context_.trace_options().WithSampling(false)); +} + bool SpanImpl::End() { absl::MutexLock l(&mu_); if (has_ended_) { @@ -161,6 +167,15 @@ bool SpanImpl::HasEnded() const { return has_ended_; } +bool SpanImpl::IsSampled() const { + return context().trace_options().IsSampled(); +} + +SpanContext SpanImpl::context() const { + absl::MutexLock l(&mu_); + return context_; +} + exporter::SpanData SpanImpl::ToSpanData() const { absl::MutexLock l(&mu_); // Make a deep copy of attributes. diff --git a/opencensus/trace/internal/span_impl.h b/opencensus/trace/internal/span_impl.h index 5ff62493..b4b28475 100644 --- a/opencensus/trace/internal/span_impl.h +++ b/opencensus/trace/internal/span_impl.h @@ -87,20 +87,27 @@ class SpanImpl final { void SetName(absl::string_view name) LOCKS_EXCLUDED(mu_); + void MarkAbandoned() LOCKS_EXCLUDED(mu_); + // Returns true on success (if this is the first time the Span has ended) and // also marks the end of the Span and sets its end_time_. bool End() LOCKS_EXCLUDED(mu_); - // Returns true if the span has ended. + // Returns true if the Span has ended. bool HasEnded() const LOCKS_EXCLUDED(mu_); + // Returns true if the Span is sampled for export. Returns false if the Span + // was abandoned. + bool IsSampled() const LOCKS_EXCLUDED(mu_); + absl::string_view name() const { return name_; } // Returns the name of the span as a constref string. const std::string& name_constref() const { return name_; } // Returns the SpanContext associated with this Span. - SpanContext context() const { return context_; } + // The trace_options do reflect MarkAbandoned(). + SpanContext context() const LOCKS_EXCLUDED(mu_); SpanId parent_span_id() const { return parent_span_id_; } @@ -126,7 +133,7 @@ class SpanImpl final { // a root span. const SpanId parent_span_id_; // TraceId, SpanId, and TraceOptions for the current span. - const SpanContext context_; + SpanContext context_ GUARDED_BY(mu_); // Queue of recorded annotations. TraceEvents> annotations_ GUARDED_BY(mu_); // Queue of recorded network events. @@ -138,6 +145,8 @@ class SpanImpl final { AttributeList attributes_ GUARDED_BY(mu_); // Marks if the span has ended. bool has_ended_ GUARDED_BY(mu_); + // Marks if the span was abandoned. + bool is_abandoned_ GUARDED_BY(mu_); // True if the parent Span is in a different process. const bool remote_parent_; }; diff --git a/opencensus/trace/internal/span_test.cc b/opencensus/trace/internal/span_test.cc index c1ef5b8b..e96019dd 100644 --- a/opencensus/trace/internal/span_test.cc +++ b/opencensus/trace/internal/span_test.cc @@ -365,6 +365,16 @@ TEST(SpanTest, BlankSpan) { span.End(); } +TEST(SpanTest, Abandon) { + AlwaysSampler sampler; + auto span = Span::StartSpan("SpanName", /*parent=*/nullptr, {&sampler}); + EXPECT_TRUE(span.IsSampled()); + span.Abandon(); + EXPECT_FALSE(span.IsSampled()); + auto data = SpanTestPeer::ToSpanData(&span); + EXPECT_FALSE(data.context().trace_options().IsSampled()); +} + } // namespace } // namespace trace } // namespace opencensus diff --git a/opencensus/trace/span.h b/opencensus/trace/span.h index 0607bab8..d409d8c9 100644 --- a/opencensus/trace/span.h +++ b/opencensus/trace/span.h @@ -79,10 +79,11 @@ struct StartSpanOptions { // implementation-defined data structure, hence all operations on it are marked // const. // -// Span is thread-compatible. If swap() and operator= are avoided, everything -// else is thread-safe. Avoid mutating Span objects in-place; treat them like -// read-only handles. When using multiple threads, give each thread a copy of -// the Span. +// Span is thread-compatible. Avoid mutating Span objects in-place; treat them +// like read-only handles. When using multiple threads, give each thread a copy +// of the Span. +// +// Almost everything is thread-safe except: swap(), operator=, Abandon(). // // As an alternative to explicitly passing Span objects between functions, // consider using Context. (see the ../context/ directory). @@ -154,13 +155,18 @@ class Span final { void SetStatus(StatusCode canonical_code, absl::string_view message = "") const; - // Set the span name. + // Set the Span name. void SetName(absl::string_view name) const; // Marks the end of a Span. No further changes can be made to the Span after // End is called. void End() const; + // If the Span was sampled, un-samples it and End()s it. Note that other + // copies of this Span, and child spans, do not become unsampled. + // TODO(opencensus-specs): Should Abandon() not call End()? + void Abandon(); + // Returns the SpanContext associated with this Span. const SpanContext& context() const;