ari_subscriber/
layer.rs

1//! A [`tracing-subscriber`] Layer which outputs to `stdout`.
2//!
3//! See the documentation on [`Layer`] for more details.
4//!
5//! [`tracing-subscriber`]: tracing_subscriber
6use chrono::{DateTime, Utc};
7use tracing::{span, subscriber::Interest, Subscriber};
8use tracing_subscriber::registry::LookupSpan;
9
10use crate::fmt::{FmtEvent, FmtFields, FmtSpan};
11
12/// Creates a new [`Layer`].
13///
14/// See the [`Layer`] documentation for details on customization.
15///
16/// # Examples
17///
18/// ```rust
19/// use tracing_subscriber::prelude::*;
20///
21/// let layer = ari_subscriber::layer();
22/// tracing_subscriber::registry().with(layer).init();
23///
24/// // Will be printed by `ari_subscriber`
25/// tracing::info!("nice!");
26/// ```
27#[must_use = "A Layer does nothing if it is not added to a registry."]
28pub fn layer() -> Layer {
29    Layer::new()
30}
31
32/// A [`tracing-subscriber`] Layer which outputs to `stdout`.
33///
34/// The layer can be added to a [`Registry`] and will output trace information
35/// to `stdout`.
36///
37/// [`tracing-subscriber`]: tracing_subscriber
38/// [`Registry`]: struct@tracing_subscriber::Registry
39
40pub struct Layer {}
41
42impl Layer {
43    /// Creates a new [`Layer`].
44    ///
45    /// Currently, no customization is possible.
46    ///
47    /// # Examples
48    ///
49    /// ```rust
50    /// use tracing_subscriber::prelude::*;
51    ///
52    /// use ari_subscriber::Layer;
53    ///
54    /// let layer = Layer::new();
55    /// tracing_subscriber::registry().with(layer).init();
56    ///
57    /// // Will be printed by `ari_subscriber`
58    /// tracing::info!("wonderful!");
59    /// ```
60    #[must_use = "A Layer does nothing if it is not added to a registry."]
61    pub fn new() -> Self {
62        Self {}
63    }
64}
65
66impl Default for Layer {
67    #[must_use = "A Layer does nothing if it is not added to a registry."]
68    fn default() -> Self {
69        Self::new()
70    }
71}
72
73impl Layer {
74    // Self kept because it will be needed with pending changes.
75    #[allow(clippy::unused_self)]
76    fn write_event(&self, fmt_event: &mut FmtEvent) {
77        println!("{}", fmt_event.formatted());
78    }
79
80    fn span_event<S>(
81        &self,
82        now: DateTime<Utc>,
83        id: &span::Id,
84        ctx: &tracing_subscriber::layer::Context<'_, S>,
85        message: String,
86    ) where
87        S: Subscriber + for<'a> LookupSpan<'a>,
88    {
89        let span = ctx.span(id).expect("Span not found, this is a bug");
90        let extensions = span.extensions();
91        let Some(fmt_span) = extensions.get::<FmtSpan>() else {
92            // We can't print anything if the fmt_span isn't present.
93            return;
94        };
95
96        let formatted_scope = ctx
97            .span_scope(id)
98            .map(|scope| self.formatted_scope(scope))
99            .unwrap_or_default();
100        let mut fmt_event =
101            FmtEvent::new_span_event(now, fmt_span, span.metadata(), &formatted_scope, message);
102
103        self.write_event(&mut fmt_event);
104    }
105
106    // Self kept because it will be needed with pending changes.
107    #[allow(clippy::unused_self)]
108    fn formatted_scope<S>(&self, scope: tracing_subscriber::registry::Scope<'_, S>) -> String
109    where
110        S: Subscriber + for<'a> LookupSpan<'a>,
111    {
112        let mut formatted_scope = String::new();
113        for span in scope.from_root() {
114            let extensions = span.extensions();
115            let span = extensions
116                .get::<FmtSpan>()
117                .expect("cannot get fields for in-scope span. This is a bug!");
118            formatted_scope.push_str(&format!("{span} ", span = span.formatted()));
119        }
120        formatted_scope
121    }
122}
123
124impl<S> tracing_subscriber::Layer<S> for Layer
125where
126    S: Subscriber + for<'a> LookupSpan<'a>,
127{
128    fn register_callsite(&self, _metadata: &'static tracing::Metadata<'static>) -> Interest {
129        Interest::always()
130    }
131
132    fn enabled(
133        &self,
134        _metadata: &tracing::Metadata<'_>,
135        _ctx: tracing_subscriber::layer::Context<'_, S>,
136    ) -> bool {
137        true
138    }
139
140    fn on_new_span(
141        &self,
142        attrs: &span::Attributes<'_>,
143        id: &span::Id,
144        ctx: tracing_subscriber::layer::Context<'_, S>,
145    ) {
146        let now = Utc::now();
147        let span = ctx.span(id).expect("Span not found, this is a bug");
148        {
149            let mut extensions = span.extensions_mut();
150
151            if extensions.get_mut::<FmtSpan>().is_none() {
152                let mut fields = FmtFields::new_span();
153                attrs.record(&mut fields);
154                fields.format();
155                let span = FmtSpan::new(id, attrs, fields);
156                extensions.insert(span);
157            }
158        }
159
160        self.span_event(now, id, &ctx, "new".into());
161    }
162
163    fn on_event(&self, event: &tracing::Event<'_>, ctx: tracing_subscriber::layer::Context<'_, S>) {
164        let now = Utc::now();
165
166        let mut fields = FmtFields::new_event();
167        event.record(&mut fields);
168
169        let formatted_scope = ctx
170            .event_scope(event)
171            .map(|scope| self.formatted_scope(scope))
172            .unwrap_or_default();
173
174        let mut fmt_event = FmtEvent::new(now, event.metadata(), &formatted_scope, fields);
175        self.write_event(&mut fmt_event);
176    }
177
178    fn on_enter(&self, id: &span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) {
179        let now: DateTime<Utc> = Utc::now();
180        self.span_event(now, id, &ctx, "enter".into());
181    }
182
183    fn on_exit(&self, id: &span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) {
184        let now: DateTime<Utc> = Utc::now();
185        self.span_event(now, id, &ctx, "exit".into());
186    }
187
188    fn on_close(&self, id: span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) {
189        let now: DateTime<Utc> = Utc::now();
190        self.span_event(now, &id, &ctx, "close".into());
191    }
192}