1use std::io::Write;
74use std::time::SystemTime;
75use tracing_core::{Event, Subscriber};
76use tracing_subscriber::Layer;
77use tracing_subscriber::fmt::format::Writer as FmtWriter;
78use tracing_subscriber::layer::Context;
79use tracing_subscriber::registry::LookupSpan;
80
81pub use tracing_subscriber::fmt::time::FormatTime;
82
83mod visitor;
84
85#[cfg(feature = "_bench_internals")]
86pub mod writer;
87#[cfg(not(feature = "_bench_internals"))]
88mod writer;
89
90use visitor::JsonVisitor;
91use writer::JsonWriter;
92
93pub struct SystemTimestamp;
99
100impl FormatTime for SystemTimestamp {
101 fn format_time(&self, w: &mut FmtWriter<'_>) -> std::fmt::Result {
102 w.write_str(&format_timestamp(SystemTime::now()))
103 }
104}
105
106struct SpanFields(String);
108
109pub struct JsonLayer<W, T = SystemTimestamp> {
114 make_writer: W,
115 timer: T,
116 display_target: bool,
117 display_filename: bool,
118 display_line_number: bool,
119 display_thread_id: bool,
120 display_thread_name: bool,
121 flatten_event: bool,
122}
123
124impl<W> JsonLayer<W>
125where
126 W: for<'w> tracing_subscriber::fmt::MakeWriter<'w> + 'static,
127{
128 pub fn new(make_writer: W) -> Self {
133 Self {
134 make_writer,
135 timer: SystemTimestamp,
136 display_target: true,
137 display_filename: false,
138 display_line_number: false,
139 display_thread_id: false,
140 display_thread_name: false,
141 flatten_event: false,
142 }
143 }
144}
145
146impl<W, T> JsonLayer<W, T>
147where
148 W: for<'w> tracing_subscriber::fmt::MakeWriter<'w> + 'static,
149{
150 pub fn with_target(mut self, display_target: bool) -> Self {
154 self.display_target = display_target;
155 self
156 }
157
158 pub fn with_file(mut self, display_filename: bool) -> Self {
162 self.display_filename = display_filename;
163 self
164 }
165
166 pub fn with_line_number(mut self, display_line: bool) -> Self {
170 self.display_line_number = display_line;
171 self
172 }
173
174 pub fn with_thread_ids(mut self, display_thread_id: bool) -> Self {
178 self.display_thread_id = display_thread_id;
179 self
180 }
181
182 pub fn with_thread_names(mut self, display_thread_name: bool) -> Self {
186 self.display_thread_name = display_thread_name;
187 self
188 }
189
190 pub fn flatten_event(mut self, flatten: bool) -> Self {
195 self.flatten_event = flatten;
196 self
197 }
198
199 pub fn with_timer<T2: FormatTime>(self, timer: T2) -> JsonLayer<W, T2> {
208 JsonLayer {
209 make_writer: self.make_writer,
210 timer,
211 display_target: self.display_target,
212 display_filename: self.display_filename,
213 display_line_number: self.display_line_number,
214 display_thread_id: self.display_thread_id,
215 display_thread_name: self.display_thread_name,
216 flatten_event: self.flatten_event,
217 }
218 }
219
220 pub fn without_time(self) -> JsonLayer<W, ()> {
224 self.with_timer(())
225 }
226}
227
228impl<S, W, T> Layer<S> for JsonLayer<W, T>
229where
230 S: Subscriber + for<'a> LookupSpan<'a>,
231 W: for<'w> tracing_subscriber::fmt::MakeWriter<'w> + 'static,
232 T: FormatTime + 'static,
233{
234 fn on_new_span(
235 &self,
236 attrs: &tracing_core::span::Attributes<'_>,
237 id: &tracing_core::span::Id,
238 ctx: Context<'_, S>,
239 ) {
240 let span = match ctx.span(id) {
241 Some(s) => s,
242 None => return,
243 };
244 let mut jw = JsonWriter::new();
245 let mut visitor = JsonVisitor::new(&mut jw);
246 attrs.record(&mut visitor);
247 span.extensions_mut().insert(SpanFields(jw.into_string()));
248 }
249
250 fn on_record(
251 &self,
252 id: &tracing_core::span::Id,
253 values: &tracing_core::span::Record<'_>,
254 ctx: Context<'_, S>,
255 ) {
256 let span = match ctx.span(id) {
257 Some(s) => s,
258 None => return,
259 };
260 let mut ext = span.extensions_mut();
261 if let Some(fields) = ext.get_mut::<SpanFields>() {
262 let has_existing = !fields.0.is_empty();
263 let mut jw = JsonWriter::continuing(&fields.0);
264 let mut visitor = if has_existing {
265 JsonVisitor::continuing(&mut jw)
266 } else {
267 JsonVisitor::new(&mut jw)
268 };
269 values.record(&mut visitor);
270 fields.0 = jw.into_string();
271 }
272 }
273
274 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
275 let mut jw = JsonWriter::new();
276
277 jw.obj_start();
278
279 let mut ts_buf = String::new();
281 {
282 let mut fmt_writer = FmtWriter::new(&mut ts_buf);
283 let _ = self.timer.format_time(&mut fmt_writer);
284 }
285 let wrote_timestamp = !ts_buf.is_empty();
286 if wrote_timestamp {
287 jw.key("timestamp");
288 jw.val_str(&ts_buf);
289 }
290
291 if wrote_timestamp {
293 jw.comma();
294 }
295 jw.key("level");
296 jw.val_str(&event.metadata().level().to_string());
297
298 if self.flatten_event {
299 let mut visitor = JsonVisitor::continuing(&mut jw);
301 event.record(&mut visitor);
302 } else {
303 jw.comma();
305 jw.key("fields");
306 jw.obj_start();
307 let mut visitor = JsonVisitor::new(&mut jw);
308 event.record(&mut visitor);
309 jw.obj_end();
310 }
311
312 if self.display_target {
314 jw.comma();
315 jw.key("target");
316 jw.val_str(event.metadata().target());
317 }
318
319 if self.display_filename
321 && let Some(file) = event.metadata().file()
322 {
323 jw.comma();
324 jw.key("filename");
325 jw.val_str(file);
326 }
327
328 if self.display_line_number
330 && let Some(line) = event.metadata().line()
331 {
332 jw.comma();
333 jw.key("line_number");
334 jw.val_u64(line as u64);
335 }
336
337 if self.display_thread_id {
339 jw.comma();
340 jw.key("threadId");
341 jw.val_str(&format!("{:?}", std::thread::current().id()));
342 }
343
344 if self.display_thread_name {
346 jw.comma();
347 jw.key("threadName");
348 if let Some(name) = std::thread::current().name() {
349 jw.val_str(name);
350 } else {
351 jw.val_str("");
352 }
353 }
354
355 if let Some(scope) = ctx.event_scope(event) {
357 let spans: Vec<_> = scope.collect();
358
359 if let Some(leaf) = spans.first() {
361 jw.comma();
362 jw.key("span");
363 jw.obj_start();
364 jw.key("name");
365 jw.val_str(leaf.name());
366 let ext = leaf.extensions();
367 if let Some(fields) = ext.get::<SpanFields>()
368 && !fields.0.is_empty()
369 {
370 jw.comma();
371 jw.raw(&fields.0);
372 }
373 jw.obj_end();
374 }
375
376 jw.comma();
378 jw.key("spans");
379 jw.arr_start();
380 for (i, span) in spans.iter().rev().enumerate() {
381 if i > 0 {
382 jw.comma();
383 }
384 jw.obj_start();
385 jw.key("name");
386 jw.val_str(span.name());
387 let ext = span.extensions();
388 if let Some(fields) = ext.get::<SpanFields>()
389 && !fields.0.is_empty()
390 {
391 jw.comma();
392 jw.raw(&fields.0);
393 }
394 jw.obj_end();
395 }
396 jw.arr_end();
397 }
398
399 jw.obj_end();
400 jw.finish_line();
401
402 let line = jw.into_string();
403 let mut writer = self.make_writer.make_writer();
404 let _ = writer.write_all(line.as_bytes());
405 }
406}
407
408fn format_timestamp(t: SystemTime) -> String {
411 let dur = t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default();
412 let secs = dur.as_secs();
413 let micros = dur.subsec_micros();
414
415 let (year, month, day, hour, min, sec) = secs_to_datetime(secs);
417
418 format!("{year:04}-{month:02}-{day:02}T{hour:02}:{min:02}:{sec:02}.{micros:06}Z")
419}
420
421fn secs_to_datetime(secs: u64) -> (u64, u64, u64, u64, u64, u64) {
423 let sec = secs % 60;
424 let mins = secs / 60;
425 let min = mins % 60;
426 let hours = mins / 60;
427 let hour = hours % 24;
428 let days = hours / 24;
429
430 let (year, month, day) = days_to_ymd(days);
432
433 (year, month, day, hour, min, sec)
434}
435
436fn days_to_ymd(days: u64) -> (u64, u64, u64) {
437 let z = days + 719468;
439 let era = z / 146097;
440 let doe = z % 146097;
441 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
442 let y = yoe + era * 400;
443 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
444 let mp = (5 * doy + 2) / 153;
445 let d = doy - (153 * mp + 2) / 5 + 1;
446 let m = if mp < 10 { mp + 3 } else { mp - 9 };
447 let y = if m <= 2 { y + 1 } else { y };
448 (y, m, d)
449}
450
451#[cfg(test)]
452mod tests {
453 use super::*;
454
455 fn val_str_output(s: &str) -> String {
457 let mut jw = JsonWriter::new();
458 jw.val_str(s);
459 jw.into_string()
460 }
461
462 #[test]
463 fn test_val_str_basic() {
464 assert_eq!(val_str_output("hello"), r#""hello""#);
465 assert_eq!(val_str_output("say \"hi\""), r#""say \"hi\"""#);
466 assert_eq!(val_str_output("back\\slash"), r#""back\\slash""#);
467 assert_eq!(val_str_output(""), r#""""#);
468 }
469
470 #[test]
471 fn test_val_str_control_chars() {
472 assert_eq!(val_str_output("\n"), r#""\n""#);
473 assert_eq!(val_str_output("\r"), r#""\r""#);
474 assert_eq!(val_str_output("\t"), r#""\t""#);
475 assert_eq!(val_str_output("\x08"), r#""\b""#);
476 assert_eq!(val_str_output("\x0C"), r#""\f""#);
477 assert_eq!(val_str_output("\x01"), r#""\u0001""#);
479 assert_eq!(val_str_output("\x1F"), r#""\u001f""#);
480 }
481
482 #[test]
483 fn test_val_str_unicode_passthrough() {
484 assert_eq!(val_str_output("café"), "\"café\"");
486 assert_eq!(val_str_output("日本語"), "\"日本語\"");
487 }
488
489 #[test]
490 fn test_f64_edge_cases() {
491 let mut jw = JsonWriter::new();
492 jw.val_f64(f64::NAN);
493 assert_eq!(jw.into_string(), "null");
494
495 let mut jw = JsonWriter::new();
496 jw.val_f64(f64::INFINITY);
497 assert_eq!(jw.into_string(), "null");
498
499 let mut jw = JsonWriter::new();
500 jw.val_f64(f64::NEG_INFINITY);
501 assert_eq!(jw.into_string(), "null");
502
503 let mut jw = JsonWriter::new();
504 jw.val_f64(-0.0_f64);
505 let s = jw.into_string();
506 assert!(
508 s == "-0" || s == "0" || s == "-0.0" || s == "0.0",
509 "got: {s}"
510 );
511
512 let mut jw = JsonWriter::new();
513 jw.val_f64(2.78);
514 let s = jw.into_string();
515 assert!(s.contains("2.78"), "got: {s}");
516 }
517
518 #[test]
519 fn test_timestamp_format() {
520 let epoch = SystemTime::UNIX_EPOCH;
522 let s = format_timestamp(epoch);
523 assert_eq!(s, "1970-01-01T00:00:00.000000Z");
524
525 let t = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1771588800);
527 let s = format_timestamp(t);
528 assert_eq!(s, "2026-02-20T12:00:00.000000Z");
529 }
530
531 #[test]
532 fn test_timestamp_microsecond_precision() {
533 let t = SystemTime::UNIX_EPOCH
535 + std::time::Duration::from_micros(1_771_588_800 * 1_000_000 + 123_456);
536 let s = format_timestamp(t);
537 assert_eq!(s, "2026-02-20T12:00:00.123456Z");
538
539 let t = SystemTime::UNIX_EPOCH + std::time::Duration::from_micros(1);
541 let s = format_timestamp(t);
542 assert_eq!(s, "1970-01-01T00:00:00.000001Z");
543
544 let t = SystemTime::UNIX_EPOCH + std::time::Duration::from_micros(999_999);
546 let s = format_timestamp(t);
547 assert_eq!(s, "1970-01-01T00:00:00.999999Z");
548 }
549}