Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

538
vendor/tracing-log/src/interest_cache.rs vendored Normal file
View File

@@ -0,0 +1,538 @@
use ahash::AHasher;
use log::{Level, Metadata};
use lru::LruCache;
use once_cell::sync::Lazy;
use std::cell::RefCell;
use std::hash::Hasher;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
/// The interest cache configuration.
#[derive(Debug)]
pub struct InterestCacheConfig {
min_verbosity: Level,
lru_cache_size: usize,
}
impl Default for InterestCacheConfig {
fn default() -> Self {
InterestCacheConfig {
min_verbosity: Level::Debug,
lru_cache_size: 1024,
}
}
}
impl InterestCacheConfig {
fn disabled() -> Self {
Self {
lru_cache_size: 0,
..Self::default()
}
}
}
impl InterestCacheConfig {
/// Sets the minimum logging verbosity for which the cache will apply.
///
/// The interest for logs with a lower verbosity than specified here
/// will not be cached.
///
/// It should be set to the lowest verbosity level for which the majority
/// of the logs in your application are usually *disabled*.
///
/// In normal circumstances with typical logger usage patterns
/// you shouldn't ever have to change this.
///
/// By default this is set to `Debug`.
pub fn with_min_verbosity(mut self, level: Level) -> Self {
self.min_verbosity = level;
self
}
/// Sets the number of entries in the LRU cache used to cache interests
/// for `log` records.
///
/// The bigger the cache, the more unlikely it will be for the interest
/// in a given callsite to be recalculated, at the expense of extra
/// memory usage per every thread which tries to log events.
///
/// Every unique [level] + [target] pair consumes a single slot
/// in the cache. Entries will be added to the cache until its size
/// reaches the value configured here, and from then on it will evict
/// the least recently seen level + target pair when adding a new entry.
///
/// The ideal value to set here widely depends on how much exactly
/// you're logging, and how diverse the targets are to which you are logging.
///
/// If your application spends a significant amount of time filtering logs
/// which are *not* getting printed out then increasing this value will most
/// likely help.
///
/// Setting this to zero will disable the cache.
///
/// By default this is set to 1024.
///
/// [level]: log::Metadata::level
/// [target]: log::Metadata::target
pub fn with_lru_cache_size(mut self, size: usize) -> Self {
self.lru_cache_size = size;
self
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
struct Key {
target_address: usize,
level_and_length: usize,
}
struct State {
min_verbosity: Level,
epoch: usize,
cache: LruCache<Key, u64, ahash::RandomState>,
}
impl State {
fn new(epoch: usize, config: &InterestCacheConfig) -> Self {
State {
epoch,
min_verbosity: config.min_verbosity,
cache: LruCache::new(config.lru_cache_size),
}
}
}
// When the logger's filters are reconfigured the interest cache in core is cleared,
// and we also want to get notified when that happens so that we can clear our cache too.
//
// So what we do here is to register a dummy callsite with the core, just so that we can be
// notified when that happens. It doesn't really matter how exactly our dummy callsite looks
// like and whether subscribers will actually be interested in it, since nothing will actually
// be logged from it.
static INTEREST_CACHE_EPOCH: AtomicUsize = AtomicUsize::new(0);
fn interest_cache_epoch() -> usize {
INTEREST_CACHE_EPOCH.load(Ordering::Relaxed)
}
struct SentinelCallsite;
impl tracing_core::Callsite for SentinelCallsite {
fn set_interest(&self, _: tracing_core::subscriber::Interest) {
INTEREST_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);
}
fn metadata(&self) -> &tracing_core::Metadata<'_> {
&SENTINEL_METADATA
}
}
static SENTINEL_CALLSITE: SentinelCallsite = SentinelCallsite;
static SENTINEL_METADATA: tracing_core::Metadata<'static> = tracing_core::Metadata::new(
"log interest cache",
"log",
tracing_core::Level::ERROR,
None,
None,
None,
tracing_core::field::FieldSet::new(&[], tracing_core::identify_callsite!(&SENTINEL_CALLSITE)),
tracing_core::metadata::Kind::EVENT,
);
static CONFIG: Lazy<Mutex<InterestCacheConfig>> = Lazy::new(|| {
tracing_core::callsite::register(&SENTINEL_CALLSITE);
Mutex::new(InterestCacheConfig::disabled())
});
thread_local! {
static STATE: RefCell<State> = {
let config = CONFIG.lock().unwrap();
RefCell::new(State::new(interest_cache_epoch(), &config))
};
}
pub(crate) fn configure(new_config: Option<InterestCacheConfig>) {
*CONFIG.lock().unwrap() = new_config.unwrap_or_else(InterestCacheConfig::disabled);
INTEREST_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);
}
pub(crate) fn try_cache(metadata: &Metadata<'_>, callback: impl FnOnce() -> bool) -> bool {
STATE.with(|state| {
let mut state = state.borrow_mut();
// If the interest cache in core was rebuilt we need to reset the cache here too.
let epoch = interest_cache_epoch();
if epoch != state.epoch {
*state = State::new(epoch, &CONFIG.lock().unwrap());
}
let level = metadata.level();
if state.cache.cap() == 0 || level < state.min_verbosity {
return callback();
}
let target = metadata.target();
let mut hasher = AHasher::default();
hasher.write(target.as_bytes());
const HASH_MASK: u64 = !1;
const INTEREST_MASK: u64 = 1;
// We mask out the least significant bit of the hash since we'll use
// that space to save the interest.
//
// Since we use a good hashing function the loss of only a single bit
// won't really affect us negatively.
let target_hash = hasher.finish() & HASH_MASK;
// Since log targets are usually static strings we just use the address of the pointer
// as the key for our cache.
//
// We want each level to be cached separately so we also use the level as key, and since
// some linkers at certain optimization levels deduplicate strings if their prefix matches
// (e.g. "ham" and "hamster" might actually have the same address in memory) we also use the length.
let key = Key {
target_address: target.as_ptr() as usize,
// For extra efficiency we pack both the level and the length into a single field.
// The `level` can be between 1 and 5, so it can take at most 3 bits of space.
level_and_length: level as usize | target.len().wrapping_shl(3),
};
if let Some(&cached) = state.cache.get(&key) {
// And here we make sure that the target actually matches.
//
// This is just a hash of the target string, so theoretically we're not guaranteed
// that it won't collide, however in practice it shouldn't matter as it is quite
// unlikely that the target string's address and its length and the level and
// the hash will *all* be equal at the same time.
//
// We could of course actually store the whole target string in our cache,
// but we really want to avoid doing that as the necessary memory allocations
// would completely tank our performance, especially in cases where the cache's
// size is too small so it needs to regularly replace entries.
if cached & HASH_MASK == target_hash {
return (cached & INTEREST_MASK) != 0;
}
// Realistically we should never land here, unless someone is using a non-static
// target string with the same length and level, or is very lucky and found a hash
// collision for the cache's key.
}
let interest = callback();
state.cache.put(key, target_hash | interest as u64);
interest
})
}
#[cfg(test)]
mod tests {
use super::*;
fn lock_for_test() -> impl Drop {
// We need to make sure only one test runs at a time.
static LOCK: Lazy<Mutex<()>> = Lazy::new(Mutex::new);
match LOCK.lock() {
Ok(guard) => guard,
Err(poison) => poison.into_inner(),
}
}
#[test]
fn test_when_disabled_the_callback_is_always_called() {
let _lock = lock_for_test();
*CONFIG.lock().unwrap() = InterestCacheConfig::disabled();
std::thread::spawn(|| {
let metadata = log::MetadataBuilder::new()
.level(Level::Trace)
.target("dummy")
.build();
let mut count = 0;
try_cache(&metadata, || {
count += 1;
true
});
assert_eq!(count, 1);
try_cache(&metadata, || {
count += 1;
true
});
assert_eq!(count, 2);
})
.join()
.unwrap();
}
#[test]
fn test_when_enabled_the_callback_is_called_only_once_for_a_high_enough_verbosity() {
let _lock = lock_for_test();
*CONFIG.lock().unwrap() = InterestCacheConfig::default().with_min_verbosity(Level::Debug);
std::thread::spawn(|| {
let metadata = log::MetadataBuilder::new()
.level(Level::Debug)
.target("dummy")
.build();
let mut count = 0;
try_cache(&metadata, || {
count += 1;
true
});
assert_eq!(count, 1);
try_cache(&metadata, || {
count += 1;
true
});
assert_eq!(count, 1);
})
.join()
.unwrap();
}
#[test]
fn test_when_core_interest_cache_is_rebuilt_this_cache_is_also_flushed() {
let _lock = lock_for_test();
*CONFIG.lock().unwrap() = InterestCacheConfig::default().with_min_verbosity(Level::Debug);
std::thread::spawn(|| {
let metadata = log::MetadataBuilder::new()
.level(Level::Debug)
.target("dummy")
.build();
{
let mut count = 0;
try_cache(&metadata, || {
count += 1;
true
});
try_cache(&metadata, || {
count += 1;
true
});
assert_eq!(count, 1);
}
tracing_core::callsite::rebuild_interest_cache();
{
let mut count = 0;
try_cache(&metadata, || {
count += 1;
true
});
try_cache(&metadata, || {
count += 1;
true
});
assert_eq!(count, 1);
}
})
.join()
.unwrap();
}
#[test]
fn test_when_enabled_the_callback_is_always_called_for_a_low_enough_verbosity() {
let _lock = lock_for_test();
*CONFIG.lock().unwrap() = InterestCacheConfig::default().with_min_verbosity(Level::Debug);
std::thread::spawn(|| {
let metadata = log::MetadataBuilder::new()
.level(Level::Info)
.target("dummy")
.build();
let mut count = 0;
try_cache(&metadata, || {
count += 1;
true
});
assert_eq!(count, 1);
try_cache(&metadata, || {
count += 1;
true
});
assert_eq!(count, 2);
})
.join()
.unwrap();
}
#[test]
fn test_different_log_levels_are_cached_separately() {
let _lock = lock_for_test();
*CONFIG.lock().unwrap() = InterestCacheConfig::default().with_min_verbosity(Level::Debug);
std::thread::spawn(|| {
let metadata_debug = log::MetadataBuilder::new()
.level(Level::Debug)
.target("dummy")
.build();
let metadata_trace = log::MetadataBuilder::new()
.level(Level::Trace)
.target("dummy")
.build();
let mut count_debug = 0;
let mut count_trace = 0;
try_cache(&metadata_debug, || {
count_debug += 1;
true
});
try_cache(&metadata_trace, || {
count_trace += 1;
true
});
try_cache(&metadata_debug, || {
count_debug += 1;
true
});
try_cache(&metadata_trace, || {
count_trace += 1;
true
});
assert_eq!(count_debug, 1);
assert_eq!(count_trace, 1);
})
.join()
.unwrap();
}
#[test]
fn test_different_log_targets_are_cached_separately() {
let _lock = lock_for_test();
*CONFIG.lock().unwrap() = InterestCacheConfig::default().with_min_verbosity(Level::Debug);
std::thread::spawn(|| {
let metadata_1 = log::MetadataBuilder::new()
.level(Level::Trace)
.target("dummy_1")
.build();
let metadata_2 = log::MetadataBuilder::new()
.level(Level::Trace)
.target("dummy_2")
.build();
let mut count_1 = 0;
let mut count_2 = 0;
try_cache(&metadata_1, || {
count_1 += 1;
true
});
try_cache(&metadata_2, || {
count_2 += 1;
true
});
try_cache(&metadata_1, || {
count_1 += 1;
true
});
try_cache(&metadata_2, || {
count_2 += 1;
true
});
assert_eq!(count_1, 1);
assert_eq!(count_2, 1);
})
.join()
.unwrap();
}
#[test]
fn test_when_cache_runs_out_of_space_the_callback_is_called_again() {
let _lock = lock_for_test();
*CONFIG.lock().unwrap() = InterestCacheConfig::default()
.with_min_verbosity(Level::Debug)
.with_lru_cache_size(1);
std::thread::spawn(|| {
let metadata_1 = log::MetadataBuilder::new()
.level(Level::Trace)
.target("dummy_1")
.build();
let metadata_2 = log::MetadataBuilder::new()
.level(Level::Trace)
.target("dummy_2")
.build();
let mut count = 0;
try_cache(&metadata_1, || {
count += 1;
true
});
try_cache(&metadata_1, || {
count += 1;
true
});
assert_eq!(count, 1);
try_cache(&metadata_2, || true);
try_cache(&metadata_1, || {
count += 1;
true
});
assert_eq!(count, 2);
})
.join()
.unwrap();
}
#[test]
fn test_cache_returns_previously_computed_value() {
let _lock = lock_for_test();
*CONFIG.lock().unwrap() = InterestCacheConfig::default().with_min_verbosity(Level::Debug);
std::thread::spawn(|| {
let metadata_1 = log::MetadataBuilder::new()
.level(Level::Trace)
.target("dummy_1")
.build();
let metadata_2 = log::MetadataBuilder::new()
.level(Level::Trace)
.target("dummy_2")
.build();
try_cache(&metadata_1, || true);
assert_eq!(try_cache(&metadata_1, || { unreachable!() }), true);
try_cache(&metadata_2, || false);
assert_eq!(try_cache(&metadata_2, || { unreachable!() }), false);
})
.join()
.unwrap();
}
#[test]
fn test_cache_handles_non_static_target_string() {
let _lock = lock_for_test();
*CONFIG.lock().unwrap() = InterestCacheConfig::default().with_min_verbosity(Level::Debug);
std::thread::spawn(|| {
let mut target = *b"dummy_1";
let metadata_1 = log::MetadataBuilder::new()
.level(Level::Trace)
.target(std::str::from_utf8(&target).unwrap())
.build();
try_cache(&metadata_1, || true);
assert_eq!(try_cache(&metadata_1, || { unreachable!() }), true);
*target.last_mut().unwrap() = b'2';
let metadata_2 = log::MetadataBuilder::new()
.level(Level::Trace)
.target(std::str::from_utf8(&target).unwrap())
.build();
try_cache(&metadata_2, || false);
assert_eq!(try_cache(&metadata_2, || { unreachable!() }), false);
})
.join()
.unwrap();
}
}

600
vendor/tracing-log/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,600 @@
//! Adapters for connecting unstructured log records from the `log` crate into
//! the `tracing` ecosystem.
//!
//! # Overview
//!
//! [`tracing`] is a framework for instrumenting Rust programs with context-aware,
//! structured, event-based diagnostic information. This crate provides
//! compatibility layers for using `tracing` alongside the logging facade provided
//! by the [`log`] crate.
//!
//! This crate provides:
//!
//! - [`AsTrace`] and [`AsLog`] traits for converting between `tracing` and `log` types.
//! - [`LogTracer`], a [`log::Log`] implementation that consumes [`log::Record`]s
//! and outputs them as [`tracing::Event`].
//!
//! *Compiler support: [requires `rustc` 1.56+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
//! # Usage
//!
//! ## Convert log records to tracing `Event`s
//!
//! To convert [`log::Record`]s as [`tracing::Event`]s, set `LogTracer` as the default
//! logger by calling its [`init`] or [`init_with_filter`] methods.
//!
//! ```rust
//! # use std::error::Error;
//! use tracing_log::LogTracer;
//! use log;
//!
//! # fn main() -> Result<(), Box<Error>> {
//! LogTracer::init()?;
//!
//! // will be available for Subscribers as a tracing Event
//! log::trace!("an example trace log");
//! # Ok(())
//! # }
//! ```
//!
//! This conversion does not convert unstructured data in log records (such as
//! values passed as format arguments to the `log!` macro) to structured
//! `tracing` fields. However, it *does* attach these new events to to the
//! span that was currently executing when the record was logged. This is the
//! primary use-case for this library: making it possible to locate the log
//! records emitted by dependencies which use `log` within the context of a
//! trace.
//!
//! ## Convert tracing `Event`s to logs
//!
//! Enabling the ["log" and "log-always" feature flags][flags] on the `tracing`
//! crate will cause all `tracing` spans and events to emit `log::Record`s as
//! they occur.
//!
//! ## Caution: Mixing both conversions
//!
//! Note that `log::Logger` implementations that convert log records to trace events
//! should not be used with `Subscriber`s that convert trace events _back_ into
//! `log` records, as doing so will result in the event recursing between the subscriber
//! and the logger forever (or, in real life, probably overflowing the call stack).
//!
//! If the logging of trace events generated from log records produced by the
//! `log` crate is desired, either the `log` crate should not be used to
//! implement this logging, or an additional layer of filtering will be
//! required to avoid infinitely converting between `Event` and `log::Record`.
//!
//! ## Feature Flags
//!
//! * `std`: enables features that require the Rust standard library (on by default)
//! * `log-tracer`: enables the `LogTracer` type (on by default)
//! * `interest-cache`: makes it possible to configure an interest cache for
//! logs emitted through the `log` crate (see [`Builder::with_interest_cache`]); requires `std`
//!
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
//! version is 1.56. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
//! project. The current stable Rust compiler and the three most recent minor
//! versions before it will always be supported. For example, if the current
//! stable compiler version is 1.69, the minimum supported version will not be
//! increased past 1.66, three minor versions prior. Increasing the minimum
//! supported compiler version is not considered a semver breaking change as
//! long as doing so complies with this policy.
//!
//! [`init`]: LogTracer::init
//! [`init_with_filter`]: LogTracer::init_with_filter
//! [`tracing`]: https://crates.io/crates/tracing
//! [`tracing::Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
//! [`Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
//! [`tracing::Event`]: https://docs.rs/tracing/latest/tracing/struct.Event.html
//! [flags]: https://docs.rs/tracing/latest/tracing/#crate-feature-flags
//! [`Builder::with_interest_cache`]: log_tracer::Builder::with_interest_cache
#![doc(
html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
)]
#![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub,
bad_style,
dead_code,
improper_ctypes,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
private_in_public,
unconditional_recursion,
unused,
unused_allocation,
unused_comparisons,
unused_parens,
while_true
)]
use once_cell::sync::Lazy;
use std::{fmt, io};
use tracing_core::{
callsite::{self, Callsite},
dispatcher,
field::{self, Field, Visit},
identify_callsite,
metadata::{Kind, Level},
subscriber, Event, Metadata,
};
#[cfg(feature = "log-tracer")]
#[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
pub mod log_tracer;
#[cfg(feature = "log-tracer")]
#[cfg_attr(docsrs, doc(cfg(feature = "log-tracer")))]
#[doc(inline)]
pub use self::log_tracer::LogTracer;
pub use log;
#[cfg(all(feature = "interest-cache", feature = "log-tracer", feature = "std"))]
mod interest_cache;
#[cfg(all(feature = "interest-cache", feature = "log-tracer", feature = "std"))]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "interest-cache", feature = "log-tracer", feature = "std")))
)]
pub use crate::interest_cache::InterestCacheConfig;
/// Format a log record as a trace event in the current span.
pub fn format_trace(record: &log::Record<'_>) -> io::Result<()> {
dispatch_record(record);
Ok(())
}
// XXX(eliza): this is factored out so that we don't have to deal with the pub
// function `format_trace`'s `Result` return type...maybe we should get rid of
// that in 0.2...
pub(crate) fn dispatch_record(record: &log::Record<'_>) {
dispatcher::get_default(|dispatch| {
let filter_meta = record.as_trace();
if !dispatch.enabled(&filter_meta) {
return;
}
let (_, keys, meta) = loglevel_to_cs(record.level());
let log_module = record.module_path();
let log_file = record.file();
let log_line = record.line();
let module = log_module.as_ref().map(|s| s as &dyn field::Value);
let file = log_file.as_ref().map(|s| s as &dyn field::Value);
let line = log_line.as_ref().map(|s| s as &dyn field::Value);
dispatch.event(&Event::new(
meta,
&meta.fields().value_set(&[
(&keys.message, Some(record.args() as &dyn field::Value)),
(&keys.target, Some(&record.target())),
(&keys.module, module),
(&keys.file, file),
(&keys.line, line),
]),
));
});
}
/// Trait implemented for `tracing` types that can be converted to a `log`
/// equivalent.
pub trait AsLog: crate::sealed::Sealed {
/// The `log` type that this type can be converted into.
type Log;
/// Returns the `log` equivalent of `self`.
fn as_log(&self) -> Self::Log;
}
/// Trait implemented for `log` types that can be converted to a `tracing`
/// equivalent.
pub trait AsTrace: crate::sealed::Sealed {
/// The `tracing` type that this type can be converted into.
type Trace;
/// Returns the `tracing` equivalent of `self`.
fn as_trace(&self) -> Self::Trace;
}
impl<'a> crate::sealed::Sealed for Metadata<'a> {}
impl<'a> AsLog for Metadata<'a> {
type Log = log::Metadata<'a>;
fn as_log(&self) -> Self::Log {
log::Metadata::builder()
.level(self.level().as_log())
.target(self.target())
.build()
}
}
impl<'a> crate::sealed::Sealed for log::Metadata<'a> {}
impl<'a> AsTrace for log::Metadata<'a> {
type Trace = Metadata<'a>;
fn as_trace(&self) -> Self::Trace {
let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
Metadata::new(
"log record",
self.target(),
self.level().as_trace(),
None,
None,
None,
field::FieldSet::new(FIELD_NAMES, cs_id),
Kind::EVENT,
)
}
}
struct Fields {
message: field::Field,
target: field::Field,
module: field::Field,
file: field::Field,
line: field::Field,
}
static FIELD_NAMES: &[&str] = &[
"message",
"log.target",
"log.module_path",
"log.file",
"log.line",
];
impl Fields {
fn new(cs: &'static dyn Callsite) -> Self {
let fieldset = cs.metadata().fields();
let message = fieldset.field("message").unwrap();
let target = fieldset.field("log.target").unwrap();
let module = fieldset.field("log.module_path").unwrap();
let file = fieldset.field("log.file").unwrap();
let line = fieldset.field("log.line").unwrap();
Fields {
message,
target,
module,
file,
line,
}
}
}
macro_rules! log_cs {
($level:expr, $cs:ident, $meta:ident, $ty:ident) => {
struct $ty;
static $cs: $ty = $ty;
static $meta: Metadata<'static> = Metadata::new(
"log event",
"log",
$level,
::core::option::Option::None,
::core::option::Option::None,
::core::option::Option::None,
field::FieldSet::new(FIELD_NAMES, identify_callsite!(&$cs)),
Kind::EVENT,
);
impl callsite::Callsite for $ty {
fn set_interest(&self, _: subscriber::Interest) {}
fn metadata(&self) -> &'static Metadata<'static> {
&$meta
}
}
};
}
log_cs!(
tracing_core::Level::TRACE,
TRACE_CS,
TRACE_META,
TraceCallsite
);
log_cs!(
tracing_core::Level::DEBUG,
DEBUG_CS,
DEBUG_META,
DebugCallsite
);
log_cs!(tracing_core::Level::INFO, INFO_CS, INFO_META, InfoCallsite);
log_cs!(tracing_core::Level::WARN, WARN_CS, WARN_META, WarnCallsite);
log_cs!(
tracing_core::Level::ERROR,
ERROR_CS,
ERROR_META,
ErrorCallsite
);
static TRACE_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&TRACE_CS));
static DEBUG_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&DEBUG_CS));
static INFO_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&INFO_CS));
static WARN_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&WARN_CS));
static ERROR_FIELDS: Lazy<Fields> = Lazy::new(|| Fields::new(&ERROR_CS));
fn level_to_cs(level: Level) -> (&'static dyn Callsite, &'static Fields) {
match level {
Level::TRACE => (&TRACE_CS, &*TRACE_FIELDS),
Level::DEBUG => (&DEBUG_CS, &*DEBUG_FIELDS),
Level::INFO => (&INFO_CS, &*INFO_FIELDS),
Level::WARN => (&WARN_CS, &*WARN_FIELDS),
Level::ERROR => (&ERROR_CS, &*ERROR_FIELDS),
}
}
fn loglevel_to_cs(
level: log::Level,
) -> (
&'static dyn Callsite,
&'static Fields,
&'static Metadata<'static>,
) {
match level {
log::Level::Trace => (&TRACE_CS, &*TRACE_FIELDS, &TRACE_META),
log::Level::Debug => (&DEBUG_CS, &*DEBUG_FIELDS, &DEBUG_META),
log::Level::Info => (&INFO_CS, &*INFO_FIELDS, &INFO_META),
log::Level::Warn => (&WARN_CS, &*WARN_FIELDS, &WARN_META),
log::Level::Error => (&ERROR_CS, &*ERROR_FIELDS, &ERROR_META),
}
}
impl<'a> crate::sealed::Sealed for log::Record<'a> {}
impl<'a> AsTrace for log::Record<'a> {
type Trace = Metadata<'a>;
fn as_trace(&self) -> Self::Trace {
let cs_id = identify_callsite!(loglevel_to_cs(self.level()).0);
Metadata::new(
"log record",
self.target(),
self.level().as_trace(),
self.file(),
self.line(),
self.module_path(),
field::FieldSet::new(FIELD_NAMES, cs_id),
Kind::EVENT,
)
}
}
impl crate::sealed::Sealed for tracing_core::Level {}
impl AsLog for tracing_core::Level {
type Log = log::Level;
fn as_log(&self) -> log::Level {
match *self {
tracing_core::Level::ERROR => log::Level::Error,
tracing_core::Level::WARN => log::Level::Warn,
tracing_core::Level::INFO => log::Level::Info,
tracing_core::Level::DEBUG => log::Level::Debug,
tracing_core::Level::TRACE => log::Level::Trace,
}
}
}
impl crate::sealed::Sealed for log::Level {}
impl AsTrace for log::Level {
type Trace = tracing_core::Level;
#[inline]
fn as_trace(&self) -> tracing_core::Level {
match self {
log::Level::Error => tracing_core::Level::ERROR,
log::Level::Warn => tracing_core::Level::WARN,
log::Level::Info => tracing_core::Level::INFO,
log::Level::Debug => tracing_core::Level::DEBUG,
log::Level::Trace => tracing_core::Level::TRACE,
}
}
}
impl crate::sealed::Sealed for log::LevelFilter {}
impl AsTrace for log::LevelFilter {
type Trace = tracing_core::LevelFilter;
#[inline]
fn as_trace(&self) -> tracing_core::LevelFilter {
match self {
log::LevelFilter::Off => tracing_core::LevelFilter::OFF,
log::LevelFilter::Error => tracing_core::LevelFilter::ERROR,
log::LevelFilter::Warn => tracing_core::LevelFilter::WARN,
log::LevelFilter::Info => tracing_core::LevelFilter::INFO,
log::LevelFilter::Debug => tracing_core::LevelFilter::DEBUG,
log::LevelFilter::Trace => tracing_core::LevelFilter::TRACE,
}
}
}
impl crate::sealed::Sealed for tracing_core::LevelFilter {}
impl AsLog for tracing_core::LevelFilter {
type Log = log::LevelFilter;
#[inline]
fn as_log(&self) -> Self::Log {
match *self {
tracing_core::LevelFilter::OFF => log::LevelFilter::Off,
tracing_core::LevelFilter::ERROR => log::LevelFilter::Error,
tracing_core::LevelFilter::WARN => log::LevelFilter::Warn,
tracing_core::LevelFilter::INFO => log::LevelFilter::Info,
tracing_core::LevelFilter::DEBUG => log::LevelFilter::Debug,
tracing_core::LevelFilter::TRACE => log::LevelFilter::Trace,
}
}
}
/// Extends log `Event`s to provide complete `Metadata`.
///
/// In `tracing-log`, an `Event` produced by a log (through [`AsTrace`]) has an hard coded
/// "log" target and no `file`, `line`, or `module_path` attributes. This happens because `Event`
/// requires its `Metadata` to be `'static`, while [`log::Record`]s provide them with a generic
/// lifetime.
///
/// However, these values are stored in the `Event`'s fields and
/// the [`normalized_metadata`] method allows to build a new `Metadata`
/// that only lives as long as its source `Event`, but provides complete
/// data.
///
/// It can typically be used by `Subscriber`s when processing an `Event`,
/// to allow accessing its complete metadata in a consistent way,
/// regardless of the source of its source.
///
/// [`normalized_metadata`]: NormalizeEvent#normalized_metadata
pub trait NormalizeEvent<'a>: crate::sealed::Sealed {
/// If this `Event` comes from a `log`, this method provides a new
/// normalized `Metadata` which has all available attributes
/// from the original log, including `file`, `line`, `module_path`
/// and `target`.
/// Returns `None` is the `Event` is not issued from a `log`.
fn normalized_metadata(&'a self) -> Option<Metadata<'a>>;
/// Returns whether this `Event` represents a log (from the `log` crate)
fn is_log(&self) -> bool;
}
impl<'a> crate::sealed::Sealed for Event<'a> {}
impl<'a> NormalizeEvent<'a> for Event<'a> {
fn normalized_metadata(&'a self) -> Option<Metadata<'a>> {
let original = self.metadata();
if self.is_log() {
let mut fields = LogVisitor::new_for(self, level_to_cs(*original.level()).1);
self.record(&mut fields);
Some(Metadata::new(
"log event",
fields.target.unwrap_or("log"),
*original.level(),
fields.file,
fields.line.map(|l| l as u32),
fields.module_path,
field::FieldSet::new(&["message"], original.callsite()),
Kind::EVENT,
))
} else {
None
}
}
fn is_log(&self) -> bool {
self.metadata().callsite() == identify_callsite!(level_to_cs(*self.metadata().level()).0)
}
}
struct LogVisitor<'a> {
target: Option<&'a str>,
module_path: Option<&'a str>,
file: Option<&'a str>,
line: Option<u64>,
fields: &'static Fields,
}
impl<'a> LogVisitor<'a> {
// We don't actually _use_ the provided event argument; it is simply to
// ensure that the `LogVisitor` does not outlive the event whose fields it
// is visiting, so that the reference casts in `record_str` are safe.
fn new_for(_event: &'a Event<'a>, fields: &'static Fields) -> Self {
Self {
target: None,
module_path: None,
file: None,
line: None,
fields,
}
}
}
impl<'a> Visit for LogVisitor<'a> {
fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) {}
fn record_u64(&mut self, field: &Field, value: u64) {
if field == &self.fields.line {
self.line = Some(value);
}
}
fn record_str(&mut self, field: &Field, value: &str) {
unsafe {
// The `Visit` API erases the string slice's lifetime. However, we
// know it is part of the `Event` struct with a lifetime of `'a`. If
// (and only if!) this `LogVisitor` was constructed with the same
// lifetime parameter `'a` as the event in question, it's safe to
// cast these string slices to the `'a` lifetime.
if field == &self.fields.file {
self.file = Some(&*(value as *const _));
} else if field == &self.fields.target {
self.target = Some(&*(value as *const _));
} else if field == &self.fields.module {
self.module_path = Some(&*(value as *const _));
}
}
}
}
mod sealed {
pub trait Sealed {}
}
#[cfg(test)]
mod test {
use super::*;
fn test_callsite(level: log::Level) {
let record = log::Record::builder()
.args(format_args!("Error!"))
.level(level)
.target("myApp")
.file(Some("server.rs"))
.line(Some(144))
.module_path(Some("server"))
.build();
let meta = record.as_trace();
let (cs, _keys, _) = loglevel_to_cs(record.level());
let cs_meta = cs.metadata();
assert_eq!(
meta.callsite(),
cs_meta.callsite(),
"actual: {:#?}\nexpected: {:#?}",
meta,
cs_meta
);
assert_eq!(meta.level(), &level.as_trace());
}
#[test]
fn error_callsite_is_correct() {
test_callsite(log::Level::Error);
}
#[test]
fn warn_callsite_is_correct() {
test_callsite(log::Level::Warn);
}
#[test]
fn info_callsite_is_correct() {
test_callsite(log::Level::Info);
}
#[test]
fn debug_callsite_is_correct() {
test_callsite(log::Level::Debug);
}
#[test]
fn trace_callsite_is_correct() {
test_callsite(log::Level::Trace);
}
}

307
vendor/tracing-log/src/log_tracer.rs vendored Normal file
View File

@@ -0,0 +1,307 @@
//! An adapter for converting [`log`] records into `tracing` `Event`s.
//!
//! This module provides the [`LogTracer`] type which implements `log`'s [logger
//! interface] by recording log records as `tracing` `Event`s. This is intended for
//! use in conjunction with a `tracing` `Subscriber` to consume events from
//! dependencies that emit [`log`] records within a trace context.
//!
//! # Usage
//!
//! To create and initialize a `LogTracer` with the default configurations, use:
//!
//! * [`init`] if you want to convert all logs, regardless of log level,
//! allowing the tracing `Subscriber` to perform any filtering
//! * [`init_with_filter`] to convert all logs up to a specified log level
//!
//! In addition, a [builder] is available for cases where more advanced
//! configuration is required. In particular, the builder can be used to [ignore
//! log records][ignore] emitted by particular crates. This is useful in cases
//! such as when a crate emits both `tracing` diagnostics _and_ log records by
//! default.
//!
//! [logger interface]: log::Log
//! [`init`]: LogTracer.html#method.init
//! [`init_with_filter`]: LogTracer.html#method.init_with_filter
//! [builder]: LogTracer::builder()
//! [ignore]: Builder::ignore_crate()
use crate::AsTrace;
pub use log::SetLoggerError;
use tracing_core::dispatcher;
/// A simple "logger" that converts all log records into `tracing` `Event`s.
#[derive(Debug)]
pub struct LogTracer {
ignore_crates: Box<[String]>,
}
/// Configures a new `LogTracer`.
#[derive(Debug)]
pub struct Builder {
ignore_crates: Vec<String>,
filter: log::LevelFilter,
#[cfg(all(feature = "interest-cache", feature = "std"))]
interest_cache_config: Option<crate::InterestCacheConfig>,
}
// ===== impl LogTracer =====
impl LogTracer {
/// Returns a builder that allows customizing a `LogTracer` and setting it
/// the default logger.
///
/// For example:
/// ```rust
/// # use std::error::Error;
/// use tracing_log::LogTracer;
/// use log;
///
/// # fn main() -> Result<(), Box<Error>> {
/// LogTracer::builder()
/// .ignore_crate("foo") // suppose the `foo` crate is using `tracing`'s log feature
/// .with_max_level(log::LevelFilter::Info)
/// .init()?;
///
/// // will be available for Subscribers as a tracing Event
/// log::info!("an example info log");
/// # Ok(())
/// # }
/// ```
pub fn builder() -> Builder {
Builder::default()
}
/// Creates a new `LogTracer` that can then be used as a logger for the `log` crate.
///
/// It is generally simpler to use the [`init`] or [`init_with_filter`] methods
/// which will create the `LogTracer` and set it as the global logger.
///
/// Logger setup without the initialization methods can be done with:
///
/// ```rust
/// # use std::error::Error;
/// use tracing_log::LogTracer;
/// use log;
///
/// # fn main() -> Result<(), Box<Error>> {
/// let logger = LogTracer::new();
/// log::set_boxed_logger(Box::new(logger))?;
/// log::set_max_level(log::LevelFilter::Trace);
///
/// // will be available for Subscribers as a tracing Event
/// log::trace!("an example trace log");
/// # Ok(())
/// # }
/// ```
///
/// [`init`]: LogTracer::init()
/// [`init_with_filter`]: .#method.init_with_filter
pub fn new() -> Self {
Self {
ignore_crates: Vec::new().into_boxed_slice(),
}
}
/// Sets up `LogTracer` as global logger for the `log` crate,
/// with the given level as max level filter.
///
/// Setting a global logger can only be done once.
///
/// The [`builder`] function can be used to customize the `LogTracer` before
/// initializing it.
///
/// [`builder`]: LogTracer::builder()
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn init_with_filter(level: log::LevelFilter) -> Result<(), SetLoggerError> {
Self::builder().with_max_level(level).init()
}
/// Sets a `LogTracer` as the global logger for the `log` crate.
///
/// Setting a global logger can only be done once.
///
/// ```rust
/// # use std::error::Error;
/// use tracing_log::LogTracer;
/// use log;
///
/// # fn main() -> Result<(), Box<Error>> {
/// LogTracer::init()?;
///
/// // will be available for Subscribers as a tracing Event
/// log::trace!("an example trace log");
/// # Ok(())
/// # }
/// ```
///
/// This will forward all logs to `tracing` and lets the current `Subscriber`
/// determine if they are enabled.
///
/// The [`builder`] function can be used to customize the `LogTracer` before
/// initializing it.
///
/// If you know in advance you want to filter some log levels,
/// use [`builder`] or [`init_with_filter`] instead.
///
/// [`init_with_filter`]: LogTracer::init_with_filter()
/// [`builder`]: LogTracer::builder()
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn init() -> Result<(), SetLoggerError> {
Self::builder().init()
}
}
impl Default for LogTracer {
fn default() -> Self {
Self::new()
}
}
#[cfg(all(feature = "interest-cache", feature = "std"))]
use crate::interest_cache::try_cache as try_cache_interest;
#[cfg(not(all(feature = "interest-cache", feature = "std")))]
fn try_cache_interest(_: &log::Metadata<'_>, callback: impl FnOnce() -> bool) -> bool {
callback()
}
impl log::Log for LogTracer {
fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
// First, check the log record against the current max level enabled by
// the current `tracing` subscriber.
if metadata.level().as_trace() > tracing_core::LevelFilter::current() {
// If the log record's level is above that, disable it.
return false;
}
// Okay, it wasn't disabled by the max level — do we have any specific
// modules to ignore?
if !self.ignore_crates.is_empty() {
// If we are ignoring certain module paths, ensure that the metadata
// does not start with one of those paths.
let target = metadata.target();
for ignored in &self.ignore_crates[..] {
if target.starts_with(ignored) {
return false;
}
}
}
try_cache_interest(metadata, || {
// Finally, check if the current `tracing` dispatcher cares about this.
dispatcher::get_default(|dispatch| dispatch.enabled(&metadata.as_trace()))
})
}
fn log(&self, record: &log::Record<'_>) {
if self.enabled(record.metadata()) {
crate::dispatch_record(record);
}
}
fn flush(&self) {}
}
// ===== impl Builder =====
impl Builder {
/// Returns a new `Builder` to construct a [`LogTracer`].
///
pub fn new() -> Self {
Self::default()
}
/// Sets a global maximum level for `log` records.
///
/// Log records whose level is more verbose than the provided level will be
/// disabled.
///
/// By default, all `log` records will be enabled.
pub fn with_max_level(self, filter: impl Into<log::LevelFilter>) -> Self {
let filter = filter.into();
Self { filter, ..self }
}
/// Configures the `LogTracer` to ignore all log records whose target
/// starts with the given string.
///
/// This should be used when a crate enables the `tracing/log` feature to
/// emit log records for tracing events. Otherwise, those events will be
/// recorded twice.
pub fn ignore_crate(mut self, name: impl Into<String>) -> Self {
self.ignore_crates.push(name.into());
self
}
/// Configures the `LogTracer` to ignore all log records whose target
/// starts with any of the given the given strings.
///
/// This should be used when a crate enables the `tracing/log` feature to
/// emit log records for tracing events. Otherwise, those events will be
/// recorded twice.
pub fn ignore_all<I>(self, crates: impl IntoIterator<Item = I>) -> Self
where
I: Into<String>,
{
crates.into_iter().fold(self, Self::ignore_crate)
}
/// Configures the `LogTracer` to either disable or enable the interest cache.
///
/// When enabled, a per-thread LRU cache will be used to cache whenever the logger
/// is interested in a given [level] + [target] pair for records generated through
/// the `log` crate.
///
/// When no `trace!` logs are enabled the logger is able to cheaply filter
/// them out just by comparing their log level to the globally specified
/// maximum, and immediately reject them. When *any* other `trace!` log is
/// enabled (even one which doesn't actually exist!) the logger has to run
/// its full filtering machinery on each and every `trace!` log, which can
/// potentially be very expensive.
///
/// Enabling this cache is useful in such situations to improve performance.
///
/// You most likely do not want to enabled this if you have registered any dynamic
/// filters on your logger and you want them to be run every time.
///
/// This is disabled by default.
///
/// [level]: log::Metadata::level
/// [target]: log::Metadata::target
#[cfg(all(feature = "interest-cache", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "interest-cache", feature = "std"))))]
pub fn with_interest_cache(mut self, config: crate::InterestCacheConfig) -> Self {
self.interest_cache_config = Some(config);
self
}
/// Constructs a new `LogTracer` with the provided configuration and sets it
/// as the default logger.
///
/// Setting a global logger can only be done once.
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[allow(unused_mut)]
pub fn init(mut self) -> Result<(), SetLoggerError> {
#[cfg(all(feature = "interest-cache", feature = "std"))]
crate::interest_cache::configure(self.interest_cache_config.take());
let ignore_crates = self.ignore_crates.into_boxed_slice();
let logger = Box::new(LogTracer { ignore_crates });
log::set_boxed_logger(logger)?;
log::set_max_level(self.filter);
Ok(())
}
}
impl Default for Builder {
fn default() -> Self {
Self {
ignore_crates: Vec::new(),
filter: log::LevelFilter::max(),
#[cfg(all(feature = "interest-cache", feature = "std"))]
interest_cache_config: None,
}
}
}