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
171type OutputSerializer = serde_json::Serializer<Vec<u8>, serde_json::ser::CompactFormatter>;
174
175struct FlatJsonEventRecorder<'state: 'state_borrow, 'state_borrow> {
177 json_out: &'state_borrow mut <&'state mut OutputSerializer as Serializer>::SerializeMap,
178 seen_fields: &'state_borrow mut HashSet<&'static str>,
179}
180
181impl FlatJsonRecorder for FlatJsonEventRecorder<'_, '_> {
182 #[inline]
183 fn record_value<T>(&mut self, field: &Field, value: T)
184 where
185 T: Serialize + Into<serde_json::Value>,
186 {
187 if self.seen_fields.insert(field.name()) {
188 self.json_out
189 .serialize_entry(field.name(), &value)
190 .expect_serialization_infallible();
191 }
192 }
193}
194
195struct FlatJsonSpanRecorder<'a>(&'a mut HashMap<&'static str, serde_json::Value>);
199
200impl FlatJsonRecorder for FlatJsonSpanRecorder<'_> {
201 #[inline]
202 fn record_value<T>(&mut self, field: &Field, value: T)
203 where
204 T: Serialize + Into<serde_json::Value>,
205 {
206 self.0.insert(field.name(), value.into());
207 }
208}
209
210#[repr(transparent)]
212struct FlatJsonVisitor<T: FlatJsonRecorder>(T);
213
214impl<T: FlatJsonRecorder> Visit for FlatJsonVisitor<T> {
215 #[inline]
216 fn record_f64(&mut self, field: &Field, value: f64) {
217 self.0.record_value(field, value);
218 }
219 #[inline]
220 fn record_i64(&mut self, field: &Field, value: i64) {
221 self.0.record_int(field, value);
222 }
223 #[inline]
224 fn record_u64(&mut self, field: &Field, value: u64) {
225 self.0.record_int(field, value);
226 }
227 #[inline]
228 fn record_i128(&mut self, field: &Field, value: i128) {
229 self.0.record_int(field, value);
230 }
231 #[inline]
232 fn record_u128(&mut self, field: &Field, value: u128) {
233 self.0.record_int(field, value);
234 }
235 #[inline]
236 fn record_bool(&mut self, field: &Field, value: bool) {
237 self.0.record_value(field, value);
238 }
239 #[inline]
240 fn record_str(&mut self, field: &Field, value: &str) {
241 self.0.record_value(field, value);
242 }
243 #[inline]
244 fn record_bytes(&mut self, field: &Field, value: &[u8]) {
245 self.0.record_value(field, value.escape_ascii().to_string());
247 }
248 #[inline]
249 fn record_error(&mut self, field: &Field, value: &(dyn Error + 'static)) {
250 self.0.record_value(field, format!("{value:#}"));
252 }
253 #[inline]
254 fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
255 self.0.record_value(field, format!("{value:?}"));
256 }
257}
258
259#[cfg(feature = "tracing-log")]
263struct TracingLogVisitor<'state: 'state_borrow, 'state_borrow> {
264 json_out: &'state_borrow mut <&'state mut OutputSerializer as Serializer>::SerializeMap,
265 seen_fields: &'state_borrow mut HashSet<&'static str>,
266}
267
268#[cfg(feature = "tracing-log")]
269impl Visit for TracingLogVisitor<'_, '_> {
270 #[inline]
271 fn record_f64(&mut self, field: &Field, _value: f64) {
272 debug_assert!(false, "{}", field.name());
273 }
274 #[inline]
275 fn record_i64(&mut self, field: &Field, _value: i64) {
276 debug_assert!(false, "{}", field.name());
277 }
278 #[inline]
279 fn record_u64(&mut self, field: &Field, _value: u64) {
280 debug_assert_eq!(field.name(), "log.line");
281 }
282 #[inline]
283 fn record_i128(&mut self, field: &Field, _value: i128) {
284 debug_assert!(false, "{}", field.name());
285 }
286 #[inline]
287 fn record_u128(&mut self, field: &Field, _value: u128) {
288 debug_assert!(false, "{}", field.name());
289 }
290 #[inline]
291 fn record_bool(&mut self, field: &Field, _value: bool) {
292 debug_assert!(false, "{}", field.name());
293 }
294 #[inline]
295 fn record_str(&mut self, field: &Field, _value: &str) {
296 let n = field.name();
297 debug_assert!(
298 n == "log.target" || n == "log.module_path" || n == "log.file",
299 "{n}"
300 );
301 }
302 #[inline]
303 fn record_bytes(&mut self, field: &Field, _value: &[u8]) {
304 debug_assert!(false, "{}", field.name());
305 }
306 #[inline]
307 fn record_error(&mut self, field: &Field, _value: &(dyn Error + 'static)) {
308 debug_assert!(false, "{}", field.name());
309 }
310 #[inline]
311 fn record_debug(&mut self, field: &Field, value: &dyn Debug) {
312 const MESSAGE: &str = "message";
313 debug_assert_eq!(field.name(), MESSAGE);
314 if field.name() == MESSAGE {
315 let is_new = self.seen_fields.insert(MESSAGE);
316 debug_assert!(is_new);
317 self.json_out
318 .serialize_entry(MESSAGE, &format!("{value:?}"))
319 .expect_serialization_infallible();
320 }
321 }
322}
323
324pub struct FlatJsonLayer<S, W>
326where
327 W: for<'writer> MakeWriter<'writer> + 'static,
328{
329 make_writer: W,
330 _inner: PhantomData<fn(S)>,
331}
332
333impl<S, W> FlatJsonLayer<S, W>
334where
335 W: for<'writer> MakeWriter<'writer> + 'static,
336{
337 #[inline]
340 pub fn new(make_writer: W) -> Self {
341 Self {
342 make_writer,
343 _inner: PhantomData,
344 }
345 }
346}
347
348struct FlatJsonSpanData(HashMap<&'static str, serde_json::Value>);
350
351impl<S, W> Layer<S> for FlatJsonLayer<S, W>
352where
353 S: Subscriber + for<'a> LookupSpan<'a>,
354 W: for<'w> MakeWriter<'w> + 'static,
355{
356 fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
357 if attrs.fields().is_empty() {
358 return;
359 }
360 let span = ctx
361 .span(id)
362 .expect("span not found, this is a subscriber bug");
363 let mut extensions = span.extensions_mut();
364 let mut json_map = HashMap::with_capacity(attrs.fields().len());
365 attrs.record(&mut FlatJsonVisitor(FlatJsonSpanRecorder(&mut json_map)));
366 extensions.insert(FlatJsonSpanData(json_map));
367 }
368
369 fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
370 let span = ctx
371 .span(id)
372 .expect("span not found, this is a subscriber bug");
373 let mut extensions = span.extensions_mut();
374 let FlatJsonSpanData(json_map) = extensions
378 .get_mut::<FlatJsonSpanData>()
379 .expect("tracing-flat-json span extension not found, this is a bug");
380 values.record(&mut FlatJsonVisitor(FlatJsonSpanRecorder(json_map)));
381 }
382
383 #[expect(clippy::too_many_lines)]
384 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
385 const MAX_TIME_LEN: usize = "0000-00-00T00:00:00.000000000Z".len();
389 let mut ts_buf = [0u8; MAX_TIME_LEN];
390 let mut ts_cursor = &mut ts_buf[..];
391 UtcDateTime::now()
392 .format_into(&mut ts_cursor, &Rfc3339)
393 .expect("formatting current UTC time should never fail");
394 let ts_len = MAX_TIME_LEN - ts_cursor.len();
395 let ts = unsafe { std::str::from_utf8_unchecked(&ts_buf[..ts_len]) };
398
399 let mut serializer: OutputSerializer =
400 serde_json::Serializer::new(Vec::with_capacity(1024));
401 let mut json_out = serializer
403 .serialize_map(None)
404 .expect_serialization_infallible();
405
406 #[cfg(feature = "tracing-log")]
407 let log_metadata = {
408 use tracing_log::NormalizeEvent;
409 event.normalized_metadata()
410 };
411 #[cfg(feature = "tracing-log")]
412 let metadata = match &log_metadata {
413 Some(metadata) => metadata,
414 None => event.metadata(),
415 };
416 #[cfg(not(feature = "tracing-log"))]
417 let log_metadata: Option<()> = None;
418 #[cfg(not(feature = "tracing-log"))]
419 let metadata = event.metadata();
420
421 let total_event_fields = 4 + metadata.fields().len();
423 #[cfg(feature = "tracing-opentelemetry")]
425 let total_event_fields = total_event_fields + 1;
426
427 let mut seen_fields = HashSet::with_capacity(total_event_fields + 4);
432 macro_rules! serialize_entry {
433 ($k:literal, $v: expr) => {
434 let is_new = seen_fields.insert($k);
435 debug_assert!(is_new, "{}", $k);
436 json_out
437 .serialize_entry($k, $v)
438 .expect_serialization_infallible();
439 };
440 }
441
442 serialize_entry!("timestamp", &ts);
446 serialize_entry!("level", metadata.level().as_str());
447
448 #[cfg(feature = "tracing-opentelemetry")]
450 {
451 use opentelemetry::trace::{TraceContextExt, TraceId};
452 use tracing_opentelemetry::OtelData;
453
454 if let Some(span) = ctx.event_span(event) {
455 if let Some(otel_data) = span.extensions().get::<OtelData>() {
456 let mut trace_id = otel_data.parent_cx.span().span_context().trace_id();
457
458 if trace_id == TraceId::INVALID {
468 if let Some(builder_trace_id) = otel_data.builder.trace_id {
469 trace_id = builder_trace_id;
470 }
471 }
472
473 debug_assert_ne!(trace_id, TraceId::INVALID);
478 serialize_entry!("trace_id", &format!("{trace_id}"));
479 }
480 }
481 }
482
483 if let Some(file) = metadata.file() {
485 serialize_entry!("code.file.path", file);
486 }
487 if let Some(line) = metadata.line() {
488 serialize_entry!("code.line.number", &line);
489 }
490
491 if log_metadata.is_some() {
496 #[cfg(feature = "tracing-log")]
497 event.record(&mut TracingLogVisitor {
498 json_out: &mut json_out,
499 seen_fields: &mut seen_fields,
500 });
501 #[cfg(not(feature = "tracing-log"))]
502 unreachable!();
503 } else {
504 event.record(&mut FlatJsonVisitor(FlatJsonEventRecorder {
505 json_out: &mut json_out,
506 seen_fields: &mut seen_fields,
507 }));
508 }
509 if let Some(scope) = ctx.event_scope(event) {
510 for span in scope {
511 if let Some(FlatJsonSpanData(json_map)) = span.extensions().get() {
513 for field in span.fields() {
515 let k = field.name();
516 if let Some(v) = json_map.get(k) {
519 if seen_fields.insert(k) {
521 json_out
522 .serialize_entry(k, v)
523 .expect_serialization_infallible();
524 }
525 }
526 }
527 }
528 }
529 }
530
531 json_out.end().expect_serialization_infallible();
532 let mut buf = serializer.into_inner();
533 buf.push(b'\n');
534
535 if let Err(e) = self.make_writer.make_writer_for(metadata).write_all(&buf) {
536 if e.kind() != std::io::ErrorKind::BrokenPipe {
539 eprintln!("[tracing-flat-json] write error: {e:?}");
540 }
541 }
542 }
543}
544
545#[cfg(test)]
546mod tests {
547 use std::sync::{Arc, Mutex};
548
549 use tracing::dispatcher::DefaultGuard;
550 use tracing::{debug, error, info, info_span, warn};
551 use tracing_subscriber::layer::SubscriberExt;
552 use tracing_subscriber::registry::SpanData;
553 use tracing_subscriber::util::SubscriberInitExt;
554 use tracing_subscriber::Registry;
555
556 use super::*;
557
558 #[derive(Clone, Default)]
559 struct MockWriter(Arc<Mutex<Vec<u8>>>);
560
561 impl Write for MockWriter {
562 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
563 self.0.try_lock().unwrap().write(buf)
565 }
566 fn flush(&mut self) -> std::io::Result<()> {
567 self.0.try_lock().unwrap().flush()
569 }
570 }
571
572 impl MockWriter {
573 fn read_json(&self) -> serde_json::Value {
574 let mut buf = self.0.try_lock().unwrap();
575 let json = serde_json::from_slice(&buf).unwrap();
576 buf.clear();
577 json
578 }
579 fn read_string(&self) -> String {
580 let mut buf = self.0.try_lock().unwrap();
581 let vec = std::mem::take(&mut *buf);
582 String::from_utf8(vec).unwrap()
583 }
584 }
585
586 fn mock_subscriber() -> (DefaultGuard, MockWriter) {
587 let writer = MockWriter::default();
588 let make_writer = move || writer.clone();
589 let return_writer = make_writer();
590
591 #[cfg(feature = "tracing-opentelemetry")]
592 let subscriber = {
593 use opentelemetry::trace::TracerProvider;
594 let exporter = opentelemetry_stdout::SpanExporter::default();
595 let provider = opentelemetry_sdk::trace::SdkTracerProvider::builder()
596 .with_simple_exporter(exporter)
597 .build();
598 let tracer = provider.tracer(env!("CARGO_PKG_NAME"));
599 let layer = tracing_opentelemetry::layer().with_tracer(tracer);
600 Registry::default().with(layer)
601 };
602
603 #[cfg(not(feature = "tracing-opentelemetry"))]
604 let subscriber = Registry::default();
605
606 let subscriber = subscriber.with(FlatJsonLayer::new(make_writer));
607 (subscriber.set_default(), return_writer)
608 }
609
610 macro_rules! pline {
611 ($n:literal) => {
612 i64::from(line!()) - $n
613 };
614 }
615
616 #[test]
617 fn field_values() {
618 let (_dispatch, writer) = mock_subscriber();
619
620 info!("test message");
621 let json = writer.read_json();
622 println!("{json}");
623 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
624 assert_eq!(json["level"].as_str().unwrap(), "INFO");
625 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
626 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
627 assert_eq!(json["message"].as_str().unwrap(), "test message");
628 assert_eq!(json.as_object().unwrap().len(), 5);
629
630 warn!(a = 1, b = "two", "test fields");
631 let json = writer.read_json();
632 println!("{json}");
633 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
634 assert_eq!(json["level"].as_str().unwrap(), "WARN");
635 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
636 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
637 assert_eq!(json["message"].as_str().unwrap(), "test fields");
638 assert_eq!(json["a"].as_i64().unwrap(), 1);
639 assert_eq!(json["b"].as_str().unwrap(), "two");
640 assert_eq!(json.as_object().unwrap().len(), 7);
641
642 #[cfg(feature = "tracing-opentelemetry")]
644 let extra_fields = 1;
645 #[cfg(not(feature = "tracing-opentelemetry"))]
646 let extra_fields = 0;
647
648 let span1 = info_span!("span1", c = "three");
649 let _g1 = span1.enter();
650 error!("test span1");
651 let json = writer.read_json();
652 println!("{json}");
653 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
654 assert_eq!(json["level"].as_str().unwrap(), "ERROR");
655 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
656 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
657 #[cfg(feature = "tracing-opentelemetry")]
658 assert_eq!(json["trace_id"].as_str().unwrap().len(), 32);
659 assert_eq!(json["message"].as_str().unwrap(), "test span1");
660 assert_eq!(json["c"].as_str().unwrap(), "three");
661 assert_eq!(json.as_object().unwrap().len(), 6 + extra_fields);
662
663 let span2 = info_span!("span2", d = 4.0);
664 let _g2 = span2.enter();
665 debug!("test span2");
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(), "DEBUG");
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 span2");
675 assert_eq!(json["c"].as_str().unwrap(), "three");
676 assert_eq!(json["d"].as_f64().unwrap(), 4.0);
677 assert_eq!(json.as_object().unwrap().len(), 7 + extra_fields);
678 }
679
680 #[test]
681 fn no_dup_fields() {
682 let (_dispatch, writer) = mock_subscriber();
683
684 let span1 = info_span!("span1", f = "a");
686 let _g1 = span1.enter();
687 info!("e");
688 let s = writer.read_string();
689 println!("{s}");
690 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
691 println!("{json}");
692 assert_eq!(json["f"].as_str().unwrap(), "a");
693 let f_count = s.split(',').filter(|s| s.starts_with("\"f\":")).count();
694 assert_eq!(f_count, 1);
695
696 let span2 = info_span!("span2", f = "b");
698 let _g2 = span2.enter();
699 info!("e");
700 let s = writer.read_string();
701 println!("{s}");
702 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
703 println!("{json}");
704 assert_eq!(json["f"].as_str().unwrap(), "b");
705 let f_count = s.split(',').filter(|s| s.starts_with("\"f\":")).count();
706 assert_eq!(f_count, 1);
707
708 info!(f = "c");
710 let s = writer.read_string();
711 println!("{s}");
712 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
713 println!("{json}");
714 assert_eq!(json["f"].as_str().unwrap(), "c");
715 let f_count = s.split(',').filter(|s| s.starts_with("\"f\":")).count();
716 assert_eq!(f_count, 1);
717 }
718
719 #[test]
720 fn record_fields() {
721 let (_dispatch, writer) = mock_subscriber();
722
723 let span = info_span!(
724 "span",
725 f1 = tracing::field::Empty,
726 f2 = "a",
727 f3 = tracing::field::Empty
728 );
729 let _g = span.enter();
730
731 info!("e");
733 let s = writer.read_string();
734 println!("{s}");
735 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
736 println!("{json}");
737 assert_eq!(json["f1"], serde_json::Value::Null);
738 assert_eq!(json["f2"].as_str().unwrap(), "a");
739 assert_eq!(json["f3"], serde_json::Value::Null);
740
741 span.record("f1", "b");
743 info!("e");
744 let s = writer.read_string();
745 println!("{s}");
746 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
747 println!("{json}");
748 assert_eq!(json["f1"].as_str().unwrap(), "b");
749 assert_eq!(json["f2"].as_str().unwrap(), "a");
750 assert_eq!(json["f3"], serde_json::Value::Null);
751
752 span.record("f1", "c");
758 span.record("f4", "invalid, not initially declared");
759 info!("e");
760 let s = writer.read_string();
761 println!("{s}");
762 let json: serde_json::Value = serde_json::from_str(&s).unwrap();
763 println!("{json}");
764 assert_eq!(json["f1"].as_str().unwrap(), "c");
765 assert_eq!(json["f2"].as_str().unwrap(), "a");
766 assert_eq!(json["f3"], serde_json::Value::Null);
767 assert_eq!(json["f4"], serde_json::Value::Null);
768
769 let f1_count = s.split(',').filter(|s| s.starts_with("\"f1\":")).count();
771 assert_eq!(f1_count, 1);
772
773 assert!(s.contains(r#""f1":"c","f2":"a""#));
775 }
776
777 #[test]
778 fn no_extension() {
779 let (_dispatch, writer) = mock_subscriber();
780
781 let span = info_span!("span2");
782 let _g = span.enter();
783
784 span.with_subscriber(|(id, subscriber)| {
787 let registry = subscriber.downcast_ref::<Registry>().unwrap();
788 let span_data = registry.span_data(id).unwrap();
789 assert!(span_data.extensions().get::<FlatJsonSpanData>().is_none());
790 });
791
792 span.record("f", 1);
795 info!("e");
796 let json = writer.read_json();
797 println!("{json}");
798 assert_eq!(json["f"], serde_json::Value::Null);
799 }
800
801 #[test]
802 fn field_order() {
803 let (_dispatch, writer) = mock_subscriber();
804
805 let span1 = info_span!("span1", b = "b", c = "c");
806 let _g1 = span1.enter();
807 let span2 = info_span!("span2", d = "d", a = "a");
808 let _g2 = span2.enter();
809 info!(e = "e", "event1");
810 let s = writer.read_string();
811 let s = s.trim_ascii_end();
812 println!("{s}");
813
814 let mut field_names = Vec::new();
815 let field_str = s.strip_prefix("{").unwrap().strip_suffix("}").unwrap();
816 for field in field_str.split(',') {
817 let field_key_str = field.split(':').next().unwrap();
818 field_names.push(&field_key_str[1..field_key_str.len() - 1]);
819 }
820 assert_eq!(
821 field_names,
822 &[
823 "timestamp",
824 "level",
825 #[cfg(feature = "tracing-opentelemetry")]
826 "trace_id",
827 "code.file.path",
828 "code.line.number",
829 "message",
830 "e",
831 "d",
832 "a",
833 "b",
834 "c"
835 ]
836 );
837 }
838
839 #[cfg(feature = "tracing-log")]
840 #[test]
841 fn tracing_log() {
842 let (_dispatch, writer) = mock_subscriber();
843
844 log::warn!("test log");
845 let json = writer.read_json();
846 println!("{json}");
847 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
848 assert_eq!(json["level"].as_str().unwrap(), "WARN");
849 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
850 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
851 assert_eq!(json["message"].as_str().unwrap(), "test log");
852 assert_eq!(json.as_object().unwrap().len(), 5);
853
854 #[cfg(feature = "tracing-opentelemetry")]
855 let extra_fields = 1;
856 #[cfg(not(feature = "tracing-opentelemetry"))]
857 let extra_fields = 0;
858
859 let span1 = info_span!("span1", c = "three");
860 let _g1 = span1.enter();
861 log::error!("test log span1");
862 let json = writer.read_json();
863 println!("{json}");
864 assert!(json["timestamp"].as_str().unwrap().ends_with('Z'));
865 assert_eq!(json["level"].as_str().unwrap(), "ERROR");
866 assert_eq!(json["code.file.path"].as_str().unwrap(), "src/lib.rs");
867 assert_eq!(json["code.line.number"].as_i64().unwrap(), pline!(6));
868 #[cfg(feature = "tracing-opentelemetry")]
869 assert_eq!(json["trace_id"].as_str().unwrap().len(), 32);
870 assert_eq!(json["message"].as_str().unwrap(), "test log span1");
871 assert_eq!(json["c"].as_str().unwrap(), "three");
872 assert_eq!(json.as_object().unwrap().len(), 6 + extra_fields);
873
874 let span2 = info_span!("span2", d = 4.0);
875 let _g2 = span2.enter();
876 log::debug!("test log span2");
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(), "DEBUG");
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 span2");
886 assert_eq!(json["c"].as_str().unwrap(), "three");
887 assert_eq!(json["d"].as_f64().unwrap(), 4.0);
888 assert_eq!(json.as_object().unwrap().len(), 7 + extra_fields);
889 }
890
891 #[cfg(feature = "tracing-opentelemetry")]
892 #[test]
893 fn tracing_opentelemetry() {
894 use std::collections::HashMap;
895
896 use opentelemetry::propagation::TextMapPropagator;
897 use opentelemetry::trace::TraceContextExt;
898 use opentelemetry_sdk::propagation::TraceContextPropagator;
899 use tracing_opentelemetry::OpenTelemetrySpanExt;
900
901 let (_dispatch, writer) = mock_subscriber();
902
903 {
905 warn!("test event");
907 let json = writer.read_json();
908 println!("{json}");
909 assert_eq!(json["trace_id"], serde_json::Value::Null);
910
911 let span1 = info_span!("span1");
912 let generated_trace_id =
914 format!("{}", span1.context().span().span_context().trace_id());
915
916 let _g1 = span1.enter();
917 error!("test span1");
918 let json = writer.read_json();
919 println!("{json}");
920 assert_eq!(json["trace_id"].as_str().unwrap(), generated_trace_id);
921
922 let span2 = info_span!("span2");
923 let _g2 = span2.enter();
924 debug!("test span2");
925 let json = writer.read_json();
926 println!("{json}");
927 assert_eq!(json["trace_id"].as_str().unwrap(), generated_trace_id);
928 }
929
930 {
932 let fixed_trace_id = "09f91f229168786afbed35fa3a98d86a".to_owned();
933 let mut m = HashMap::with_capacity(1);
934 m.insert(
935 "traceparent".to_owned(),
936 format!("00-{fixed_trace_id}-ce812d502e8f1299-01"),
937 );
938 let cx = TraceContextPropagator::new().extract(&m);
939
940 warn!("test event");
942 let json = writer.read_json();
943 println!("{json}");
944 assert_eq!(json["trace_id"], serde_json::Value::Null);
945
946 let span1 = info_span!("span1");
947 span1.set_parent(cx);
948
949 let _g1 = span1.enter();
950 error!("test span1");
951 let json = writer.read_json();
952 println!("{json}");
953 assert_eq!(json["trace_id"].as_str().unwrap(), fixed_trace_id);
954
955 let span2 = info_span!("span2");
956 let _g2 = span2.enter();
957 debug!("test span2");
958 let json = writer.read_json();
959 println!("{json}");
960 assert_eq!(json["trace_id"].as_str().unwrap(), fixed_trace_id);
961 }
962 }
963}