tracing_bunyan_formatter/
storage_layer.rs

1use std::collections::HashMap;
2use std::fmt;
3use std::time::Instant;
4use tracing::field::{Field, Visit};
5use tracing::span::{Attributes, Record};
6use tracing::{Id, Subscriber};
7use tracing_subscriber::layer::Context;
8use tracing_subscriber::Layer;
9
10/// This layer is only concerned with information storage, it does not do any formatting or provide any output.
11///
12/// It's purpose is to store the fields associated to spans in an easy-to-consume format
13/// for downstream layers concerned with emitting a formatted representation of
14/// spans or events.
15#[derive(Clone, Debug)]
16pub struct JsonStorageLayer;
17
18/// `JsonStorage` will collect information about a span when it's created (`new_span` handler)
19/// or when new records are attached to it (`on_record` handler) and store it in its `extensions`
20/// for future retrieval from other layers interested in formatting or further enrichment.
21///
22/// We are re-implementing (well, copy-pasting, apart from using an HashMap instead of a BTreeMap)
23/// `JsonVisitor` from `tracing-subscriber` given that we can't access/insert/iterate over
24/// the underlying BTreeMap using its public API.
25///
26/// For spans, we also store the duration of each span with the `elapsed_milliseconds` key using
27/// the `on_exit`/`on_enter` handlers.
28#[derive(Clone, Debug)]
29pub struct JsonStorage<'a> {
30    values: HashMap<&'a str, serde_json::Value>,
31}
32
33impl<'a> JsonStorage<'a> {
34    /// Get the set of stored values, as a set of keys and JSON values.
35    pub fn values(&self) -> &HashMap<&'a str, serde_json::Value> {
36        &self.values
37    }
38}
39
40/// Get a new visitor, with an empty bag of key-value pairs.
41impl Default for JsonStorage<'_> {
42    fn default() -> Self {
43        Self {
44            values: HashMap::new(),
45        }
46    }
47}
48
49/// Taken verbatim from tracing-subscriber
50impl Visit for JsonStorage<'_> {
51    /// Visit a signed 64-bit integer value.
52    fn record_i64(&mut self, field: &Field, value: i64) {
53        self.values
54            .insert(field.name(), serde_json::Value::from(value));
55    }
56
57    /// Visit an unsigned 64-bit integer value.
58    fn record_u64(&mut self, field: &Field, value: u64) {
59        self.values
60            .insert(field.name(), serde_json::Value::from(value));
61    }
62
63    /// Visit a 64-bit floating point value.
64    fn record_f64(&mut self, field: &Field, value: f64) {
65        self.values
66            .insert(field.name(), serde_json::Value::from(value));
67    }
68
69    /// Visit a boolean value.
70    fn record_bool(&mut self, field: &Field, value: bool) {
71        self.values
72            .insert(field.name(), serde_json::Value::from(value));
73    }
74
75    /// Visit a string value.
76    fn record_str(&mut self, field: &Field, value: &str) {
77        self.values
78            .insert(field.name(), serde_json::Value::from(value));
79    }
80
81    fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
82        match field.name() {
83            // Skip fields that are actually log metadata that have already been handled
84            name if name.starts_with("log.") => (),
85            name if name.starts_with("r#") => {
86                self.values
87                    .insert(&name[2..], serde_json::Value::from(format!("{:?}", value)));
88            }
89            name => {
90                self.values
91                    .insert(name, serde_json::Value::from(format!("{:?}", value)));
92            }
93        };
94    }
95
96    #[cfg(all(tracing_unstable, feature = "valuable"))]
97    #[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))]
98    fn record_value(&mut self, field: &Field, value: valuable::Value<'_>) {
99        let serializable = valuable_serde::Serializable::new(value);
100
101        match serde_json::to_value(serializable) {
102            Ok(json_value) => {
103                self.values.insert(field.name(), json_value);
104            }
105            Err(error) => {
106                tracing::debug!(
107                    // The parent span may be the one with this
108                    // unserializable field value. If so logging an event
109                    // under this parent span might trigger it field value
110                    // to be serialized again, causing an infinite loop.
111                    // Avoid this by explicitly setting the parent span to `None`.
112                    parent: None,
113                    ?error,
114                    field_name = field.name(),
115                    "serde_json serialization error while recording valuable field."
116                );
117            }
118        }
119    }
120}
121
122impl<S: Subscriber + for<'a> tracing_subscriber::registry::LookupSpan<'a>> Layer<S>
123    for JsonStorageLayer
124{
125    /// Span creation.
126    /// This is the only occasion we have to store the fields attached to the span
127    /// given that they might have been borrowed from the surrounding context.
128    fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
129        let span = ctx.span(id).expect("Span not found, this is a bug");
130
131        // We want to inherit the fields from the parent span, if there is one.
132        let mut visitor = if let Some(parent_span) = span.parent() {
133            // Extensions can be used to associate arbitrary data to a span.
134            // We'll use it to store our representation of its fields.
135            // We create a copy of the parent visitor!
136            let mut extensions = parent_span.extensions_mut();
137            extensions
138                .get_mut::<JsonStorage>()
139                .map(|v| v.to_owned())
140                .unwrap_or_default()
141        } else {
142            JsonStorage::default()
143        };
144
145        let mut extensions = span.extensions_mut();
146
147        // Register all fields.
148        // Fields on the new span should override fields on the parent span if there is a conflict.
149        attrs.record(&mut visitor);
150        // Associate the visitor with the Span for future usage via the Span's extensions
151        extensions.insert(visitor);
152    }
153
154    fn on_record(&self, span: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
155        let span = ctx.span(span).expect("Span not found, this is a bug");
156
157        // Before you can associate a record to an existing Span, well, that Span has to be created!
158        // We can thus rely on the invariant that we always associate a JsonVisitor with a Span
159        // on creation (`new_span` method), hence it's safe to unwrap the Option.
160        let mut extensions = span.extensions_mut();
161        let visitor = extensions
162            .get_mut::<JsonStorage>()
163            .expect("Visitor not found on 'record', this is a bug");
164        // Register all new fields
165        values.record(visitor);
166    }
167
168    /// When we enter a span **for the first time** save the timestamp in its extensions.
169    fn on_enter(&self, span: &Id, ctx: Context<'_, S>) {
170        let span = ctx.span(span).expect("Span not found, this is a bug");
171
172        let mut extensions = span.extensions_mut();
173        if extensions.get_mut::<Instant>().is_none() {
174            extensions.insert(Instant::now());
175        }
176    }
177
178    /// When we close a span, register how long it took in milliseconds.
179    fn on_close(&self, span: Id, ctx: Context<'_, S>) {
180        let span = ctx.span(&span).expect("Span not found, this is a bug");
181
182        // Using a block to drop the immutable reference to extensions
183        // given that we want to borrow it mutably just below
184        let elapsed_milliseconds = {
185            let extensions = span.extensions();
186            extensions
187                .get::<Instant>()
188                .map(|i| i.elapsed().as_millis())
189                // If `Instant` is not in the span extensions it means that the span was never
190                // entered into.
191                .unwrap_or(0)
192        };
193
194        #[cfg(not(feature = "arbitrary-precision"))]
195        // without the arbitrary_precision feature u128 values are not supported,
196        // but u64 is still more than enough for our purposes
197        let elapsed_milliseconds: u64 = {
198            use std::convert::TryInto;
199
200            elapsed_milliseconds.try_into().unwrap_or_default()
201        };
202
203        let mut extensions_mut = span.extensions_mut();
204        let visitor = extensions_mut
205            .get_mut::<JsonStorage>()
206            .expect("Visitor not found on 'record', this is a bug");
207
208        if let Ok(elapsed) = serde_json::to_value(elapsed_milliseconds) {
209            visitor.values.insert("elapsed_milliseconds", elapsed);
210        }
211    }
212}