json_subscriber/layer/mod.rs
1use std::{
2 borrow::Cow,
3 cell::RefCell,
4 collections::BTreeMap,
5 fmt,
6 io,
7 sync::{Arc, OnceLock},
8};
9
10use serde::Serialize;
11use tracing::{dispatcher::WeakDispatch, Dispatch};
12use tracing_core::{
13 span::{Attributes, Id, Record},
14 Event,
15 Subscriber,
16};
17use tracing_serde::fields::AsMap;
18use tracing_subscriber::{
19 fmt::{format::Writer, time::FormatTime, MakeWriter, TestWriter},
20 layer::Context,
21 registry::{LookupSpan, SpanRef},
22 Layer,
23 Registry,
24};
25
26mod event;
27
28use event::EventRef;
29use uuid::Uuid;
30
31use crate::{
32 cached::Cached,
33 field_writer::FieldWriter,
34 fields::{JsonFields, JsonFieldsInner},
35 serde::RenamedFields,
36 visitor::JsonVisitor,
37};
38
39/// Layer that implements logging JSON to a configured output. This is a lower-level API that may
40/// change a bit in next versions.
41///
42/// See [`fmt::Layer`](crate::fmt::Layer) for an alternative especially if you're migrating from
43/// `tracing_subscriber`.
44pub struct JsonLayer<S: for<'lookup> LookupSpan<'lookup> = Registry, W = fn() -> io::Stdout> {
45 make_writer: W,
46 log_internal_errors: bool,
47 keyed_values: BTreeMap<SchemaKey, JsonValue<S>>,
48 flattened_values: BTreeMap<FlatSchemaKey, JsonValue<S>>,
49 dispatch: OnceLock<WeakDispatch>,
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
53pub(crate) enum SchemaKey {
54 Static(Cow<'static, str>),
55}
56
57#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
58pub(crate) enum FlatSchemaKey {
59 Uuid(Uuid),
60 FlattenedEvent,
61 FlattenedCurrentSpan,
62 FlattenedSpanList,
63}
64
65impl FlatSchemaKey {
66 fn new_uuid() -> Self {
67 Self::Uuid(uuid::Uuid::new_v4())
68 }
69}
70
71impl From<Cow<'static, str>> for SchemaKey {
72 fn from(value: Cow<'static, str>) -> Self {
73 Self::Static(value)
74 }
75}
76
77impl From<&'static str> for SchemaKey {
78 fn from(value: &'static str) -> Self {
79 Self::Static(value.into())
80 }
81}
82
83impl From<String> for SchemaKey {
84 fn from(value: String) -> Self {
85 Self::Static(value.into())
86 }
87}
88
89#[allow(clippy::type_complexity)]
90pub(crate) enum JsonValue<S: for<'lookup> LookupSpan<'lookup>> {
91 Serde(serde_json::Value),
92 DynamicFromEvent(
93 Box<dyn Fn(&EventRef<'_, '_, '_, S>) -> Option<serde_json::Value> + Send + Sync>,
94 ),
95 DynamicFromSpan(Box<dyn Fn(&SpanRef<'_, S>) -> Option<serde_json::Value> + Send + Sync>),
96 DynamicFromSpanWithDispatch(
97 Box<dyn Fn(&SpanRef<'_, S>, &Dispatch) -> Option<serde_json::Value> + Send + Sync>,
98 ),
99 DynamicCachedFromSpan(Box<dyn Fn(&SpanRef<'_, S>) -> Option<Cached> + Send + Sync>),
100 DynamicRawFromEvent(
101 Box<dyn Fn(&EventRef<'_, '_, '_, S>, &mut dyn fmt::Write) -> fmt::Result + Send + Sync>,
102 ),
103 DynamicFromEventWithWriter(
104 Box<dyn Fn(&EventRef<'_, '_, '_, S>, &mut FieldWriter<'_>) + Send + Sync>,
105 ),
106}
107
108impl<S, W> Layer<S> for JsonLayer<S, W>
109where
110 S: Subscriber + for<'lookup> LookupSpan<'lookup>,
111 W: for<'writer> MakeWriter<'writer> + 'static,
112{
113 fn on_register_dispatch(&self, subscriber: &tracing::Dispatch) {
114 _ = self.dispatch.set(subscriber.downgrade());
115 }
116
117 fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
118 let Some(span) = ctx.span(id) else {
119 if self.log_internal_errors {
120 eprintln!("[json-subscriber] Span not found, this is a bug.");
121 }
122 return;
123 };
124
125 let mut extensions = span.extensions_mut();
126
127 if extensions.get_mut::<JsonFields>().is_none() {
128 let mut fields = JsonFieldsInner::default();
129 let mut visitor = JsonVisitor::new(&mut fields);
130 attrs.record(&mut visitor);
131 fields
132 .fields
133 .insert("name", serde_json::Value::from(attrs.metadata().name()));
134 let fields = fields.finish();
135 extensions.insert(fields);
136 } else if self.log_internal_errors {
137 eprintln!(
138 "[json-subscriber] Unable to format the following event, ignoring: {attrs:?}",
139 );
140 }
141 }
142
143 fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) {
144 let Some(span) = ctx.span(id) else {
145 if self.log_internal_errors {
146 eprintln!("[json-subscriber] Span not found, this is a bug.");
147 }
148 return;
149 };
150
151 let mut extensions = span.extensions_mut();
152 let Some(fields) = extensions.get_mut::<JsonFields>() else {
153 if self.log_internal_errors {
154 eprintln!(
155 "[json-subscriber] Span was created but does not contain formatted fields, \
156 this is a bug and some fields may have been lost."
157 );
158 }
159 return;
160 };
161
162 values.record(&mut JsonVisitor::new(&mut fields.inner));
163 let serialized = serde_json::to_string(&fields).unwrap();
164 fields.serialized = Arc::from(serialized.as_str());
165 }
166
167 fn on_enter(&self, _id: &Id, _ctx: Context<'_, S>) {}
168
169 fn on_exit(&self, _id: &Id, _ctx: Context<'_, S>) {}
170
171 fn on_close(&self, _id: Id, _ctx: Context<'_, S>) {}
172
173 fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
174 thread_local! {
175 static BUF: RefCell<String> = const { RefCell::new(String::new()) };
176 }
177
178 BUF.with(|buf| {
179 let borrow = buf.try_borrow_mut();
180 let mut a;
181 let mut b;
182 let buf = if let Ok(buf) = borrow {
183 a = buf;
184 &mut *a
185 } else {
186 b = String::new();
187 &mut b
188 };
189
190 if self.format_event(&ctx, buf, event).is_ok() {
191 let mut writer = self.make_writer.make_writer_for(event.metadata());
192 let res = io::Write::write_all(&mut writer, buf.as_bytes());
193 if self.log_internal_errors {
194 if let Err(e) = res {
195 eprintln!(
196 "[tracing-json] Unable to write an event to the Writer for this \
197 Subscriber! Error: {e}\n",
198 );
199 }
200 }
201 } else if self.log_internal_errors {
202 eprintln!(
203 "[tracing-json] Unable to format the following event. Name: {}; Fields: {:?}",
204 event.metadata().name(),
205 event.fields()
206 );
207 }
208
209 buf.clear();
210 });
211 }
212}
213
214impl<S> JsonLayer<S>
215where
216 S: Subscriber + for<'lookup> LookupSpan<'lookup>,
217{
218 /// Creates an empty [`JsonLayer`] which will output logs to stdout.
219 pub fn stdout() -> JsonLayer<S, fn() -> io::Stdout> {
220 JsonLayer::new(io::stdout)
221 }
222
223 /// Creates an empty [`JsonLayer`] which will output logs to stderr.
224 pub fn stderr() -> JsonLayer<S, fn() -> io::Stderr> {
225 JsonLayer::new(io::stderr)
226 }
227
228 /// Creates an empty [`JsonLayer`] which will output logs to the configured
229 /// [`Writer`](io::Write).
230 pub fn new<W>(make_writer: W) -> JsonLayer<S, W>
231 where
232 W: for<'writer> MakeWriter<'writer> + 'static,
233 {
234 JsonLayer::<S, W> {
235 make_writer,
236 log_internal_errors: false,
237 keyed_values: BTreeMap::new(),
238 flattened_values: BTreeMap::new(),
239 dispatch: OnceLock::new(),
240 }
241 }
242}
243
244impl<S, W> JsonLayer<S, W>
245where
246 S: Subscriber + for<'lookup> LookupSpan<'lookup>,
247{
248 /// Sets the [`MakeWriter`] that the [`JsonLayer`] being built will use to write events.
249 ///
250 /// # Examples
251 ///
252 /// Using `stderr` rather than `stdout`:
253 ///
254 /// ```rust
255 /// # use tracing_subscriber::prelude::*;
256 /// let layer = json_subscriber::JsonLayer::stdout()
257 /// .with_writer(std::io::stderr);
258 /// # tracing_subscriber::registry().with(layer);
259 /// ```
260 ///
261 /// [`MakeWriter`]: MakeWriter
262 /// [`JsonLayer`]: super::JsonLayer
263 pub fn with_writer<W2>(self, make_writer: W2) -> JsonLayer<S, W2>
264 where
265 W2: for<'writer> MakeWriter<'writer> + 'static,
266 {
267 JsonLayer {
268 make_writer,
269 log_internal_errors: self.log_internal_errors,
270 keyed_values: self.keyed_values,
271 flattened_values: self.flattened_values,
272 dispatch: self.dispatch,
273 }
274 }
275
276 /// Borrows the [writer] for this subscriber.
277 ///
278 /// [writer]: MakeWriter
279 pub fn writer(&self) -> &W {
280 &self.make_writer
281 }
282
283 /// Mutably borrows the [writer] for this subscriber.
284 ///
285 /// This method is primarily expected to be used with the
286 /// [`reload::Handle::modify`](tracing_subscriber::reload::Handle::modify) method.
287 ///
288 /// # Examples
289 ///
290 /// ```
291 /// # use tracing::info;
292 /// # use tracing_subscriber::{fmt,reload,Registry,prelude::*};
293 /// # fn non_blocking<T: std::io::Write>(writer: T) -> (fn() -> std::io::Stdout) {
294 /// # std::io::stdout
295 /// # }
296 /// # fn main() {
297 /// let layer = json_subscriber::JsonLayer::stdout().with_writer(non_blocking(std::io::stderr()));
298 /// let (layer, reload_handle) = reload::Layer::new(layer);
299 ///
300 /// tracing_subscriber::registry().with(layer).init();
301 ///
302 /// info!("This will be logged to stderr");
303 /// reload_handle.modify(|subscriber| *subscriber.writer_mut() = non_blocking(std::io::stdout()));
304 /// info!("This will be logged to stdout");
305 /// # }
306 /// ```
307 ///
308 /// [writer]: MakeWriter
309 pub fn writer_mut(&mut self) -> &mut W {
310 &mut self.make_writer
311 }
312
313 /// Configures the subscriber to support [`libtest`'s output capturing][capturing] when used in
314 /// unit tests.
315 ///
316 /// See [`TestWriter`] for additional details.
317 ///
318 /// # Examples
319 ///
320 /// Using [`TestWriter`] to let `cargo test` capture test output:
321 ///
322 /// ```rust
323 /// # use tracing_subscriber::prelude::*;
324 /// let layer = json_subscriber::JsonLayer::stdout()
325 /// .with_test_writer();
326 /// # tracing_subscriber::registry().with(layer);
327 /// ```
328 /// [capturing]:
329 /// https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
330 pub fn with_test_writer(self) -> JsonLayer<S, TestWriter> {
331 JsonLayer {
332 make_writer: TestWriter::default(),
333 log_internal_errors: self.log_internal_errors,
334 keyed_values: self.keyed_values,
335 flattened_values: self.flattened_values,
336 dispatch: self.dispatch,
337 }
338 }
339
340 /// Sets whether to write errors from [`FormatEvent`] to the writer.
341 /// Defaults to true.
342 ///
343 /// By default, `fmt::JsonLayer` will write any `FormatEvent`-internal errors to
344 /// the writer. These errors are unlikely and will only occur if there is a
345 /// bug in the `FormatEvent` implementation or its dependencies.
346 ///
347 /// If writing to the writer fails, the error message is printed to stderr
348 /// as a fallback.
349 ///
350 /// [`FormatEvent`]: tracing_subscriber::fmt::FormatEvent
351 pub fn log_internal_errors(&mut self, log_internal_errors: bool) -> &mut Self {
352 self.log_internal_errors = log_internal_errors;
353 self
354 }
355
356 /// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`].
357 ///
358 /// This sets the [`MakeWriter`] that the subscriber being built will use to write events.
359 ///
360 /// # Examples
361 ///
362 /// Redirect output to stderr if level is <= WARN:
363 ///
364 /// ```rust
365 /// # use tracing_subscriber::prelude::*;
366 /// use tracing_subscriber::fmt::writer::MakeWriterExt;
367 ///
368 /// let stderr = std::io::stderr.with_max_level(tracing::Level::WARN);
369 /// let layer = json_subscriber::JsonLayer::stdout()
370 /// .map_writer(move |w| stderr.or_else(w));
371 /// # tracing_subscriber::registry().with(layer);
372 /// ```
373 pub fn map_writer<W2>(self, f: impl FnOnce(W) -> W2) -> JsonLayer<S, W2>
374 where
375 W2: for<'writer> MakeWriter<'writer> + 'static,
376 {
377 JsonLayer {
378 make_writer: f(self.make_writer),
379 log_internal_errors: self.log_internal_errors,
380 keyed_values: self.keyed_values,
381 flattened_values: self.flattened_values,
382 dispatch: self.dispatch,
383 }
384 }
385
386 /// Adds a new static field with a given key to the output.
387 ///
388 /// # Examples
389 ///
390 /// Print hostname in each log:
391 ///
392 /// ```rust
393 /// # use tracing_subscriber::prelude::*;
394 /// let mut layer = json_subscriber::JsonLayer::stdout();
395 /// layer.add_static_field(
396 /// "hostname",
397 /// serde_json::Value::String(get_hostname().to_owned()),
398 /// );
399 /// # tracing_subscriber::registry().with(layer);
400 /// # fn get_hostname() -> &'static str { "localhost" }
401 /// ```
402 pub fn add_static_field(&mut self, key: impl Into<String>, value: serde_json::Value) {
403 self.keyed_values
404 .insert(SchemaKey::from(key.into()), JsonValue::Serde(value));
405 }
406
407 /// Removes a field that was inserted to the output. This can only remove fields that have a
408 /// static key, not keys added with
409 /// [`add_multiple_dynamic_fields`](Self::add_multiple_dynamic_fields).
410 ///
411 /// # Examples
412 ///
413 /// Add a field and then remove it:
414 ///
415 /// ```rust
416 /// # use tracing_subscriber::prelude::*;
417 /// let mut layer = json_subscriber::JsonLayer::stdout();
418 /// layer.add_static_field(
419 /// "deleteMe",
420 /// serde_json::json!("accident"),
421 /// );
422 /// layer.remove_field("deleteMe");
423 ///
424 /// # tracing_subscriber::registry().with(layer);
425 /// ```
426 pub fn remove_field(&mut self, key: impl Into<String>) {
427 self.keyed_values.remove(&SchemaKey::from(key.into()));
428 }
429
430 pub(crate) fn remove_flattened_field(&mut self, key: &FlatSchemaKey) {
431 self.flattened_values.remove(key);
432 }
433
434 /// Adds a new dynamic field with a given key to the output. This method is more general than
435 /// [`add_static_field`](Self::add_static_field) but also more expensive.
436 ///
437 /// This method takes a closure argument that will be called with the event and tracing context.
438 /// Through these, the parent span can be accessed among other things. This closure returns an
439 /// `Option` where nothing will be added to the output if `None` is returned.
440 ///
441 /// # Examples
442 ///
443 /// Print an atomic counter.
444 ///
445 /// ```rust
446 /// # use tracing_subscriber::prelude::*;
447 /// # use std::sync::atomic::{AtomicU32, Ordering};
448 /// static COUNTER: AtomicU32 = AtomicU32::new(42);
449 ///
450 /// let mut layer = json_subscriber::JsonLayer::stdout();
451 /// layer.add_dynamic_field(
452 /// "counter",
453 /// |_event, _context| {
454 /// Some(serde_json::Value::Number(COUNTER.load(Ordering::Relaxed).into()))
455 /// });
456 /// # tracing_subscriber::registry().with(layer);
457 /// ```
458 pub fn add_dynamic_field<Fun, Res>(&mut self, key: impl Into<String>, mapper: Fun)
459 where
460 for<'a> Fun: Fn(&'a Event<'_>, &Context<'_, S>) -> Option<Res> + Send + Sync + 'a,
461 Res: serde::Serialize,
462 {
463 self.keyed_values.insert(
464 SchemaKey::from(key.into()),
465 JsonValue::DynamicFromEvent(Box::new(move |event| {
466 serde_json::to_value(mapper(event.event(), event.context())?).ok()
467 })),
468 );
469 }
470
471 /// Adds multiple new dynamic fields where the keys may not be known when calling this method.
472 ///
473 /// This method takes a closure argument that will be called with the event and tracing context
474 /// along with a [`FieldWriter`]. Call [`FieldWriter::write_field`] to write key-value pairs
475 /// directly into the JSON output. Values accept any type implementing [`serde::Serialize`].
476 ///
477 /// It is the user's responsibility to make sure that no two keys clash as that would create an
478 /// invalid JSON. It's generally better to use [`add_dynamic_field`](Self::add_dynamic_field)
479 /// instead if the field names are known.
480 ///
481 /// # Examples
482 ///
483 /// Print a question or an answer:
484 ///
485 /// ```rust
486 /// # use tracing_subscriber::prelude::*;
487 ///
488 /// let mut layer = json_subscriber::JsonLayer::stdout();
489 /// layer.add_multiple_dynamic_fields(
490 /// |_event, _context, writer| {
491 /// # let condition = true;
492 /// if condition {
493 /// _ = writer.write_field("question", "What?");
494 /// } else {
495 /// _ = writer.write_field("answer", 42u64);
496 /// }
497 /// }
498 /// );
499 /// # tracing_subscriber::registry().with(layer);
500 /// ```
501 pub fn add_multiple_dynamic_fields<Fun>(&mut self, mapper: Fun)
502 where
503 Fun: Fn(&Event<'_>, &Context<'_, S>, &mut FieldWriter<'_>) + Send + Sync + 'static,
504 {
505 self.flattened_values.insert(
506 FlatSchemaKey::new_uuid(),
507 JsonValue::DynamicFromEventWithWriter(Box::new(move |event, writer| {
508 mapper(event.event(), event.context(), writer);
509 })),
510 );
511 }
512
513 /// Adds a new dynamic field with a given key to the output. This method is a specialized
514 /// version of [`add_dynamic_field`](Self::add_dynamic_field) where just a reference to the
515 /// parent span is needed.
516 ///
517 /// This method takes a closure argument that will be called with the parent span context. This
518 /// closure returns an `Option` where nothing will be added to the output if `None` is returned.
519 ///
520 /// # Examples
521 ///
522 /// Print uppercase target:
523 ///
524 /// ```rust
525 /// # use tracing_subscriber::prelude::*;
526 ///
527 /// let mut layer = json_subscriber::JsonLayer::stdout();
528 /// layer.add_from_span(
529 /// "TARGET",
530 /// |span| Some(span.metadata().target().to_uppercase())
531 /// );
532 /// # tracing_subscriber::registry().with(layer);
533 /// ```
534 pub fn add_from_span<Fun, Res>(&mut self, key: impl Into<String>, mapper: Fun)
535 where
536 for<'a> Fun: Fn(&'a SpanRef<'_, S>) -> Option<Res> + Send + Sync + 'a,
537 Res: serde::Serialize,
538 {
539 self.keyed_values.insert(
540 SchemaKey::from(key.into()),
541 JsonValue::DynamicFromSpan(Box::new(move |span| {
542 serde_json::to_value(mapper(span)?).ok()
543 })),
544 );
545 }
546
547 /// Adds a field with a given key to the output. The value will be serialized JSON of the
548 /// provided extension. Other [`Layer`]s may add these extensions to the span.
549 ///
550 /// The serialization happens every time a log line is emitted so if the extension changes, the
551 /// latest version will be emitted.
552 ///
553 /// If the extension is not found, nothing is added to the output.
554 ///
555 /// # Examples
556 ///
557 /// ```rust
558 /// # use tracing::span::Attributes;
559 /// # use tracing::Id;
560 /// # use tracing::Subscriber;
561 /// # use tracing_subscriber::registry;
562 /// # use tracing_subscriber::registry::LookupSpan;
563 /// # use tracing_subscriber::Layer;
564 /// # use tracing_subscriber::layer::Context;
565 /// # use tracing_subscriber::prelude::*;
566 /// # use serde::Serialize;
567 /// struct FooLayer;
568 ///
569 /// #[derive(Serialize)]
570 /// struct Foo(String);
571 ///
572 /// impl<S: Subscriber + for<'lookup> LookupSpan<'lookup>> Layer<S> for FooLayer {
573 /// fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
574 /// let span = ctx.span(id).unwrap();
575 /// let mut extensions = span.extensions_mut();
576 /// let foo = Foo("hello".to_owned());
577 /// extensions.insert(foo);
578 /// }
579 /// }
580 ///
581 /// # fn main() {
582 /// let foo_layer = FooLayer;
583 ///
584 /// let mut layer = json_subscriber::JsonLayer::stdout();
585 /// layer.serialize_extension::<Foo>("foo");
586 ///
587 /// registry().with(foo_layer).with(layer);
588 /// # }
589 /// ```
590 pub fn serialize_extension<Ext: Serialize + 'static>(&mut self, key: impl Into<String>) {
591 self.add_from_extension_ref(key, |extension: &Ext| Some(extension));
592 }
593
594 /// Adds a field with a given key to the output. The user-provided closure can transform the
595 /// extension and return reference to any serializable structure.
596 ///
597 /// The mapping and serialization happens every time a log line is emitted so if the extension
598 /// changes, the latest version will be emitted.
599 ///
600 /// If the extension is not found, or the mapping returns `None`, nothing is added to the
601 /// output.
602 ///
603 /// Use [`Self::add_from_extension`] if you cannot return a reference.
604 ///
605 /// # Examples
606 ///
607 /// ```rust
608 /// # use tracing::span::Attributes;
609 /// # use tracing::Id;
610 /// # use tracing::Subscriber;
611 /// # use tracing_subscriber::registry;
612 /// # use tracing_subscriber::registry::LookupSpan;
613 /// # use tracing_subscriber::Layer;
614 /// # use tracing_subscriber::layer::Context;
615 /// # use tracing_subscriber::prelude::*;
616 /// # use serde::Serialize;
617 /// struct FooLayer;
618 ///
619 /// #[derive(Serialize)]
620 /// struct Foo(String);
621 ///
622 /// impl<S: Subscriber + for<'lookup> LookupSpan<'lookup>> Layer<S> for FooLayer {
623 /// fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
624 /// let span = ctx.span(id).unwrap();
625 /// let mut extensions = span.extensions_mut();
626 /// let foo = Foo("hello".to_owned());
627 /// extensions.insert(foo);
628 /// }
629 /// }
630 ///
631 /// # fn main() {
632 /// let foo_layer = FooLayer;
633 ///
634 /// let mut layer = json_subscriber::JsonLayer::stdout();
635 /// layer.add_from_extension_ref::<Foo, _, _>("foo", |foo| Some(&foo.0));
636 ///
637 /// registry().with(foo_layer).with(layer);
638 /// # }
639 /// ```
640 pub fn add_from_extension_ref<Ext, Fun, Res>(&mut self, key: impl Into<String>, mapper: Fun)
641 where
642 Ext: 'static,
643 for<'a> Fun: Fn(&'a Ext) -> Option<&'a Res> + Send + Sync + 'a,
644 Res: serde::Serialize,
645 {
646 self.keyed_values.insert(
647 SchemaKey::from(key.into()),
648 JsonValue::DynamicFromSpan(Box::new(move |span| {
649 let extensions = span.extensions();
650 let extension = extensions.get::<Ext>()?;
651 serde_json::to_value(mapper(extension)).ok()
652 })),
653 );
654 }
655
656 /// Adds a field with a given key to the output. The user-provided closure can transform the
657 /// extension and return any serializable structure.
658 ///
659 /// The mapping and serialization happens every time a log line is emitted so if the extension
660 /// changes, the latest version will be emitted.
661 ///
662 /// If the extension is not found, or the mapping returns `None`, nothing is added to the
663 /// output.
664 ///
665 /// Use [`Self::add_from_extension_ref`] if you want to return a reference to data in the
666 /// extension.
667 ///
668 /// # Examples
669 ///
670 /// ```rust
671 /// # use tracing::span::Attributes;
672 /// # use tracing::Id;
673 /// # use tracing::Subscriber;
674 /// # use tracing_subscriber::registry;
675 /// # use tracing_subscriber::registry::LookupSpan;
676 /// # use tracing_subscriber::Layer;
677 /// # use tracing_subscriber::layer::Context;
678 /// # use tracing_subscriber::prelude::*;
679 /// # use serde::Serialize;
680 /// struct FooLayer;
681 ///
682 /// #[derive(Serialize)]
683 /// struct Foo(String);
684 ///
685 /// impl<S: Subscriber + for<'lookup> LookupSpan<'lookup>> Layer<S> for FooLayer {
686 /// fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
687 /// let span = ctx.span(id).unwrap();
688 /// let mut extensions = span.extensions_mut();
689 /// let foo = Foo("hello".to_owned());
690 /// extensions.insert(foo);
691 /// }
692 /// }
693 ///
694 /// # fn main() {
695 /// let foo_layer = FooLayer;
696 ///
697 /// let mut layer = json_subscriber::JsonLayer::stdout();
698 /// layer.add_from_extension::<Foo, _, _>("foo", |foo| foo.0.parse::<u64>().ok());
699 ///
700 /// registry().with(foo_layer).with(layer);
701 /// # }
702 /// ```
703 pub fn add_from_extension<Ext, Fun, Res>(&mut self, key: impl Into<String>, mapper: Fun)
704 where
705 Ext: 'static,
706 for<'a> Fun: Fn(&'a Ext) -> Option<Res> + Send + Sync + 'a,
707 Res: serde::Serialize,
708 {
709 self.keyed_values.insert(
710 SchemaKey::from(key.into()),
711 JsonValue::DynamicFromSpan(Box::new(move |span| {
712 let extensions = span.extensions();
713 let extension = extensions.get::<Ext>()?;
714 serde_json::to_value(mapper(extension)).ok()
715 })),
716 );
717 }
718
719 /// Print all event fields in an object with the key as specified.
720 pub fn with_event(&mut self, key: impl Into<String>) -> &mut Self {
721 self.keyed_values.insert(
722 SchemaKey::from(key.into()),
723 JsonValue::DynamicFromEvent(Box::new(move |event| {
724 serde_json::to_value(event.field_map()).ok()
725 })),
726 );
727 self
728 }
729
730 /// Print all current span fields, each as its own top level member of the JSON.
731 ///
732 /// It is the user's responsibility to make sure that the field names will not clash with other
733 /// defined members of the output JSON. If they clash, invalid JSON with multiple fields with
734 /// the same key may be generated.
735 ///
736 /// It's therefore preferable to use [`with_current_span`](Self::with_current_span) instead.
737 pub fn with_top_level_flattened_current_span(&mut self) -> &mut Self {
738 self.flattened_values.insert(
739 FlatSchemaKey::FlattenedCurrentSpan,
740 JsonValue::DynamicCachedFromSpan(Box::new(move |span| {
741 span.extensions()
742 .get::<JsonFields>()
743 .map(|fields| Cached::Raw(fields.serialized.clone()))
744 })),
745 );
746 self
747 }
748
749 /// Print all parent spans' fields, each as its own top level member of the JSON.
750 ///
751 /// If multiple spans define the same field, the one furthest from the root span will be kept.
752 ///
753 /// It is the user's responsibility to make sure that the field names will not clash with other
754 /// defined members of the output JSON. If they clash, invalid JSON with multiple fields with
755 /// the same key may be generated.
756 ///
757 /// It's therefore preferable to use [`with_span_list`](Self::with_span_list) instead.
758 pub fn with_top_level_flattened_span_list(&mut self) -> &mut Self {
759 self.flattened_values.insert(
760 FlatSchemaKey::FlattenedSpanList,
761 JsonValue::DynamicFromSpan(Box::new(|span| {
762 let fields =
763 span.scope()
764 .from_root()
765 .fold(BTreeMap::new(), |mut accumulator, span| {
766 let extensions = span.extensions();
767 let Some(fields) = extensions.get::<JsonFields>() else {
768 return accumulator;
769 };
770 accumulator.extend(
771 fields
772 .inner
773 .fields
774 .iter()
775 .map(|(key, value)| (*key, value.clone())),
776 );
777 accumulator
778 });
779
780 serde_json::to_value(fields).ok()
781 })),
782 );
783 self
784 }
785
786 /// Print all event fields, each as its own top level member of the JSON.
787 ///
788 /// It is the user's responsibility to make sure that the field names will not clash with other
789 /// defined members of the output JSON. If they clash, invalid JSON with multiple fields with
790 /// the same key may be generated.
791 ///
792 /// It's therefore preferable to use [`with_event`](Self::with_event) instead.
793 pub fn with_flattened_event(&mut self) -> &mut Self {
794 self.flattened_values.insert(
795 FlatSchemaKey::FlattenedEvent,
796 JsonValue::DynamicFromEvent(Box::new(move |event| {
797 serde_json::to_value(event.field_map()).ok()
798 })),
799 );
800 self
801 }
802
803 /// Print all event fields, each as its own top level member of the JSON. This also allows the
804 /// fields to have different keys than the field names used in tracing.
805 ///
806 /// Only field names can be renamed this way, not other fields such as `timestamp` or `target`
807 /// which usually take the key as their parameter (such as `Self::with_target`).
808 ///
809 /// The renames are realized by a provided function that will be passed the original field name
810 /// and a user-defined context and it must produce the new name.
811 ///
812 /// For example when simple static renames are needed the following can work:
813 ///
814 /// ```rust
815 /// # use std::collections::HashMap;
816 /// # use tracing_subscriber::prelude::*;
817 ///
818 /// let mut layer = json_subscriber::JsonLayer::stdout();
819 ///
820 /// let renames = HashMap::from([
821 /// ("message".to_owned(), "msg".to_owned()),
822 /// ("foo".to_owned(), "bar".to_owned()),
823 /// ]);
824 /// layer.with_flattened_event_with_renames(
825 /// move |name, map| map.get(name).map_or(name, String::as_str),
826 /// renames,
827 /// );
828 /// # tracing_subscriber::registry().with(layer);
829 ///
830 /// // This will produce something like `{"bar":3,"msg":"x",...}`
831 /// tracing::info!(foo = 3, "x");
832 /// ```
833 ///
834 /// It is the user's responsibility to make sure that the field names will not clash with other
835 /// defined members of the output JSON. If they clash, invalid JSON with multiple fields with
836 /// the same key may be generated.
837 pub fn with_flattened_event_with_renames<F, T>(&mut self, renames: F, context: T) -> &mut Self
838 where
839 F: for<'a> Fn(&'a str, &'a T) -> &'a str + Send + Sync + 'static + Clone,
840 T: Clone + Send + Sync + 'static,
841 {
842 self.flattened_values.insert(
843 FlatSchemaKey::FlattenedEvent,
844 JsonValue::DynamicFromEvent(Box::new(move |event| {
845 serde_json::to_value(RenamedFields::new(event.event(), renames.clone(), &context))
846 .ok()
847 })),
848 );
849 self
850 }
851
852 /// Sets whether or not the log line will include the current span in formatted events.
853 pub fn with_current_span(&mut self, key: impl Into<String>) -> &mut Self {
854 self.keyed_values.insert(
855 SchemaKey::from(key.into()),
856 JsonValue::DynamicCachedFromSpan(Box::new(move |span| {
857 span.extensions()
858 .get::<JsonFields>()
859 .map(|fields| Cached::Raw(fields.serialized.clone()))
860 })),
861 );
862 self
863 }
864
865 /// Sets whether or not the formatter will include a list (from root to leaf) of all currently
866 /// entered spans in formatted events.
867 pub fn with_span_list(&mut self, key: impl Into<String>) -> &mut Self {
868 self.keyed_values.insert(
869 SchemaKey::from(key.into()),
870 JsonValue::DynamicCachedFromSpan(Box::new(|span| {
871 Some(Cached::Array(
872 span.scope()
873 .from_root()
874 .filter_map(|span| {
875 span.extensions()
876 .get::<JsonFields>()
877 .map(|fields| fields.serialized.clone())
878 })
879 .collect::<Vec<_>>(),
880 ))
881 })),
882 );
883 self
884 }
885
886 /// Sets the formatter to include an object containing all parent spans' fields. If multiple
887 /// ancestor spans recorded the same field, the span closer to the leaf span overrides the
888 /// values of spans that are closer to the root spans.
889 pub fn with_flattened_span_fields(&mut self, key: impl Into<String>) -> &mut Self {
890 self.keyed_values.insert(
891 SchemaKey::from(key.into()),
892 JsonValue::DynamicFromSpan(Box::new(|span| {
893 let fields =
894 span.scope()
895 .from_root()
896 .fold(BTreeMap::new(), |mut accumulator, span| {
897 let extensions = span.extensions();
898 let Some(fields) = extensions.get::<JsonFields>() else {
899 return accumulator;
900 };
901 accumulator.extend(
902 fields
903 .inner
904 .fields
905 .iter()
906 .map(|(key, value)| (*key, value.clone())),
907 );
908 accumulator
909 });
910
911 serde_json::to_value(fields).ok()
912 })),
913 );
914 self
915 }
916
917 /// Use the given [`timer`] for log message timestamps with the `timestamp` key.
918 ///
919 /// See the [`time` module] for the provided timer implementations.
920 ///
921 /// [`timer`]: tracing_subscriber::fmt::time::FormatTime
922 /// [`time` module]: mod@tracing_subscriber::fmt::time
923 pub fn with_timer<T: FormatTime + Send + Sync + 'static>(
924 &mut self,
925 key: impl Into<String>,
926 timer: T,
927 ) -> &mut Self {
928 self.keyed_values.insert(
929 SchemaKey::from(key.into()),
930 JsonValue::DynamicFromEvent(Box::new(move |_| {
931 let mut timestamp = String::with_capacity(32);
932 timer.format_time(&mut Writer::new(&mut timestamp)).ok()?;
933 Some(timestamp.into())
934 })),
935 );
936 self
937 }
938
939 /// Sets whether or not an event's target is displayed. It will use the `target` key if so.
940 pub fn with_target(&mut self, key: impl Into<String>) -> &mut Self {
941 self.keyed_values.insert(
942 SchemaKey::from(key.into()),
943 JsonValue::DynamicRawFromEvent(Box::new(|event, writer| {
944 write_escaped(writer, event.metadata().target())
945 })),
946 );
947
948 self
949 }
950
951 /// Sets whether or not an event's [source code file path][file] is displayed. It will use the
952 /// `file` key if so.
953 ///
954 /// [file]: tracing_core::Metadata::file
955 pub fn with_file(&mut self, key: impl Into<String>) -> &mut Self {
956 self.keyed_values.insert(
957 SchemaKey::from(key.into()),
958 JsonValue::DynamicRawFromEvent(Box::new(|event, writer| {
959 match event.metadata().file() {
960 Some(file) => write_escaped(writer, file),
961 None => write!(writer, "null"),
962 }
963 })),
964 );
965 self
966 }
967
968 /// Sets whether or not an event's [source code line number][line] is displayed. It will use the
969 /// `line_number` key if so.
970 ///
971 /// [line]: tracing_core::Metadata::line
972 pub fn with_line_number(&mut self, key: impl Into<String>) -> &mut Self {
973 self.keyed_values.insert(
974 SchemaKey::from(key.into()),
975 JsonValue::DynamicRawFromEvent(Box::new(|event, writer| {
976 match event.metadata().line() {
977 Some(line) => write!(writer, "{line}"),
978 None => write!(writer, "null"),
979 }
980 })),
981 );
982 self
983 }
984
985 /// Sets whether or not an event's level is displayed. It will use the `level` key if so.
986 pub fn with_level(&mut self, key: impl Into<String>) -> &mut Self {
987 self.keyed_values.insert(
988 SchemaKey::from(key.into()),
989 JsonValue::DynamicRawFromEvent(Box::new(|event, writer| {
990 write_escaped(writer, event.metadata().level().as_str())
991 })),
992 );
993 self
994 }
995
996 /// Sets whether or not the [name] of the current thread is displayed when formatting events. It
997 /// will use the `threadName` key if so.
998 ///
999 /// [name]: std::thread#naming-threads
1000 pub fn with_thread_names(&mut self, key: impl Into<String>) -> &mut Self {
1001 self.keyed_values.insert(
1002 SchemaKey::from(key.into()),
1003 JsonValue::DynamicRawFromEvent(Box::new(|_event, writer| {
1004 match std::thread::current().name() {
1005 Some(name) => write_escaped(writer, name),
1006 None => write!(writer, "null"),
1007 }
1008 })),
1009 );
1010 self
1011 }
1012
1013 /// Sets whether or not the [thread ID] of the current thread is displayed when formatting
1014 /// events. It will use the `threadId` key if so.
1015 ///
1016 /// [thread ID]: std::thread::ThreadId
1017 pub fn with_thread_ids(&mut self, key: impl Into<String>) -> &mut Self {
1018 self.keyed_values.insert(
1019 SchemaKey::from(key.into()),
1020 JsonValue::DynamicRawFromEvent(Box::new(|_event, writer| {
1021 use std::fmt::Write;
1022 let mut value = String::with_capacity(12);
1023 write!(&mut value, "{:?}", std::thread::current().id())?;
1024 write_escaped(writer, &value)
1025 })),
1026 );
1027
1028 self
1029 }
1030
1031 /// Sets whether or not [OpenTelemetry] trace ID and span ID is displayed when formatting
1032 /// events. It will use the `openTelemetry` key if so and the value will be an object with
1033 /// `traceId` and `spanId` fields, each being a string.
1034 ///
1035 /// This works only if your `tracing-opentelemetry` version and this crate's features match. If
1036 /// you update that dependency, you need to change the feature here or this call will do
1037 /// nothing.
1038 ///
1039 /// [OpenTelemetry]: https://opentelemetry.io
1040 #[cfg(feature = "__any-tracing-opentelemetry")]
1041 #[cfg_attr(docsrs, doc(feature = "__any-tracing-opentelemetry"))]
1042 pub fn with_opentelemetry_ids(&mut self, display_opentelemetry_ids: bool) -> &mut Self {
1043 if display_opentelemetry_ids {
1044 self.keyed_values.insert(
1045 SchemaKey::from("openTelemetry"),
1046 JsonValue::DynamicFromSpanWithDispatch(Box::new(move |span, dispatch| {
1047 let mut ids: Option<serde_json::Value> = None;
1048
1049 macro_rules! otel_extraction {
1050 ($feature:literal, $tracing_otel_crate:ident, $otel_crate:ident) => {
1051 #[cfg(feature = $feature)]
1052 {
1053 use $otel_crate::trace::{TraceContextExt, TraceId};
1054 ids = ids.or_else(|| {
1055 span.extensions()
1056 .get::<$tracing_otel_crate::OtelData>()
1057 .and_then(|otel_data| {
1058 // We should use the parent first if available because
1059 // we can create a new trace and then change the
1060 // parent. In that case the value in the builder is not
1061 // updated.
1062 let mut trace_id = otel_data
1063 .parent_cx
1064 .span()
1065 .span_context()
1066 .trace_id();
1067 if trace_id == TraceId::INVALID {
1068 trace_id = otel_data.builder.trace_id?;
1069 }
1070 let span_id = otel_data.builder.span_id?;
1071 Some(serde_json::json!({
1072 "traceId": trace_id.to_string(),
1073 "spanId": span_id.to_string(),
1074 }))
1075 })
1076 });
1077 }
1078 };
1079 }
1080
1081 #[cfg(feature = "tracing-opentelemetry-0-33")]
1082 {
1083 ids = ids.or_else(|| {
1084 tracing_opentelemetry_0_33::get_otel_context(&span.id(), dispatch).map(
1085 |context| {
1086 use opentelemetry_0_32::trace::TraceContextExt;
1087
1088 serde_json::json!({
1089 "traceId": context.span().span_context().trace_id().to_string(),
1090 "spanId": context.span().span_context().span_id().to_string(),
1091 })
1092 },
1093 )
1094 });
1095 }
1096 #[cfg(feature = "tracing-opentelemetry-0-32")]
1097 {
1098 ids = ids.or_else(|| {
1099 span.extensions()
1100 .get::<tracing_opentelemetry_0_32::OtelData>()
1101 .and_then(|otel_data| {
1102 let trace_id = otel_data.trace_id()?;
1103 let span_id = otel_data.span_id()?;
1104 Some(serde_json::json!({
1105 "traceId": trace_id.to_string(),
1106 "spanId": span_id.to_string(),
1107 }))
1108 })
1109 });
1110 }
1111 otel_extraction!(
1112 "tracing-opentelemetry-0-31",
1113 tracing_opentelemetry_0_31,
1114 opentelemetry_0_30
1115 );
1116 otel_extraction!(
1117 "tracing-opentelemetry-0-30",
1118 tracing_opentelemetry_0_30,
1119 opentelemetry_0_29
1120 );
1121 otel_extraction!(
1122 "tracing-opentelemetry-0-29",
1123 tracing_opentelemetry_0_29,
1124 opentelemetry_0_28
1125 );
1126 otel_extraction!(
1127 "tracing-opentelemetry-0-28",
1128 tracing_opentelemetry_0_28,
1129 opentelemetry_0_27
1130 );
1131 otel_extraction!(
1132 "opentelemetry",
1133 tracing_opentelemetry_0_25,
1134 opentelemetry_0_24
1135 );
1136
1137 ids
1138 })),
1139 );
1140 } else {
1141 self.keyed_values.remove(&SchemaKey::from("openTelemetry"));
1142 }
1143
1144 self
1145 }
1146}
1147
1148fn write_escaped(writer: &mut dyn fmt::Write, value: &str) -> Result<(), fmt::Error> {
1149 let mut rest = value;
1150 writer.write_str("\"")?;
1151 let mut shift = 0;
1152 while let Some(position) = rest
1153 .get(shift..)
1154 .and_then(|haystack| haystack.find(['\"', '\\']))
1155 {
1156 let (before, after) = rest.split_at(position + shift);
1157 writer.write_str(before)?;
1158 writer.write_char('\\')?;
1159 rest = after;
1160 shift = 1;
1161 }
1162 writer.write_str(rest)?;
1163 writer.write_str("\"")
1164}
1165
1166#[cfg(test)]
1167mod tests {
1168 use std::collections::HashMap;
1169
1170 use serde_json::json;
1171 use tracing::subscriber::with_default;
1172 use tracing_subscriber::{registry, Layer, Registry};
1173
1174 use super::JsonLayer;
1175 use crate::tests::MockMakeWriter;
1176
1177 fn test_json<W, T>(
1178 expected: &serde_json::Value,
1179 layer: JsonLayer<Registry, W>,
1180 producer: impl FnOnce() -> T,
1181 ) {
1182 let actual = produce_log_line(layer, producer);
1183 assert_eq!(
1184 expected,
1185 &serde_json::from_str::<serde_json::Value>(&actual).unwrap(),
1186 );
1187 }
1188
1189 fn produce_log_line<W, T>(
1190 layer: JsonLayer<Registry, W>,
1191 producer: impl FnOnce() -> T,
1192 ) -> String {
1193 let make_writer = MockMakeWriter::default();
1194 let collector = layer
1195 .with_writer(make_writer.clone())
1196 .with_subscriber(registry());
1197
1198 with_default(collector, producer);
1199
1200 let buf = make_writer.buf();
1201 dbg!(std::str::from_utf8(&buf[..]).unwrap()).to_owned()
1202 }
1203
1204 #[test]
1205 fn add_and_remove_static() {
1206 let mut layer = JsonLayer::stdout();
1207 layer.add_static_field("static", json!({"lorem": "ipsum", "answer": 42}));
1208 layer.add_static_field(String::from("zero"), json!(0));
1209 layer.add_static_field(String::from("one").as_str(), json!(1));
1210 layer.add_static_field("nonExistent", json!(1));
1211 layer.remove_field("nonExistent");
1212
1213 let expected = json!({
1214 "static": {
1215 "lorem": "ipsum",
1216 "answer": 42,
1217 },
1218 "zero": 0,
1219 "one": 1,
1220 });
1221
1222 test_json(&expected, layer, || {
1223 tracing::info!(does = "not matter", "whatever");
1224 });
1225 }
1226
1227 #[test]
1228 fn flattened_event_with_renames() {
1229 let renames = HashMap::from([
1230 ("message".to_owned(), "msg".to_owned()),
1231 ("msg".to_owned(), "message".to_owned()),
1232 ("same".to_owned(), "same".to_owned()),
1233 ("different".to_owned(), "gone".to_owned()),
1234 ]);
1235 let mut layer = JsonLayer::stdout();
1236 layer.with_flattened_event_with_renames(
1237 move |name, map| map.get(name).map_or(name, String::as_str),
1238 renames,
1239 );
1240
1241 let expected = json!({
1242 "message": "msg",
1243 "msg": "message",
1244 "same": "same",
1245 "gone": "different",
1246 "another": "another",
1247 });
1248
1249 test_json(&expected, layer, || {
1250 tracing::info!(
1251 msg = "msg",
1252 same = "same",
1253 different = "different",
1254 another = "another",
1255 "message"
1256 );
1257 });
1258 }
1259}