Skip to main content

tracing_ndjson/
layer.rs

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