opentelemetry/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::{
7    propagation::{text_map_propagator::FieldIter, Extractor, Injector, TextMapPropagator},
8    trace::{self, TraceContextExt as _},
9    Context, InstrumentationLibrary, KeyValue,
10};
11use std::{borrow::Cow, sync::Arc, time::SystemTime};
12
13/// A no-op instance of a `TracerProvider`.
14#[derive(Clone, Debug, Default)]
15pub struct NoopTracerProvider {
16    _private: (),
17}
18
19impl NoopTracerProvider {
20    /// Create a new no-op tracer provider
21    pub fn new() -> Self {
22        NoopTracerProvider { _private: () }
23    }
24}
25
26impl trace::TracerProvider for NoopTracerProvider {
27    type Tracer = NoopTracer;
28
29    /// Returns a new `NoopTracer` instance.
30    fn library_tracer(&self, _library: Arc<InstrumentationLibrary>) -> Self::Tracer {
31        NoopTracer::new()
32    }
33}
34
35/// A no-op instance of a `Span`.
36#[derive(Clone, Debug)]
37pub struct NoopSpan {
38    span_context: trace::SpanContext,
39}
40
41impl NoopSpan {
42    /// The default `NoopSpan`, as a constant
43    pub const DEFAULT: NoopSpan = NoopSpan {
44        span_context: trace::SpanContext::NONE,
45    };
46}
47
48impl trace::Span for NoopSpan {
49    /// Ignores all events
50    fn add_event<T>(&mut self, _name: T, _attributes: Vec<KeyValue>)
51    where
52        T: Into<Cow<'static, str>>,
53    {
54        // Ignored
55    }
56
57    /// Ignores all events with timestamps
58    fn add_event_with_timestamp<T>(
59        &mut self,
60        _name: T,
61        _timestamp: SystemTime,
62        _attributes: Vec<KeyValue>,
63    ) where
64        T: Into<Cow<'static, str>>,
65    {
66        // Ignored
67    }
68
69    /// Returns an invalid `SpanContext`.
70    fn span_context(&self) -> &trace::SpanContext {
71        &self.span_context
72    }
73
74    /// Returns false, signifying that this span is never recording.
75    fn is_recording(&self) -> bool {
76        false
77    }
78
79    /// Ignores all attributes
80    fn set_attribute(&mut self, _attribute: KeyValue) {
81        // Ignored
82    }
83
84    /// Ignores status
85    fn set_status(&mut self, _status: trace::Status) {
86        // Ignored
87    }
88
89    /// Ignores name updates
90    fn update_name<T>(&mut self, _new_name: T)
91    where
92        T: Into<Cow<'static, str>>,
93    {
94        // Ignored
95    }
96
97    fn add_link(&mut self, _span_context: trace::SpanContext, _attributes: Vec<KeyValue>) {
98        // Ignored
99    }
100
101    /// Ignores `Span` endings
102    fn end_with_timestamp(&mut self, _timestamp: SystemTime) {
103        // Ignored
104    }
105}
106
107/// A no-op instance of a `Tracer`.
108#[derive(Clone, Debug, Default)]
109pub struct NoopTracer {
110    _private: (),
111}
112
113impl NoopTracer {
114    /// Create a new no-op tracer
115    pub fn new() -> Self {
116        NoopTracer { _private: () }
117    }
118}
119
120impl trace::Tracer for NoopTracer {
121    type Span = NoopSpan;
122
123    /// Builds a `NoopSpan` from a `SpanBuilder`.
124    ///
125    /// If the span builder or the context's current span contains a valid span context, it is
126    /// propagated.
127    fn build_with_context(&self, _builder: trace::SpanBuilder, parent_cx: &Context) -> Self::Span {
128        NoopSpan {
129            span_context: parent_cx.span().span_context().clone(),
130        }
131    }
132}
133
134/// A no-op instance of an [`TextMapPropagator`].
135///
136/// [`TextMapPropagator`]: crate::propagation::TextMapPropagator
137#[derive(Debug, Default)]
138pub struct NoopTextMapPropagator {
139    _private: (),
140}
141
142impl NoopTextMapPropagator {
143    /// Create a new noop text map propagator
144    pub fn new() -> Self {
145        NoopTextMapPropagator { _private: () }
146    }
147}
148
149impl TextMapPropagator for NoopTextMapPropagator {
150    fn inject_context(&self, _cx: &Context, _injector: &mut dyn Injector) {
151        // ignored
152    }
153
154    fn extract_with_context(&self, _cx: &Context, _extractor: &dyn Extractor) -> Context {
155        Context::current()
156    }
157
158    fn fields(&self) -> FieldIter<'_> {
159        FieldIter::new(&[])
160    }
161}
162
163#[cfg(all(test, feature = "testing", feature = "trace"))]
164mod tests {
165    use super::*;
166    use crate::testing::trace::TestSpan;
167    use crate::trace::{Span, TraceState, Tracer};
168
169    fn valid_span_context() -> trace::SpanContext {
170        trace::SpanContext::new(
171            trace::TraceId::from_u128(42),
172            trace::SpanId::from_u64(42),
173            trace::TraceFlags::default(),
174            true,
175            TraceState::default(),
176        )
177    }
178
179    #[test]
180    fn noop_tracer_defaults_to_invalid_span() {
181        let tracer = NoopTracer::new();
182        let span = tracer.start_with_context("foo", &Context::new());
183        assert!(!span.span_context().is_valid());
184    }
185
186    #[test]
187    fn noop_tracer_propagates_valid_span_context_from_builder() {
188        let tracer = NoopTracer::new();
189        let builder = tracer.span_builder("foo");
190        let span = tracer.build_with_context(
191            builder,
192            &Context::new().with_span(TestSpan(valid_span_context())),
193        );
194        assert!(span.span_context().is_valid());
195    }
196
197    #[test]
198    fn noop_tracer_propagates_valid_span_context_from_explicitly_specified_context() {
199        let tracer = NoopTracer::new();
200        let cx = Context::new().with_span(NoopSpan {
201            span_context: valid_span_context(),
202        });
203        let span = tracer.start_with_context("foo", &cx);
204        assert!(span.span_context().is_valid());
205    }
206
207    #[test]
208    fn noop_tracer_propagates_valid_span_context_from_remote_span_context() {
209        let tracer = NoopTracer::new();
210        let cx = Context::new().with_remote_span_context(valid_span_context());
211        let span = tracer.start_with_context("foo", &cx);
212        assert!(span.span_context().is_valid());
213    }
214}