Skip to main content

asupersync/
tracing_compat.rs

1//! Tracing compatibility layer for structured logging and spans.
2//!
3//! This module provides a unified interface for tracing that works whether or not
4//! the `tracing-integration` feature is enabled:
5//!
6//! - **With feature enabled**: Re-exports from the `tracing` crate for full functionality.
7//! - **Without feature**: No-op macros that compile to nothing for zero runtime overhead.
8//!
9//! # Usage
10//!
11//! ```rust,ignore
12//! use asupersync::tracing_compat::{info, debug, trace, span, Level};
13//!
14//! // These compile to no-ops when tracing-integration is disabled
15//! info!("Starting operation");
16//! debug!(task_id = ?id, "Task spawned");
17//!
18//! let _span = span!(Level::INFO, "my_operation");
19//! ```
20//!
21//! # Feature Flag
22//!
23//! Enable tracing by adding the feature to your `Cargo.toml`:
24//!
25//! ```toml
26//! asupersync = { version = "0.1", features = ["tracing-integration"] }
27//! ```
28
29#[cfg(feature = "tracing-integration")]
30pub use tracing::{
31    debug, debug_span, error, error_span, event, info, info_span, instrument, span, trace,
32    trace_span, warn, warn_span, Instrument, Level, Span,
33};
34
35// When tracing is disabled, provide no-op macros
36#[cfg(not(feature = "tracing-integration"))]
37mod noop {
38    //! No-op implementations when tracing is disabled.
39    //!
40    //! These macros expand to nothing, ensuring zero compile-time and runtime cost.
41
42    /// No-op trace-level logging macro.
43    #[macro_export]
44    macro_rules! trace {
45        ($($arg:tt)*) => {};
46    }
47
48    /// No-op debug-level logging macro.
49    #[macro_export]
50    macro_rules! debug {
51        ($($arg:tt)*) => {};
52    }
53
54    /// No-op info-level logging macro.
55    #[macro_export]
56    macro_rules! info {
57        ($($arg:tt)*) => {};
58    }
59
60    /// No-op warn-level logging macro.
61    #[macro_export]
62    macro_rules! warn {
63        ($($arg:tt)*) => {};
64    }
65
66    /// No-op error-level logging macro.
67    #[macro_export]
68    macro_rules! error {
69        ($($arg:tt)*) => {};
70    }
71
72    /// No-op event macro.
73    #[macro_export]
74    macro_rules! event {
75        ($($arg:tt)*) => {};
76    }
77
78    /// No-op span macro that returns a `NoopSpan`.
79    #[macro_export]
80    macro_rules! span {
81        ($($arg:tt)*) => {
82            $crate::tracing_compat::NoopSpan
83        };
84    }
85
86    /// No-op trace_span macro.
87    #[macro_export]
88    macro_rules! trace_span {
89        ($($arg:tt)*) => {
90            $crate::tracing_compat::NoopSpan
91        };
92    }
93
94    /// No-op debug_span macro.
95    #[macro_export]
96    macro_rules! debug_span {
97        ($($arg:tt)*) => {
98            $crate::tracing_compat::NoopSpan
99        };
100    }
101
102    /// No-op info_span macro.
103    #[macro_export]
104    macro_rules! info_span {
105        ($($arg:tt)*) => {
106            $crate::tracing_compat::NoopSpan
107        };
108    }
109
110    /// No-op warn_span macro.
111    #[macro_export]
112    macro_rules! warn_span {
113        ($($arg:tt)*) => {
114            $crate::tracing_compat::NoopSpan
115        };
116    }
117
118    /// No-op error_span macro.
119    #[macro_export]
120    macro_rules! error_span {
121        ($($arg:tt)*) => {
122            $crate::tracing_compat::NoopSpan
123        };
124    }
125
126    /// No-op instrument attribute (does nothing).
127    /// This is a placeholder - the actual `#[instrument]` attribute requires proc-macro.
128    #[macro_export]
129    macro_rules! instrument {
130        ($($arg:tt)*) => {};
131    }
132
133    // Re-export the macros at module level
134    pub use crate::{
135        debug, debug_span, error, error_span, event, info, info_span, instrument, span, trace,
136        trace_span, warn, warn_span,
137    };
138}
139
140#[cfg(not(feature = "tracing-integration"))]
141pub use noop::*;
142
143/// A no-op span that does nothing.
144///
145/// When tracing is disabled, span macros return this type. It implements
146/// the necessary methods to allow code like `span.enter()` to compile
147/// without the tracing feature.
148#[cfg(not(feature = "tracing-integration"))]
149#[derive(Debug, Clone, Copy)]
150pub struct NoopSpan;
151
152#[cfg(not(feature = "tracing-integration"))]
153impl NoopSpan {
154    /// Returns a no-op guard that does nothing on drop.
155    #[inline]
156    #[must_use]
157    pub fn enter(&self) -> NoopGuard {
158        NoopGuard
159    }
160
161    /// Returns self (no-op).
162    #[inline]
163    #[must_use]
164    pub fn entered(self) -> Self {
165        self
166    }
167
168    /// Returns self (no-op).
169    #[inline]
170    #[must_use]
171    pub fn or_current(self) -> Self {
172        self
173    }
174
175    /// Returns self (no-op).
176    #[inline]
177    pub fn follows_from(&self, _span: &Self) {}
178
179    /// Returns true (always "enabled" to avoid branch differences).
180    #[inline]
181    #[must_use]
182    pub fn is_disabled(&self) -> bool {
183        true
184    }
185
186    /// Records a value (no-op).
187    #[inline]
188    pub fn record<V>(&self, _field: &str, _value: V) {}
189
190    /// Returns a no-op span (current span is always a no-op when disabled).
191    #[inline]
192    #[must_use]
193    pub fn current() -> Self {
194        Self
195    }
196
197    /// Returns a no-op span (none is always a no-op when disabled).
198    #[inline]
199    #[must_use]
200    pub fn none() -> Self {
201        Self
202    }
203}
204
205/// A no-op span guard that does nothing on drop.
206#[cfg(not(feature = "tracing-integration"))]
207#[derive(Debug)]
208pub struct NoopGuard;
209
210/// No-op level type for when tracing is disabled.
211#[cfg(not(feature = "tracing-integration"))]
212#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
213pub struct Level;
214
215#[cfg(not(feature = "tracing-integration"))]
216impl Level {
217    /// Trace level (most verbose).
218    pub const TRACE: Self = Self;
219    /// Debug level.
220    pub const DEBUG: Self = Self;
221    /// Info level.
222    pub const INFO: Self = Self;
223    /// Warn level.
224    pub const WARN: Self = Self;
225    /// Error level (least verbose).
226    pub const ERROR: Self = Self;
227}
228
229/// Alias for `NoopSpan` when tracing is disabled.
230#[cfg(not(feature = "tracing-integration"))]
231pub type Span = NoopSpan;
232
233/// No-op `Instrument` trait when tracing is disabled.
234///
235/// This trait is implemented for all `Future` types and does nothing,
236/// allowing code using `.instrument(span)` to compile without the feature.
237#[cfg(not(feature = "tracing-integration"))]
238pub trait Instrument: Sized {
239    /// Instruments this future with a span (no-op when disabled).
240    #[must_use]
241    fn instrument(self, _span: NoopSpan) -> Self {
242        self
243    }
244
245    /// Instruments this future with the current span (no-op when disabled).
246    #[must_use]
247    fn in_current_span(self) -> Self {
248        self
249    }
250}
251
252#[cfg(not(feature = "tracing-integration"))]
253impl<T> Instrument for T {}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258    use crate::test_utils::init_test_logging;
259
260    fn init_test(test_name: &str) {
261        init_test_logging();
262        crate::test_phase!(test_name);
263    }
264
265    #[test]
266    fn test_noop_macros_compile() {
267        init_test("test_noop_macros_compile");
268        // These should all compile and do nothing
269        trace!("trace message");
270        debug!("debug message");
271        info!("info message");
272        warn!("warn message");
273        error!("error message");
274
275        trace!(field = "value", "trace with field");
276        debug!(count = 42, "debug with field");
277        info!(name = "test", "info with field");
278        crate::test_complete!("test_noop_macros_compile");
279    }
280
281    #[test]
282    fn test_noop_span_compile() {
283        init_test("test_noop_span_compile");
284        let span = span!(Level::INFO, "test_span");
285        let _guard = span.enter();
286
287        let span2 = info_span!("info_span");
288        let _entered = span2.entered();
289
290        let span3 = debug_span!("debug_span", task_id = 42);
291        span3.record("field", "value");
292        crate::test_complete!("test_noop_span_compile");
293    }
294
295    #[test]
296    fn test_noop_level_constants() {
297        init_test("test_noop_level_constants");
298        #[cfg(not(feature = "tracing-integration"))]
299        {
300            let _ = Level::TRACE;
301            let _ = Level::DEBUG;
302            let _ = Level::INFO;
303            let _ = Level::WARN;
304            let _ = Level::ERROR;
305        }
306        crate::test_complete!("test_noop_level_constants");
307    }
308
309    #[test]
310    fn test_noop_span_methods() {
311        init_test("test_noop_span_methods");
312        #[cfg(not(feature = "tracing-integration"))]
313        {
314            let span = NoopSpan;
315            let disabled = span.is_disabled();
316            crate::assert_with_log!(disabled, "noop span should be disabled", true, disabled);
317
318            let current = NoopSpan::current();
319            let none = NoopSpan::none();
320            let _ = current.or_current();
321            none.follows_from(&span);
322        }
323        crate::test_complete!("test_noop_span_methods");
324    }
325}