opentelemetry_spanprocessor_any/trace/
noop.rs

1//! No-op trace impls
2//!
3//! This implementation is returned as the global tracer if no `Tracer`
4//! has been set. It is also useful for testing purposes as it is intended
5//! to have minimal resource utilization and runtime impact.
6use crate::trace::TraceResult;
7use crate::{
8    sdk::export::trace::{ExportResult, SpanData, SpanExporter},
9    trace,
10    trace::{SpanBuilder, TraceContextExt, TraceFlags, TraceState},
11    Context, KeyValue,
12};
13use async_trait::async_trait;
14use std::borrow::Cow;
15use std::time::SystemTime;
16
17/// A no-op instance of a `TracerProvider`.
18#[derive(Clone, Debug, Default)]
19pub struct NoopTracerProvider {
20    _private: (),
21}
22
23impl NoopTracerProvider {
24    /// Create a new no-op tracer provider
25    pub fn new() -> Self {
26        NoopTracerProvider { _private: () }
27    }
28}
29
30impl trace::TracerProvider for NoopTracerProvider {
31    type Tracer = NoopTracer;
32
33    /// Returns a new `NoopTracer` instance.
34    fn versioned_tracer(
35        &self,
36        _name: impl Into<Cow<'static, str>>,
37        _version: Option<&'static str>,
38        _schema_url: Option<&'static str>,
39    ) -> Self::Tracer {
40        NoopTracer::new()
41    }
42
43    /// Return an empty `Vec` as there isn't any span processors in `NoopTracerProvider`
44    fn force_flush(&self) -> Vec<TraceResult<()>> {
45        Vec::new()
46    }
47}
48
49/// A no-op instance of a `Span`.
50#[derive(Clone, Debug)]
51pub struct NoopSpan {
52    span_context: trace::SpanContext,
53}
54
55impl Default for NoopSpan {
56    fn default() -> Self {
57        NoopSpan::new()
58    }
59}
60
61impl NoopSpan {
62    /// Creates a new `NoopSpan` instance.
63    pub fn new() -> Self {
64        NoopSpan {
65            span_context: trace::SpanContext::new(
66                trace::TraceId::INVALID,
67                trace::SpanId::INVALID,
68                TraceFlags::default(),
69                false,
70                TraceState::default(),
71            ),
72        }
73    }
74}
75
76impl trace::Span for NoopSpan {
77    /// Ignores all events
78    fn add_event<T>(&mut self, _name: T, _attributes: Vec<KeyValue>)
79    where
80        T: Into<Cow<'static, str>>,
81    {
82        // Ignore
83    }
84
85    /// Ignores all events with timestamps
86    fn add_event_with_timestamp<T>(
87        &mut self,
88        _name: T,
89        _timestamp: SystemTime,
90        _attributes: Vec<KeyValue>,
91    ) where
92        T: Into<Cow<'static, str>>,
93    {
94        // Ignored
95    }
96
97    /// Returns an invalid `SpanContext`.
98    fn span_context(&self) -> &trace::SpanContext {
99        &self.span_context
100    }
101
102    /// Returns false, signifying that this span is never recording.
103    fn is_recording(&self) -> bool {
104        false
105    }
106
107    /// Ignores all attributes
108    fn set_attribute(&mut self, _attribute: KeyValue) {
109        // Ignored
110    }
111
112    /// Ignores status
113    fn set_status(&mut self, _code: trace::StatusCode, _message: String) {
114        // Ignored
115    }
116
117    /// Ignores name updates
118    fn update_name<T>(&mut self, _new_name: T)
119    where
120        T: Into<Cow<'static, str>>,
121    {
122        // Ignored
123    }
124
125    /// Ignores `Span` endings
126    fn end_with_timestamp(&mut self, _timestamp: SystemTime) {
127        // Ignored
128    }
129}
130
131/// A no-op instance of a `Tracer`.
132#[derive(Clone, Debug, Default)]
133pub struct NoopTracer {
134    _private: (),
135}
136
137impl NoopTracer {
138    /// Create a new no-op tracer
139    pub fn new() -> Self {
140        NoopTracer { _private: () }
141    }
142}
143
144impl trace::Tracer for NoopTracer {
145    type Span = NoopSpan;
146
147    /// Starts a new `NoopSpan` with a given context.
148    ///
149    /// If the context contains a valid span, it's span context is propagated.
150    fn start_with_context<T>(&self, name: T, parent_cx: &Context) -> Self::Span
151    where
152        T: Into<std::borrow::Cow<'static, str>>,
153    {
154        self.build_with_context(SpanBuilder::from_name(name), parent_cx)
155    }
156
157    /// Starts a `SpanBuilder`.
158    fn span_builder<T>(&self, name: T) -> trace::SpanBuilder
159    where
160        T: Into<std::borrow::Cow<'static, str>>,
161    {
162        trace::SpanBuilder::from_name(name)
163    }
164
165    /// Builds a `NoopSpan` from a `SpanBuilder`.
166    ///
167    /// If the span builder or the context's current span contains a valid span context, it is
168    /// propagated.
169    fn build_with_context(&self, _builder: trace::SpanBuilder, parent_cx: &Context) -> Self::Span {
170        if parent_cx.has_active_span() {
171            NoopSpan {
172                span_context: parent_cx.span().span_context().clone(),
173            }
174        } else {
175            NoopSpan::new()
176        }
177    }
178}
179
180/// A no-op instance of an [`SpanExporter`].
181///
182/// [`SpanExporter`]: crate::sdk::export::trace::SpanExporter
183#[derive(Debug, Default)]
184pub struct NoopSpanExporter {
185    _private: (),
186}
187
188impl NoopSpanExporter {
189    /// Create a new noop span exporter
190    pub fn new() -> Self {
191        NoopSpanExporter { _private: () }
192    }
193}
194
195#[async_trait]
196impl SpanExporter for NoopSpanExporter {
197    async fn export(&mut self, _batch: Vec<SpanData>) -> ExportResult {
198        Ok(())
199    }
200}
201
202#[cfg(all(test, feature = "testing", feature = "trace"))]
203mod tests {
204    use super::*;
205    use crate::testing::trace::TestSpan;
206    use crate::trace::{self, Span, Tracer};
207
208    fn valid_span_context() -> trace::SpanContext {
209        trace::SpanContext::new(
210            trace::TraceId::from_u128(42),
211            trace::SpanId::from_u64(42),
212            trace::TraceFlags::default(),
213            true,
214            TraceState::default(),
215        )
216    }
217
218    #[test]
219    fn noop_tracer_defaults_to_invalid_span() {
220        let tracer = NoopTracer::new();
221        let span = tracer.start_with_context("foo", &Context::new());
222        assert!(!span.span_context().is_valid());
223    }
224
225    #[test]
226    fn noop_tracer_propagates_valid_span_context_from_builder() {
227        let tracer = NoopTracer::new();
228        let builder = tracer.span_builder("foo");
229        let span = tracer.build_with_context(
230            builder,
231            &Context::new().with_span(TestSpan(valid_span_context())),
232        );
233        assert!(span.span_context().is_valid());
234    }
235
236    #[test]
237    fn noop_tracer_propagates_valid_span_context_from_explicitly_specified_context() {
238        let tracer = NoopTracer::new();
239        let cx = Context::new().with_span(NoopSpan {
240            span_context: valid_span_context(),
241        });
242        let span = tracer.start_with_context("foo", &cx);
243        assert!(span.span_context().is_valid());
244    }
245
246    #[test]
247    fn noop_tracer_propagates_valid_span_context_from_remote_span_context() {
248        let tracer = NoopTracer::new();
249        let cx = Context::new().with_remote_span_context(valid_span_context());
250        let span = tracer.start_with_context("foo", &cx);
251        assert!(span.span_context().is_valid());
252    }
253}