1use std::cell::Cell;
75use std::io::Write;
76use std::time::SystemTime;
77use tracing_core::{Event, Subscriber};
78use tracing_subscriber::Layer;
79use tracing_subscriber::fmt::format::Writer as FmtWriter;
80use tracing_subscriber::layer::Context;
81use tracing_subscriber::registry::LookupSpan;
82
83pub use tracing_subscriber::fmt::time::FormatTime;
84
85mod visitor;
86
87#[cfg(feature = "_bench_internals")]
88pub mod writer;
89#[cfg(not(feature = "_bench_internals"))]
90mod writer;
91
92use visitor::JsonVisitor;
93use writer::JsonWriter;
94
95pub struct SystemTimestamp;
101
102impl FormatTime for SystemTimestamp {
103 fn format_time(&self, w: &mut FmtWriter<'_>) -> std::fmt::Result {
104 write_timestamp(SystemTime::now(), w)
105 }
106}
107
108struct SpanFields(Vec<u8>);
110
111thread_local! {
112 static EVENT_BUF: Cell<Vec<u8>> = const { Cell::new(Vec::new()) };
113}
114
115pub struct JsonLayer<W, T = SystemTimestamp> {
120 make_writer: W,
121 timer: T,
122 display_target: bool,
123 display_filename: bool,
124 display_line_number: bool,
125 display_thread_id: bool,
126 display_thread_name: bool,
127 flatten_event: bool,
128 buf_cap_limit: usize,
129}
130
131impl<W, T> JsonLayer<W, T> {
132 const DEFAULT_BUF_CAPACITY: usize = 256;
133 const DEFAULT_BUF_CAP_LIMIT: usize = 4096;
134}
135
136impl<W> JsonLayer<W>
137where
138 W: for<'w> tracing_subscriber::fmt::MakeWriter<'w> + 'static,
139{
140 pub fn new(make_writer: W) -> Self {
145 Self {
146 make_writer,
147 timer: SystemTimestamp,
148 display_target: true,
149 display_filename: false,
150 display_line_number: false,
151 display_thread_id: false,
152 display_thread_name: false,
153 flatten_event: false,
154 buf_cap_limit: Self::DEFAULT_BUF_CAP_LIMIT,
155 }
156 }
157}
158
159impl<W, T> JsonLayer<W, T>
160where
161 W: for<'w> tracing_subscriber::fmt::MakeWriter<'w> + 'static,
162{
163 pub fn with_target(mut self, display_target: bool) -> Self {
167 self.display_target = display_target;
168 self
169 }
170
171 pub fn with_file(mut self, display_filename: bool) -> Self {
175 self.display_filename = display_filename;
176 self
177 }
178
179 pub fn with_line_number(mut self, display_line: bool) -> Self {
183 self.display_line_number = display_line;
184 self
185 }
186
187 pub fn with_thread_ids(mut self, display_thread_id: bool) -> Self {
191 self.display_thread_id = display_thread_id;
192 self
193 }
194
195 pub fn with_thread_names(mut self, display_thread_name: bool) -> Self {
199 self.display_thread_name = display_thread_name;
200 self
201 }
202
203 pub fn flatten_event(mut self, flatten: bool) -> Self {
208 self.flatten_event = flatten;
209 self
210 }
211
212 pub fn with_buffer_capacity_limit(mut self, limit: usize) -> Self {
222 self.buf_cap_limit = limit;
223 self
224 }
225
226 pub fn with_timer<T2: FormatTime>(self, timer: T2) -> JsonLayer<W, T2> {
235 JsonLayer {
236 make_writer: self.make_writer,
237 timer,
238 display_target: self.display_target,
239 display_filename: self.display_filename,
240 display_line_number: self.display_line_number,
241 display_thread_id: self.display_thread_id,
242 display_thread_name: self.display_thread_name,
243 flatten_event: self.flatten_event,
244 buf_cap_limit: self.buf_cap_limit,
245 }
246 }
247
248 pub fn without_time(self) -> JsonLayer<W, ()> {
252 self.with_timer(())
253 }
254}
255
256impl<S, W, T> Layer<S> for JsonLayer<W, T>
257where
258 S: Subscriber + for<'a> LookupSpan<'a>,
259 W: for<'w> tracing_subscriber::fmt::MakeWriter<'w> + 'static,
260 T: FormatTime + 'static,
261{
262 fn on_new_span(
263 &self,
264 attrs: &tracing_core::span::Attributes<'_>,
265 id: &tracing_core::span::Id,
266 ctx: Context<'_, S>,
267 ) {
268 let span = match ctx.span(id) {
269 Some(s) => s,
270 None => return,
271 };
272 let mut jw = JsonWriter::new();
273 let mut visitor = JsonVisitor::new(&mut jw);
274 attrs.record(&mut visitor);
275 span.extensions_mut().insert(SpanFields(jw.into_vec()));
276 }
277
278 fn on_record(
279 &self,
280 id: &tracing_core::span::Id,
281 values: &tracing_core::span::Record<'_>,
282 ctx: Context<'_, S>,
283 ) {
284 let span = match ctx.span(id) {
285 Some(s) => s,
286 None => return,
287 };
288 let mut ext = span.extensions_mut();
289 if let Some(fields) = ext.get_mut::<SpanFields>() {
290 let has_existing = !fields.0.is_empty();
291 let mut jw = JsonWriter::continuing(&fields.0);
292 let mut visitor = if has_existing {
293 JsonVisitor::continuing(&mut jw)
294 } else {
295 JsonVisitor::new(&mut jw)
296 };
297 values.record(&mut visitor);
298 fields.0 = jw.into_vec();
299 }
300 }
301
302 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
303 EVENT_BUF.with(|cell| {
304 let mut buf = cell.take();
305 buf.clear();
306 let mut jw = JsonWriter::from_vec(buf);
307
308 jw.obj_start();
309
310 let wrote_timestamp = {
316 let rollback = jw.len();
317 jw.raw(b"\"timestamp\":\"");
318 let val_start = jw.len();
319 {
320 let mut fw = FmtWriter::new(&mut jw);
321 let _ = self.timer.format_time(&mut fw);
322 }
323 if jw.len() > val_start {
324 jw.push_byte(b'"');
325 true
326 } else {
327 jw.truncate(rollback);
328 false
329 }
330 };
331
332 if wrote_timestamp {
334 jw.comma();
335 }
336 jw.key("level");
337 jw.val_str(event.metadata().level().as_str());
338
339 if self.flatten_event {
340 let mut visitor = JsonVisitor::continuing(&mut jw);
342 event.record(&mut visitor);
343 } else {
344 jw.comma();
346 jw.key("fields");
347 jw.obj_start();
348 let mut visitor = JsonVisitor::new(&mut jw);
349 event.record(&mut visitor);
350 jw.obj_end();
351 }
352
353 if self.display_target {
355 jw.comma();
356 jw.key("target");
357 jw.val_str(event.metadata().target());
358 }
359
360 if self.display_filename
362 && let Some(file) = event.metadata().file()
363 {
364 jw.comma();
365 jw.key("filename");
366 jw.val_str(file);
367 }
368
369 if self.display_line_number
371 && let Some(line) = event.metadata().line()
372 {
373 jw.comma();
374 jw.key("line_number");
375 jw.val_u64(line as u64);
376 }
377
378 if self.display_thread_id {
380 jw.comma();
381 jw.key("threadId");
382 jw.val_debug(&std::thread::current().id());
383 }
384
385 if self.display_thread_name {
387 jw.comma();
388 jw.key("threadName");
389 if let Some(name) = std::thread::current().name() {
390 jw.val_str(name);
391 } else {
392 jw.val_str("");
393 }
394 }
395
396 if let Some(scope) = ctx.event_scope(event) {
398 let spans: Vec<_> = scope.collect();
399
400 if let Some(leaf) = spans.first() {
402 jw.comma();
403 jw.key("span");
404 jw.obj_start();
405 jw.key("name");
406 jw.val_str(leaf.name());
407 let ext = leaf.extensions();
408 if let Some(fields) = ext.get::<SpanFields>()
409 && !fields.0.is_empty()
410 {
411 jw.comma();
412 jw.raw(&fields.0);
413 }
414 jw.obj_end();
415 }
416
417 jw.comma();
419 jw.key("spans");
420 jw.arr_start();
421 for (i, span) in spans.iter().rev().enumerate() {
422 if i > 0 {
423 jw.comma();
424 }
425 jw.obj_start();
426 jw.key("name");
427 jw.val_str(span.name());
428 let ext = span.extensions();
429 if let Some(fields) = ext.get::<SpanFields>()
430 && !fields.0.is_empty()
431 {
432 jw.comma();
433 jw.raw(&fields.0);
434 }
435 jw.obj_end();
436 }
437 jw.arr_end();
438 }
439
440 jw.obj_end();
441 jw.finish_line();
442
443 let mut writer = self.make_writer.make_writer();
444 let _ = writer.write_all(jw.as_bytes());
445
446 let mut buf = jw.into_vec();
448 if buf.capacity() > self.buf_cap_limit {
449 buf.shrink_to(Self::DEFAULT_BUF_CAPACITY);
450 }
451 cell.set(buf);
452 });
453 }
454}
455
456fn write_timestamp(t: SystemTime, w: &mut impl std::fmt::Write) -> std::fmt::Result {
459 let dur = t.duration_since(SystemTime::UNIX_EPOCH).unwrap_or_default();
460 let secs = dur.as_secs();
461 let micros = dur.subsec_micros();
462
463 let (year, month, day, hour, min, sec) = secs_to_datetime(secs);
464
465 write!(
466 w,
467 "{year:04}-{month:02}-{day:02}T{hour:02}:{min:02}:{sec:02}.{micros:06}Z"
468 )
469}
470
471#[cfg(test)]
474fn format_timestamp(t: SystemTime) -> String {
475 let mut buf = String::with_capacity(27);
476 write_timestamp(t, &mut buf).unwrap();
477 buf
478}
479
480fn secs_to_datetime(secs: u64) -> (u64, u64, u64, u64, u64, u64) {
482 let sec = secs % 60;
483 let mins = secs / 60;
484 let min = mins % 60;
485 let hours = mins / 60;
486 let hour = hours % 24;
487 let days = hours / 24;
488
489 let (year, month, day) = days_to_ymd(days);
491
492 (year, month, day, hour, min, sec)
493}
494
495fn days_to_ymd(days: u64) -> (u64, u64, u64) {
496 let z = days + 719468;
498 let era = z / 146097;
499 let doe = z % 146097;
500 let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
501 let y = yoe + era * 400;
502 let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
503 let mp = (5 * doy + 2) / 153;
504 let d = doy - (153 * mp + 2) / 5 + 1;
505 let m = if mp < 10 { mp + 3 } else { mp - 9 };
506 let y = if m <= 2 { y + 1 } else { y };
507 (y, m, d)
508}
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513
514 fn to_string(jw: JsonWriter) -> String {
516 String::from_utf8(jw.into_vec()).unwrap()
517 }
518
519 fn val_str_output(s: &str) -> String {
521 let mut jw = JsonWriter::new();
522 jw.val_str(s);
523 to_string(jw)
524 }
525
526 #[test]
527 fn test_val_str_basic() {
528 assert_eq!(val_str_output("hello"), r#""hello""#);
529 assert_eq!(val_str_output("say \"hi\""), r#""say \"hi\"""#);
530 assert_eq!(val_str_output("back\\slash"), r#""back\\slash""#);
531 assert_eq!(val_str_output(""), r#""""#);
532 }
533
534 #[test]
535 fn test_val_str_control_chars() {
536 assert_eq!(val_str_output("\n"), r#""\n""#);
537 assert_eq!(val_str_output("\r"), r#""\r""#);
538 assert_eq!(val_str_output("\t"), r#""\t""#);
539 assert_eq!(val_str_output("\x08"), r#""\b""#);
540 assert_eq!(val_str_output("\x0C"), r#""\f""#);
541 assert_eq!(val_str_output("\x01"), r#""\u0001""#);
543 assert_eq!(val_str_output("\x1F"), r#""\u001f""#);
544 }
545
546 #[test]
547 fn test_val_str_unicode_passthrough() {
548 assert_eq!(val_str_output("café"), "\"café\"");
550 assert_eq!(val_str_output("日本語"), "\"日本語\"");
551 }
552
553 #[test]
554 fn test_f64_edge_cases() {
555 let mut jw = JsonWriter::new();
556 jw.val_f64(f64::NAN);
557 assert_eq!(to_string(jw), "null");
558
559 let mut jw = JsonWriter::new();
560 jw.val_f64(f64::INFINITY);
561 assert_eq!(to_string(jw), "null");
562
563 let mut jw = JsonWriter::new();
564 jw.val_f64(f64::NEG_INFINITY);
565 assert_eq!(to_string(jw), "null");
566
567 let mut jw = JsonWriter::new();
568 jw.val_f64(-0.0_f64);
569 let s = to_string(jw);
570 assert!(
572 s == "-0" || s == "0" || s == "-0.0" || s == "0.0",
573 "got: {s}"
574 );
575
576 let mut jw = JsonWriter::new();
577 jw.val_f64(2.78);
578 let s = to_string(jw);
579 assert!(s.contains("2.78"), "got: {s}");
580 }
581
582 #[test]
583 fn test_timestamp_format() {
584 let epoch = SystemTime::UNIX_EPOCH;
586 let s = format_timestamp(epoch);
587 assert_eq!(s, "1970-01-01T00:00:00.000000Z");
588
589 let t = SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1771588800);
591 let s = format_timestamp(t);
592 assert_eq!(s, "2026-02-20T12:00:00.000000Z");
593 }
594
595 #[test]
596 fn test_timestamp_microsecond_precision() {
597 let t = SystemTime::UNIX_EPOCH
599 + std::time::Duration::from_micros(1_771_588_800 * 1_000_000 + 123_456);
600 let s = format_timestamp(t);
601 assert_eq!(s, "2026-02-20T12:00:00.123456Z");
602
603 let t = SystemTime::UNIX_EPOCH + std::time::Duration::from_micros(1);
605 let s = format_timestamp(t);
606 assert_eq!(s, "1970-01-01T00:00:00.000001Z");
607
608 let t = SystemTime::UNIX_EPOCH + std::time::Duration::from_micros(999_999);
610 let s = format_timestamp(t);
611 assert_eq!(s, "1970-01-01T00:00:00.999999Z");
612 }
613}