1use std::collections::{HashMap, HashSet};
55use std::error::Error;
56use std::fmt::Debug;
57use std::io::Write;
58use std::marker::PhantomData;
59
60use serde::ser::{SerializeMap, Serializer};
61use serde::Serialize;
62use time::format_description::well_known::Rfc3339;
63use time::UtcDateTime;
64use tracing::field::Field;
65use tracing::span::{Attributes, Record};
66use tracing::{Event, Id, Subscriber};
67use tracing_core::field::Visit;
68use tracing_subscriber::fmt::MakeWriter;
69use tracing_subscriber::layer::{Context, Layer};
70use tracing_subscriber::registry::LookupSpan;
71
72trait SerializationInfallibleExt<T> {
73 fn expect_serialization_infallible(self) -> T;
74}
75impl<T, E> SerializationInfallibleExt<T> for Result<T, E>
76where
77 E: Debug,
78{
79 #[inline]
80 fn expect_serialization_infallible(self: Result<T, E>) -> T {
81 self.expect("serialization to buffer should never fail")
82 }
83}
84
85const MAX_SAFE_INTEGER: i64 = (1i64 << 53) - 1;
87const MIN_SAFE_INTEGER: i64 = -MAX_SAFE_INTEGER;
88
89trait TryIntoSafeInteger
91where
92 Self: itoa::Integer,
93{
94 fn try_into_safe_integer(self) -> Option<i64>;
95}
96
97impl TryIntoSafeInteger for i64 {
98 #[inline]
99 fn try_into_safe_integer(self) -> Option<i64> {
100 #[allow(clippy::manual_range_contains)]
101 if self <= MAX_SAFE_INTEGER && self >= MIN_SAFE_INTEGER {
102 Some(self)
103 } else {
104 None
105 }
106 }
107}
108
109impl TryIntoSafeInteger for u64 {
110 #[inline]
111 fn try_into_safe_integer(self) -> Option<i64> {
112 if self <= MAX_SAFE_INTEGER as u64 {
113 #[expect(clippy::cast_possible_wrap, reason = "explicitly checked")]
114 Some(self as i64)
115 } else {
116 None
117 }
118 }
119}
120
121impl TryIntoSafeInteger for i128 {
122 #[inline]
123 fn try_into_safe_integer(self) -> Option<i64> {
124 if self <= i128::from(MAX_SAFE_INTEGER) && self >= i128::from(MIN_SAFE_INTEGER) {
125 #[expect(clippy::cast_possible_truncation, reason = "explicitly checked")]
126 Some(self as i64)
127 } else {
128 None
129 }
130 }
131}
132
133impl TryIntoSafeInteger for u128 {
134 #[inline]
135 fn try_into_safe_integer(self) -> Option<i64> {
136 if self <= MAX_SAFE_INTEGER as u128 {
137 #[expect(clippy::cast_possible_truncation, reason = "explicitly checked")]
138 Some(self as i64)
139 } else {
140 None
141 }
142 }
143}
144
145trait FlatJsonRecorder {
151 fn record_value<T>(&mut self, field: &Field, value: T)
152 where
153 T: Serialize + Into<serde_json::Value>;
154
155 #[inline]
160 fn record_int(&mut self, field: &Field, value: impl TryIntoSafeInteger) {
161 if let Some(i) = value.try_into_safe_integer() {
162 self.record_value(field, i);
163 } else {
164 let mut b = itoa::Buffer::new();
165 let s = b.format(value);
166 self.record_value(field, s);
167 }
168 }
169
170 #[inline]
171 fn record_float(&mut self, field: &Field, value: f64) {
172 if value.is_finite() {
173 self.record_value(field, value);
174 } else if value.is_nan() {
175 self.record_value(field, "NaN");
176 } else if value == f64::INFINITY {
177 self.record_value(field, "inf");
178 } else if value == f64::NEG_INFINITY {
179 self.record_value(field, "-inf");
180 } else {
181 unreachable!("floats must be finite, NaN, or infinite");
182 }
183 }
184}
185
186type OutputSerializer = serde_json::Serializer<Vec<u8>, serde_json::ser::CompactFormatter>;
189
190struct FlatJsonEventRecorder<'state: 'state_borrow, 'state_borrow> {
192 json_out: &'state_borrow mut <&'state mut OutputSerializer as Serializer>::SerializeMap,
193 seen_fields: &'state_borrow mut HashSet<&'static str>,
194}
195
196impl FlatJsonRecorder for FlatJsonEventRecorder<'_, '_> {
197 #[inline]
198 fn record_value<T>(&mut self, field: &Field, value: T)
199 where
200 T: Serialize + Into<serde_json::Value>,
201 {
202 if self.seen_fields.insert(field.name()) {
203 self.json_out
204 .serialize_entry(field.name(), &value)
205 .expect_serialization_infallible();
206 }
207 }
208}
209
210struct FlatJsonSpanRecorder<'a>(&'a mut HashMap<&'static str, serde_json::Value>);
214
215impl FlatJsonRecorder for FlatJsonSpanRecorder<'_> {
216 #[inline]
217 fn record_value<T>(&mut self, field: &Field, value: T)
218 where
219 T: Serialize + Into<serde_json::Value>,
220 {
221 self.0.insert(field.name(), value.into());
222 }
223}
224
225#[repr(transparent)]
227struct FlatJsonVisitor<T: FlatJsonRecorder>(T);
228
229impl<T: FlatJsonRecorder> Visit for FlatJsonVisitor<T> {
230 #[inline]
231 fn record_f64(&mut self, field: &Field, value: f64) {
232 self.0.record_float(field, value);
233 }
234 #[inline]
235 fn record_i64(&mut self, field: &Field, value: i64) {
236 self.0.record_int(field, value);
237 }
238 #[inline]
239 fn record_u64(&mut self, field: &Field, value: u64) {
240 self.0.record_int(field, value);
241 }
242 #[inline]
243 fn record_i128(&mut self, field: &Field, value: i128) {
244 self.0.record_int(field, value);
245 }
246 #[inline]
247 fn record_u128(&mut self, field: &Field, value: u128) {
248 self.0.record_int(field, value);
249 }
250 #[inline]
251 fn record_bool(&mut self, field: &Field, value: bool) {
252 self.0.record_value(field, value);
253 }
254 #[inline]
255 fn record_str(&mut self, field: &Field, value: &str) {
256 self.0.record_value(field, value);
257 }
258 #[inline]
259 fn record_bytes(&mut self, field: &Field, value: &[u8]) {
260 self.0.record_value(field, value.escape_ascii().to_string());
262 }
263 #[inline]
264 fn record_error(&mut self, field: &Field, value: &(dyn Error + 'static)) {
265 self.0.record_value(field, format!("{value:#}"));
267 }
268 #[inline]
269 fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
270 self.0.record_value(field, format!("{value:?}"));
271 }
272}
273
274#[cfg(feature = "tracing-log")]
278struct TracingLogVisitor<'state: 'state_borrow, 'state_borrow> {
279 json_out: &'state_borrow mut <&'state mut OutputSerializer as Serializer>::SerializeMap,
280 seen_fields: &'state_borrow mut HashSet<&'static str>,
281}
282
283#[cfg(feature = "tracing-log")]
284impl Visit for TracingLogVisitor<'_, '_> {
285 #[inline]
286 fn record_f64(&mut self, field: &Field, _value: f64) {
287 debug_assert!(false, "{}", field.name());
288 }
289 #[inline]
290 fn record_i64(&mut self, field: &Field, _value: i64) {
291 debug_assert!(false, "{}", field.name());
292 }
293 #[inline]
294 fn record_u64(&mut self, field: &Field, _value: u64) {
295 debug_assert_eq!(field.name(), "log.line");
296 }
297 #[inline]
298 fn record_i128(&mut self, field: &Field, _value: i128) {
299 debug_assert!(false, "{}", field.name());
300 }
301 #[inline]
302 fn record_u128(&mut self, field: &Field, _value: u128) {
303 debug_assert!(false, "{}", field.name());
304 }
305 #[inline]
306 fn record_bool(&mut self, field: &Field, _value: bool) {
307 debug_assert!(false, "{}", field.name());
308 }
309 #[inline]
310 fn record_str(&mut self, field: &Field, _value: &str) {
311 let n = field.name();
312 debug_assert!(
313 n == "log.target" || n == "log.module_path" || n == "log.file",
314 "{n}"
315 );
316 }
317 #[inline]
318 fn record_bytes(&mut self, field: &Field, _value: &[u8]) {
319 debug_assert!(false, "{}", field.name());
320 }
321 #[inline]
322 fn record_error(&mut self, field: &Field, _value: &(dyn Error + 'static)) {
323 debug_assert!(false, "{}", field.name());
324 }
325 #[inline]
326 fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
327 const MESSAGE: &str = "message";
328 debug_assert_eq!(field.name(), MESSAGE);
329 if field.name() == MESSAGE {
330 let is_new = self.seen_fields.insert(MESSAGE);
331 debug_assert!(is_new);
332 self.json_out
333 .serialize_entry(MESSAGE, &format!("{value:?}"))
334 .expect_serialization_infallible();
335 }
336 }
337}
338
339pub struct FlatJsonLayer<S, W>
341where
342 W: for<'writer> MakeWriter<'writer> + 'static,
343{
344 make_writer: W,
345 _inner: PhantomData<fn(S)>,
346}
347
348impl<S, W> FlatJsonLayer<S, W>
349where
350 W: for<'writer> MakeWriter<'writer> + 'static,
351{
352 #[inline]
355 pub fn new(make_writer: W) -> Self {
356 Self {
357 make_writer,
358 _inner: PhantomData,
359 }
360 }
361}
362
363struct FlatJsonSpanData(HashMap<&'static str, serde_json::Value>);
365
366impl<S, W> Layer<S> for FlatJsonLayer<S, W>
367where
368 S: Subscriber + for<'a> LookupSpan<'a>,
369 W: for<'w> MakeWriter<'w> + 'static,
370{
371 fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
372 if attrs.fields().is_empty() {
373 return;
374 }
375 let span = ctx
376 .span(id)
377 .expect("span not found, this is a subscriber bug");
378 let mut extensions = span.extensions_mut();
379 let mut json_map = HashMap::with_capacity(attrs.fields().len());
380 attrs.record(&mut FlatJsonVisitor(FlatJsonSpanRecorder(&mut json_map)));
381 extensions.insert(FlatJsonSpanData(json_map));
382 }
383
384 fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
385 let span = ctx
386 .span(id)
387 .expect("span not found, this is a subscriber bug");
388 let mut extensions = span.extensions_mut();
389 let FlatJsonSpanData(json_map) = extensions
393 .get_mut::<FlatJsonSpanData>()
394 .expect("tracing-flat-json span extension not found, this is a bug");
395 values.record(&mut FlatJsonVisitor(FlatJsonSpanRecorder(json_map)));
396 }
397
398 #[expect(clippy::too_many_lines)]
399 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
400 const MAX_TIME_LEN: usize = "0000-00-00T00:00:00.000000000Z".len();
404 let mut ts_buf = [0u8; MAX_TIME_LEN];
405 let mut ts_cursor = &mut ts_buf[..];
406 UtcDateTime::now()
407 .format_into(&mut ts_cursor, &Rfc3339)
408 .expect("formatting current UTC time should never fail");
409 let ts_len = MAX_TIME_LEN - ts_cursor.len();
410 let ts = unsafe { std::str::from_utf8_unchecked(&ts_buf[..ts_len]) };
413
414 let mut serializer: OutputSerializer =
415 serde_json::Serializer::new(Vec::with_capacity(1024));
416 let mut json_out = serializer
418 .serialize_map(None)
419 .expect_serialization_infallible();
420
421 #[cfg(feature = "tracing-log")]
422 let log_metadata = {
423 use tracing_log::NormalizeEvent;
424 event.normalized_metadata()
425 };
426 #[cfg(feature = "tracing-log")]
427 let metadata = match &log_metadata {
428 Some(metadata) => metadata,
429 None => event.metadata(),
430 };
431 #[cfg(not(feature = "tracing-log"))]
432 let log_metadata: Option<()> = None;
433 #[cfg(not(feature = "tracing-log"))]
434 let metadata = event.metadata();
435
436 let total_event_fields = 4 + metadata.fields().len();
438 #[cfg(feature = "tracing-opentelemetry")]
440 let total_event_fields = total_event_fields + 1;
441
442 let mut seen_fields = HashSet::with_capacity(total_event_fields + 4);
447 macro_rules! serialize_entry {
448 ($k:literal, $v: expr) => {
449 let is_new = seen_fields.insert($k);
450 debug_assert!(is_new, "{}", $k);
451 json_out
452 .serialize_entry($k, $v)
453 .expect_serialization_infallible();
454 };
455 }
456
457 serialize_entry!("timestamp", &ts);
461 serialize_entry!("level", metadata.level().as_str());
462
463 #[cfg(feature = "tracing-opentelemetry")]
465 {
466 use opentelemetry::trace::{TraceContextExt, TraceId};
467 use tracing_opentelemetry::OtelData;
468
469 if let Some(span) = ctx.event_span(event) {
470 if let Some(otel_data) = span.extensions().get::<OtelData>() {
471 let mut trace_id = otel_data.parent_cx.span().span_context().trace_id();
472
473 if trace_id == TraceId::INVALID {
483 if let Some(builder_trace_id) = otel_data.builder.trace_id {
484 trace_id = builder_trace_id;
485 }
486 }
487
488 debug_assert_ne!(trace_id, TraceId::INVALID);
493 serialize_entry!("trace_id", &format!("{trace_id}"));
494 }
495 }
496 }
497
498 if let Some(file) = metadata.file() {
500 serialize_entry!("code.file.path", file);
501 }
502 if let Some(line) = metadata.line() {
503 serialize_entry!("code.line.number", &line);
504 }
505
506 if log_metadata.is_some() {
511 #[cfg(feature = "tracing-log")]
512 event.record(&mut TracingLogVisitor {
513 json_out: &mut json_out,
514 seen_fields: &mut seen_fields,
515 });
516 #[cfg(not(feature = "tracing-log"))]
517 unreachable!();
518 } else {
519 event.record(&mut FlatJsonVisitor(FlatJsonEventRecorder {
520 json_out: &mut json_out,
521 seen_fields: &mut seen_fields,
522 }));
523 }
524 if let Some(scope) = ctx.event_scope(event) {
525 for span in scope {
526 if let Some(FlatJsonSpanData(json_map)) = span.extensions().get() {
528 for field in span.fields() {
530 let k = field.name();
531 if let Some(v) = json_map.get(k) {
534 if seen_fields.insert(k) {
536 json_out
537 .serialize_entry(k, v)
538 .expect_serialization_infallible();
539 }
540 }
541 }
542 }
543 }
544 }
545
546 json_out.end().expect_serialization_infallible();
547 let mut buf = serializer.into_inner();
548 buf.push(b'\n');
549
550 if let Err(e) = self.make_writer.make_writer_for(metadata).write_all(&buf) {
551 if e.kind() != std::io::ErrorKind::BrokenPipe {
554 eprintln!("[tracing-flat-json] write error: {e:?}");
555 }
556 }
557 }
558}
559
560#[cfg(test)]
561mod tests {
562 use std::sync::{Arc, Mutex};
563
564 use tracing::dispatcher::DefaultGuard;
565 use tracing::{debug, error, info, info_span, warn};
566 use tracing_subscriber::layer::SubscriberExt;
567 use tracing_subscriber::registry::SpanData;
568 use tracing_subscriber::util::SubscriberInitExt;
569 use tracing_subscriber::Registry;
570
571 use super::*;
572
573 #[derive(Clone, Default)]
574 struct MockWriter(Arc<Mutex<Vec<u8>>>);
575
576 impl Write for MockWriter {
577 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
578 self.0.try_lock().unwrap().write(buf)
580 }
581 fn flush(&mut self) -> std::io::Result<()> {
582 self.0.try_lock().unwrap().flush()
584 }
585 }
586
587 impl MockWriter {
588 fn read_json(&self) -> serde_json::Value {
589 let mut buf = self.0.try_lock().unwrap();
590 let json = serde_json::from_slice(&buf).unwrap();
591 buf.clear();
592 json
593 }
594 fn read_string(&self) -> String {
595 let mut buf = self.0.try_lock().unwrap();
596 let vec = std::mem::take(&mut *buf);
597 String::from_utf8(vec).unwrap()
598 }
599 }
600
601 fn mock_subscriber() -> (DefaultGuard, MockWriter) {
602 let writer = MockWriter::default();
603 let make_writer = move || writer.clone();
604 let return_writer = make_writer();
605
606 #[cfg(feature = "tracing-opentelemetry")]
607 let subscriber = {
608 use opentelemetry::trace::TracerProvider;
609 let exporter = opentelemetry_stdout::SpanExporter::default();
610 let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
611 .with_simple_exporter(exporter)
612 .build();
613 let tracer = provider.tracer(env!("CARGO_PKG_NAME"));
614 let layer = tracing_opentelemetry::layer().with_tracer(tracer);
615 Registry::default().with(layer)
616 };
617
618 #[cfg(not(feature = "tracing-opentelemetry"))]
619 let subscriber = Registry::default();
620
621 let subscriber = subscriber.with(FlatJsonLayer::new(make_writer));
622 (subscriber.set_default(), return_writer)
623 }
624
625 macro_rules! pline {
626 ($n:literal) => {
627 i64::from(line!()) - $n
628 };
629 }
630
631 #[test]
632 fn field_values() {
633 let (_dispatch, writer) = mock_subscriber();
634
635 info!("test message");
636 let json = writer.read_json();
637 println!("{json}");
638 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
639 assert_eq!(json["level"].as_str().unwrap(), "INFO");
640 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
641 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
642 assert_eq!(json["message"].as_str().unwrap(), "test message");
643 assert_eq!(json.as_object().unwrap().len(), 5);
644
645 warn!(a = 1, b = "two", "test fields");
646 let json = writer.read_json();
647 println!("{json}");
648 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
649 assert_eq!(json["level"].as_str().unwrap(), "WARN");
650 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
651 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
652 assert_eq!(json["message"].as_str().unwrap(), "test fields");
653 assert_eq!(json["a"].as_i64().unwrap(), 1);
654 assert_eq!(json["b"].as_str().unwrap(), "two");
655 assert_eq!(json.as_object().unwrap().len(), 7);
656
657 #[cfg(feature = "tracing-opentelemetry")]
659 let extra_fields = 1;
660 #[cfg(not(feature = "tracing-opentelemetry"))]
661 let extra_fields = 0;
662
663 let span1 = info_span!("span1", c = "three");
664 let _g1 = span1.enter();
665 error!("test span1");
666 let json = writer.read_json();
667 println!("{json}");
668 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
669 assert_eq!(json["level"].as_str().unwrap(), "ERROR");
670 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
671 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
672 #[cfg(feature = "tracing-opentelemetry")]
673 assert_eq!(json["trace_id"].as_str().unwrap().len(), 32);
674 assert_eq!(json["message"].as_str().unwrap(), "test span1");
675 assert_eq!(json["c"].as_str().unwrap(), "three");
676 assert_eq!(json.as_object().unwrap().len(), 6 + extra_fields);
677
678 let span2 = info_span!("span2", d = 4.0);
679 let _g2 = span2.enter();
680 debug!("test span2");
681 let json = writer.read_json();
682 println!("{json}");
683 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
684 assert_eq!(json["level"].as_str().unwrap(), "DEBUG");
685 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
686 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
687 #[cfg(feature = "tracing-opentelemetry")]
688 assert_eq!(json["trace_id"].as_str().unwrap().len(), 32);
689 assert_eq!(json["message"].as_str().unwrap(), "test span2");
690 assert_eq!(json["c"].as_str().unwrap(), "three");
691 assert_eq!(json["d"].as_f64().unwrap(), 4.0);
692 assert_eq!(json.as_object().unwrap().len(), 7 + extra_fields);
693 }
694
695 #[test]
696 fn no_dup_fields() {
697 let (_dispatch, writer) = mock_subscriber();
698
699 let span1 = info_span!("span1", f = "a");
701 let _g1 = span1.enter();
702 info!("e");
703 let s = writer.read_string();
704 println!("{s}");
705 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
706 println!("{json}");
707 assert_eq!(json["f"].as_str().unwrap(), "a");
708 let f_count = s.split(',').filter(|s| s.starts_with("\"f\":")).count();
709 assert_eq!(f_count, 1);
710
711 let span2 = info_span!("span2", f = "b");
713 let _g2 = span2.enter();
714 info!("e");
715 let s = writer.read_string();
716 println!("{s}");
717 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
718 println!("{json}");
719 assert_eq!(json["f"].as_str().unwrap(), "b");
720 let f_count = s.split(',').filter(|s| s.starts_with("\"f\":")).count();
721 assert_eq!(f_count, 1);
722
723 info!(f = "c");
725 let s = writer.read_string();
726 println!("{s}");
727 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
728 println!("{json}");
729 assert_eq!(json["f"].as_str().unwrap(), "c");
730 let f_count = s.split(',').filter(|s| s.starts_with("\"f\":")).count();
731 assert_eq!(f_count, 1);
732 }
733
734 #[test]
735 fn record_fields() {
736 let (_dispatch, writer) = mock_subscriber();
737
738 let span = info_span!(
739 "span",
740 f1 = tracing::field::Empty,
741 f2 = "a",
742 f3 = tracing::field::Empty
743 );
744 let _g = span.enter();
745
746 info!("e");
748 let s = writer.read_string();
749 println!("{s}");
750 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
751 println!("{json}");
752 assert_eq!(json["f1"], serde_json::Value::Null);
753 assert_eq!(json["f2"].as_str().unwrap(), "a");
754 assert_eq!(json["f3"], serde_json::Value::Null);
755
756 span.record("f1", "b");
758 info!("e");
759 let s = writer.read_string();
760 println!("{s}");
761 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
762 println!("{json}");
763 assert_eq!(json["f1"].as_str().unwrap(), "b");
764 assert_eq!(json["f2"].as_str().unwrap(), "a");
765 assert_eq!(json["f3"], serde_json::Value::Null);
766
767 span.record("f1", "c");
773 span.record("f4", "invalid, not initially declared");
774 info!("e");
775 let s = writer.read_string();
776 println!("{s}");
777 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
778 println!("{json}");
779 assert_eq!(json["f1"].as_str().unwrap(), "c");
780 assert_eq!(json["f2"].as_str().unwrap(), "a");
781 assert_eq!(json["f3"], serde_json::Value::Null);
782 assert_eq!(json["f4"], serde_json::Value::Null);
783
784 let f1_count = s.split(',').filter(|s| s.starts_with("\"f1\":")).count();
786 assert_eq!(f1_count, 1);
787
788 assert!(s.contains(r#""f1":"c","f2":"a""#));
790 }
791
792 #[test]
793 fn no_extension() {
794 let (_dispatch, writer) = mock_subscriber();
795
796 let span = info_span!("span2");
797 let _g = span.enter();
798
799 span.with_subscriber(|(id, subscriber)| {
802 let registry = subscriber.downcast_ref::<Registry>().unwrap();
803 let span_data = registry.span_data(id).unwrap();
804 assert!(span_data.extensions().get::<FlatJsonSpanData>().is_none());
805 });
806
807 span.record("f", 1);
810 info!("e");
811 let json = writer.read_json();
812 println!("{json}");
813 assert_eq!(json["f"], serde_json::Value::Null);
814 }
815
816 #[test]
817 fn field_order() {
818 let (_dispatch, writer) = mock_subscriber();
819
820 let span1 = info_span!("span1", b = "b", c = "c");
821 let _g1 = span1.enter();
822 let span2 = info_span!("span2", d = "d", a = "a");
823 let _g2 = span2.enter();
824 info!(e = "e", "event1");
825 let s = writer.read_string();
826 let s = s.trim_ascii_end();
827 println!("{s}");
828
829 let mut field_names = Vec::new();
830 let field_str = s.strip_prefix("{").unwrap().strip_suffix("}").unwrap();
831 for field in field_str.split(',') {
832 let field_key_str = field.split(':').next().unwrap();
833 field_names.push(&field_key_str[1..field_key_str.len() - 1]);
834 }
835 assert_eq!(
836 field_names,
837 &[
838 "timestamp",
839 "level",
840 #[cfg(feature = "tracing-opentelemetry")]
841 "trace_id",
842 "code.file.path",
843 "code.line.number",
844 "message",
845 "e",
846 "d",
847 "a",
848 "b",
849 "c"
850 ]
851 );
852 }
853
854 #[cfg(feature = "tracing-log")]
855 #[test]
856 fn tracing_log() {
857 let (_dispatch, writer) = mock_subscriber();
858
859 log::warn!("test log");
860 let json = writer.read_json();
861 println!("{json}");
862 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
863 assert_eq!(json["level"].as_str().unwrap(), "WARN");
864 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
865 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
866 assert_eq!(json["message"].as_str().unwrap(), "test log");
867 assert_eq!(json.as_object().unwrap().len(), 5);
868
869 #[cfg(feature = "tracing-opentelemetry")]
870 let extra_fields = 1;
871 #[cfg(not(feature = "tracing-opentelemetry"))]
872 let extra_fields = 0;
873
874 let span1 = info_span!("span1", c = "three");
875 let _g1 = span1.enter();
876 log::error!("test log span1");
877 let json = writer.read_json();
878 println!("{json}");
879 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
880 assert_eq!(json["level"].as_str().unwrap(), "ERROR");
881 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
882 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
883 #[cfg(feature = "tracing-opentelemetry")]
884 assert_eq!(json["trace_id"].as_str().unwrap().len(), 32);
885 assert_eq!(json["message"].as_str().unwrap(), "test log span1");
886 assert_eq!(json["c"].as_str().unwrap(), "three");
887 assert_eq!(json.as_object().unwrap().len(), 6 + extra_fields);
888
889 let span2 = info_span!("span2", d = 4.0);
890 let _g2 = span2.enter();
891 log::debug!("test log span2");
892 let json = writer.read_json();
893 println!("{json}");
894 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
895 assert_eq!(json["level"].as_str().unwrap(), "DEBUG");
896 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
897 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
898 #[cfg(feature = "tracing-opentelemetry")]
899 assert_eq!(json["trace_id"].as_str().unwrap().len(), 32);
900 assert_eq!(json["message"].as_str().unwrap(), "test log span2");
901 assert_eq!(json["c"].as_str().unwrap(), "three");
902 assert_eq!(json["d"].as_f64().unwrap(), 4.0);
903 assert_eq!(json.as_object().unwrap().len(), 7 + extra_fields);
904 }
905
906 #[cfg(feature = "tracing-opentelemetry")]
907 #[test]
908 fn tracing_opentelemetry() {
909 use std::collections::HashMap;
910
911 use opentelemetry::propagation::TextMapPropagator;
912 use opentelemetry::trace::TraceContextExt;
913 use opentelemetry_sdk::propagation::TraceContextPropagator;
914 use tracing_opentelemetry::OpenTelemetrySpanExt;
915
916 let (_dispatch, writer) = mock_subscriber();
917
918 {
920 warn!("test event");
922 let json = writer.read_json();
923 println!("{json}");
924 assert_eq!(json["trace_id"], serde_json::Value::Null);
925
926 let span1 = info_span!("span1");
927 let generated_trace_id =
929 format!("{}", span1.context().span().span_context().trace_id());
930
931 let _g1 = span1.enter();
932 error!("test span1");
933 let json = writer.read_json();
934 println!("{json}");
935 assert_eq!(json["trace_id"].as_str().unwrap(), generated_trace_id);
936
937 let span2 = info_span!("span2");
938 let _g2 = span2.enter();
939 debug!("test span2");
940 let json = writer.read_json();
941 println!("{json}");
942 assert_eq!(json["trace_id"].as_str().unwrap(), generated_trace_id);
943 }
944
945 {
947 let fixed_trace_id = "09f91f229168786afbed35fa3a98d86a".to_owned();
948 let mut m = HashMap::with_capacity(1);
949 m.insert(
950 "traceparent".to_owned(),
951 format!("00-{fixed_trace_id}-ce812d502e8f1299-01"),
952 );
953 let cx = TraceContextPropagator::new().extract(&m);
954
955 warn!("test event");
957 let json = writer.read_json();
958 println!("{json}");
959 assert_eq!(json["trace_id"], serde_json::Value::Null);
960
961 let span1 = info_span!("span1");
962 span1.set_parent(cx);
963
964 let _g1 = span1.enter();
965 error!("test span1");
966 let json = writer.read_json();
967 println!("{json}");
968 assert_eq!(json["trace_id"].as_str().unwrap(), fixed_trace_id);
969
970 let span2 = info_span!("span2");
971 let _g2 = span2.enter();
972 debug!("test span2");
973 let json = writer.read_json();
974 println!("{json}");
975 assert_eq!(json["trace_id"].as_str().unwrap(), fixed_trace_id);
976 }
977 }
978
979 #[test]
980 fn float_nan_inf() {
981 let (_dispatch, writer) = mock_subscriber();
982 let nan32 = f32::NAN;
983 let nan64 = f64::NAN;
984 let inf32 = f32::INFINITY;
985 let inf64 = f64::INFINITY;
986 let ninf32 = f32::NEG_INFINITY;
987 let ninf64 = f64::NEG_INFINITY;
988 info!(nan32, nan64, inf32, inf64, ninf32, ninf64);
989 let json = writer.read_json();
990 assert_eq!(json["nan32"].as_str().unwrap(), "NaN");
991 assert_eq!(json["nan64"].as_str().unwrap(), "NaN");
992 assert_eq!(json["inf32"].as_str().unwrap(), "inf");
993 assert_eq!(json["inf64"].as_str().unwrap(), "inf");
994 assert_eq!(json["ninf32"].as_str().unwrap(), "-inf");
995 assert_eq!(json["ninf64"].as_str().unwrap(), "-inf");
996 }
997
998 #[test]
999 fn large_integer() {
1000 let (_dispatch, writer) = mock_subscriber();
1001 let max_safe = MAX_SAFE_INTEGER;
1002 let min_safe = MIN_SAFE_INTEGER;
1003 let max_representable = max_safe + 1;
1004 let min_representable = min_safe - 1;
1005 info!(max_safe, min_safe, max_representable, min_representable);
1006 let json = writer.read_json();
1007 assert_eq!(json["max_safe"].as_i64().unwrap(), max_safe);
1008 assert_eq!(json["min_safe"].as_i64().unwrap(), min_safe);
1009 assert_eq!(
1010 json["max_representable"].as_str().unwrap(),
1011 max_representable.to_string()
1012 );
1013 assert_eq!(
1014 json["min_representable"].as_str().unwrap(),
1015 min_representable.to_string()
1016 );
1017 }
1018}