tracing_ndjson/
layer.rs

1use std::collections::HashMap;
2
3use serde_json::json;
4use tracing_core::Subscriber;
5use tracing_subscriber::{registry::LookupSpan, Layer};
6
7use crate::{storage::JsonStorage, TimestampFormat};
8
9pub struct JsonFormattingLayer {
10    pub(crate) level_name: &'static str,
11    pub(crate) level_value_casing: crate::Casing,
12    pub(crate) message_name: &'static str,
13    pub(crate) target_name: &'static str,
14    pub(crate) timestamp_name: &'static str,
15    pub(crate) timestamp_format: crate::TimestampFormat,
16    pub(crate) line_numbers: bool,
17    pub(crate) file_names: bool,
18    pub(crate) flatten_fields: bool,
19    pub(crate) flatten_spans: bool,
20}
21
22impl Default for JsonFormattingLayer {
23    fn default() -> Self {
24        Self {
25            level_name: "level",
26            level_value_casing: crate::Casing::default(),
27            message_name: "message",
28            target_name: "target",
29            timestamp_name: "timestamp",
30            timestamp_format: crate::TimestampFormat::default(),
31            line_numbers: false,
32            file_names: false,
33            flatten_fields: true,
34            flatten_spans: true,
35        }
36    }
37}
38
39impl<S> Layer<S> for JsonFormattingLayer
40where
41    S: Subscriber + for<'a> LookupSpan<'a>,
42{
43    fn on_new_span(
44        &self,
45        attrs: &tracing_core::span::Attributes<'_>,
46        id: &tracing_core::span::Id,
47        ctx: tracing_subscriber::layer::Context<'_, S>,
48    ) {
49        let span = ctx.span(id).expect("Span not found, this is a bug");
50
51        // Create a new visitor to store fields
52        let mut visitor = JsonStorage::default();
53
54        // Register all fields.
55        // Fields on the new span should override fields on the parent span if there is a conflict.
56        attrs.record(&mut visitor);
57
58        // Associate the visitor with the Span for future usage via the Span's extensions
59        let mut extensions = span.extensions_mut();
60        extensions.insert(visitor);
61    }
62
63    fn on_record(
64        &self,
65        span: &tracing_core::span::Id,
66        values: &tracing_core::span::Record<'_>,
67        ctx: tracing_subscriber::layer::Context<'_, S>,
68    ) {
69        let span = ctx.span(span).expect("Span not found, this is a bug");
70
71        // Before you can associate a record to an existing Span, well, that Span has to be created!
72        // We can thus rely on the invariant that we always associate a JsonVisitor with a Span
73        // on creation (`new_span` method), hence it's safe to unwrap the Option.
74        let mut extensions = span.extensions_mut();
75        let visitor = extensions
76            .get_mut::<JsonStorage>()
77            .expect("Visitor not found on 'record', this is a bug");
78        // Register all new fields
79        values.record(visitor);
80    }
81
82    fn on_event(
83        &self,
84        event: &tracing_core::Event<'_>,
85        _ctx: tracing_subscriber::layer::Context<'_, S>,
86    ) {
87        // Record the event fields
88        let mut visitor = crate::storage::JsonStorage::default();
89        event.record(&mut visitor);
90
91        let mut root: HashMap<&str, serde_json::Value> = HashMap::new();
92
93        // level
94        root.insert(
95            self.level_name,
96            match self.level_value_casing {
97                crate::Casing::Lowercase => {
98                    json!(event.metadata().level().to_string().to_lowercase())
99                }
100                crate::Casing::Uppercase => {
101                    json!(event.metadata().level().to_string().to_uppercase())
102                }
103            },
104        );
105
106        // target
107        root.insert(self.target_name, json!(event.metadata().target()));
108
109        // timestamp
110        let timestamp = match &self.timestamp_format {
111            TimestampFormat::Unix | TimestampFormat::UnixMillis => {
112                json!(self.timestamp_format.format_number(&chrono::Utc::now()))
113            }
114            TimestampFormat::Rfc3339 | TimestampFormat::Rfc3339Nanos => {
115                json!(self.timestamp_format.format_string(&chrono::Utc::now()))
116            }
117            TimestampFormat::Custom(_) => {
118                json!(self.timestamp_format.format_string(&chrono::Utc::now()))
119            }
120        };
121        root.insert(self.timestamp_name, timestamp);
122
123        if self.file_names && event.metadata().file().is_some() {
124            root.insert("file", json!(event.metadata().file().expect("is some")));
125        }
126
127        if self.line_numbers && event.metadata().line().is_some() {
128            root.insert("line", json!(event.metadata().line().expect("is some")));
129        }
130
131        // Serialize the event fields
132        if self.flatten_fields {
133            visitor.values().iter().for_each(|(k, v)| {
134                if *k == "message" {
135                    root.insert(self.message_name, v.clone());
136                } else {
137                    root.insert(k, v.clone());
138                }
139            });
140        } else {
141            let mut fields = HashMap::new();
142            visitor.values().iter().for_each(|(k, v)| {
143                if *k == "message" {
144                    fields.insert(self.message_name, v.clone());
145                } else {
146                    fields.insert(k, v.clone());
147                }
148            });
149            root.insert("fields", json!(fields));
150        }
151
152        // Span fields (if any)
153        let mut spans = vec![];
154        if let Some(leaf_span) = _ctx.lookup_current() {
155            for span in leaf_span.scope().from_root() {
156                let mut fields = HashMap::new();
157                let ext = span.extensions();
158                let visitor = ext.get::<crate::storage::JsonStorage>();
159                if let Some(visitor) = visitor {
160                    visitor.values().iter().for_each(|(k, v)| {
161                        if *k == "message" {
162                            fields.insert(self.message_name, v.clone());
163                        } else {
164                            fields.insert(k, v.clone());
165                        }
166                    });
167                }
168                if !fields.is_empty() {
169                    spans.push(fields);
170                }
171            }
172        }
173
174        if !spans.is_empty() {
175            if self.flatten_spans {
176                spans.iter().for_each(|fields| {
177                    fields.iter().for_each(|(k, v)| {
178                        if *k == "message" {
179                            root.insert(self.message_name, v.clone());
180                        } else {
181                            root.insert(k, v.clone());
182                        }
183                    });
184                });
185            } else {
186                root.insert("spans", json!(spans));
187            }
188        }
189
190        let output = serde_json::to_string(&root).unwrap();
191        println!("{}", output);
192    }
193}