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