1#![doc(
98 html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/main/assets/logo-type.png",
99 issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
100)]
101#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
102#![warn(
103 missing_debug_implementations,
104 missing_docs,
105 rust_2018_idioms,
106 unreachable_pub,
107 bad_style,
108 dead_code,
109 improper_ctypes,
110 non_shorthand_field_patterns,
111 no_mangle_generic_items,
112 overflowing_literals,
113 path_statements,
114 patterns_in_fns_without_body,
115 private_interfaces,
116 private_bounds,
117 unconditional_recursion,
118 unused,
119 unused_allocation,
120 unused_comparisons,
121 unused_parens,
122 while_true
123)]
124use once_cell::sync::Lazy;
125
126use std::{fmt, io};
127
128use tracing_core::{
129 callsite::{self, Callsite},
130 dispatcher,
131 field::{self, Field, Visit},
132 identify_callsite,
133 metadata::{Kind, Level},
134 subscriber, Event, Metadata,
135};
136
137#[cfg(feature = "log-tracer")]
138#[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
139pub mod log_tracer;
140
141#[cfg(feature = "log-tracer")]
142#[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
143#[doc(inline)]
144pub use self::log_tracer::LogTracer;
145
146pub use log;
147
148#[cfg(all(feature = "interest-cache", feature = "log-tracer", feature = "std"))]
149mod interest_cache;
150
151#[cfg(all(feature = "interest-cache", feature = "log-tracer", feature = "std"))]
152#[cfg_attr(
153 docsrs,
154 doc(cfg(all(feature = "interest-cache", feature = "log-tracer", feature = "std")))
155)]
156pub use crate::interest_cache::InterestCacheConfig;
157
158pub fn format_trace(record: &log::Record<'_>) -> io::Result<()> {
160 dispatch_record(record);
161 Ok(())
162}
163
164pub(crate) fn dispatch_record(record: &log::Record<'_>) {
168 dispatcher::get_default(|dispatch| {
169 let filter_meta = record.as_trace();
170 if !dispatch.enabled(&filter_meta) {
171 return;
172 }
173
174 let (_, keys, meta) = loglevel_to_cs(record.level());
175
176 let log_module = record.module_path();
177 let log_file = record.file();
178 let log_line = record.line();
179
180 let module = log_module.as_ref().map(|s| s as &dyn field::Value);
181 let file = log_file.as_ref().map(|s| s as &dyn field::Value);
182 let line = log_line.as_ref().map(|s| s as &dyn field::Value);
183
184 dispatch.event(&Event::new(
185 meta,
186 &meta.fields().value_set(&[
187 (&keys.message, Some(record.args() as &dyn field::Value)),
188 (&keys.target, Some(&record.target())),
189 (&keys.module, module),
190 (&keys.file, file),
191 (&keys.line, line),
192 ]),
193 ));
194 });
195}
196
197pub trait AsLog: crate::sealed::Sealed {
200 type Log;
202 fn as_log(&self) -> Self::Log;
204}
205
206pub trait AsTrace: crate::sealed::Sealed {
209 type Trace;
211 fn as_trace(&self) -> Self::Trace;
213}
214
215impl crate::sealed::Sealed for Metadata<'_> {}
216
217impl<'a> AsLog for Metadata<'a> {
218 type Log = log::Metadata<'a>;
219 fn as_log(&self) -> Self::Log {
220 log::Metadata::builder()
221 .level(self.level().as_log())
222 .target(self.target())
223 .build()
224 }
225}
226impl crate::sealed::Sealed for log::Metadata<'_> {}
227
228impl<'a> AsTrace for log::Metadata<'a> {
229 type Trace = Metadata<'a>;
230 fn as_trace(&self) -> Self::Trace {
231 let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
232 Metadata::new(
233 "log record",
234 self.target(),
235 self.level().as_trace(),
236 None,
237 None,
238 None,
239 field::FieldSet::new(FIELD_NAMES, cs_id),
240 Kind::EVENT,
241 )
242 }
243}
244
245struct Fields {
246 message: field::Field,
247 target: field::Field,
248 module: field::Field,
249 file: field::Field,
250 line: field::Field,
251}
252
253static FIELD_NAMES: &[&str] = &[
254 "message",
255 "log.target",
256 "log.module_path",
257 "log.file",
258 "log.line",
259];
260
261impl Fields {
262 fn new(cs: &'static dyn Callsite) -> Self {
263 let fieldset = cs.metadata().fields();
264 let message = fieldset.field("message").unwrap();
265 let target = fieldset.field("log.target").unwrap();
266 let module = fieldset.field("log.module_path").unwrap();
267 let file = fieldset.field("log.file").unwrap();
268 let line = fieldset.field("log.line").unwrap();
269 Fields {
270 message,
271 target,
272 module,
273 file,
274 line,
275 }
276 }
277}
278
279macro_rules! log_cs {
280 ($level:expr, $cs:ident, $meta:ident, $ty:ident) => {
281 struct $ty;
282 static $cs: $ty = $ty;
283 static $meta: Metadata<'static> = Metadata::new(
284 "log event",
285 "log",
286 $level,
287 ::core::option::Option::None,
288 ::core::option::Option::None,
289 ::core::option::Option::None,
290 field::FieldSet::new(FIELD_NAMES, identify_callsite!(&$cs)),
291 Kind::EVENT,
292 );
293
294 impl callsite::Callsite for $ty {
295 fn set_interest(&self, _: subscriber::Interest) {}
296 fn metadata(&self) -> &'static Metadata<'static> {
297 &$meta
298 }
299 }
300 };
301}
302
303log_cs!(
304 tracing_core::Level::TRACE,
305 TRACE_CS,
306 TRACE_META,
307 TraceCallsite
308);
309log_cs!(
310 tracing_core::Level::DEBUG,
311 DEBUG_CS,
312 DEBUG_META,
313 DebugCallsite
314);
315log_cs!(tracing_core::Level::INFO, INFO_CS, INFO_META, InfoCallsite);
316log_cs!(tracing_core::Level::WARN, WARN_CS, WARN_META, WarnCallsite);
317log_cs!(
318 tracing_core::Level::ERROR,
319 ERROR_CS,
320 ERROR_META,
321 ErrorCallsite
322);
323
324static TRACE_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&TRACE_CS));
325static DEBUG_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&DEBUG_CS));
326static INFO_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&INFO_CS));
327static WARN_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&WARN_CS));
328static ERROR_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&ERROR_CS));
329
330fn level_to_cs(level: Level) -> (&'static dyn Callsite, &'static Fields) {
331 match level {
332 Level::TRACE => (&TRACE_CS, &*TRACE_FIELDS),
333 Level::DEBUG => (&DEBUG_CS, &*DEBUG_FIELDS),
334 Level::INFO => (&INFO_CS, &*INFO_FIELDS),
335 Level::WARN => (&WARN_CS, &*WARN_FIELDS),
336 Level::ERROR => (&ERROR_CS, &*ERROR_FIELDS),
337 }
338}
339
340fn loglevel_to_cs(
341 level: log::Level,
342) -> (
343 &'static dyn Callsite,
344 &'static Fields,
345 &'static Metadata<'static>,
346) {
347 match level {
348 log::Level::Trace => (&TRACE_CS, &*TRACE_FIELDS, &TRACE_META),
349 log::Level::Debug => (&DEBUG_CS, &*DEBUG_FIELDS, &DEBUG_META),
350 log::Level::Info => (&INFO_CS, &*INFO_FIELDS, &INFO_META),
351 log::Level::Warn => (&WARN_CS, &*WARN_FIELDS, &WARN_META),
352 log::Level::Error => (&ERROR_CS, &*ERROR_FIELDS, &ERROR_META),
353 }
354}
355
356impl crate::sealed::Sealed for log::Record<'_> {}
357
358impl<'a> AsTrace for log::Record<'a> {
359 type Trace = Metadata<'a>;
360 fn as_trace(&self) -> Self::Trace {
361 let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
362 Metadata::new(
363 "log record",
364 self.target(),
365 self.level().as_trace(),
366 self.file(),
367 self.line(),
368 self.module_path(),
369 field::FieldSet::new(FIELD_NAMES, cs_id),
370 Kind::EVENT,
371 )
372 }
373}
374
375impl crate::sealed::Sealed for tracing_core::Level {}
376
377impl AsLog for tracing_core::Level {
378 type Log = log::Level;
379 fn as_log(&self) -> log::Level {
380 match *self {
381 tracing_core::Level::ERROR => log::Level::Error,
382 tracing_core::Level::WARN => log::Level::Warn,
383 tracing_core::Level::INFO => log::Level::Info,
384 tracing_core::Level::DEBUG => log::Level::Debug,
385 tracing_core::Level::TRACE => log::Level::Trace,
386 }
387 }
388}
389
390impl crate::sealed::Sealed for log::Level {}
391
392impl AsTrace for log::Level {
393 type Trace = tracing_core::Level;
394 #[inline]
395 fn as_trace(&self) -> tracing_core::Level {
396 match self {
397 log::Level::Error => tracing_core::Level::ERROR,
398 log::Level::Warn => tracing_core::Level::WARN,
399 log::Level::Info => tracing_core::Level::INFO,
400 log::Level::Debug => tracing_core::Level::DEBUG,
401 log::Level::Trace => tracing_core::Level::TRACE,
402 }
403 }
404}
405
406impl crate::sealed::Sealed for log::LevelFilter {}
407
408impl AsTrace for log::LevelFilter {
409 type Trace = tracing_core::LevelFilter;
410 #[inline]
411 fn as_trace(&self) -> tracing_core::LevelFilter {
412 match self {
413 log::LevelFilter::Off => tracing_core::LevelFilter::OFF,
414 log::LevelFilter::Error => tracing_core::LevelFilter::ERROR,
415 log::LevelFilter::Warn => tracing_core::LevelFilter::WARN,
416 log::LevelFilter::Info => tracing_core::LevelFilter::INFO,
417 log::LevelFilter::Debug => tracing_core::LevelFilter::DEBUG,
418 log::LevelFilter::Trace => tracing_core::LevelFilter::TRACE,
419 }
420 }
421}
422
423impl crate::sealed::Sealed for tracing_core::LevelFilter {}
424
425impl AsLog for tracing_core::LevelFilter {
426 type Log = log::LevelFilter;
427 #[inline]
428 fn as_log(&self) -> Self::Log {
429 match *self {
430 tracing_core::LevelFilter::OFF => log::LevelFilter::Off,
431 tracing_core::LevelFilter::ERROR => log::LevelFilter::Error,
432 tracing_core::LevelFilter::WARN => log::LevelFilter::Warn,
433 tracing_core::LevelFilter::INFO => log::LevelFilter::Info,
434 tracing_core::LevelFilter::DEBUG => log::LevelFilter::Debug,
435 tracing_core::LevelFilter::TRACE => log::LevelFilter::Trace,
436 }
437 }
438}
439pub trait NormalizeEvent<'a>: crate::sealed::Sealed {
457 fn normalized_metadata(&'a self) -> Option<Metadata<'a>>;
463 fn is_log(&self) -> bool;
465}
466
467impl crate::sealed::Sealed for Event<'_> {}
468
469impl<'a> NormalizeEvent<'a> for Event<'a> {
470 fn normalized_metadata(&'a self) -> Option<Metadata<'a>> {
471 let original = self.metadata();
472 if self.is_log() {
473 let mut fields = LogVisitor::new_for(self, level_to_cs(*original.level()).1);
474 self.record(&mut fields);
475
476 Some(Metadata::new(
477 "log event",
478 fields.target.unwrap_or("log"),
479 *original.level(),
480 fields.file,
481 fields.line.map(|l| l as u32),
482 fields.module_path,
483 field::FieldSet::new(&["message"], original.callsite()),
484 Kind::EVENT,
485 ))
486 } else {
487 None
488 }
489 }
490
491 fn is_log(&self) -> bool {
492 self.metadata().callsite() == identify_callsite!(level_to_cs(*self.metadata().level()).0)
493 }
494}
495
496struct LogVisitor<'a> {
497 target: Option<&'a str>,
498 module_path: Option<&'a str>,
499 file: Option<&'a str>,
500 line: Option<u64>,
501 fields: &'static Fields,
502}
503
504impl<'a> LogVisitor<'a> {
505 fn new_for(_event: &'a Event<'a>, fields: &'static Fields) -> Self {
509 Self {
510 target: None,
511 module_path: None,
512 file: None,
513 line: None,
514 fields,
515 }
516 }
517}
518
519impl Visit for LogVisitor<'_> {
520 fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) {}
521
522 fn record_u64(&mut self, field: &Field, value: u64) {
523 if field == &self.fields.line {
524 self.line = Some(value);
525 }
526 }
527
528 fn record_str(&mut self, field: &Field, value: &str) {
529 unsafe {
530 if field == &self.fields.file {
536 self.file = Some(&*(value as *const _));
537 } else if field == &self.fields.target {
538 self.target = Some(&*(value as *const _));
539 } else if field == &self.fields.module {
540 self.module_path = Some(&*(value as *const _));
541 }
542 }
543 }
544}
545
546mod sealed {
547 pub trait Sealed {}
548}
549
550#[cfg(test)]
551mod test {
552 use super::*;
553
554 fn test_callsite(level: log::Level) {
555 let record = log::Record::builder()
556 .args(format_args!("Error!"))
557 .level(level)
558 .target("myApp")
559 .file(Some("server.rs"))
560 .line(Some(144))
561 .module_path(Some("server"))
562 .build();
563
564 let meta = record.as_trace();
565 let (cs, _keys, _) = loglevel_to_cs(record.level());
566 let cs_meta = cs.metadata();
567 assert_eq!(
568 meta.callsite(),
569 cs_meta.callsite(),
570 "actual: {:#?}\nexpected: {:#?}",
571 meta,
572 cs_meta
573 );
574 assert_eq!(meta.level(), &level.as_trace());
575 }
576
577 #[test]
578 fn error_callsite_is_correct() {
579 test_callsite(log::Level::Error);
580 }
581
582 #[test]
583 fn warn_callsite_is_correct() {
584 test_callsite(log::Level::Warn);
585 }
586
587 #[test]
588 fn info_callsite_is_correct() {
589 test_callsite(log::Level::Info);
590 }
591
592 #[test]
593 fn debug_callsite_is_correct() {
594 test_callsite(log::Level::Debug);
595 }
596
597 #[test]
598 fn trace_callsite_is_correct() {
599 test_callsite(log::Level::Trace);
600 }
601}