Skip to main content

redact_core/
trace.rs

1// Copyright 2026 Censgate LLC.
2// Licensed under the Apache License, Version 2.0. See the LICENSE file
3// in the project root for license information.
4
5//! Stage-span gate for `/v1/redact` children.
6//!
7//! Recognizers live below the gateway crate, so they cannot see `TraceLevel`.
8//! The gateway enables this thread-local for the duration of a pass when
9//! operations tracing is on. Call sites still use `tracing::info_span!` with a
10//! literal name so the tracing macros stay valid.
11
12use std::cell::Cell;
13
14thread_local! {
15    static OPERATIONS: Cell<bool> = const { Cell::new(false) };
16}
17
18/// Run `f` with stage spans enabled or disabled for this thread.
19pub fn with_operation_spans<R>(enabled: bool, f: impl FnOnce() -> R) -> R {
20    struct Restore(bool);
21    impl Drop for Restore {
22        fn drop(&mut self) {
23            OPERATIONS.with(|flag| flag.set(self.0));
24        }
25    }
26    let prev = OPERATIONS.with(|flag| flag.replace(enabled));
27    let _restore = Restore(prev);
28    f()
29}
30
31/// Whether the current pass should emit detect-stage child spans.
32pub fn operations_enabled() -> bool {
33    OPERATIONS.with(Cell::get)
34}
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39
40    #[test]
41    fn off_by_default() {
42        assert!(!operations_enabled());
43    }
44
45    #[test]
46    fn on_inside_scope() {
47        with_operation_spans(true, || {
48            assert!(operations_enabled());
49        });
50        assert!(!operations_enabled());
51    }
52
53    #[test]
54    fn restores_after_panic() {
55        let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
56            with_operation_spans(true, || panic!("boom"));
57        }));
58        assert!(!operations_enabled());
59    }
60}