1use std::io::Write;
18use std::time::SystemTime;
19use tracing_core::{Event, Subscriber};
20use tracing_subscriber::Layer;
21use tracing_subscriber::layer::Context;
22use tracing_subscriber::registry::LookupSpan;
23
24mod visitor;
25
26#[cfg(feature = "_bench_internals")]
27pub mod writer;
28#[cfg(not(feature = "_bench_internals"))]
29mod writer;
30
31use visitor::JsonVisitor;
32use writer::JsonWriter;
33
34struct SpanFields(String);
36
37pub struct JsonLayer<W> {
39 make_writer: W,
40 display_target: bool,
41 display_filename: bool,
42 display_line_number: bool,
43 flatten_event: bool,
44}
45
46impl<W> JsonLayer<W>
47where
48 W: for<'w> tracing_subscriber::fmt::MakeWriter<'w> + 'static,
49{
50 pub fn new(make_writer: W) -> Self {
52 Self {
53 make_writer,
54 display_target: true,
55 display_filename: false,
56 display_line_number: false,
57 flatten_event: false,
58 }
59 }
60
61 pub fn with_target(mut self, display_target: bool) -> Self {
63 self.display_target = display_target;
64 self
65 }
66
67 pub fn with_file(mut self, display_filename: bool) -> Self {
69 self.display_filename = display_filename;
70 self
71 }
72
73 pub fn with_line_number(mut self, display_line: bool) -> Self {
75 self.display_line_number = display_line;
76 self
77 }
78
79 pub fn flatten_event(mut self, flatten: bool) -> Self {
82 self.flatten_event = flatten;
83 self
84 }
85}
86
87impl<S, W> Layer<S> for JsonLayer<W>
88where
89 S: Subscriber + for<'a> LookupSpan<'a>,
90 W: for<'w> tracing_subscriber::fmt::MakeWriter<'w> + 'static,
91{
92 fn on_new_span(
93 &self,
94 attrs: &tracing_core::span::Attributes<'_>,
95 id: &tracing_core::span::Id,
96 ctx: Context<'_, S>,
97 ) {
98 let span = match ctx.span(id) {
99 Some(s) => s,
100 None => return,
101 };
102 let mut jw = JsonWriter::new();
103 let mut visitor = JsonVisitor::new(&mut jw);
104 attrs.record(&mut visitor);
105 span.extensions_mut().insert(SpanFields(jw.into_string()));
106 }
107
108 fn on_record(
109 &self,
110 id: &tracing_core::span::Id,
111 values: &tracing_core::span::Record<'_>,
112 ctx: Context<'_, S>,
113 ) {
114 let span = match ctx.span(id) {
115 Some(s) => s,
116 None => return,
117 };
118 let mut ext = span.extensions_mut();
119 if let Some(fields) = ext.get_mut::<SpanFields>() {
120 let has_existing = !fields.0.is_empty();
121 let mut jw = JsonWriter::continuing(&fields.0);
122 let mut visitor = if has_existing {
123 JsonVisitor::continuing(&mut jw)
124 } else {
125 JsonVisitor::new(&mut jw)
126 };
127 values.record(&mut visitor);
128 fields.0 = jw.into_string();
129 }
130 }
131
132 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
133 let mut jw = JsonWriter::new();
134
135 jw.obj_start();
137 jw.key("timestamp");
138 jw.val_str(&format_timestamp(SystemTime::now()));
139
140 jw.comma();
142 jw.key("level");
143 jw.val_str(&event.metadata().level().to_string());
144
145 if self.flatten_event {
146 let mut visitor = JsonVisitor::continuing(&mut jw);
148 event.record(&mut visitor);
149 } else {
150 jw.comma();
152 jw.key("fields");
153 jw.obj_start();
154 let mut visitor = JsonVisitor::new(&mut jw);
155 event.record(&mut visitor);
156 jw.obj_end();
157 }
158
159 if self.display_target {
161 jw.comma();
162 jw.key("target");
163 jw.val_str(event.metadata().target());
164 }
165
166 if self.display_filename
168 && let Some(file) = event.metadata().file()
169 {
170 jw.comma();
171 jw.key("filename");
172 jw.val_str(file);
173 }
174
175 if self.display_line_number
177 && let Some(line) = event.metadata().line()
178 {
179 jw.comma();
180 jw.key("line_number");
181 jw.val_u64(line as u64);
182 }
183
184 if let Some(scope) = ctx.event_scope(event) {
186 let spans: Vec<_> = scope.collect();
187
188 if let Some(leaf) = spans.first() {
190 jw.comma();
191 jw.key("span");
192 jw.obj_start();
193 jw.key("name");
194 jw.val_str(leaf.name());
195 let ext = leaf.extensions();
196 if let Some(fields) = ext.get::<SpanFields>()
197 && !fields.0.is_empty()
198 {
199 jw.comma();
200 jw.raw(&fields.0);
201 }
202 jw.obj_end();
203 }
204
205 jw.comma();
207 jw.key("spans");
208 jw.arr_start();
209 for (i, span) in spans.iter().rev().enumerate() {
210 if i > 0 {
211 jw.comma();
212 }
213 jw.obj_start();
214 jw.key("name");
215 jw.val_str(span.name());
216 let ext = span.extensions();
217 if let Some(fields) = ext.get::<SpanFields>()
218 && !fields.0.is_empty()
219 {
220 jw.comma();
221 jw.raw(&fields.0);
222 }
223 jw.obj_end();
224 }
225 jw.arr_end();
226 }
227
228 jw.obj_end();
229 jw.finish_line();
230
231 let line = jw.into_string();
232 let mut writer = self.make_writer.make_writer();
233 let _ = writer.write_all(line.as_bytes());
234 }
235}
236
237fn format_timestamp(t: SystemTime) -> String {
240 let dur = t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default();
241 let secs = dur.as_secs();
242 let micros = dur.subsec_micros();
243
244 let (year, month, day, hour, min, sec) = secs_to_datetime(secs);
246
247 format!(
248 "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z",
249 year, month, day, hour, min, sec, micros
250 )
251}
252
253fn secs_to_datetime(secs: u64) -> (u64, u64, u64, u64, u64, u64) {
255 let sec = secs % 60;
256 let mins = secs / 60;
257 let min = mins % 60;
258 let hours = mins / 60;
259 let hour = hours % 24;
260 let days = hours / 24;
261
262 let (year, month, day) = days_to_ymd(days);
264
265 (year, month, day, hour, min, sec)
266}
267
268fn days_to_ymd(days: u64) -> (u64, u64, u64) {
269 let z = days + 719468;
271 let era = z / 146097;
272 let doe = z % 146097;
273 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
274 let y = yoe + era * 400;
275 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
276 let mp = (5 * doy + 2) / 153;
277 let d = doy - (153 * mp + 2) / 5 + 1;
278 let m = if mp < 10 { mp + 3 } else { mp - 9 };
279 let y = if m <= 2 { y + 1 } else { y };
280 (y, m, d)
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 fn val_str_output(s: &str) -> String {
289 let mut jw = JsonWriter::new();
290 jw.val_str(s);
291 jw.into_string()
292 }
293
294 #[test]
295 fn test_val_str_basic() {
296 assert_eq!(val_str_output("hello"), r#""hello""#);
297 assert_eq!(val_str_output("say \"hi\""), r#""say \"hi\"""#);
298 assert_eq!(val_str_output("back\\slash"), r#""back\\slash""#);
299 assert_eq!(val_str_output(""), r#""""#);
300 }
301
302 #[test]
303 fn test_val_str_control_chars() {
304 assert_eq!(val_str_output("\n"), r#""\n""#);
305 assert_eq!(val_str_output("\r"), r#""\r""#);
306 assert_eq!(val_str_output("\t"), r#""\t""#);
307 assert_eq!(val_str_output("\x08"), r#""\b""#);
308 assert_eq!(val_str_output("\x0C"), r#""\f""#);
309 assert_eq!(val_str_output("\x01"), r#""\u0001""#);
311 assert_eq!(val_str_output("\x1F"), r#""\u001f""#);
312 }
313
314 #[test]
315 fn test_val_str_unicode_passthrough() {
316 assert_eq!(val_str_output("café"), "\"café\"");
318 assert_eq!(val_str_output("日本語"), "\"日本語\"");
319 }
320
321 #[test]
322 fn test_f64_edge_cases() {
323 let mut jw = JsonWriter::new();
324 jw.val_f64(f64::NAN);
325 assert_eq!(jw.into_string(), "null");
326
327 let mut jw = JsonWriter::new();
328 jw.val_f64(f64::INFINITY);
329 assert_eq!(jw.into_string(), "null");
330
331 let mut jw = JsonWriter::new();
332 jw.val_f64(f64::NEG_INFINITY);
333 assert_eq!(jw.into_string(), "null");
334
335 let mut jw = JsonWriter::new();
336 jw.val_f64(-0.0_f64);
337 let s = jw.into_string();
338 assert!(
340 s == "-0" || s == "0" || s == "-0.0" || s == "0.0",
341 "got: {s}"
342 );
343
344 let mut jw = JsonWriter::new();
345 jw.val_f64(2.78);
346 let s = jw.into_string();
347 assert!(s.contains("2.78"), "got: {s}");
348 }
349
350 #[test]
351 fn test_timestamp_format() {
352 let epoch = SystemTime::UNIX_EPOCH;
354 let s = format_timestamp(epoch);
355 assert_eq!(s, "1970-01-01T00:00:00.000000Z");
356
357 let t = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1771588800);
359 let s = format_timestamp(t);
360 assert_eq!(s, "2026-02-20T12:00:00.000000Z");
361 }
362
363 #[test]
364 fn test_timestamp_microsecond_precision() {
365 let t = SystemTime::UNIX_EPOCH
367 + std::time::Duration::from_micros(1_771_588_800 * 1_000_000 + 123_456);
368 let s = format_timestamp(t);
369 assert_eq!(s, "2026-02-20T12:00:00.123456Z");
370
371 let t = SystemTime::UNIX_EPOCH + std::time::Duration::from_micros(1);
373 let s = format_timestamp(t);
374 assert_eq!(s, "1970-01-01T00:00:00.000001Z");
375
376 let t = SystemTime::UNIX_EPOCH + std::time::Duration::from_micros(999_999);
378 let s = format_timestamp(t);
379 assert_eq!(s, "1970-01-01T00:00:00.999999Z");
380 }
381}