json_subscriber/fmt/
builder.rs

1use std::{error::Error, io};
2
3use tracing::{Dispatch, Subscriber};
4use tracing_core::LevelFilter;
5use tracing_subscriber::{
6    fmt::{
7        time::{FormatTime, SystemTime},
8        MakeWriter,
9        TestWriter,
10    },
11    layer::{Layered, SubscriberExt},
12    registry::LookupSpan,
13    reload,
14    Layer,
15    Registry,
16};
17
18use super::names::{
19    CURRENT_SPAN,
20    FIELDS,
21    FILENAME,
22    LEVEL,
23    LINE_NUMBER,
24    SPAN_LIST,
25    TARGET,
26    THREAD_ID,
27    THREAD_NAME,
28    TIMESTAMP,
29};
30use crate::layer::JsonLayer;
31
32/// Configures and constructs `Subscriber`s.
33///
34/// This should be this library's replacement for [`tracing_subscriber::fmt::SubscriberBuilder`].
35///
36/// Returns a new [`SubscriberBuilder`] for configuring a [formatting subscriber]. The default value
37/// should be mostly equivalent to calling `tracing_subscriber::fmt().json()`.
38///
39/// # Examples
40///
41/// Using [`init`] to set the default subscriber:
42///
43/// ```rust
44/// json_subscriber::fmt::SubscriberBuilder::default().init();
45/// ```
46///
47/// Configuring the output format:
48///
49/// ```rust
50/// json_subscriber::fmt()
51///     // Configure formatting settings.
52///     .with_target(false)
53///     .with_timer(tracing_subscriber::fmt::time::uptime())
54///     .with_level(true)
55///     // Set the subscriber as the default.
56///     .init();
57/// ```
58///
59/// [`try_init`] returns an error if the default subscriber could not be set:
60///
61/// ```rust
62/// use std::error::Error;
63///
64/// fn init_subscriber() -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
65///     json_subscriber::fmt()
66///         // This is no-op. This subscriber uses only JSON.
67///         .json()
68///         // Configure the subscriber to flatten event fields in the output JSON objects.
69///         .flatten_event(true)
70///         // Set the subscriber as the default, returning an error if this fails.
71///         .try_init()?;
72///
73///     Ok(())
74/// }
75/// ```
76///
77/// Rather than setting the subscriber as the default, [`finish`] _returns_ the
78/// constructed subscriber, which may then be passed to other functions:
79///
80/// ```rust
81/// let subscriber = json_subscriber::fmt()
82///     .with_max_level(tracing::Level::DEBUG)
83///     .finish();
84///
85/// tracing::subscriber::with_default(subscriber, || {
86///     // the subscriber will only be set as the default
87///     // inside this closure...
88/// })
89/// ```
90///
91/// [formatting subscriber]: Subscriber
92/// [`SubscriberBuilder::default()`]: SubscriberBuilder::default
93/// [`init`]: SubscriberBuilder::init()
94/// [`try_init`]: SubscriberBuilder::try_init()
95/// [`finish`]: SubscriberBuilder::finish()
96pub struct SubscriberBuilder<W = fn() -> io::Stdout, T = SystemTime, F = LevelFilter> {
97    make_writer: W,
98    timer: T,
99    filter: F,
100
101    log_internal_errors: bool,
102
103    display_timestamp: bool,
104    display_target: bool,
105    display_level: bool,
106    display_thread_id: bool,
107    display_thread_name: bool,
108    display_filename: bool,
109    display_line_number: bool,
110    flatten_event: bool,
111    display_current_span: bool,
112    display_span_list: bool,
113    #[cfg(feature = "opentelemetry")]
114    display_opentelemetry_ids: bool,
115}
116
117impl Default for SubscriberBuilder {
118    fn default() -> Self {
119        Self {
120            make_writer: io::stdout,
121            filter: LevelFilter::INFO,
122            timer: SystemTime,
123            log_internal_errors: false,
124
125            display_timestamp: true,
126            display_target: true,
127            display_level: true,
128            display_thread_id: false,
129            display_thread_name: false,
130            display_filename: false,
131            display_line_number: false,
132            flatten_event: false,
133            display_current_span: true,
134            display_span_list: true,
135            #[cfg(feature = "opentelemetry")]
136            display_opentelemetry_ids: false,
137        }
138    }
139}
140
141impl<W, T, F> SubscriberBuilder<W, T, F>
142where
143    W: for<'writer> MakeWriter<'writer> + Send + Sync + 'static,
144    T: FormatTime + Send + Sync + 'static,
145    F: Layer<Layered<JsonLayer<Registry, W>, Registry>> + 'static,
146    Layered<F, Layered<JsonLayer<Registry, W>, Registry>>:
147        tracing_core::Subscriber + Into<Dispatch>,
148{
149    pub(crate) fn layers<S>(self) -> (JsonLayer<S, W>, F)
150    where
151        S: Subscriber + for<'lookup> LookupSpan<'lookup>,
152    {
153        let mut layer = JsonLayer::<S>::new(self.make_writer);
154
155        if self.display_timestamp {
156            layer.with_timer(TIMESTAMP, self.timer);
157        }
158
159        if self.display_level {
160            layer.with_level(LEVEL);
161        }
162
163        if self.display_target {
164            layer.with_target(TARGET);
165        }
166
167        if self.display_filename {
168            layer.with_file(FILENAME);
169        }
170
171        if self.display_line_number {
172            layer.with_line_number(LINE_NUMBER);
173        }
174
175        if self.display_thread_name {
176            layer.with_thread_names(THREAD_NAME);
177        }
178
179        if self.display_thread_id {
180            layer.with_thread_ids(THREAD_ID);
181        }
182
183        if self.flatten_event {
184            layer.with_flattened_event();
185        } else {
186            layer.with_event(FIELDS);
187        }
188
189        if self.display_current_span {
190            layer.with_current_span(CURRENT_SPAN);
191        }
192
193        if self.display_span_list {
194            layer.with_span_list(SPAN_LIST);
195        }
196
197        (layer, self.filter)
198    }
199
200    /// Finish the builder, returning a new [`Subscriber`] which can be used to [lookup spans].
201    ///
202    /// [lookup spans]: LookupSpan
203    pub fn finish(self) -> Layered<F, Layered<JsonLayer<Registry, W>, Registry>> {
204        let (json_layer, filter_layer) = self.layers();
205        tracing_subscriber::registry()
206            .with(json_layer)
207            .with(filter_layer)
208    }
209
210    /// Install this Subscriber as the global default if one is
211    /// not already set.
212    ///
213    /// If the `tracing-log` feature is enabled, this will also install
214    /// the `LogTracer` to convert `Log` records into `tracing` `Event`s.
215    ///
216    /// # Errors
217    /// Returns an Error if the initialization was unsuccessful, likely
218    /// because a global subscriber was already installed by another
219    /// call to `try_init`.
220    pub fn try_init(self) -> Result<(), Box<dyn Error + Send + Sync + 'static>> {
221        use tracing_subscriber::util::SubscriberInitExt;
222        self.finish().try_init()?;
223
224        Ok(())
225    }
226
227    /// Install this Subscriber as the global default.
228    ///
229    /// If the `tracing-log` feature is enabled, this will also install
230    /// the `LogTracer` to convert `Log` records into `tracing` `Event`s.
231    ///
232    /// # Panics
233    /// Panics if the initialization was unsuccessful, likely because a
234    /// global subscriber was already installed by another call to `try_init`.
235    pub fn init(self) {
236        self.try_init()
237            .expect("Unable to install global subscriber");
238    }
239}
240
241impl<W, T, F> SubscriberBuilder<W, T, F> {
242    /// This does nothing. It exists only to mimic `tracing-subscriber`'s API.
243    #[deprecated(note = "Calling `json()` does nothing.")]
244    #[must_use]
245    pub fn json(self) -> Self {
246        self
247    }
248
249    /// This does nothing. It exists only to mimic `tracing-subscriber`'s API.
250    #[deprecated(note = "Calling `with_ansi()` does nothing.")]
251    #[must_use]
252    pub fn with_ansi(self, _ansi: bool) -> Self {
253        self
254    }
255
256    /// Sets the [`MakeWriter`] that the [`SubscriberBuilder`] being built will use to write events.
257    ///
258    /// # Examples
259    ///
260    /// Using `stderr` rather than `stdout`:
261    ///
262    /// ```rust
263    /// use std::io;
264    ///
265    /// let fmt_subscriber = json_subscriber::fmt()
266    ///     .with_writer(io::stderr);
267    /// ```
268    pub fn with_writer<W2>(self, make_writer: W2) -> SubscriberBuilder<W2, T, F>
269    where
270        W2: for<'writer> MakeWriter<'writer> + 'static,
271    {
272        SubscriberBuilder {
273            make_writer,
274            timer: self.timer,
275            filter: self.filter,
276            log_internal_errors: self.log_internal_errors,
277            display_timestamp: self.display_timestamp,
278            display_target: self.display_target,
279            display_level: self.display_level,
280            display_thread_id: self.display_thread_id,
281            display_thread_name: self.display_thread_name,
282            display_filename: self.display_filename,
283            display_line_number: self.display_line_number,
284            flatten_event: self.flatten_event,
285            display_current_span: self.display_current_span,
286            display_span_list: self.display_span_list,
287            #[cfg(feature = "opentelemetry")]
288            display_opentelemetry_ids: self.display_opentelemetry_ids,
289        }
290    }
291
292    /// Borrows the [writer] for this subscriber.
293    ///
294    /// [writer]: MakeWriter
295    pub fn writer(&self) -> &W {
296        &self.make_writer
297    }
298
299    /// Mutably borrows the [writer] for this subscriber.
300    ///
301    /// This method is primarily expected to be used with the
302    /// [`reload::Handle::modify`](reload::Handle::modify) method.
303    ///
304    /// # Examples
305    ///
306    /// ```
307    /// use tracing_subscriber::{fmt::writer::EitherWriter, reload};
308    /// # fn main() {
309    /// let subscriber = json_subscriber::fmt()
310    ///     .with_writer::<Box<dyn Fn() -> EitherWriter<_, _>>>(Box::new(|| EitherWriter::A(std::io::stderr())));
311    /// let (subscriber, reload_handle) = reload::Layer::new(subscriber);
312    /// # let subscriber: reload::Layer<_, tracing_subscriber::Registry> = subscriber;
313    ///
314    /// tracing::info!("This will be logged to stderr");
315    /// reload_handle.modify(|subscriber| *subscriber.writer_mut() = Box::new(|| EitherWriter::B(std::io::stdout())));
316    /// tracing::info!("This will be logged to stdout");
317    /// # }
318    /// ```
319    ///
320    /// [writer]: MakeWriter
321    pub fn writer_mut(&mut self) -> &mut W {
322        &mut self.make_writer
323    }
324
325    /// Configures the subscriber to support [`libtest`'s output capturing][capturing] when used in
326    /// unit tests.
327    ///
328    /// See [`TestWriter`] for additional details.
329    ///
330    /// # Examples
331    ///
332    /// Using [`TestWriter`] to let `cargo test` capture test output:
333    ///
334    /// ```rust
335    /// let fmt_subscriber = json_subscriber::fmt::fmt()
336    ///     .with_test_writer();
337    /// ```
338    /// [capturing]:
339    /// https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output
340    /// [`TestWriter`]: tracing_subscriber::fmt::writer::TestWriter
341    pub fn with_test_writer(self) -> SubscriberBuilder<TestWriter, T, F> {
342        SubscriberBuilder {
343            make_writer: TestWriter::default(),
344            timer: self.timer,
345            filter: self.filter,
346            log_internal_errors: self.log_internal_errors,
347            display_timestamp: self.display_timestamp,
348            display_target: self.display_target,
349            display_level: self.display_level,
350            display_thread_id: self.display_thread_id,
351            display_thread_name: self.display_thread_name,
352            display_filename: self.display_filename,
353            display_line_number: self.display_line_number,
354            flatten_event: self.flatten_event,
355            display_current_span: self.display_current_span,
356            display_span_list: self.display_span_list,
357            #[cfg(feature = "opentelemetry")]
358            display_opentelemetry_ids: self.display_opentelemetry_ids,
359        }
360    }
361
362    /// Sets whether to write errors during internal operations to stderr.
363    /// This can help identify problems with serialization and with debugging issues.
364    ///
365    /// Defaults to false.
366    #[must_use]
367    pub fn log_internal_errors(self, log_internal_errors: bool) -> Self {
368        Self {
369            log_internal_errors,
370            ..self
371        }
372    }
373
374    /// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`].
375    ///
376    /// This sets the [`MakeWriter`] that the subscriber being built will use to write events.
377    ///
378    /// # Examples
379    ///
380    /// Redirect output to stderr if level is <= WARN:
381    ///
382    /// ```rust
383    /// use tracing::Level;
384    /// use tracing_subscriber::fmt::writer::MakeWriterExt;
385    ///
386    /// let stderr = std::io::stderr.with_max_level(Level::WARN);
387    /// let subscriber = json_subscriber::fmt::fmt()
388    ///     .map_writer(move |w| stderr.or_else(w));
389    /// ```
390    pub fn map_writer<W2>(self, f: impl FnOnce(W) -> W2) -> SubscriberBuilder<W2, T, F>
391    where
392        W2: for<'writer> MakeWriter<'writer> + 'static,
393    {
394        SubscriberBuilder {
395            make_writer: f(self.make_writer),
396            timer: self.timer,
397            filter: self.filter,
398            log_internal_errors: self.log_internal_errors,
399            display_timestamp: self.display_timestamp,
400            display_target: self.display_target,
401            display_level: self.display_level,
402            display_thread_id: self.display_thread_id,
403            display_thread_name: self.display_thread_name,
404            display_filename: self.display_filename,
405            display_line_number: self.display_line_number,
406            flatten_event: self.flatten_event,
407            display_current_span: self.display_current_span,
408            display_span_list: self.display_span_list,
409            #[cfg(feature = "opentelemetry")]
410            display_opentelemetry_ids: self.display_opentelemetry_ids,
411        }
412    }
413
414    /// Sets the JSON subscriber being built to flatten event metadata.
415    #[must_use]
416    pub fn flatten_event(self, flatten_event: bool) -> SubscriberBuilder<W, T, F> {
417        SubscriberBuilder {
418            flatten_event,
419            ..self
420        }
421    }
422
423    /// Sets whether or not the formatter will include the current span in
424    /// formatted events.
425    #[must_use]
426    pub fn with_current_span(self, display_current_span: bool) -> SubscriberBuilder<W, T, F> {
427        SubscriberBuilder {
428            display_current_span,
429            ..self
430        }
431    }
432
433    /// Sets whether or not the formatter will include a list (from root to leaf)
434    /// of all currently entered spans in formatted events.
435    #[must_use]
436    pub fn with_span_list(self, display_span_list: bool) -> SubscriberBuilder<W, T, F> {
437        SubscriberBuilder {
438            display_span_list,
439            ..self
440        }
441    }
442
443    /// Use the given [`timer`] for log message timestamps.
444    ///
445    /// See the [`tracing_subscriber::fmt::time` module][`time` module] for the
446    /// provided timer implementations.
447    ///
448    /// Note that using the `time` feature flag on `tracing_subscriber` enables the
449    /// additional time formatters [`UtcTime`] and [`LocalTime`], which use the
450    /// [`time` crate] to provide more sophisticated timestamp formatting
451    /// options.
452    ///
453    /// [`timer`]: FormatTime
454    /// [`time` module]: mod@tracing_subscriber::fmt::time
455    /// [`UtcTime`]: tracing_subscriber::fmt::time::UtcTime
456    /// [`LocalTime`]: tracing_subscriber::fmt::time::LocalTime
457    /// [`time` crate]: https://docs.rs/time/0.3
458    pub fn with_timer<T2>(self, timer: T2) -> SubscriberBuilder<W, T2, F> {
459        SubscriberBuilder {
460            make_writer: self.make_writer,
461            timer,
462            filter: self.filter,
463            log_internal_errors: self.log_internal_errors,
464            display_timestamp: self.display_timestamp,
465            display_target: self.display_target,
466            display_level: self.display_level,
467            display_thread_id: self.display_thread_id,
468            display_thread_name: self.display_thread_name,
469            display_filename: self.display_filename,
470            display_line_number: self.display_line_number,
471            flatten_event: self.flatten_event,
472            display_current_span: self.display_current_span,
473            display_span_list: self.display_span_list,
474            #[cfg(feature = "opentelemetry")]
475            display_opentelemetry_ids: self.display_opentelemetry_ids,
476        }
477    }
478
479    /// Do not emit timestamps with log messages.
480    pub fn without_time(self) -> SubscriberBuilder<W, (), F> {
481        SubscriberBuilder {
482            make_writer: self.make_writer,
483            timer: (),
484            filter: self.filter,
485            log_internal_errors: self.log_internal_errors,
486            display_timestamp: self.display_timestamp,
487            display_target: self.display_target,
488            display_level: self.display_level,
489            display_thread_id: self.display_thread_id,
490            display_thread_name: self.display_thread_name,
491            display_filename: self.display_filename,
492            display_line_number: self.display_line_number,
493            flatten_event: self.flatten_event,
494            display_current_span: self.display_current_span,
495            display_span_list: self.display_span_list,
496            #[cfg(feature = "opentelemetry")]
497            display_opentelemetry_ids: self.display_opentelemetry_ids,
498        }
499    }
500
501    // /// Configures how synthesized events are emitted at points in the [span
502    // /// lifecycle][lifecycle].
503    // ///
504    // /// The following options are available:
505    // ///
506    // /// - `FmtSpan::NONE`: No events will be synthesized when spans are
507    // ///    created, entered, exited, or closed. Data from spans will still be
508    // ///    included as the context for formatted events. This is the default.
509    // /// - `FmtSpan::NEW`: An event will be synthesized when spans are created.
510    // /// - `FmtSpan::ENTER`: An event will be synthesized when spans are entered.
511    // /// - `FmtSpan::EXIT`: An event will be synthesized when spans are exited.
512    // /// - `FmtSpan::CLOSE`: An event will be synthesized when a span closes. If
513    // ///    [timestamps are enabled][time] for this formatter, the generated
514    // ///    event will contain fields with the span's _busy time_ (the total
515    // ///    time for which it was entered) and _idle time_ (the total time that
516    // ///    the span existed but was not entered).
517    // /// - `FmtSpan::ACTIVE`: An event will be synthesized when spans are entered
518    // ///    or exited.
519    // /// - `FmtSpan::FULL`: Events will be synthesized whenever a span is
520    // ///    created, entered, exited, or closed. If timestamps are enabled, the
521    // ///    close event will contain the span's busy and idle time, as
522    // ///    described above.
523    // ///
524    // /// The options can be enabled in any combination. For instance, the following
525    // /// will synthesize events whenever spans are created and closed:
526    // ///
527    // /// ```rust
528    // /// use tracing_subscriber::fmt::format::FmtSpan;
529    // /// use tracing_subscriber::fmt;
530    // ///
531    // /// let subscriber = fmt()
532    // ///     .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
533    // ///     .finish();
534    // /// ```
535    // ///
536    // /// Note that the generated events will only be part of the log output by
537    // /// this formatter; they will not be recorded by other `Collector`s or by
538    // /// `Subscriber`s added to this subscriber.
539    // ///
540    // /// [lifecycle]: mod@tracing::span#the-span-lifecycle
541    // /// [time]: SubscriberBuilder::without_time()
542    // pub fn with_span_events(self, kind: format::FmtSpan) -> Self {
543    //     SubscriberBuilder {
544    //         inner: self.inner.with_span_events(kind),
545    //         ..self
546    //     }
547    // }
548
549    /// Sets whether or not an event's target is displayed.
550    #[must_use]
551    pub fn with_target(self, display_target: bool) -> SubscriberBuilder<W, T, F> {
552        SubscriberBuilder {
553            display_target,
554            ..self
555        }
556    }
557
558    /// Sets whether or not an event's [source code file path][file] is
559    /// displayed.
560    ///
561    /// [file]: tracing_core::Metadata::file
562    #[must_use]
563    pub fn with_file(self, display_filename: bool) -> SubscriberBuilder<W, T, F> {
564        SubscriberBuilder {
565            display_filename,
566            ..self
567        }
568    }
569
570    /// Sets whether or not an event's [source code line number][line] is
571    /// displayed.
572    ///
573    /// [line]: tracing_core::Metadata::line
574    #[must_use]
575    pub fn with_line_number(self, display_line_number: bool) -> SubscriberBuilder<W, T, F> {
576        SubscriberBuilder {
577            display_line_number,
578            ..self
579        }
580    }
581
582    /// Sets whether or not an event's level is displayed.
583    #[must_use]
584    pub fn with_level(self, display_level: bool) -> SubscriberBuilder<W, T, F> {
585        SubscriberBuilder {
586            display_level,
587            ..self
588        }
589    }
590
591    /// Sets whether or not the [name] of the current thread is displayed
592    /// when formatting events.
593    ///
594    /// [name]: std::thread#naming-threads
595    #[must_use]
596    pub fn with_thread_names(self, display_thread_name: bool) -> SubscriberBuilder<W, T, F> {
597        SubscriberBuilder {
598            display_thread_name,
599            ..self
600        }
601    }
602
603    /// Sets whether or not the [thread ID] of the current thread is displayed
604    /// when formatting events.
605    ///
606    /// [thread ID]: std::thread::ThreadId
607    #[must_use]
608    pub fn with_thread_ids(self, display_thread_id: bool) -> SubscriberBuilder<W, T, F> {
609        SubscriberBuilder {
610            display_thread_id,
611            ..self
612        }
613    }
614
615    /// Sets whether or not [OpenTelemetry] trace ID and span ID is displayed when formatting
616    /// events.
617    ///
618    /// [OpenTelemetry]: https://opentelemetry.io
619    #[cfg(feature = "opentelemetry")]
620    #[cfg_attr(docsrs, doc(cfg(feature = "opentelemetry")))]
621    #[must_use]
622    pub fn with_opentelemetry_ids(self, display_opentelemetry_ids: bool) -> Self {
623        SubscriberBuilder {
624            display_opentelemetry_ids,
625            ..self
626        }
627    }
628
629    /// Sets the [`EnvFilter`] that the collector will use to determine if
630    /// a span or event is enabled.
631    ///
632    /// Note that this method requires the "env-filter" feature flag to be enabled.
633    ///
634    /// If a filter was previously set, or a maximum level was set by the
635    /// [`with_max_level`] method, that value is replaced by the new filter.
636    ///
637    /// # Examples
638    ///
639    /// Setting a filter based on the value of the `RUST_LOG` environment
640    /// variable:
641    /// ```rust
642    /// use tracing_subscriber::EnvFilter;
643    ///
644    /// json_subscriber::fmt()
645    ///     .with_env_filter(EnvFilter::from_default_env())
646    ///     .init();
647    /// ```
648    ///
649    /// Setting a filter based on a pre-set filter directive string:
650    /// ```rust
651    /// json_subscriber::fmt()
652    ///     .with_env_filter("my_crate=info,my_crate::my_mod=debug,[my_span]=trace")
653    ///     .init();
654    /// ```
655    ///
656    /// Adding additional directives to a filter constructed from an env var:
657    /// ```rust
658    /// use tracing_subscriber::filter::{EnvFilter, LevelFilter};
659    ///
660    /// # fn filter() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
661    /// let filter = EnvFilter::try_from_env("MY_CUSTOM_FILTER_ENV_VAR")?
662    ///     // Set the base level when not matched by other directives to WARN.
663    ///     .add_directive(LevelFilter::WARN.into())
664    ///     // Set the max level for `my_crate::my_mod` to DEBUG, overriding
665    ///     // any directives parsed from the env variable.
666    ///     .add_directive("my_crate::my_mod=debug".parse()?);
667    ///
668    /// json_subscriber::fmt()
669    ///     .with_env_filter(filter)
670    ///     .try_init()?;
671    /// # Ok(())}
672    /// ```
673    /// [`EnvFilter`]: tracing_subscriber::filter::EnvFilter
674    /// [`with_max_level`]: Self::with_max_level()
675    #[cfg(feature = "env-filter")]
676    #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
677    pub fn with_env_filter(
678        self,
679        filter: impl Into<tracing_subscriber::EnvFilter>,
680    ) -> SubscriberBuilder<W, T, tracing_subscriber::EnvFilter> {
681        SubscriberBuilder {
682            make_writer: self.make_writer,
683            timer: self.timer,
684            filter: filter.into(),
685            log_internal_errors: self.log_internal_errors,
686            display_timestamp: self.display_timestamp,
687            display_target: self.display_target,
688            display_level: self.display_level,
689            display_thread_id: self.display_thread_id,
690            display_thread_name: self.display_thread_name,
691            display_filename: self.display_filename,
692            display_line_number: self.display_line_number,
693            flatten_event: self.flatten_event,
694            display_current_span: self.display_current_span,
695            display_span_list: self.display_span_list,
696            #[cfg(feature = "opentelemetry")]
697            display_opentelemetry_ids: self.display_opentelemetry_ids,
698        }
699    }
700
701    /// Sets the maximum [verbosity level] that will be enabled by the
702    /// collector.
703    ///
704    /// If the max level has already been set, or a [`EnvFilter`] was added by
705    /// [`with_env_filter`], this replaces that configuration with the new
706    /// maximum level.
707    ///
708    /// # Examples
709    ///
710    /// Enable up to the `DEBUG` verbosity level:
711    /// ```rust
712    /// use tracing_subscriber::fmt;
713    /// use tracing::Level;
714    ///
715    /// fmt()
716    ///     .with_max_level(Level::DEBUG)
717    ///     .init();
718    /// ```
719    /// This collector won't record any spans or events!
720    /// ```rust
721    /// use tracing_subscriber::{fmt, filter::LevelFilter};
722    ///
723    /// let subscriber = fmt()
724    ///     .with_max_level(LevelFilter::OFF)
725    ///     .finish();
726    /// ```
727    /// [verbosity level]: tracing_core::Level
728    /// [`EnvFilter`]: struct@tracing_subscriber::filter::EnvFilter
729    /// [`with_env_filter`]: fn@Self::with_env_filter
730    pub fn with_max_level(
731        self,
732        filter: impl Into<LevelFilter>,
733    ) -> SubscriberBuilder<W, T, LevelFilter> {
734        SubscriberBuilder {
735            make_writer: self.make_writer,
736            timer: self.timer,
737            filter: filter.into(),
738            log_internal_errors: self.log_internal_errors,
739            display_timestamp: self.display_timestamp,
740            display_target: self.display_target,
741            display_level: self.display_level,
742            display_thread_id: self.display_thread_id,
743            display_thread_name: self.display_thread_name,
744            display_filename: self.display_filename,
745            display_line_number: self.display_line_number,
746            flatten_event: self.flatten_event,
747            display_current_span: self.display_current_span,
748            display_span_list: self.display_span_list,
749            #[cfg(feature = "opentelemetry")]
750            display_opentelemetry_ids: self.display_opentelemetry_ids,
751        }
752    }
753
754    /// Configures the collector being built to allow filter reloading at
755    /// runtime.
756    ///
757    /// The returned builder will have a [`reload_handle`] method, which returns
758    /// a [`reload::Handle`] that may be used to set a new filter value.
759    ///
760    /// For example:
761    ///
762    /// ```rust
763    /// # use tracing_subscriber::prelude::*;
764    /// use tracing::Level;
765    ///
766    /// let builder = json_subscriber::fmt()
767    ///      // Set a max level filter on the collector
768    ///     .with_max_level(Level::INFO)
769    ///     .with_filter_reloading();
770    ///
771    /// // Get a handle for modifying the collector's max level filter.
772    /// let handle = builder.reload_handle();
773    ///
774    /// // Finish building the collector, and set it as the default.
775    /// builder.finish().init();
776    ///
777    /// // Currently, the max level is INFO, so this event will be disabled.
778    /// tracing::debug!("this is not recorded!");
779    ///
780    /// // Use the handle to set a new max level filter.
781    /// // (this returns an error if the collector has been dropped, which shouldn't
782    /// // happen in this example.)
783    /// handle.reload(Level::DEBUG).expect("the collector should still exist");
784    ///
785    /// // Now, the max level is INFO, so this event will be recorded.
786    /// tracing::debug!("this is recorded!");
787    /// ```
788    ///
789    /// [`reload_handle`]: Self::reload_handle
790    #[cfg(feature = "env-filter")]
791    #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
792    pub fn with_filter_reloading<S>(self) -> SubscriberBuilder<W, T, reload::Layer<F, S>> {
793        let (filter, _) = reload::Layer::new(self.filter);
794        SubscriberBuilder {
795            make_writer: self.make_writer,
796            timer: self.timer,
797            filter,
798            log_internal_errors: self.log_internal_errors,
799            display_timestamp: self.display_timestamp,
800            display_target: self.display_target,
801            display_level: self.display_level,
802            display_thread_id: self.display_thread_id,
803            display_thread_name: self.display_thread_name,
804            display_filename: self.display_filename,
805            display_line_number: self.display_line_number,
806            flatten_event: self.flatten_event,
807            display_current_span: self.display_current_span,
808            display_span_list: self.display_span_list,
809            #[cfg(feature = "opentelemetry")]
810            display_opentelemetry_ids: self.display_opentelemetry_ids,
811        }
812    }
813}
814
815impl<W, T, F, S> SubscriberBuilder<W, T, reload::Layer<F, S>> {
816    /// Returns a `Handle` that may be used to reload the constructed collector's
817    /// filter.
818    pub fn reload_handle(&self) -> reload::Handle<F, S> {
819        self.filter.handle()
820    }
821}
822
823#[cfg(test)]
824mod tests {
825    //! These tests are copied from `tracing-subscriber` for compatibility.
826
827    use std::path::Path;
828
829    use tracing::subscriber::with_default;
830    use tracing_core::Dispatch;
831    use tracing_subscriber::{filter::LevelFilter, registry::LookupSpan, Registry};
832
833    use super::SubscriberBuilder;
834    use crate::{
835        layer::JsonLayer,
836        tests::{MockMakeWriter, MockTime},
837    };
838
839    fn subscriber() -> SubscriberBuilder {
840        SubscriberBuilder::default()
841    }
842
843    #[rustfmt::skip]
844    // TODO uncomment when `tracing` releases version where `&[u8]: Value`
845    // #[test]
846    // fn json() {
847    //     let expected =
848    //     "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3,\"slice\":[97,98,99]},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3,\"slice\":[97,98,99]}],\"target\":\"tracing_json::layer::test\",\"fields\":{\"message\":\"some json test\"}}\n";
849    //     let collector = subscriber()
850    //         .flatten_event(false)
851    //         .with_current_span(true)
852    //         .with_span_list(true);
853    //     test_json(expected, collector, || {
854    //         let span = tracing::span!(
855    //             tracing::Level::INFO,
856    //             "json_span",
857    //             answer = 42,
858    //             number = 3,
859    //             slice = &b"abc"[..]
860    //         );
861    //         let _guard = span.enter();
862    //         tracing::info!("some json test");
863    //     });
864    // }
865
866    #[test]
867    fn json_filename() {
868        let current_path = Path::new("src")
869            .join("fmt")
870            .join("builder.rs")
871            .to_str()
872            .expect("path must be valid unicode")
873            // escape windows backslashes
874            .replace('\\', "\\\\");
875        #[rustfmt::skip]
876        let expected = &format!("{}{}{}",
877            "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"json_subscriber::fmt::builder::tests\",\"filename\":\"",
878            current_path,
879            "\",\"fields\":{\"message\":\"some json test\"}}\n"
880        );
881        let collector = subscriber()
882            .flatten_event(false)
883            .with_current_span(true)
884            .with_file(true)
885            .with_span_list(true);
886        test_json(expected, collector, || {
887            let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
888            let _guard = span.enter();
889            tracing::info!("some json test");
890        });
891    }
892
893    #[test]
894    fn json_line_number() {
895        #[rustfmt::skip]
896        let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"json_subscriber::fmt::builder::tests\",\"line_number\":42,\"fields\":{\"message\":\"some json test\"}}\n";
897        let collector = subscriber()
898            .flatten_event(false)
899            .with_current_span(true)
900            .with_line_number(true)
901            .with_span_list(true);
902        test_json_with_line_number(expected, collector, || {
903            let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
904            let _guard = span.enter();
905            tracing::info!("some json test");
906        });
907    }
908
909    #[test]
910    fn json_flattened_event() {
911        #[rustfmt::skip]
912        let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"json_subscriber::fmt::builder::tests\",\"message\":\"some json test\"}\n";
913
914        let collector = subscriber()
915            .flatten_event(true)
916            .with_current_span(true)
917            .with_span_list(true);
918        test_json(expected, collector, || {
919            let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
920            let _guard = span.enter();
921            tracing::info!("some json test");
922        });
923    }
924
925    #[test]
926    fn json_disabled_current_span_event() {
927        #[rustfmt::skip]
928        let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"json_subscriber::fmt::builder::tests\",\"fields\":{\"message\":\"some json test\"}}\n";
929        let collector = subscriber()
930            .flatten_event(false)
931            .with_current_span(false)
932            .with_span_list(true);
933        test_json(expected, collector, || {
934            let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
935            let _guard = span.enter();
936            tracing::info!("some json test");
937        });
938    }
939
940    #[test]
941    fn json_disabled_span_list_event() {
942        #[rustfmt::skip]
943        let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"target\":\"json_subscriber::fmt::builder::tests\",\"fields\":{\"message\":\"some json test\"}}\n";
944        let collector = subscriber()
945            .flatten_event(false)
946            .with_current_span(true)
947            .with_span_list(false);
948        test_json(expected, collector, || {
949            let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
950            let _guard = span.enter();
951            tracing::info!("some json test");
952        });
953    }
954
955    #[test]
956    fn json_nested_span() {
957        #[rustfmt::skip]
958        let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":43,\"name\":\"nested_json_span\",\"number\":4},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3},{\"answer\":43,\"name\":\"nested_json_span\",\"number\":4}],\"target\":\"json_subscriber::fmt::builder::tests\",\"fields\":{\"message\":\"some json test\"}}\n";
959        let collector = subscriber()
960            .flatten_event(false)
961            .with_current_span(true)
962            .with_span_list(true);
963        test_json(expected, collector, || {
964            let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
965            let _guard = span.enter();
966            let span = tracing::span!(
967                tracing::Level::INFO,
968                "nested_json_span",
969                answer = 43,
970                number = 4
971            );
972            let _guard = span.enter();
973            tracing::info!("some json test");
974        });
975    }
976
977    #[test]
978    fn json_explicit_span() {
979        #[rustfmt::skip]
980        let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":43,\"name\":\"nested_json_span\",\"number\":4},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3},{\"answer\":43,\"name\":\"nested_json_span\",\"number\":4}],\"target\":\"json_subscriber::fmt::builder::tests\",\"fields\":{\"message\":\"some json test\"}}\n";
981        let collector = subscriber()
982            .flatten_event(false)
983            .with_current_span(true)
984            .with_span_list(true);
985        test_json(expected, collector, || {
986            let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
987            let span = tracing::span!(
988                parent: &span,
989                tracing::Level::INFO,
990                "nested_json_span",
991                answer = 43,
992                number = 4
993            );
994            // No enter
995            tracing::info!(parent: &span, "some json test");
996        });
997    }
998
999    #[test]
1000    fn json_explicit_no_span() {
1001        #[rustfmt::skip]
1002        let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"target\":\"json_subscriber::fmt::builder::tests\",\"fields\":{\"message\":\"some json test\"}}\n";
1003        let collector = subscriber()
1004            .flatten_event(false)
1005            .with_current_span(true)
1006            .with_span_list(true);
1007        test_json(expected, collector, || {
1008            let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3);
1009            let _guard = span.enter();
1010            let span = tracing::span!(
1011                tracing::Level::INFO,
1012                "nested_json_span",
1013                answer = 43,
1014                number = 4
1015            );
1016            let _guard = span.enter();
1017            tracing::info!(parent: None, "some json test");
1018        });
1019    }
1020
1021    #[test]
1022    fn json_no_span() {
1023        #[rustfmt::skip]
1024        let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"target\":\"json_subscriber::fmt::builder::tests\",\"fields\":{\"message\":\"some json test\"}}\n";
1025        let collector = subscriber()
1026            .flatten_event(false)
1027            .with_current_span(true)
1028            .with_span_list(true);
1029        test_json(expected, collector, || {
1030            tracing::info!("some json test");
1031        });
1032    }
1033
1034    #[test]
1035    fn record_works() {
1036        // This test reproduces tracing issue #707, where using `Span::record` causes
1037        // any events inside the span to be ignored.
1038
1039        let buffer = MockMakeWriter::default();
1040        let subscriber = SubscriberBuilder::default()
1041            .with_writer(buffer.clone())
1042            .finish();
1043
1044        with_default(subscriber, || {
1045            tracing::info!("an event outside the root span");
1046            assert_eq!(
1047                parse_as_json(&buffer)["fields"]["message"],
1048                "an event outside the root span"
1049            );
1050
1051            let span = tracing::info_span!("the span", na = tracing::field::Empty);
1052            span.record("na", "value");
1053            let _enter = span.enter();
1054
1055            tracing::info!("an event inside the root span");
1056            assert_eq!(
1057                parse_as_json(&buffer)["fields"]["message"],
1058                "an event inside the root span"
1059            );
1060        });
1061    }
1062
1063    fn parse_as_json(buffer: &MockMakeWriter) -> serde_json::Value {
1064        let buf = String::from_utf8(buffer.buf().to_vec()).unwrap();
1065        let json = buf
1066            .lines()
1067            .last()
1068            .expect("expected at least one line to be written!");
1069        match serde_json::from_str(json) {
1070            Ok(v) => v,
1071            Err(e) => {
1072                panic!(
1073                    "assertion failed: JSON shouldn't be malformed\n  error: {e}\n  json: {json}"
1074                )
1075            },
1076        }
1077    }
1078
1079    fn test_json<T>(expected: &str, builder: SubscriberBuilder, producer: impl FnOnce() -> T) {
1080        let make_writer = MockMakeWriter::default();
1081        let collector = builder
1082            .with_writer(make_writer.clone())
1083            .with_timer(MockTime)
1084            .finish();
1085
1086        with_default(collector, producer);
1087
1088        let buf = make_writer.buf();
1089        let actual = dbg!(std::str::from_utf8(&buf[..]).unwrap());
1090        assert_eq!(
1091            serde_json::from_str::<std::collections::HashMap<&str, serde_json::Value>>(expected)
1092                .unwrap(),
1093            serde_json::from_str(actual).unwrap()
1094        );
1095    }
1096
1097    fn test_json_with_line_number<T>(
1098        expected: &str,
1099        builder: SubscriberBuilder,
1100        producer: impl FnOnce() -> T,
1101    ) {
1102        let make_writer = MockMakeWriter::default();
1103        let collector = builder
1104            .with_writer(make_writer.clone())
1105            .with_timer(MockTime)
1106            .finish();
1107
1108        with_default(collector, producer);
1109
1110        let buf = make_writer.buf();
1111        let actual = std::str::from_utf8(&buf[..]).unwrap();
1112        let mut expected =
1113            serde_json::from_str::<std::collections::HashMap<&str, serde_json::Value>>(expected)
1114                .unwrap();
1115        let expect_line_number = expected.remove("line_number").is_some();
1116        let mut actual: std::collections::HashMap<&str, serde_json::Value> =
1117            serde_json::from_str(actual).unwrap();
1118        let line_number = actual.remove("line_number");
1119        if expect_line_number {
1120            assert_eq!(line_number.map(|x| x.is_number()), Some(true));
1121        } else {
1122            assert!(line_number.is_none());
1123        }
1124        assert_eq!(actual, expected);
1125    }
1126
1127    #[test]
1128    fn subscriber_downcasts() {
1129        let subscriber = SubscriberBuilder::default().finish();
1130        let dispatch = Dispatch::new(subscriber);
1131        assert!(dispatch.downcast_ref::<Registry>().is_some());
1132    }
1133
1134    #[test]
1135    fn subscriber_downcasts_to_parts() {
1136        let subscriber = SubscriberBuilder::default().finish();
1137        let dispatch = Dispatch::new(subscriber);
1138        assert!(dispatch.downcast_ref::<JsonLayer>().is_some());
1139        assert!(dispatch.downcast_ref::<LevelFilter>().is_some());
1140    }
1141
1142    #[test]
1143    fn is_lookup_span() {
1144        fn assert_lookup_span<T: for<'a> LookupSpan<'a>>(_: T) {}
1145        let subscriber = SubscriberBuilder::default().finish();
1146        assert_lookup_span(subscriber);
1147    }
1148
1149    #[test]
1150    #[cfg(feature = "env-filter")]
1151    fn reload_filter_works() {
1152        use tracing::Level;
1153        use tracing_subscriber::util::SubscriberInitExt;
1154
1155        let builder = SubscriberBuilder::default()
1156            // Set a max level filter on the collector
1157            .with_max_level(Level::INFO)
1158            .with_filter_reloading();
1159
1160        // Get a handle for modifying the collector's max level filter.
1161        let handle = builder.reload_handle();
1162
1163        // Finish building the collector, and set it as the default.
1164        builder.finish().init();
1165
1166        // Currently, the max level is INFO, so this event will be disabled.
1167        tracing::debug!("this is not recorded!");
1168
1169        // Use the handle to set a new max level filter.
1170        // (this returns an error if the collector has been dropped, which shouldn't
1171        // happen in this example.)
1172        handle
1173            .reload(Level::DEBUG)
1174            .expect("the collector should still exist");
1175
1176        // Now, the max level is INFO, so this event will be recorded.
1177        tracing::debug!("this is recorded!");
1178    }
1179}