• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Adapters for connecting unstructured log records from the `log` crate into
2 //! the `tracing` ecosystem.
3 //!
4 //! # Overview
5 //!
6 //! [`tracing`] is a framework for instrumenting Rust programs with context-aware,
7 //! structured, event-based diagnostic information. This crate provides
8 //! compatibility layers for using `tracing` alongside the logging facade provided
9 //! by the [`log`] crate.
10 //!
11 //! This crate provides:
12 //!
13 //! - [`AsTrace`] and [`AsLog`] traits for converting between `tracing` and `log` types.
14 //! - [`LogTracer`], a [`log::Log`] implementation that consumes [`log::Record`]s
15 //!   and outputs them as [`tracing::Event`].
16 //! - An [`env_logger`] module, with helpers for using the [`env_logger` crate]
17 //!   with `tracing` (optional, enabled by the `env-logger` feature).
18 //!
19 //! *Compiler support: [requires `rustc` 1.49+][msrv]*
20 //!
21 //! [msrv]: #supported-rust-versions
22 //!
23 //! # Usage
24 //!
25 //! ## Convert log records to tracing `Event`s
26 //!
27 //! To convert [`log::Record`]s as [`tracing::Event`]s, set `LogTracer` as the default
28 //! logger by calling its [`init`] or [`init_with_filter`] methods.
29 //!
30 //! ```rust
31 //! # use std::error::Error;
32 //! use tracing_log::LogTracer;
33 //! use log;
34 //!
35 //! # fn main() -> Result<(), Box<Error>> {
36 //! LogTracer::init()?;
37 //!
38 //! // will be available for Subscribers as a tracing Event
39 //! log::trace!("an example trace log");
40 //! # Ok(())
41 //! # }
42 //! ```
43 //!
44 //! This conversion does not convert unstructured data in log records (such as
45 //! values passed as format arguments to the `log!` macro) to structured
46 //! `tracing` fields. However, it *does* attach these new events to to the
47 //! span that was currently executing when the record was logged. This is the
48 //! primary use-case for this library: making it possible to locate the log
49 //! records emitted by dependencies which use `log` within the context of a
50 //! trace.
51 //!
52 //! ## Convert tracing `Event`s to logs
53 //!
54 //! Enabling the ["log" and "log-always" feature flags][flags] on the `tracing`
55 //! crate will cause all `tracing` spans and events to emit `log::Record`s as
56 //! they occur.
57 //!
58 //! ## Caution: Mixing both conversions
59 //!
60 //! Note that logger implementations that convert log records to trace events
61 //! should not be used with `Subscriber`s that convert trace events _back_ into
62 //! log records (such as the `TraceLogger`), as doing so will result in the
63 //! event recursing between the subscriber and the logger forever (or, in real
64 //! life, probably overflowing the call stack).
65 //!
66 //! If the logging of trace events generated from log records produced by the
67 //! `log` crate is desired, either the `log` crate should not be used to
68 //! implement this logging, or an additional layer of filtering will be
69 //! required to avoid infinitely converting between `Event` and `log::Record`.
70 //!
71 //! # Feature Flags
72 //! * `trace-logger`: enables an experimental `log` subscriber, deprecated since
73 //!   version 0.1.1.
74 //! * `log-tracer`: enables the `LogTracer` type (on by default)
75 //! * `env_logger`: enables the `env_logger` module, with helpers for working
76 //!   with the [`env_logger` crate].
77 //! * `interest-cache`: makes it possible to configure an interest cache for
78 //!   logs emitted through the `log` crate (see [`Builder::with_interest_cache`]); requires `std`
79 //!
80 //! ## Supported Rust Versions
81 //!
82 //! Tracing is built against the latest stable release. The minimum supported
83 //! version is 1.49. The current Tracing version is not guaranteed to build on
84 //! Rust versions earlier than the minimum supported version.
85 //!
86 //! Tracing follows the same compiler support policies as the rest of the Tokio
87 //! project. The current stable Rust compiler and the three most recent minor
88 //! versions before it will always be supported. For example, if the current
89 //! stable compiler version is 1.45, the minimum supported version will not be
90 //! increased past 1.42, three minor versions prior. Increasing the minimum
91 //! supported compiler version is not considered a semver breaking change as
92 //! long as doing so complies with this policy.
93 //!
94 //! [`init`]: LogTracer::init
95 //! [`init_with_filter`]: LogTracer::init_with_filter
96 //! [`tracing`]: https://crates.io/crates/tracing
97 //! [`env_logger` crate]: https://crates.io/crates/env-logger
98 //! [`tracing::Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
99 //! [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
100 //! [`tracing::Event`]: https://docs.rs/tracing/latest/tracing/struct.Event.html
101 //! [flags]: https://docs.rs/tracing/latest/tracing/#crate-feature-flags
102 //! [`Builder::with_interest_cache`]: log_tracer::Builder::with_interest_cache
103 #![doc(html_root_url = "https://docs.rs/tracing-log/0.1.3")]
104 #![doc(
105     html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
106     issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
107 )]
108 #![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
109 #![warn(
110     missing_debug_implementations,
111     missing_docs,
112     rust_2018_idioms,
113     unreachable_pub,
114     bad_style,
115     const_err,
116     dead_code,
117     improper_ctypes,
118     non_shorthand_field_patterns,
119     no_mangle_generic_items,
120     overflowing_literals,
121     path_statements,
122     patterns_in_fns_without_body,
123     private_in_public,
124     unconditional_recursion,
125     unused,
126     unused_allocation,
127     unused_comparisons,
128     unused_parens,
129     while_true
130 )]
131 use once_cell::sync::Lazy;
132 
133 use std::{fmt, io};
134 
135 use tracing_core::{
136     callsite::{self, Callsite},
137     dispatcher,
138     field::{self, Field, Visit},
139     identify_callsite,
140     metadata::{Kind, Level},
141     subscriber, Event, Metadata,
142 };
143 
144 #[cfg(feature = "log-tracer")]
145 #[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
146 pub mod log_tracer;
147 
148 #[cfg(feature = "trace-logger")]
149 #[cfg_attr(docsrs, doc(cfg(feature = "trace-logger")))]
150 pub mod trace_logger;
151 
152 #[cfg(feature = "log-tracer")]
153 #[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
154 #[doc(inline)]
155 pub use self::log_tracer::LogTracer;
156 
157 #[cfg(feature = "trace-logger")]
158 #[cfg_attr(docsrs, doc(cfg(feature = "trace-logger")))]
159 #[deprecated(
160     since = "0.1.1",
161     note = "use the `tracing` crate's \"log\" feature flag instead"
162 )]
163 #[allow(deprecated)]
164 #[doc(inline)]
165 pub use self::trace_logger::TraceLogger;
166 
167 #[cfg(feature = "env_logger")]
168 #[cfg_attr(docsrs, doc(cfg(feature = "env_logger")))]
169 pub mod env_logger;
170 
171 pub use log;
172 
173 #[cfg(all(feature = "interest-cache", feature = "log-tracer", feature = "std"))]
174 mod interest_cache;
175 
176 #[cfg(all(feature = "interest-cache", feature = "log-tracer", feature = "std"))]
177 #[cfg_attr(
178     docsrs,
179     doc(cfg(all(feature = "interest-cache", feature = "log-tracer", feature = "std")))
180 )]
181 pub use crate::interest_cache::InterestCacheConfig;
182 
183 /// Format a log record as a trace event in the current span.
format_trace(record: &log::Record<'_>) -> io::Result<()>184 pub fn format_trace(record: &log::Record<'_>) -> io::Result<()> {
185     dispatch_record(record);
186     Ok(())
187 }
188 
189 // XXX(eliza): this is factored out so that we don't have to deal with the pub
190 // function `format_trace`'s `Result` return type...maybe we should get rid of
191 // that in 0.2...
dispatch_record(record: &log::Record<'_>)192 pub(crate) fn dispatch_record(record: &log::Record<'_>) {
193     dispatcher::get_default(|dispatch| {
194         let filter_meta = record.as_trace();
195         if !dispatch.enabled(&filter_meta) {
196             return;
197         }
198 
199         let (_, keys, meta) = loglevel_to_cs(record.level());
200 
201         let log_module = record.module_path();
202         let log_file = record.file();
203         let log_line = record.line();
204 
205         let module = log_module.as_ref().map(|s| s as &dyn field::Value);
206         let file = log_file.as_ref().map(|s| s as &dyn field::Value);
207         let line = log_line.as_ref().map(|s| s as &dyn field::Value);
208 
209         dispatch.event(&Event::new(
210             meta,
211             &meta.fields().value_set(&[
212                 (&keys.message, Some(record.args() as &dyn field::Value)),
213                 (&keys.target, Some(&record.target())),
214                 (&keys.module, module),
215                 (&keys.file, file),
216                 (&keys.line, line),
217             ]),
218         ));
219     });
220 }
221 
222 /// Trait implemented for `tracing` types that can be converted to a `log`
223 /// equivalent.
224 pub trait AsLog: crate::sealed::Sealed {
225     /// The `log` type that this type can be converted into.
226     type Log;
227     /// Returns the `log` equivalent of `self`.
as_log(&self) -> Self::Log228     fn as_log(&self) -> Self::Log;
229 }
230 
231 /// Trait implemented for `log` types that can be converted to a `tracing`
232 /// equivalent.
233 pub trait AsTrace: crate::sealed::Sealed {
234     /// The `tracing` type that this type can be converted into.
235     type Trace;
236     /// Returns the `tracing` equivalent of `self`.
as_trace(&self) -> Self::Trace237     fn as_trace(&self) -> Self::Trace;
238 }
239 
240 impl<'a> crate::sealed::Sealed for Metadata<'a> {}
241 
242 impl<'a> AsLog for Metadata<'a> {
243     type Log = log::Metadata<'a>;
as_log(&self) -> Self::Log244     fn as_log(&self) -> Self::Log {
245         log::Metadata::builder()
246             .level(self.level().as_log())
247             .target(self.target())
248             .build()
249     }
250 }
251 impl<'a> crate::sealed::Sealed for log::Metadata<'a> {}
252 
253 impl<'a> AsTrace for log::Metadata<'a> {
254     type Trace = Metadata<'a>;
as_trace(&self) -> Self::Trace255     fn as_trace(&self) -> Self::Trace {
256         let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
257         Metadata::new(
258             "log record",
259             self.target(),
260             self.level().as_trace(),
261             None,
262             None,
263             None,
264             field::FieldSet::new(FIELD_NAMES, cs_id),
265             Kind::EVENT,
266         )
267     }
268 }
269 
270 struct Fields {
271     message: field::Field,
272     target: field::Field,
273     module: field::Field,
274     file: field::Field,
275     line: field::Field,
276 }
277 
278 static FIELD_NAMES: &[&str] = &[
279     "message",
280     "log.target",
281     "log.module_path",
282     "log.file",
283     "log.line",
284 ];
285 
286 impl Fields {
new(cs: &'static dyn Callsite) -> Self287     fn new(cs: &'static dyn Callsite) -> Self {
288         let fieldset = cs.metadata().fields();
289         let message = fieldset.field("message").unwrap();
290         let target = fieldset.field("log.target").unwrap();
291         let module = fieldset.field("log.module_path").unwrap();
292         let file = fieldset.field("log.file").unwrap();
293         let line = fieldset.field("log.line").unwrap();
294         Fields {
295             message,
296             target,
297             module,
298             file,
299             line,
300         }
301     }
302 }
303 
304 macro_rules! log_cs {
305     ($level:expr, $cs:ident, $meta:ident, $ty:ident) => {
306         struct $ty;
307         static $cs: $ty = $ty;
308         static $meta: Metadata<'static> = Metadata::new(
309             "log event",
310             "log",
311             $level,
312             None,
313             None,
314             None,
315             field::FieldSet::new(FIELD_NAMES, identify_callsite!(&$cs)),
316             Kind::EVENT,
317         );
318 
319         impl callsite::Callsite for $ty {
320             fn set_interest(&self, _: subscriber::Interest) {}
321             fn metadata(&self) -> &'static Metadata<'static> {
322                 &$meta
323             }
324         }
325     };
326 }
327 
328 log_cs!(
329     tracing_core::Level::TRACE,
330     TRACE_CS,
331     TRACE_META,
332     TraceCallsite
333 );
334 log_cs!(
335     tracing_core::Level::DEBUG,
336     DEBUG_CS,
337     DEBUG_META,
338     DebugCallsite
339 );
340 log_cs!(tracing_core::Level::INFO, INFO_CS, INFO_META, InfoCallsite);
341 log_cs!(tracing_core::Level::WARN, WARN_CS, WARN_META, WarnCallsite);
342 log_cs!(
343     tracing_core::Level::ERROR,
344     ERROR_CS,
345     ERROR_META,
346     ErrorCallsite
347 );
348 
349 static TRACE_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&TRACE_CS));
350 static DEBUG_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&DEBUG_CS));
351 static INFO_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&INFO_CS));
352 static WARN_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&WARN_CS));
353 static ERROR_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&ERROR_CS));
354 
level_to_cs(level: Level) -> (&'static dyn Callsite, &'static Fields)355 fn level_to_cs(level: Level) -> (&'static dyn Callsite, &'static Fields) {
356     match level {
357         Level::TRACE => (&TRACE_CS, &*TRACE_FIELDS),
358         Level::DEBUG => (&DEBUG_CS, &*DEBUG_FIELDS),
359         Level::INFO => (&INFO_CS, &*INFO_FIELDS),
360         Level::WARN => (&WARN_CS, &*WARN_FIELDS),
361         Level::ERROR => (&ERROR_CS, &*ERROR_FIELDS),
362     }
363 }
364 
loglevel_to_cs( level: log::Level, ) -> ( &'static dyn Callsite, &'static Fields, &'static Metadata<'static>, )365 fn loglevel_to_cs(
366     level: log::Level,
367 ) -> (
368     &'static dyn Callsite,
369     &'static Fields,
370     &'static Metadata<'static>,
371 ) {
372     match level {
373         log::Level::Trace => (&TRACE_CS, &*TRACE_FIELDS, &TRACE_META),
374         log::Level::Debug => (&DEBUG_CS, &*DEBUG_FIELDS, &DEBUG_META),
375         log::Level::Info => (&INFO_CS, &*INFO_FIELDS, &INFO_META),
376         log::Level::Warn => (&WARN_CS, &*WARN_FIELDS, &WARN_META),
377         log::Level::Error => (&ERROR_CS, &*ERROR_FIELDS, &ERROR_META),
378     }
379 }
380 
381 impl<'a> crate::sealed::Sealed for log::Record<'a> {}
382 
383 impl<'a> AsTrace for log::Record<'a> {
384     type Trace = Metadata<'a>;
as_trace(&self) -> Self::Trace385     fn as_trace(&self) -> Self::Trace {
386         let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
387         Metadata::new(
388             "log record",
389             self.target(),
390             self.level().as_trace(),
391             self.file(),
392             self.line(),
393             self.module_path(),
394             field::FieldSet::new(FIELD_NAMES, cs_id),
395             Kind::EVENT,
396         )
397     }
398 }
399 
400 impl crate::sealed::Sealed for tracing_core::Level {}
401 
402 impl AsLog for tracing_core::Level {
403     type Log = log::Level;
as_log(&self) -> log::Level404     fn as_log(&self) -> log::Level {
405         match *self {
406             tracing_core::Level::ERROR => log::Level::Error,
407             tracing_core::Level::WARN => log::Level::Warn,
408             tracing_core::Level::INFO => log::Level::Info,
409             tracing_core::Level::DEBUG => log::Level::Debug,
410             tracing_core::Level::TRACE => log::Level::Trace,
411         }
412     }
413 }
414 
415 impl crate::sealed::Sealed for log::Level {}
416 
417 impl AsTrace for log::Level {
418     type Trace = tracing_core::Level;
419     #[inline]
as_trace(&self) -> tracing_core::Level420     fn as_trace(&self) -> tracing_core::Level {
421         match self {
422             log::Level::Error => tracing_core::Level::ERROR,
423             log::Level::Warn => tracing_core::Level::WARN,
424             log::Level::Info => tracing_core::Level::INFO,
425             log::Level::Debug => tracing_core::Level::DEBUG,
426             log::Level::Trace => tracing_core::Level::TRACE,
427         }
428     }
429 }
430 
431 impl crate::sealed::Sealed for log::LevelFilter {}
432 
433 impl AsTrace for log::LevelFilter {
434     type Trace = tracing_core::LevelFilter;
435     #[inline]
as_trace(&self) -> tracing_core::LevelFilter436     fn as_trace(&self) -> tracing_core::LevelFilter {
437         match self {
438             log::LevelFilter::Off => tracing_core::LevelFilter::OFF,
439             log::LevelFilter::Error => tracing_core::LevelFilter::ERROR,
440             log::LevelFilter::Warn => tracing_core::LevelFilter::WARN,
441             log::LevelFilter::Info => tracing_core::LevelFilter::INFO,
442             log::LevelFilter::Debug => tracing_core::LevelFilter::DEBUG,
443             log::LevelFilter::Trace => tracing_core::LevelFilter::TRACE,
444         }
445     }
446 }
447 
448 impl crate::sealed::Sealed for tracing_core::LevelFilter {}
449 
450 impl AsLog for tracing_core::LevelFilter {
451     type Log = log::LevelFilter;
452     #[inline]
as_log(&self) -> Self::Log453     fn as_log(&self) -> Self::Log {
454         match *self {
455             tracing_core::LevelFilter::OFF => log::LevelFilter::Off,
456             tracing_core::LevelFilter::ERROR => log::LevelFilter::Error,
457             tracing_core::LevelFilter::WARN => log::LevelFilter::Warn,
458             tracing_core::LevelFilter::INFO => log::LevelFilter::Info,
459             tracing_core::LevelFilter::DEBUG => log::LevelFilter::Debug,
460             tracing_core::LevelFilter::TRACE => log::LevelFilter::Trace,
461         }
462     }
463 }
464 /// Extends log `Event`s to provide complete `Metadata`.
465 ///
466 /// In `tracing-log`, an `Event` produced by a log (through [`AsTrace`]) has an hard coded
467 /// "log" target and no `file`, `line`, or `module_path` attributes. This happens because `Event`
468 /// requires its `Metadata` to be `'static`, while [`log::Record`]s provide them with a generic
469 /// lifetime.
470 ///
471 /// However, these values are stored in the `Event`'s fields and
472 /// the [`normalized_metadata`] method allows to build a new `Metadata`
473 /// that only lives as long as its source `Event`, but provides complete
474 /// data.
475 ///
476 /// It can typically be used by `Subscriber`s when processing an `Event`,
477 /// to allow accessing its complete metadata in a consistent way,
478 /// regardless of the source of its source.
479 ///
480 /// [`normalized_metadata`]: NormalizeEvent#normalized_metadata
481 pub trait NormalizeEvent<'a>: crate::sealed::Sealed {
482     /// If this `Event` comes from a `log`, this method provides a new
483     /// normalized `Metadata` which has all available attributes
484     /// from the original log, including `file`, `line`, `module_path`
485     /// and `target`.
486     /// Returns `None` is the `Event` is not issued from a `log`.
normalized_metadata(&'a self) -> Option<Metadata<'a>>487     fn normalized_metadata(&'a self) -> Option<Metadata<'a>>;
488     /// Returns whether this `Event` represents a log (from the `log` crate)
is_log(&self) -> bool489     fn is_log(&self) -> bool;
490 }
491 
492 impl<'a> crate::sealed::Sealed for Event<'a> {}
493 
494 impl<'a> NormalizeEvent<'a> for Event<'a> {
normalized_metadata(&'a self) -> Option<Metadata<'a>>495     fn normalized_metadata(&'a self) -> Option<Metadata<'a>> {
496         let original = self.metadata();
497         if self.is_log() {
498             let mut fields = LogVisitor::new_for(self, level_to_cs(*original.level()).1);
499             self.record(&mut fields);
500 
501             Some(Metadata::new(
502                 "log event",
503                 fields.target.unwrap_or("log"),
504                 *original.level(),
505                 fields.file,
506                 fields.line.map(|l| l as u32),
507                 fields.module_path,
508                 field::FieldSet::new(&["message"], original.callsite()),
509                 Kind::EVENT,
510             ))
511         } else {
512             None
513         }
514     }
515 
is_log(&self) -> bool516     fn is_log(&self) -> bool {
517         self.metadata().callsite() == identify_callsite!(level_to_cs(*self.metadata().level()).0)
518     }
519 }
520 
521 struct LogVisitor<'a> {
522     target: Option<&'a str>,
523     module_path: Option<&'a str>,
524     file: Option<&'a str>,
525     line: Option<u64>,
526     fields: &'static Fields,
527 }
528 
529 impl<'a> LogVisitor<'a> {
530     // We don't actually _use_ the provided event argument; it is simply to
531     // ensure that the `LogVisitor` does not outlive the event whose fields it
532     // is visiting, so that the reference casts in `record_str` are safe.
new_for(_event: &'a Event<'a>, fields: &'static Fields) -> Self533     fn new_for(_event: &'a Event<'a>, fields: &'static Fields) -> Self {
534         Self {
535             target: None,
536             module_path: None,
537             file: None,
538             line: None,
539             fields,
540         }
541     }
542 }
543 
544 impl<'a> Visit for LogVisitor<'a> {
record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug)545     fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) {}
546 
record_u64(&mut self, field: &Field, value: u64)547     fn record_u64(&mut self, field: &Field, value: u64) {
548         if field == &self.fields.line {
549             self.line = Some(value);
550         }
551     }
552 
record_str(&mut self, field: &Field, value: &str)553     fn record_str(&mut self, field: &Field, value: &str) {
554         unsafe {
555             // The `Visit` API erases the string slice's lifetime. However, we
556             // know it is part of the `Event` struct with a lifetime of `'a`. If
557             // (and only if!) this `LogVisitor` was constructed with the same
558             // lifetime parameter `'a` as the event in question, it's safe to
559             // cast these string slices to the `'a` lifetime.
560             if field == &self.fields.file {
561                 self.file = Some(&*(value as *const _));
562             } else if field == &self.fields.target {
563                 self.target = Some(&*(value as *const _));
564             } else if field == &self.fields.module {
565                 self.module_path = Some(&*(value as *const _));
566             }
567         }
568     }
569 }
570 
571 mod sealed {
572     pub trait Sealed {}
573 }
574 
575 #[cfg(test)]
576 mod test {
577     use super::*;
578 
test_callsite(level: log::Level)579     fn test_callsite(level: log::Level) {
580         let record = log::Record::builder()
581             .args(format_args!("Error!"))
582             .level(level)
583             .target("myApp")
584             .file(Some("server.rs"))
585             .line(Some(144))
586             .module_path(Some("server"))
587             .build();
588 
589         let meta = record.as_trace();
590         let (cs, _keys, _) = loglevel_to_cs(record.level());
591         let cs_meta = cs.metadata();
592         assert_eq!(
593             meta.callsite(),
594             cs_meta.callsite(),
595             "actual: {:#?}\nexpected: {:#?}",
596             meta,
597             cs_meta
598         );
599         assert_eq!(meta.level(), &level.as_trace());
600     }
601 
602     #[test]
error_callsite_is_correct()603     fn error_callsite_is_correct() {
604         test_callsite(log::Level::Error);
605     }
606 
607     #[test]
warn_callsite_is_correct()608     fn warn_callsite_is_correct() {
609         test_callsite(log::Level::Warn);
610     }
611 
612     #[test]
info_callsite_is_correct()613     fn info_callsite_is_correct() {
614         test_callsite(log::Level::Info);
615     }
616 
617     #[test]
debug_callsite_is_correct()618     fn debug_callsite_is_correct() {
619         test_callsite(log::Level::Debug);
620     }
621 
622     #[test]
trace_callsite_is_correct()623     fn trace_callsite_is_correct() {
624         test_callsite(log::Level::Trace);
625     }
626 }
627