From cdbd122933c73c8cdd36f275df89d83ae43bffea Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 21 Apr 2022 13:14:50 -0700 Subject: [PATCH 001/142] log: prepare to release v0.1.3 (#2086) # 0.1.3 (April 21st, 2022) ### Added - **log-tracer**: Added `LogTracer::with_interest_cache` to enable a limited form of per-record `Interest` caching for `log` records ([#1636]) ### Changed - Updated minimum supported Rust version (MSRV) to Rust 1.49.0 ([#1913]) ### Fixed - **log-tracer**: Fixed `LogTracer` not honoring `tracing` max level filters ([#1543]) - Broken links in documentation ([#2068], [#2077]) Thanks to @Millione, @teozkr, @koute, @Folyd, and @ben0x539 for contributing to this release! [#1636]: https://github.com/tokio-rs/tracing/pulls/1636 [#1913]: https://github.com/tokio-rs/tracing/pulls/1913 [#1543]: https://github.com/tokio-rs/tracing/pulls/1543 [#2068]: https://github.com/tokio-rs/tracing/pulls/2068 [#2077]: https://github.com/tokio-rs/tracing/pulls/2077 Fixes #1884 Fixes #1664 --- tracing-log/CHANGELOG.md | 26 ++++++++++++++++++++++++++ tracing-log/Cargo.toml | 2 +- tracing-log/src/lib.rs | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/tracing-log/CHANGELOG.md b/tracing-log/CHANGELOG.md index 7a1370e31a..2ae9f0e6d8 100644 --- a/tracing-log/CHANGELOG.md +++ b/tracing-log/CHANGELOG.md @@ -1,3 +1,29 @@ +# 0.1.3 (April 21st, 2022) + +### Added + +- **log-tracer**: Added `LogTracer::with_interest_cache` to enable a limited + form of per-record `Interest` caching for `log` records ([#1636]) + +### Changed + +- Updated minimum supported Rust version (MSRV) to Rust 1.49.0 ([#1913]) + +### Fixed + +- **log-tracer**: Fixed `LogTracer` not honoring `tracing` max level filters + ([#1543]) +- Broken links in documentation ([#2068], [#2077]) + +Thanks to @Millione, @teozkr, @koute, @Folyd, and @ben0x539 for contributing to +this release! + +[#1636]: https://github.com/tokio-rs/tracing/pulls/1636 +[#1913]: https://github.com/tokio-rs/tracing/pulls/1913 +[#1543]: https://github.com/tokio-rs/tracing/pulls/1543 +[#2068]: https://github.com/tokio-rs/tracing/pulls/2068 +[#2077]: https://github.com/tokio-rs/tracing/pulls/2077 + # 0.1.2 (February 19th, 2020) ### Added diff --git a/tracing-log/Cargo.toml b/tracing-log/Cargo.toml index e75165f9ae..b482341eab 100644 --- a/tracing-log/Cargo.toml +++ b/tracing-log/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-log" -version = "0.1.2" +version = "0.1.3" authors = ["Tokio Contributors "] edition = "2018" repository = "https://github.com/tokio-rs/tracing" diff --git a/tracing-log/src/lib.rs b/tracing-log/src/lib.rs index ee41aa89b6..09e9114a4c 100644 --- a/tracing-log/src/lib.rs +++ b/tracing-log/src/lib.rs @@ -100,7 +100,7 @@ //! [`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_root_url = "https://docs.rs/tracing-log/0.1.2")] +#![doc(html_root_url = "https://docs.rs/tracing-log/0.1.3")] #![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/" From 45a5df13735ede10475a91ffd0cc063a8a512629 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 22 Apr 2022 09:11:08 -0700 Subject: [PATCH 002/142] core: add a limited form of the linked-list callsite registry for v0.1.x (#2083) ## Motivation Currently on `v0.1.x`, the global callsite registry is implemented as a `Mutex>`. This has a few downsides: * Every time a callsite is registered, the `Mutex` must be locked. This can cause a deadlock when a `register_callsite` implementation calls into code that _also_ registers a callsite. See #2020 for details. * Registering callsites is relatively slow and forces the program to synchronize on the callsite registry (and not on *individual* callsites). This means that if two threads are both registering totally different callsites, both threads must wait for the lock. Although this overhead is amortized over the entire rest of the program, it does have an impact in short-running applications where any given callsite may only be hit once or twice. * Memory allocation may occur while the registry is locked. This makes the use of `tracing` inside of memory allocators difficult or impossible. On the `master` branch (v0.2.x), PR #988 rewrote the callsite registry to use an intrusive atomic singly-linked list of `Callsite`s. This removed the need for locking the callsite registry, and made it possible to register callsites without ever allocating memory. Because the callsite registry on v0.2 will *never* allocate, this also makes it possible to compile `tracing-core` for `no_std` platforms without requiring `liballoc`. Unfortunately, however, we cannot use an identical implementation on v0.1.x, because using the intrusive linked-list registry for *all* callsites requires a breaking change to the `Callsite` trait (in order to add a way to get the callsite's linked-list node), which we cannot make on v0.1.x. ## Solution This branch adds a linked-list callsite registry for v0.1.x in a way that allows us to benefit from *some* of the advantages of this approach in a majority of cases. The trick is introducing a new `DefaultCallsite` type in `tracing-core` that implements the `Callsite` trait. This type can contain an intrusive list node, and *when a callsite is a `DefaultCallsite`*, we can register it without having to push it to the `Mutex>`. The locked vec still _exists_, for `Callsite`s that are *not* `DefaultCallsite`s, so we can't remove the `liballoc` dependency, but in most cases, we can avoid the mutex and allocation. Naturally, `tracing` has been updated to use the `DefaultCallsite` type from `tracing-core`, so the `Vec` will only be used in the following cases: * User code has a custom implementation of the `Callsite` trait, which is [not terribly common][1]. * An older version of the `tracing` crate is in use. This fixes the deadlock described in #2020 when `DefaultCallsite`s are used. Additionally, it should reduce the performance impact and memory usage of the callsite registry. Furthermore, I've changed the subscriber registry so that a `RwLock>` is only used when there actually are multiple subscribers in use. When there's only a global default subscriber, we can once again avoid locking for the registry of subscribers. When the `std` feature is disabled, thread-local scoped dispatchers are unavailable, so the single global subscriber will always be used on `no_std`. We can make additional changes, such as the ones from #2020, to _also_ resolve potential deadlocks when non-default callsites are in use, but since this branch rewrites a lot of the callsite registry code, that should probably be done in a follow-up. [1]: https://cs.github.com/?scopeName=All+repos&scope=&q=%28%2Fimpl+.*Callsite%2F+AND+language%3Arust%29+NOT+%28path%3A%2Ftracing%2F**+OR+path%3A%2Ftracing-*%2F**+OR+path%3A%2Ftokio-trace*%2F**%29%29 Signed-off-by: Eliza Weisman --- tracing-core/src/callsite.rs | 455 +++++++++++++++++--- tracing-core/src/dispatcher.rs | 13 +- tracing-mock/src/subscriber.rs | 2 + tracing/Cargo.toml | 2 +- tracing/src/lib.rs | 189 +++----- tracing/src/macros.rs | 35 +- tracing/tests/register_callsite_deadlock.rs | 47 ++ 7 files changed, 517 insertions(+), 226 deletions(-) create mode 100644 tracing/tests/register_callsite_deadlock.rs diff --git a/tracing-core/src/callsite.rs b/tracing-core/src/callsite.rs index 38ff14a2f0..273fecec4e 100644 --- a/tracing-core/src/callsite.rs +++ b/tracing-core/src/callsite.rs @@ -1,74 +1,23 @@ //! Callsites represent the source locations from which spans or events //! originate. use crate::stdlib::{ + any::TypeId, fmt, hash::{Hash, Hasher}, - sync::Mutex, + ptr, + sync::{ + atomic::{AtomicBool, AtomicPtr, AtomicU8, Ordering}, + Mutex, + }, vec::Vec, }; use crate::{ - dispatcher::{self, Dispatch}, + dispatcher::Dispatch, metadata::{LevelFilter, Metadata}, subscriber::Interest, }; -crate::lazy_static! { - static ref REGISTRY: Mutex = Mutex::new(Registry { - callsites: Vec::new(), - dispatchers: Vec::new(), - }); -} - -struct Registry { - callsites: Vec<&'static dyn Callsite>, - dispatchers: Vec, -} - -impl Registry { - fn rebuild_callsite_interest(&self, callsite: &'static dyn Callsite) { - let meta = callsite.metadata(); - - // Iterate over the subscribers in the registry, and — if they are - // active — register the callsite with them. - let mut interests = self - .dispatchers - .iter() - .filter_map(|registrar| registrar.try_register(meta)); - - // Use the first subscriber's `Interest` as the base value. - let interest = if let Some(interest) = interests.next() { - // Combine all remaining `Interest`s. - interests.fold(interest, Interest::and) - } else { - // If nobody was interested in this thing, just return `never`. - Interest::never() - }; - - callsite.set_interest(interest) - } - - fn rebuild_interest(&mut self) { - let mut max_level = LevelFilter::OFF; - self.dispatchers.retain(|registrar| { - if let Some(dispatch) = registrar.upgrade() { - // If the subscriber did not provide a max level hint, assume - // that it may enable every level. - let level_hint = dispatch.max_level_hint().unwrap_or(LevelFilter::TRACE); - if level_hint > max_level { - max_level = level_hint; - } - true - } else { - false - } - }); - - self.callsites.iter().for_each(|&callsite| { - self.rebuild_callsite_interest(callsite); - }); - LevelFilter::set_max(max_level); - } -} +use self::dispatchers::Dispatchers; /// Trait implemented by callsites. /// @@ -84,6 +33,28 @@ pub trait Callsite: Sync { /// /// [metadata]: super::metadata::Metadata fn metadata(&self) -> &Metadata<'_>; + + /// This method is an *internal implementation detail* of `tracing-core`. It + /// is *not* intended to be called or overridden from downstream code. + /// + /// The `Private` type can only be constructed from within `tracing-core`. + /// Because this method takes a `Private` as an argument, it cannot be + /// called from (safe) code external to `tracing-core`. Because it must + /// *return* a `Private`, the only valid implementation possible outside of + /// `tracing-core` would have to always unconditionally panic. + /// + /// THIS IS BY DESIGN. There is currently no valid reason for code outside + /// of `tracing-core` to override this method. + // TODO(eliza): this could be used to implement a public downcasting API + // for `&dyn Callsite`s in the future. + #[doc(hidden)] + #[inline] + fn private_type_id(&self, _: private::Private<()>) -> private::Private + where + Self: 'static, + { + private::Private(TypeId::of::()) + } } /// Uniquely identifies a [`Callsite`] @@ -104,6 +75,15 @@ pub struct Identifier( pub &'static dyn Callsite, ); +/// A default [`Callsite`] implementation. +#[derive(Debug)] +pub struct DefaultCallsite { + interest: AtomicU8, + registration: AtomicU8, + meta: &'static Metadata<'static>, + next: AtomicPtr, +} + /// Clear and reregister interest on every [`Callsite`] /// /// This function is intended for runtime reconfiguration of filters on traces @@ -124,8 +104,7 @@ pub struct Identifier( /// [`Interest::sometimes()`]: super::subscriber::Interest::sometimes /// [`Subscriber`]: super::subscriber::Subscriber pub fn rebuild_interest_cache() { - let mut registry = REGISTRY.lock().unwrap(); - registry.rebuild_interest(); + CALLSITES.rebuild_interest(DISPATCHERS.rebuilder()); } /// Register a new `Callsite` with the global registry. @@ -133,15 +112,137 @@ pub fn rebuild_interest_cache() { /// This should be called once per callsite after the callsite has been /// constructed. pub fn register(callsite: &'static dyn Callsite) { - let mut registry = REGISTRY.lock().unwrap(); - registry.rebuild_callsite_interest(callsite); - registry.callsites.push(callsite); + rebuild_callsite_interest(callsite, &DISPATCHERS.rebuilder()); + + // Is this a `DefaultCallsite`? If so, use the fancy linked list! + if callsite.private_type_id(private::Private(())).0 == TypeId::of::() { + let callsite = unsafe { + // Safety: the pointer cast is safe because the type id of the + // provided callsite matches that of the target type for the cast + // (`DefaultCallsite`). Because user implementations of `Callsite` + // cannot override `private_type_id`, we can trust that the callsite + // is not lying about its type ID. + &*(callsite as *const dyn Callsite as *const DefaultCallsite) + }; + CALLSITES.push_default(callsite); + return; + } + + CALLSITES.push_dyn(callsite); } -pub(crate) fn register_dispatch(dispatch: &Dispatch) { - let mut registry = REGISTRY.lock().unwrap(); - registry.dispatchers.push(dispatch.registrar()); - registry.rebuild_interest(); +static CALLSITES: Callsites = Callsites { + list_head: AtomicPtr::new(ptr::null_mut()), + has_locked_callsites: AtomicBool::new(false), +}; + +static DISPATCHERS: Dispatchers = Dispatchers::new(); + +crate::lazy_static! { + static ref LOCKED_CALLSITES: Mutex> = Mutex::new(Vec::new()); +} + +struct Callsites { + list_head: AtomicPtr, + has_locked_callsites: AtomicBool, +} + +// === impl DefaultCallsite === + +impl DefaultCallsite { + const UNREGISTERED: u8 = 0; + const REGISTERING: u8 = 1; + const REGISTERED: u8 = 2; + + const INTEREST_NEVER: u8 = 0; + const INTEREST_SOMETIMES: u8 = 1; + const INTEREST_ALWAYS: u8 = 2; + + /// Returns a new `DefaultCallsite` with the specified `Metadata`. + pub const fn new(meta: &'static Metadata<'static>) -> Self { + Self { + interest: AtomicU8::new(0xFF), + meta, + next: AtomicPtr::new(ptr::null_mut()), + registration: AtomicU8::new(Self::UNREGISTERED), + } + } + + /// Registers this callsite with the global callsite registry. + /// + /// If the callsite is already registered, this does nothing. When using + /// [`DefaultCallsite`], this method should be preferred over + /// [`tracing_core::callsite::register`], as it ensures that the callsite is + /// only registered a single time. + /// + /// Other callsite implementations will generally ensure that + /// callsites are not re-registered through another mechanism. + #[inline(never)] + // This only happens once (or if the cached interest value was corrupted). + #[cold] + pub fn register(&'static self) -> Interest { + // Attempt to advance the registration state to `REGISTERING`... + match self.registration.compare_exchange( + Self::UNREGISTERED, + Self::REGISTERING, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Okay, we advanced the state, try to register the callsite. + rebuild_callsite_interest(self, &DISPATCHERS.rebuilder()); + CALLSITES.push_default(self); + self.registration.store(Self::REGISTERED, Ordering::Release); + } + // Great, the callsite is already registered! Just load its + // previous cached interest. + Err(Self::REGISTERED) => {} + // Someone else is registering... + Err(_state) => { + debug_assert_eq!( + _state, + Self::REGISTERING, + "weird callsite registration state" + ); + // Just hit `enabled` this time. + return Interest::sometimes(); + } + } + + match self.interest.load(Ordering::Relaxed) { + Self::INTEREST_NEVER => Interest::never(), + Self::INTEREST_ALWAYS => Interest::always(), + _ => Interest::sometimes(), + } + } + + /// Returns the callsite's cached `Interest`, or registers it for the + /// first time if it has not yet been registered. + #[inline] + pub fn interest(&'static self) -> Interest { + match self.interest.load(Ordering::Relaxed) { + Self::INTEREST_NEVER => Interest::never(), + Self::INTEREST_SOMETIMES => Interest::sometimes(), + Self::INTEREST_ALWAYS => Interest::always(), + _ => self.register(), + } + } +} + +impl Callsite for DefaultCallsite { + fn set_interest(&self, interest: Interest) { + let interest = match () { + _ if interest.is_never() => Self::INTEREST_NEVER, + _ if interest.is_always() => Self::INTEREST_ALWAYS, + _ => Self::INTEREST_SOMETIMES, + }; + self.interest.store(interest, Ordering::SeqCst); + } + + #[inline(always)] + fn metadata(&self) -> &Metadata<'static> { + self.meta + } } // ===== impl Identifier ===== @@ -171,3 +272,221 @@ impl Hash for Identifier { (self.0 as *const dyn Callsite).hash(state) } } + +// === impl Callsites === + +impl Callsites { + /// Rebuild `Interest`s for all callsites in the registry. + /// + /// This also re-computes the max level hint. + fn rebuild_interest(&self, dispatchers: dispatchers::Rebuilder<'_>) { + let mut max_level = LevelFilter::OFF; + dispatchers.for_each(|dispatch| { + // If the subscriber did not provide a max level hint, assume + // that it may enable every level. + let level_hint = dispatch.max_level_hint().unwrap_or(LevelFilter::TRACE); + if level_hint > max_level { + max_level = level_hint; + } + }); + + self.for_each(|callsite| { + rebuild_callsite_interest(callsite, &dispatchers); + }); + LevelFilter::set_max(max_level); + } + + /// Push a `dyn Callsite` trait object to the callsite registry. + /// + /// This will attempt to lock the callsites vector. + fn push_dyn(&self, callsite: &'static dyn Callsite) { + let mut lock = LOCKED_CALLSITES.lock().unwrap(); + self.has_locked_callsites.store(true, Ordering::Release); + lock.push(callsite); + } + + /// Push a `DefaultCallsite` to the callsite registry. + /// + /// If we know the callsite being pushed is a `DefaultCallsite`, we can push + /// it to the linked list without having to acquire a lock. + fn push_default(&self, callsite: &'static DefaultCallsite) { + let mut head = self.list_head.load(Ordering::Acquire); + + loop { + callsite.next.store(head, Ordering::Release); + + assert_ne!( + callsite as *const _, head, + "Attempted to register a `DefaultCallsite` that already exists! \ + This will cause an infinite loop when attempting to read from the \ + callsite cache. This is likely a bug! You should only need to call \ + `DefaultCallsite::register` once per `DefaultCallsite`." + ); + + match self.list_head.compare_exchange( + head, + callsite as *const _ as *mut _, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + break; + } + Err(current) => head = current, + } + } + } + + /// Invokes the provided closure `f` with each callsite in the registry. + fn for_each(&self, mut f: impl FnMut(&'static dyn Callsite)) { + let mut head = self.list_head.load(Ordering::Acquire); + + while let Some(cs) = unsafe { head.as_ref() } { + f(cs); + + head = cs.next.load(Ordering::Acquire); + } + + if self.has_locked_callsites.load(Ordering::Acquire) { + let locked = LOCKED_CALLSITES.lock().unwrap(); + for &cs in locked.iter() { + f(cs); + } + } + } +} + +pub(crate) fn register_dispatch(dispatch: &Dispatch) { + let dispatchers = DISPATCHERS.register_dispatch(dispatch); + CALLSITES.rebuild_interest(dispatchers); +} + +fn rebuild_callsite_interest( + callsite: &'static dyn Callsite, + dispatchers: &dispatchers::Rebuilder<'_>, +) { + let meta = callsite.metadata(); + + let mut interest = None; + dispatchers.for_each(|dispatch| { + let this_interest = dispatch.register_callsite(meta); + interest = match interest.take() { + None => Some(this_interest), + Some(that_interest) => Some(that_interest.and(this_interest)), + } + }); + + let interest = interest.unwrap_or_else(Interest::never); + callsite.set_interest(interest) +} + +mod private { + /// Don't call this function, it's private. + #[allow(missing_debug_implementations)] + pub struct Private(pub(crate) T); +} + +#[cfg(feature = "std")] +mod dispatchers { + use crate::dispatcher; + use std::sync::{ + atomic::{AtomicBool, Ordering}, + RwLock, RwLockReadGuard, RwLockWriteGuard, + }; + + pub(super) struct Dispatchers { + has_just_one: AtomicBool, + } + + crate::lazy_static! { + static ref LOCKED_DISPATCHERS: RwLock> = RwLock::new(Vec::new()); + } + + pub(super) enum Rebuilder<'a> { + JustOne, + Read(RwLockReadGuard<'a, Vec>), + Write(RwLockWriteGuard<'a, Vec>), + } + + impl Dispatchers { + pub(super) const fn new() -> Self { + Self { + has_just_one: AtomicBool::new(true), + } + } + + pub(super) fn rebuilder(&self) -> Rebuilder<'_> { + if self.has_just_one.load(Ordering::SeqCst) { + return Rebuilder::JustOne; + } + Rebuilder::Read(LOCKED_DISPATCHERS.read().unwrap()) + } + + pub(super) fn register_dispatch(&self, dispatch: &dispatcher::Dispatch) -> Rebuilder<'_> { + let mut dispatchers = LOCKED_DISPATCHERS.write().unwrap(); + dispatchers.retain(|d| d.upgrade().is_some()); + dispatchers.push(dispatch.registrar()); + self.has_just_one + .store(dispatchers.len() <= 1, Ordering::SeqCst); + Rebuilder::Write(dispatchers) + } + } + + impl Rebuilder<'_> { + pub(super) fn for_each(&self, mut f: impl FnMut(&dispatcher::Dispatch)) { + let iter = match self { + Rebuilder::JustOne => { + dispatcher::get_default(f); + return; + } + Rebuilder::Read(vec) => vec.iter(), + Rebuilder::Write(vec) => vec.iter(), + }; + iter.filter_map(dispatcher::Registrar::upgrade) + .for_each(|dispatch| f(&dispatch)) + } + } +} + +#[cfg(not(feature = "std"))] +mod dispatchers { + use crate::dispatcher; + + pub(super) struct Dispatchers(()); + pub(super) struct Rebuilder<'a>(Option<&'a dispatcher::Dispatch>); + + impl Dispatchers { + pub(super) const fn new() -> Self { + Self(()) + } + + pub(super) fn rebuilder(&self) -> Rebuilder<'_> { + Rebuilder(None) + } + + pub(super) fn register_dispatch<'dispatch>( + &self, + dispatch: &'dispatch dispatcher::Dispatch, + ) -> Rebuilder<'dispatch> { + // nop; on no_std, there can only ever be one dispatcher + Rebuilder(Some(dispatch)) + } + } + + impl Rebuilder<'_> { + #[inline] + pub(super) fn for_each(&self, mut f: impl FnMut(&dispatcher::Dispatch)) { + if let Some(dispatch) = self.0 { + // we are rebuilding the interest cache because a new dispatcher + // is about to be set. on `no_std`, this should only happen + // once, because the new dispatcher will be the global default. + f(dispatch) + } else { + // otherwise, we are rebuilding the cache because the subscriber + // configuration changed, so use the global default. + // on no_std, there can only ever be one dispatcher + dispatcher::get_default(f) + } + } + } +} diff --git a/tracing-core/src/dispatcher.rs b/tracing-core/src/dispatcher.rs index d6b5c6e6a1..741744437c 100644 --- a/tracing-core/src/dispatcher.rs +++ b/tracing-core/src/dispatcher.rs @@ -134,7 +134,7 @@ use crate::stdlib::{ fmt, sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, - Arc, Weak, + Arc, }, }; @@ -142,6 +142,7 @@ use crate::stdlib::{ use crate::stdlib::{ cell::{Cell, RefCell, RefMut}, error, + sync::Weak, }; /// `Dispatch` trace data to a [`Subscriber`]. @@ -387,6 +388,7 @@ fn get_global() -> Option<&'static Dispatch> { } } +#[cfg(feature = "std")] pub(crate) struct Registrar(Weak); impl Dispatch { @@ -412,6 +414,7 @@ impl Dispatch { me } + #[cfg(feature = "std")] pub(crate) fn registrar(&self) -> Registrar { Registrar(Arc::downgrade(&self.subscriber)) } @@ -651,14 +654,8 @@ where } } +#[cfg(feature = "std")] impl Registrar { - pub(crate) fn try_register( - &self, - metadata: &'static Metadata<'static>, - ) -> Option { - self.0.upgrade().map(|s| s.register_callsite(metadata)) - } - pub(crate) fn upgrade(&self) -> Option { self.0.upgrade().map(|subscriber| Dispatch { subscriber }) } diff --git a/tracing-mock/src/subscriber.rs b/tracing-mock/src/subscriber.rs index 8fbffbb396..17e1a7ed73 100644 --- a/tracing-mock/src/subscriber.rs +++ b/tracing-mock/src/subscriber.rs @@ -198,7 +198,9 @@ where Interest::never() } } + fn max_level_hint(&self) -> Option { + println!("[{}] max_level_hint -> {:?}", self.name, self.max_level); self.max_level } diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 4f5e1deccf..558fd3332b 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -28,7 +28,7 @@ edition = "2018" rust-version = "1.49.0" [dependencies] -tracing-core = { path = "../tracing-core", version = "0.1.22", default-features = false } +tracing-core = { path = "../tracing-core", version = "0.1.26", default-features = false } log = { version = "0.4", optional = true } tracing-attributes = { path = "../tracing-attributes", version = "0.1.20", optional = true } cfg-if = "1.0.0" diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index f9c0669cb9..fdd12bd3eb 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -967,13 +967,8 @@ pub mod subscriber; #[doc(hidden)] pub mod __macro_support { pub use crate::callsite::Callsite; - use crate::stdlib::{ - fmt, - sync::atomic::{AtomicUsize, Ordering}, - }; use crate::{subscriber::Interest, Metadata}; pub use core::concat; - use tracing_core::Once; /// Callsite implementation used by macro-generated code. /// @@ -983,138 +978,70 @@ pub mod __macro_support { /// by the `tracing` macros, but it is not part of the stable versioned API. /// Breaking changes to this module may occur in small-numbered versions /// without warning. - pub struct MacroCallsite { - interest: AtomicUsize, - meta: &'static Metadata<'static>, - registration: Once, - } - - impl MacroCallsite { - /// Returns a new `MacroCallsite` with the specified `Metadata`. - /// - /// /!\ WARNING: This is *not* a stable API! /!\ - /// This method, and all code contained in the `__macro_support` module, is - /// a *private* API of `tracing`. It is exposed publicly because it is used - /// by the `tracing` macros, but it is not part of the stable versioned API. - /// Breaking changes to this module may occur in small-numbered versions - /// without warning. - pub const fn new(meta: &'static Metadata<'static>) -> Self { - Self { - interest: AtomicUsize::new(0xDEAD), - meta, - registration: Once::new(), - } - } - - /// Registers this callsite with the global callsite registry. - /// - /// If the callsite is already registered, this does nothing. - /// - /// /!\ WARNING: This is *not* a stable API! /!\ - /// This method, and all code contained in the `__macro_support` module, is - /// a *private* API of `tracing`. It is exposed publicly because it is used - /// by the `tracing` macros, but it is not part of the stable versioned API. - /// Breaking changes to this module may occur in small-numbered versions - /// without warning. - #[inline(never)] - // This only happens once (or if the cached interest value was corrupted). - #[cold] - pub fn register(&'static self) -> Interest { - self.registration - .call_once(|| crate::callsite::register(self)); - match self.interest.load(Ordering::Relaxed) { - 0 => Interest::never(), - 2 => Interest::always(), - _ => Interest::sometimes(), - } - } - - /// Returns the callsite's cached Interest, or registers it for the - /// first time if it has not yet been registered. - /// - /// /!\ WARNING: This is *not* a stable API! /!\ - /// This method, and all code contained in the `__macro_support` module, is - /// a *private* API of `tracing`. It is exposed publicly because it is used - /// by the `tracing` macros, but it is not part of the stable versioned API. - /// Breaking changes to this module may occur in small-numbered versions - /// without warning. - #[inline] - pub fn interest(&'static self) -> Interest { - match self.interest.load(Ordering::Relaxed) { - 0 => Interest::never(), - 1 => Interest::sometimes(), - 2 => Interest::always(), - _ => self.register(), - } - } - - pub fn is_enabled(&self, interest: Interest) -> bool { - interest.is_always() - || crate::dispatcher::get_default(|default| default.enabled(self.meta)) - } - - #[inline] - #[cfg(feature = "log")] - pub fn disabled_span(&self) -> crate::Span { - crate::Span::new_disabled(self.meta) - } - - #[inline] - #[cfg(not(feature = "log"))] - pub fn disabled_span(&self) -> crate::Span { - crate::Span::none() - } + pub use tracing_core::callsite::DefaultCallsite as MacroCallsite; - #[cfg(feature = "log")] - pub fn log( - &self, - logger: &'static dyn log::Log, - log_meta: log::Metadata<'_>, - values: &tracing_core::field::ValueSet<'_>, - ) { - let meta = self.metadata(); - logger.log( - &crate::log::Record::builder() - .file(meta.file()) - .module_path(meta.module_path()) - .line(meta.line()) - .metadata(log_meta) - .args(format_args!( - "{}", - crate::log::LogValueSet { - values, - is_first: true - } - )) - .build(), - ); - } + /// /!\ WARNING: This is *not* a stable API! /!\ + /// This function, and all code contained in the `__macro_support` module, is + /// a *private* API of `tracing`. It is exposed publicly because it is used + /// by the `tracing` macros, but it is not part of the stable versioned API. + /// Breaking changes to this module may occur in small-numbered versions + /// without warning. + pub fn __is_enabled(meta: &Metadata<'static>, interest: Interest) -> bool { + interest.is_always() || crate::dispatcher::get_default(|default| default.enabled(meta)) } - impl Callsite for MacroCallsite { - fn set_interest(&self, interest: Interest) { - let interest = match () { - _ if interest.is_never() => 0, - _ if interest.is_always() => 2, - _ => 1, - }; - self.interest.store(interest, Ordering::SeqCst); - } + /// /!\ WARNING: This is *not* a stable API! /!\ + /// This function, and all code contained in the `__macro_support` module, is + /// a *private* API of `tracing`. It is exposed publicly because it is used + /// by the `tracing` macros, but it is not part of the stable versioned API. + /// Breaking changes to this module may occur in small-numbered versions + /// without warning. + #[inline] + #[cfg(feature = "log")] + pub fn __disabled_span(meta: &'static Metadata<'static>) -> crate::Span { + crate::Span::new_disabled(meta) + } - #[inline(always)] - fn metadata(&self) -> &Metadata<'static> { - self.meta - } + /// /!\ WARNING: This is *not* a stable API! /!\ + /// This function, and all code contained in the `__macro_support` module, is + /// a *private* API of `tracing`. It is exposed publicly because it is used + /// by the `tracing` macros, but it is not part of the stable versioned API. + /// Breaking changes to this module may occur in small-numbered versions + /// without warning. + #[inline] + #[cfg(not(feature = "log"))] + pub fn __disabled_span(_: &'static Metadata<'static>) -> crate::Span { + crate::Span::none() } - impl fmt::Debug for MacroCallsite { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("MacroCallsite") - .field("interest", &self.interest) - .field("meta", &self.meta) - .field("registration", &self.registration) - .finish() - } + /// /!\ WARNING: This is *not* a stable API! /!\ + /// This function, and all code contained in the `__macro_support` module, is + /// a *private* API of `tracing`. It is exposed publicly because it is used + /// by the `tracing` macros, but it is not part of the stable versioned API. + /// Breaking changes to this module may occur in small-numbered versions + /// without warning. + #[cfg(feature = "log")] + pub fn __tracing_log( + meta: &Metadata<'static>, + logger: &'static dyn log::Log, + log_meta: log::Metadata<'_>, + values: &tracing_core::field::ValueSet<'_>, + ) { + logger.log( + &crate::log::Record::builder() + .file(meta.file()) + .module_path(meta.module_path()) + .line(meta.line()) + .metadata(log_meta) + .args(format_args!( + "{}", + crate::log::LogValueSet { + values, + is_first: true + } + )) + .build(), + ); } } diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index 825c0d8689..bca42933b3 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -24,7 +24,7 @@ macro_rules! span { (target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { { use $crate::__macro_support::Callsite as _; - static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + static CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! { name: $name, kind: $crate::metadata::Kind::SPAN, target: $target, @@ -34,7 +34,7 @@ macro_rules! span { let mut interest = $crate::subscriber::Interest::never(); if $crate::level_enabled!($lvl) && { interest = CALLSITE.interest(); !interest.is_never() } - && CALLSITE.is_enabled(interest) + && $crate::__macro_support::__is_enabled(CALLSITE.metadata(), interest) { let meta = CALLSITE.metadata(); // span with explicit parent @@ -44,7 +44,7 @@ macro_rules! span { &$crate::valueset!(meta.fields(), $($fields)*), ) } else { - let span = CALLSITE.disabled_span(); + let span = $crate::__macro_support::__disabled_span(CALLSITE.metadata()); $crate::if_log_enabled! { $lvl, { span.record_all(&$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)); }}; @@ -55,7 +55,7 @@ macro_rules! span { (target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => { { use $crate::__macro_support::Callsite as _; - static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + static CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! { name: $name, kind: $crate::metadata::Kind::SPAN, target: $target, @@ -65,7 +65,7 @@ macro_rules! span { let mut interest = $crate::subscriber::Interest::never(); if $crate::level_enabled!($lvl) && { interest = CALLSITE.interest(); !interest.is_never() } - && CALLSITE.is_enabled(interest) + && $crate::__macro_support::__is_enabled(CALLSITE.metadata(), interest) { let meta = CALLSITE.metadata(); // span with contextual parent @@ -74,7 +74,7 @@ macro_rules! span { &$crate::valueset!(meta.fields(), $($fields)*), ) } else { - let span = CALLSITE.disabled_span(); + let span = $crate::__macro_support::__disabled_span(CALLSITE.metadata()); $crate::if_log_enabled! { $lvl, { span.record_all(&$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)); }}; @@ -584,7 +584,7 @@ macro_rules! error_span { macro_rules! event { (target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({ use $crate::__macro_support::Callsite as _; - static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + static CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! { name: $crate::__macro_support::concat!( "event ", file!(), @@ -599,7 +599,7 @@ macro_rules! event { let enabled = $crate::level_enabled!($lvl) && { let interest = CALLSITE.interest(); - !interest.is_never() && CALLSITE.is_enabled(interest) + !interest.is_never() && $crate::__macro_support::__is_enabled(CALLSITE.metadata(), interest) }; if enabled { (|value_set: $crate::field::ValueSet| { @@ -641,7 +641,7 @@ macro_rules! event { ); (target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({ use $crate::__macro_support::Callsite as _; - static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + static CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! { name: $crate::__macro_support::concat!( "event ", file!(), @@ -655,7 +655,7 @@ macro_rules! event { }; let enabled = $crate::level_enabled!($lvl) && { let interest = CALLSITE.interest(); - !interest.is_never() && CALLSITE.is_enabled(interest) + !interest.is_never() && $crate::__macro_support::__is_enabled(CALLSITE.metadata(), interest) }; if enabled { (|value_set: $crate::field::ValueSet| { @@ -965,7 +965,7 @@ macro_rules! enabled { (kind: $kind:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({ if $crate::level_enabled!($lvl) { use $crate::__macro_support::Callsite as _; - static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { + static CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite2! { name: $crate::__macro_support::concat!( "enabled ", file!(), @@ -978,7 +978,7 @@ macro_rules! enabled { fields: $($fields)* }; let interest = CALLSITE.interest(); - if !interest.is_never() && CALLSITE.is_enabled(interest) { + if !interest.is_never() && $crate::__macro_support::__is_enabled(CALLSITE.metadata(), interest) { let meta = CALLSITE.metadata(); $crate::dispatcher::get_default(|current| current.enabled(meta)) } else { @@ -2096,7 +2096,6 @@ macro_rules! callsite { level: $lvl:expr, fields: $($fields:tt)* ) => {{ - use $crate::__macro_support::MacroCallsite; static META: $crate::Metadata<'static> = { $crate::metadata! { name: $name, @@ -2107,7 +2106,7 @@ macro_rules! callsite { kind: $kind, } }; - static CALLSITE: MacroCallsite = MacroCallsite::new(&META); + static CALLSITE: $crate::callsite::DefaultCallsite = $crate::callsite::DefaultCallsite::new(&META); CALLSITE.register(); &CALLSITE }}; @@ -2147,7 +2146,6 @@ macro_rules! callsite2 { level: $lvl:expr, fields: $($fields:tt)* ) => {{ - use $crate::__macro_support::MacroCallsite; static META: $crate::Metadata<'static> = { $crate::metadata! { name: $name, @@ -2158,7 +2156,7 @@ macro_rules! callsite2 { kind: $kind, } }; - MacroCallsite::new(&META) + $crate::callsite::DefaultCallsite::new(&META) }}; } @@ -2428,13 +2426,14 @@ macro_rules! __tracing_log { use $crate::log; let level = $crate::level_to_log!($level); if level <= log::max_level() { + let meta = $callsite.metadata(); let log_meta = log::Metadata::builder() .level(level) - .target(CALLSITE.metadata().target()) + .target(meta.target()) .build(); let logger = log::logger(); if logger.enabled(&log_meta) { - $callsite.log(logger, log_meta, $value_set) + $crate::__macro_support::__tracing_log(meta, logger, log_meta, $value_set) } } }} diff --git a/tracing/tests/register_callsite_deadlock.rs b/tracing/tests/register_callsite_deadlock.rs new file mode 100644 index 0000000000..e4c116c75f --- /dev/null +++ b/tracing/tests/register_callsite_deadlock.rs @@ -0,0 +1,47 @@ +use std::{sync::mpsc, thread, time::Duration}; +use tracing::{ + metadata::Metadata, + span, + subscriber::{self, Interest, Subscriber}, + Event, +}; + +#[test] +fn register_callsite_doesnt_deadlock() { + pub struct EvilSubscriber; + + impl Subscriber for EvilSubscriber { + fn register_callsite(&self, meta: &'static Metadata<'static>) -> Interest { + tracing::info!(?meta, "registered a callsite"); + Interest::always() + } + + fn enabled(&self, _: &Metadata<'_>) -> bool { + true + } + fn new_span(&self, _: &span::Attributes<'_>) -> span::Id { + span::Id::from_u64(1) + } + fn record(&self, _: &span::Id, _: &span::Record<'_>) {} + fn record_follows_from(&self, _: &span::Id, _: &span::Id) {} + fn event(&self, _: &Event<'_>) {} + fn enter(&self, _: &span::Id) {} + fn exit(&self, _: &span::Id) {} + } + + subscriber::set_global_default(EvilSubscriber).unwrap(); + + // spawn a thread, and assert it doesn't hang... + let (tx, didnt_hang) = mpsc::channel(); + let th = thread::spawn(move || { + tracing::info!("hello world!"); + tx.send(()).unwrap(); + }); + + didnt_hang + // Note: 60 seconds is *way* more than enough, but let's be generous in + // case of e.g. slow CI machines. + .recv_timeout(Duration::from_secs(60)) + .expect("the thread must not have hung!"); + th.join().expect("thread should join successfully"); +} From befdca0adf5b55b571f9fbcf30403c3028f8d66c Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 22 Apr 2022 16:19:45 -0700 Subject: [PATCH 003/142] core: document the `callsite` module (#2088) Currently, there isn't a lot of documentation explaining what callsites and registration are for, and how they work. There's some documentation explaining this stuff, but it's all on the `register_callsite` *method*, rather than in the `callsite` module itself. We should fix that. This branch adds new documentation to `tracing-core`'s `callsite` module. Hopefully this should shine some light on how this part of tracing works. Thanks to @JamesHallowell for fixing some typos! Signed-off-by: Eliza Weisman --- tracing-core/src/callsite.rs | 105 ++++++++++++++++++++++++++++++++- tracing-core/src/subscriber.rs | 14 ++++- 2 files changed, 116 insertions(+), 3 deletions(-) diff --git a/tracing-core/src/callsite.rs b/tracing-core/src/callsite.rs index 273fecec4e..55728821f6 100644 --- a/tracing-core/src/callsite.rs +++ b/tracing-core/src/callsite.rs @@ -1,5 +1,85 @@ //! Callsites represent the source locations from which spans or events //! originate. +//! +//! # What Are Callsites? +//! +//! Every span or event in `tracing` is associated with a [`Callsite`]. A +//! callsite is a small `static` value that is responsible for the following: +//! +//! * Storing the span or event's [`Metadata`], +//! * Uniquely [identifying](Identifier) the span or event definition, +//! * Caching the subscriber's [`Interest`][^1] in that span or event, to avoid +//! re-evaluating filters, +//! * Storing a [`Registration`] that allows the callsite to be part of a global +//! list of all callsites in the program. +//! +//! # Registering Callsites +//! +//! When a span or event is recorded for the first time, its callsite +//! [`register`]s itself with the global callsite registry. Registering a +//! callsite calls the [`Subscriber::register_callsite`][`register_callsite`] +//! method with that callsite's [`Metadata`] on every currently active +//! subscriber. This serves two primary purposes: informing subscribers of the +//! callsite's existence, and performing static filtering. +//! +//! ## Callsite Existence +//! +//! If a [`Subscriber`] implementation wishes to allocate storage for each +//! unique span/event location in the program, or pre-compute some value +//! that will be used to record that span or event in the future, it can +//! do so in its [`register_callsite`] method. +//! +//! ## Performing Static Filtering +//! +//! The [`register_callsite`] method returns an [`Interest`] value, +//! which indicates that the subscriber either [always] wishes to record +//! that span or event, [sometimes] wishes to record it based on a +//! dynamic filter evaluation, or [never] wishes to record it. +//! +//! When registering a new callsite, the [`Interest`]s returned by every +//! currently active subscriber are combined, and the result is stored at +//! each callsite. This way, when the span or event occurs in the +//! future, the cached [`Interest`] value can be checked efficiently +//! to determine if the span or event should be recorded, without +//! needing to perform expensive filtering (i.e. calling the +//! [`Subscriber::enabled`] method every time a span or event occurs). +//! +//! ### Rebuilding Cached Interest +//! +//! When a new [`Dispatch`] is created (i.e. a new subscriber becomes +//! active), any previously cached [`Interest`] values are re-evaluated +//! for all callsites in the program. This way, if the new subscriber +//! will enable a callsite that was not previously enabled, the +//! [`Interest`] in that callsite is updated. Similarly, when a +//! subscriber is dropped, the interest cache is also re-evaluated, so +//! that any callsites enabled only by that subscriber are disabled. +//! +//! In addition, the [`rebuild_interest_cache`] function in this module can be +//! used to manually invalidate all cached interest and re-register those +//! callsites. This function is useful in situations where a subscriber's +//! interest can change, but it does so relatively infrequently. The subscriber +//! may wish for its interest to be cached most of the time, and return +//! [`Interest::always`][always] or [`Interest::never`][never] in its +//! [`register_callsite`] method, so that its [`Subscriber::enabled`] method +//! doesn't need to be evaluated every time a span or event is recorded. +//! However, when the configuration changes, the subscriber can call +//! [`rebuild_interest_cache`] to re-evaluate the entire interest cache with its +//! new configuration. This is a relatively costly operation, but if the +//! configuration changes infrequently, it may be more efficient than calling +//! [`Subscriber::enabled`] frequently. +//! +//! [^1]: Returned by the [`Subscriber::register_callsite`][`register_callsite`] +//! method. +//! +//! [`Metadata`]: crate::metadata::Metadata +//! [`Interest`]: crate::subscriber::Interest +//! [`Subscriber`]: crate::subscriber::Subscriber +//! [`register_callsite`]: crate::subscriber::Subscriber::register_callsite +//! [`Subscriber::enabled`]: crate::subscriber::Subscriber::enabled +//! [always]: crate::subscriber::Interest::always +//! [sometimes]: crate::subscriber::Interest::sometimes +//! [never]: crate::subscriber::Interest::never +//! [`Dispatch`]: crate::dispatch::Dispatch use crate::stdlib::{ any::TypeId, fmt, @@ -23,10 +103,17 @@ use self::dispatchers::Dispatchers; /// /// These functions are only intended to be called by the callsite registry, which /// correctly handles determining the common interest between all subscribers. +/// +/// See the [module-level documentation](crate::callsite) for details on +/// callsites. pub trait Callsite: Sync { /// Sets the [`Interest`] for this callsite. /// + /// See the [documentation on callsite interest caching][cache-docs] for + /// details. + /// /// [`Interest`]: super::subscriber::Interest + /// [cache-docs]: crate::callsite#performing-static-filtering fn set_interest(&self, interest: Interest); /// Returns the [metadata] associated with the callsite. @@ -98,19 +185,29 @@ pub struct DefaultCallsite { /// implementation at runtime, then it **must** call this function after that /// value changes, in order for the change to be reflected. /// +/// See the [documentation on callsite interest caching][cache-docs] for +/// additional information on this function's usage. +/// /// [`max_level_hint`]: super::subscriber::Subscriber::max_level_hint /// [`Callsite`]: super::callsite::Callsite /// [`enabled`]: super::subscriber::Subscriber#tymethod.enabled /// [`Interest::sometimes()`]: super::subscriber::Interest::sometimes /// [`Subscriber`]: super::subscriber::Subscriber +/// [cache-docs]: crate::callsite#rebuilding-cached-interest pub fn rebuild_interest_cache() { CALLSITES.rebuild_interest(DISPATCHERS.rebuilder()); } -/// Register a new `Callsite` with the global registry. +/// Register a new [`Callsite`] with the global registry. /// /// This should be called once per callsite after the callsite has been /// constructed. +/// +/// See the [documentation on callsite registration][reg-docs] for details +/// on the global callsite registry. +/// +/// [`Callsite`]: crate::callsite::Callsite +/// [reg-docs]: crate::callsite#registering-callsites pub fn register(callsite: &'static dyn Callsite) { rebuild_callsite_interest(callsite, &DISPATCHERS.rebuilder()); @@ -177,6 +274,12 @@ impl DefaultCallsite { /// /// Other callsite implementations will generally ensure that /// callsites are not re-registered through another mechanism. + /// + /// See the [documentation on callsite registration][reg-docs] for details + /// on the global callsite registry. + /// + /// [`Callsite`]: crate::callsite::Callsite + /// [reg-docs]: crate::callsite#registering-callsites #[inline(never)] // This only happens once (or if the cached interest value was corrupted). #[cold] diff --git a/tracing-core/src/subscriber.rs b/tracing-core/src/subscriber.rs index 65d49a518d..501cce733d 100644 --- a/tracing-core/src/subscriber.rs +++ b/tracing-core/src/subscriber.rs @@ -56,6 +56,10 @@ use crate::stdlib::{ /// Additionally, subscribers which wish to perform a behaviour once for each /// callsite, such as allocating storage for data related to that callsite, /// can perform it in `register_callsite`. +/// +/// See also the [documentation on the callsite registry][cs-reg] for details +/// on [`register_callsite`]. +/// /// - [`clone_span`] is called every time a span ID is cloned, and [`try_close`] /// is called when a span ID is dropped. By default, these functions do /// nothing. However, they can be used to implement reference counting for @@ -70,10 +74,11 @@ use crate::stdlib::{ /// [`enabled`]: Subscriber::enabled /// [`clone_span`]: Subscriber::clone_span /// [`try_close`]: Subscriber::try_close +/// [cs-reg]: crate::callsite#registering-callsites pub trait Subscriber: 'static { // === Span registry methods ============================================== - /// Registers a new callsite with this subscriber, returning whether or not + /// Registers a new [callsite] with this subscriber, returning whether or not /// the subscriber is interested in being notified about the callsite. /// /// By default, this function assumes that the subscriber's [filter] @@ -127,6 +132,9 @@ pub trait Subscriber: 'static { /// return `Interest::Never`, as a new subscriber may be added that _is_ /// interested. /// + /// See the [documentation on the callsite registry][cs-reg] for more + /// details on how and when the `register_callsite` method is called. + /// /// # Notes /// This function may be called again when a new subscriber is created or /// when the registry is invalidated. @@ -135,10 +143,12 @@ pub trait Subscriber: 'static { /// _may_ still see spans and events originating from that callsite, if /// another subscriber expressed interest in it. /// - /// [filter]: Subscriber::enabled() + /// [callsite]: crate::callsite + /// [filter]: Self::enabled /// [metadata]: super::metadata::Metadata /// [`enabled`]: Subscriber::enabled() /// [`rebuild_interest_cache`]: super::callsite::rebuild_interest_cache + /// [cs-reg]: crate::callsite#registering-callsites fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { if self.enabled(metadata) { Interest::always() From 0d13e5b419986a6b9002eca95edc81f2cad5b72f Mon Sep 17 00:00:00 2001 From: Jarrod Davis Date: Mon, 25 Apr 2022 10:40:48 -0600 Subject: [PATCH 004/142] attributes: remove extra braces around `async` blocks (#2090) ## Motivation When using an `async` block (as an alternative to `async fn`, e.g. when implementing a trait), `#[instrument]` adds extra braces around the wrapped `async` block. This causes `rustc` to emit an `unused_braces` lint in some cases (usually for single-line `async` blocks, as far as I can tell). See #1831 for an example. ## Solution Since the `async` block extracted by `AsyncInfo::from_fn` already has braces around its statements, there's no need to wrap it with additional braces. This updates `gen_block` to remove those extra braces when generating the code providing the value of `__tracing_instrument_future`. - [x] add repros for `unused_braces` issue - [x] remove extra braces from async blocks Fixes #1831 --- tracing-attributes/src/expand.rs | 8 ++++---- tracing-attributes/tests/async_fn.rs | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index 3928441e1f..458b75f46c 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -217,7 +217,7 @@ fn gen_block( let mk_fut = match (err_event, ret_event) { (Some(err_event), Some(ret_event)) => quote_spanned!(block.span()=> async move { - match async move { #block }.await { + match async move #block.await { #[allow(clippy::unit_arg)] Ok(x) => { #ret_event; @@ -232,7 +232,7 @@ fn gen_block( ), (Some(err_event), None) => quote_spanned!(block.span()=> async move { - match async move { #block }.await { + match async move #block.await { #[allow(clippy::unit_arg)] Ok(x) => Ok(x), Err(e) => { @@ -244,13 +244,13 @@ fn gen_block( ), (None, Some(ret_event)) => quote_spanned!(block.span()=> async move { - let x = async move { #block }.await; + let x = async move #block.await; #ret_event; x } ), (None, None) => quote_spanned!(block.span()=> - async move { #block } + async move #block ), }; diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs index 0468c874c9..7e27fb5ce2 100644 --- a/tracing-attributes/tests/async_fn.rs +++ b/tracing-attributes/tests/async_fn.rs @@ -1,5 +1,6 @@ use tracing_mock::*; +use std::convert::Infallible; use std::{future::Future, pin::Pin, sync::Arc}; use tracing::subscriber::with_default; use tracing_attributes::instrument; @@ -51,6 +52,22 @@ async fn repro_1613_2() { // else } +// Reproduces https://github.com/tokio-rs/tracing/issues/1831 +#[instrument] +#[deny(unused_braces)] +fn repro_1831() -> Pin>> { + Box::pin(async move {}) +} + +// This replicates the pattern used to implement async trait methods on nightly using the +// `type_alias_impl_trait` feature +#[instrument(ret, err)] +#[deny(unused_braces)] +#[allow(clippy::manual_async_fn)] +fn repro_1831_2() -> impl Future> { + async { Ok(()) } +} + #[test] fn async_fn_only_enters_for_polls() { let (subscriber, handle) = subscriber::mock() From 4c0e30fb32bef93b423ec0fd94b0b0c36db5a611 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 25 Apr 2022 17:54:17 -0400 Subject: [PATCH 005/142] =?UTF-8?q?attributes:=20permit=20setting=20parent?= =?UTF-8?q?=20span=20via=20`#[instrument(parent=20=3D=20=E2=80=A6)]`=20(#2?= =?UTF-8?q?091)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR extends the `#[instrument]` attribute to accept an optionally `parent = …` argument that provides an explicit parent to the generated `Span`. ## Motivation This PR resolves #2021 and partially resolves #879. (It only partly resolves #879 in that it only provides a mechanism for specifying an explicit parent, but *not* for invoking `follows_from`.) ## Solution This PR follows the implementation strategy articulated by @hawkw: https://github.com/tokio-rs/tracing/issues/879#issuecomment-668168468. The user-provided value to the `parent` argument may be any expression, and this expression is provided directly to the invocation of [`span!`](https://tracing.rs/tracing/macro.span.html). --- tracing-attributes/src/attr.rs | 25 +++++++ tracing-attributes/src/expand.rs | 3 + tracing-attributes/src/lib.rs | 23 +++++++ tracing-attributes/tests/parents.rs | 102 ++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 tracing-attributes/tests/parents.rs diff --git a/tracing-attributes/src/attr.rs b/tracing-attributes/src/attr.rs index 2e2ef1502d..b74e88afd2 100644 --- a/tracing-attributes/src/attr.rs +++ b/tracing-attributes/src/attr.rs @@ -11,6 +11,7 @@ pub(crate) struct InstrumentArgs { level: Option, pub(crate) name: Option, target: Option, + pub(crate) parent: Option, pub(crate) skips: HashSet, pub(crate) skip_all: bool, pub(crate) fields: Option, @@ -123,6 +124,12 @@ impl Parse for InstrumentArgs { } let target = input.parse::>()?.value; args.target = Some(target); + } else if lookahead.peek(kw::parent) { + if args.target.is_some() { + return Err(input.error("expected only a single `parent` argument")); + } + let parent = input.parse::>()?; + args.parent = Some(parent.value); } else if lookahead.peek(kw::level) { if args.level.is_some() { return Err(input.error("expected only a single `level` argument")); @@ -193,6 +200,23 @@ impl Parse for StrArg { } } +struct ExprArg { + value: Expr, + _p: std::marker::PhantomData, +} + +impl Parse for ExprArg { + fn parse(input: ParseStream<'_>) -> syn::Result { + let _ = input.parse::()?; + let _ = input.parse::()?; + let value = input.parse()?; + Ok(Self { + value, + _p: std::marker::PhantomData, + }) + } +} + struct Skips(HashSet); impl Parse for Skips { @@ -374,6 +398,7 @@ mod kw { syn::custom_keyword!(skip_all); syn::custom_keyword!(level); syn::custom_keyword!(target); + syn::custom_keyword!(parent); syn::custom_keyword!(name); syn::custom_keyword!(err); syn::custom_keyword!(ret); diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index 458b75f46c..a629af18bc 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -135,6 +135,8 @@ fn gen_block( let target = args.target(); + let parent = args.parent.iter(); + // filter out skipped fields let quoted_fields: Vec<_> = param_names .iter() @@ -182,6 +184,7 @@ fn gen_block( quote!(tracing::span!( target: #target, + #(parent: #parent,)* #level, #span_name, #(#quoted_fields,)* diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 0c5624687e..749affb876 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -345,6 +345,29 @@ mod expand; /// // ... /// } /// ``` +/// Overriding the generated span's parent: +/// ``` +/// # use tracing_attributes::instrument; +/// #[instrument(parent = None)] +/// pub fn my_function() { +/// // ... +/// } +/// ``` +/// ``` +/// # use tracing_attributes::instrument; +/// // A struct which owns a span handle. +/// struct MyStruct +/// { +/// span: tracing::Span +/// } +/// +/// impl MyStruct +/// { +/// // Use the struct's `span` field as the parent span +/// #[instrument(parent = &self.span, skip(self))] +/// fn my_method(&self) {} +/// } +/// ``` /// /// To skip recording an argument, pass the argument's name to the `skip`: /// diff --git a/tracing-attributes/tests/parents.rs b/tracing-attributes/tests/parents.rs new file mode 100644 index 0000000000..7069b98ea5 --- /dev/null +++ b/tracing-attributes/tests/parents.rs @@ -0,0 +1,102 @@ +use tracing::{subscriber::with_default, Id, Level}; +use tracing_attributes::instrument; +use tracing_mock::*; + +#[instrument] +fn with_default_parent() {} + +#[instrument(parent = parent_span, skip(parent_span))] +fn with_explicit_parent

(parent_span: P) +where + P: Into>, +{ +} + +#[test] +fn default_parent_test() { + let contextual_parent = span::mock().named("contextual_parent"); + let child = span::mock().named("with_default_parent"); + + let (subscriber, handle) = subscriber::mock() + .new_span( + contextual_parent + .clone() + .with_contextual_parent(None) + .with_explicit_parent(None), + ) + .new_span( + child + .clone() + .with_contextual_parent(Some("contextual_parent")) + .with_explicit_parent(None), + ) + .enter(child.clone()) + .exit(child.clone()) + .enter(contextual_parent.clone()) + .new_span( + child + .clone() + .with_contextual_parent(Some("contextual_parent")) + .with_explicit_parent(None), + ) + .enter(child.clone()) + .exit(child) + .exit(contextual_parent) + .done() + .run_with_handle(); + + with_default(subscriber, || { + let contextual_parent = tracing::span!(Level::TRACE, "contextual_parent"); + + with_default_parent(); + + contextual_parent.in_scope(|| { + with_default_parent(); + }); + }); + + handle.assert_finished(); +} + +#[test] +fn explicit_parent_test() { + let contextual_parent = span::mock().named("contextual_parent"); + let explicit_parent = span::mock().named("explicit_parent"); + let child = span::mock().named("with_explicit_parent"); + + let (subscriber, handle) = subscriber::mock() + .new_span( + contextual_parent + .clone() + .with_contextual_parent(None) + .with_explicit_parent(None), + ) + .new_span( + explicit_parent + .with_contextual_parent(None) + .with_explicit_parent(None), + ) + .enter(contextual_parent.clone()) + .new_span( + child + .clone() + .with_contextual_parent(Some("contextual_parent")) + .with_explicit_parent(Some("explicit_parent")), + ) + .enter(child.clone()) + .exit(child) + .exit(contextual_parent) + .done() + .run_with_handle(); + + with_default(subscriber, || { + let contextual_parent = tracing::span!(Level::INFO, "contextual_parent"); + let explicit_parent = tracing::span!(Level::INFO, "explicit_parent"); + + contextual_parent.in_scope(|| { + with_explicit_parent(&explicit_parent); + }); + }); + + handle.assert_finished(); +} From 138a971829cc4e0a296fa075f5a3caa24f293cf6 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 26 Apr 2022 16:37:29 -0400 Subject: [PATCH 006/142] =?UTF-8?q?attributes:=20permit=20`#[instrument(fo?= =?UTF-8?q?llows=5Ffrom=20=3D=20=E2=80=A6)]`=20(#2093)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR extends the `#[instrument]` attribute to accept an optional `follows_from = …` argument that supplies any number of `Span::follows_from` relationships to the generated `Span`. ## Motivation This PR resolves #879. ## Solution This PR largely follows the implementation strategy articulated by @hawkw: https://github.com/tokio-rs/tracing/issues/879#issuecomment-668168468 In that comment, @hawkw suggests taking one of two approaches: 1. each `follows_from` relationship is supplied with a distinct `follows_from` argument 2. the `follows_from` argument is provided once, and its value is a **list** of indirect causes I take the second approach, since it is slightly more flexible: it allows for the number of indirect causes to vary at runtime. This addition is complemented by changes to `tracing-mock` to permit making `follows_from` assertions for testing purposes. --- tracing-attributes/src/attr.rs | 8 ++ tracing-attributes/src/expand.rs | 9 +++ tracing-attributes/src/lib.rs | 20 +++++ tracing-attributes/tests/follows_from.rs | 99 ++++++++++++++++++++++++ tracing-mock/src/subscriber.rs | 61 ++++++++++++++- 5 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 tracing-attributes/tests/follows_from.rs diff --git a/tracing-attributes/src/attr.rs b/tracing-attributes/src/attr.rs index b74e88afd2..ff875e1797 100644 --- a/tracing-attributes/src/attr.rs +++ b/tracing-attributes/src/attr.rs @@ -12,6 +12,7 @@ pub(crate) struct InstrumentArgs { pub(crate) name: Option, target: Option, pub(crate) parent: Option, + pub(crate) follows_from: Option, pub(crate) skips: HashSet, pub(crate) skip_all: bool, pub(crate) fields: Option, @@ -130,6 +131,12 @@ impl Parse for InstrumentArgs { } let parent = input.parse::>()?; args.parent = Some(parent.value); + } else if lookahead.peek(kw::follows_from) { + if args.target.is_some() { + return Err(input.error("expected only a single `follows_from` argument")); + } + let follows_from = input.parse::>()?; + args.follows_from = Some(follows_from.value); } else if lookahead.peek(kw::level) { if args.level.is_some() { return Err(input.error("expected only a single `level` argument")); @@ -399,6 +406,7 @@ mod kw { syn::custom_keyword!(level); syn::custom_keyword!(target); syn::custom_keyword!(parent); + syn::custom_keyword!(follows_from); syn::custom_keyword!(name); syn::custom_keyword!(err); syn::custom_keyword!(ret); diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index a629af18bc..b563d4bbfe 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -88,6 +88,13 @@ fn gen_block( let level = args.level(); + let follows_from = args.follows_from.iter(); + let follows_from = quote! { + #(for cause in #follows_from { + __tracing_attr_span.follows_from(cause); + })* + }; + // generate this inside a closure, so we can return early on errors. let span = (|| { // Pull out the arguments-to-be-skipped first, so we can filter results @@ -261,6 +268,7 @@ fn gen_block( let __tracing_attr_span = #span; let __tracing_instrument_future = #mk_fut; if !__tracing_attr_span.is_disabled() { + #follows_from tracing::Instrument::instrument( __tracing_instrument_future, __tracing_attr_span @@ -287,6 +295,7 @@ fn gen_block( let __tracing_attr_guard; if tracing::level_enabled!(#level) { __tracing_attr_span = #span; + #follows_from __tracing_attr_guard = __tracing_attr_span.enter(); } ); diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 749affb876..115da88460 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -368,6 +368,24 @@ mod expand; /// fn my_method(&self) {} /// } /// ``` +/// Specifying [`follows_from`] relationships: +/// ``` +/// # use tracing_attributes::instrument; +/// #[instrument(follows_from = causes)] +/// pub fn my_function(causes: &[tracing::Id]) { +/// // ... +/// } +/// ``` +/// Any expression of type `impl IntoIterator>>` +/// may be provided to `follows_from`; e.g.: +/// ``` +/// # use tracing_attributes::instrument; +/// #[instrument(follows_from = [cause])] +/// pub fn my_function(cause: &tracing::span::EnteredSpan) { +/// // ... +/// } +/// ``` +/// /// /// To skip recording an argument, pass the argument's name to the `skip`: /// @@ -524,6 +542,8 @@ mod expand; /// [`INFO`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.INFO /// [empty field]: https://docs.rs/tracing/latest/tracing/field/struct.Empty.html /// [field syntax]: https://docs.rs/tracing/latest/tracing/#recording-fields +/// [`follows_from`]: https://docs.rs/tracing/latest/tracing/struct.Span.html#method.follows_from +/// [`tracing`]: https://github.com/tokio-rs/tracing /// [`fmt::Debug`]: std::fmt::Debug #[proc_macro_attribute] pub fn instrument( diff --git a/tracing-attributes/tests/follows_from.rs b/tracing-attributes/tests/follows_from.rs new file mode 100644 index 0000000000..da0eec6357 --- /dev/null +++ b/tracing-attributes/tests/follows_from.rs @@ -0,0 +1,99 @@ +use tracing::{subscriber::with_default, Id, Level, Span}; +use tracing_attributes::instrument; +use tracing_mock::*; + +#[instrument(follows_from = causes, skip(causes))] +fn with_follows_from_sync(causes: impl IntoIterator>>) {} + +#[instrument(follows_from = causes, skip(causes))] +async fn with_follows_from_async(causes: impl IntoIterator>>) {} + +#[instrument(follows_from = [&Span::current()])] +fn follows_from_current() {} + +#[test] +fn follows_from_sync_test() { + let cause_a = span::mock().named("cause_a"); + let cause_b = span::mock().named("cause_b"); + let cause_c = span::mock().named("cause_c"); + let consequence = span::mock().named("with_follows_from_sync"); + + let (subscriber, handle) = subscriber::mock() + .new_span(cause_a.clone()) + .new_span(cause_b.clone()) + .new_span(cause_c.clone()) + .new_span(consequence.clone()) + .follows_from(consequence.clone(), cause_a) + .follows_from(consequence.clone(), cause_b) + .follows_from(consequence.clone(), cause_c) + .enter(consequence.clone()) + .exit(consequence) + .done() + .run_with_handle(); + + with_default(subscriber, || { + let cause_a = tracing::span!(Level::TRACE, "cause_a"); + let cause_b = tracing::span!(Level::TRACE, "cause_b"); + let cause_c = tracing::span!(Level::TRACE, "cause_c"); + + with_follows_from_sync(&[cause_a, cause_b, cause_c]) + }); + + handle.assert_finished(); +} + +#[test] +fn follows_from_async_test() { + let cause_a = span::mock().named("cause_a"); + let cause_b = span::mock().named("cause_b"); + let cause_c = span::mock().named("cause_c"); + let consequence = span::mock().named("with_follows_from_async"); + + let (subscriber, handle) = subscriber::mock() + .new_span(cause_a.clone()) + .new_span(cause_b.clone()) + .new_span(cause_c.clone()) + .new_span(consequence.clone()) + .follows_from(consequence.clone(), cause_a) + .follows_from(consequence.clone(), cause_b) + .follows_from(consequence.clone(), cause_c) + .enter(consequence.clone()) + .exit(consequence) + .done() + .run_with_handle(); + + with_default(subscriber, || { + block_on_future(async { + let cause_a = tracing::span!(Level::TRACE, "cause_a"); + let cause_b = tracing::span!(Level::TRACE, "cause_b"); + let cause_c = tracing::span!(Level::TRACE, "cause_c"); + + with_follows_from_async(&[cause_a, cause_b, cause_c]).await + }) + }); + + handle.assert_finished(); +} + +#[test] +fn follows_from_current_test() { + let cause = span::mock().named("cause"); + let consequence = span::mock().named("follows_from_current"); + + let (subscriber, handle) = subscriber::mock() + .new_span(cause.clone()) + .enter(cause.clone()) + .new_span(consequence.clone()) + .follows_from(consequence.clone(), cause.clone()) + .enter(consequence.clone()) + .exit(consequence) + .exit(cause) + .done() + .run_with_handle(); + + with_default(subscriber, || { + tracing::span!(Level::TRACE, "cause").in_scope(follows_from_current) + }); + + handle.assert_finished(); +} diff --git a/tracing-mock/src/subscriber.rs b/tracing-mock/src/subscriber.rs index 17e1a7ed73..32f27ada00 100644 --- a/tracing-mock/src/subscriber.rs +++ b/tracing-mock/src/subscriber.rs @@ -23,6 +23,10 @@ use tracing::{ #[derive(Debug, Eq, PartialEq)] pub enum Expect { Event(MockEvent), + FollowsFrom { + consequence: MockSpan, + cause: MockSpan, + }, Enter(MockSpan), Exit(MockSpan), CloneSpan(MockSpan), @@ -35,6 +39,7 @@ pub enum Expect { struct SpanState { name: &'static str, refs: usize, + meta: &'static Metadata<'static>, } struct Running) -> bool> { @@ -97,6 +102,12 @@ where self } + pub fn follows_from(mut self, consequence: MockSpan, cause: MockSpan) -> Self { + self.expected + .push_back(Expect::FollowsFrom { consequence, cause }); + self + } + pub fn event(mut self, event: MockEvent) -> Self { self.expected.push_back(Expect::Event(event)); self @@ -250,8 +261,37 @@ where } } - fn record_follows_from(&self, _span: &Id, _follows: &Id) { - // TODO: it should be possible to expect spans to follow from other spans + fn record_follows_from(&self, consequence_id: &Id, cause_id: &Id) { + let spans = self.spans.lock().unwrap(); + if let Some(consequence_span) = spans.get(consequence_id) { + if let Some(cause_span) = spans.get(cause_id) { + println!( + "[{}] record_follows_from: {} (id={:?}) follows {} (id={:?})", + self.name, consequence_span.name, consequence_id, cause_span.name, cause_id, + ); + match self.expected.lock().unwrap().pop_front() { + None => {} + Some(Expect::FollowsFrom { + consequence: ref expected_consequence, + cause: ref expected_cause, + }) => { + if let Some(name) = expected_consequence.name() { + assert_eq!(name, consequence_span.name); + } + if let Some(name) = expected_cause.name() { + assert_eq!(name, cause_span.name); + } + } + Some(ex) => ex.bad( + &self.name, + format_args!( + "consequence {:?} followed cause {:?}", + consequence_span.name, cause_span.name + ), + ), + } + } + }; } fn new_span(&self, span: &Attributes<'_>) -> Id { @@ -284,6 +324,7 @@ where id.clone(), SpanState { name: meta.name(), + meta, refs: 1, }, ); @@ -415,6 +456,18 @@ where } } } + + fn current_span(&self) -> tracing_core::span::Current { + let stack = self.current.lock().unwrap(); + match stack.last() { + Some(id) => { + let spans = self.spans.lock().unwrap(); + let state = spans.get(id).expect("state for current span"); + tracing_core::span::Current::new(id.clone(), state.meta) + } + None => tracing_core::span::Current::none(), + } + } } impl MockHandle { @@ -442,6 +495,10 @@ impl Expect { "\n[{}] expected event {}\n[{}] but instead {}", name, e, name, what, ), + Expect::FollowsFrom { consequence, cause } => panic!( + "\n[{}] expected consequence {} to follow cause {} but instead {}", + name, consequence, cause, what, + ), Expect::Enter(e) => panic!( "\n[{}] expected to enter {}\n[{}] but instead {}", name, e, name, what, From 45c0a1099aa786006fa79277a43b629d100c46f2 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 26 Apr 2022 17:04:10 -0700 Subject: [PATCH 007/142] attributes: prepare to release v0.1.21 (#2097) # 0.1.21 (April 26, 2022) This release adds support for setting explicit parent and follows-from spans in the `#[instrument]` attribute. ### Added - `#[instrument(follows_from = ...)]` argument for setting one or more follows-from span ([#2093]) - `#[instrument(parent = ...)]` argument for overriding the generated span's parent ([#2091]) ### Fixed - Extra braces around `async` blocks in expanded code (causes a Clippy warning) ([#2090]) - Broken documentation links ([#2068], [#2077]) Thanks to @jarrodldavis, @ben0x539, and new contributor @jswrenn for contributing to this release! [#2093]: https://github.com/tokio-rs/tracing/pull/2093 [#2091]: https://github.com/tokio-rs/tracing/pull/2091 [#2090]: https://github.com/tokio-rs/tracing/pull/2090 [#2077]: https://github.com/tokio-rs/tracing/pull/2077 [#2068]: https://github.com/tokio-rs/tracing/pull/2068 --- tracing-attributes/CHANGELOG.md | 28 ++++++++++++++++++++++++++++ tracing-attributes/Cargo.toml | 2 +- tracing-attributes/README.md | 4 ++-- tracing-attributes/src/lib.rs | 4 ++-- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/tracing-attributes/CHANGELOG.md b/tracing-attributes/CHANGELOG.md index 26c2a23281..a52baa7d2b 100644 --- a/tracing-attributes/CHANGELOG.md +++ b/tracing-attributes/CHANGELOG.md @@ -1,3 +1,31 @@ +# 0.1.21 (April 26, 2022) + +This release adds support for setting explicit parent and follows-from spans +in the `#[instrument]` attribute. + +### Added + +- `#[instrument(follows_from = ...)]` argument for setting one or more + follows-from span ([#2093]) +- `#[instrument(parent = ...)]` argument for overriding the generated span's + parent ([#2091]) + +### Fixed + +- Extra braces around `async` blocks in expanded code (causes a Clippy warning) + ([#2090]) +- Broken documentation links ([#2068], [#2077]) + +Thanks to @jarrodldavis, @ben0x539, and new contributor @jswrenn for +contributing to this release! + + +[#2093]: https://github.com/tokio-rs/tracing/pull/2093 +[#2091]: https://github.com/tokio-rs/tracing/pull/2091 +[#2090]: https://github.com/tokio-rs/tracing/pull/2090 +[#2077]: https://github.com/tokio-rs/tracing/pull/2077 +[#2068]: https://github.com/tokio-rs/tracing/pull/2068 + # 0.1.20 (March 8, 2022) ### Fixed diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index bbff156169..8bc4a96069 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -8,7 +8,7 @@ name = "tracing-attributes" # - README.md # - Update CHANGELOG.md. # - Create "v0.1.x" git tag. -version = "0.1.20" +version = "0.1.21" authors = [ "Tokio Contributors ", "Eliza Weisman ", diff --git a/tracing-attributes/README.md b/tracing-attributes/README.md index 61dab5d426..7d8009ac3c 100644 --- a/tracing-attributes/README.md +++ b/tracing-attributes/README.md @@ -18,7 +18,7 @@ Macro attributes for application-level tracing. [crates-badge]: https://img.shields.io/crates/v/tracing-attributes.svg [crates-url]: https://crates.io/crates/tracing-attributes [docs-badge]: https://docs.rs/tracing-attributes/badge.svg -[docs-url]: https://docs.rs/tracing-attributes/0.1.20 +[docs-url]: https://docs.rs/tracing-attributes/0.1.21 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_attributes [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg @@ -47,7 +47,7 @@ First, add this to your `Cargo.toml`: ```toml [dependencies] -tracing-attributes = "0.1.20" +tracing-attributes = "0.1.21" ``` diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 115da88460..a1d2d09d21 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -16,7 +16,7 @@ //! //! ```toml //! [dependencies] -//! tracing-attributes = "0.1.20" +//! tracing-attributes = "0.1.21" //! ``` //! //! The [`#[instrument]`][instrument] attribute can now be added to a function @@ -52,7 +52,7 @@ //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! -#![doc(html_root_url = "https://docs.rs/tracing-attributes/0.1.20")] +#![doc(html_root_url = "https://docs.rs/tracing-attributes/0.1.21")] #![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/" From e7a686cbb9ab5c72c40107af29c4bce18ec44ea5 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 6 May 2022 11:32:24 -0700 Subject: [PATCH 008/142] opentelemetry: Fix example in README.md (#2109) The example code for `tracing-opentelemetry` in the README.md file was based on older versions of `tracing`, `tracing-opentelemetry` and `opentelemetry`. It did not compile with the latest versions of these crates. ## Motivation Developers should be able to copy/paste example code and it should work. ## Solution Following some type renames allows this code to work. This is @lilymara-onesignal's branch #1414 but rebased to point at the `v0.1.x` branch as the base. I think we will also want to update the use of the otel APIs on `master`, but `master` currently uses the correct `tracing` type names _for v0.2.x_. Co-authored-by: Lily Mara Closes #1414 --- tracing-opentelemetry/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tracing-opentelemetry/README.md b/tracing-opentelemetry/README.md index 09d57f6702..9dcc115f68 100644 --- a/tracing-opentelemetry/README.md +++ b/tracing-opentelemetry/README.md @@ -33,10 +33,10 @@ Utilities for adding [OpenTelemetry] interoperability to [`tracing`]. ## Overview [`tracing`] is a framework for instrumenting Rust programs to collect -structured, event-based diagnostic information. This crate provides a layer -that connects spans from multiple systems into a trace and emits them to -[OpenTelemetry]-compatible distributed tracing systems for processing and -visualization. +structured, event-based diagnostic information. This crate provides a +subscriber that connects spans from multiple systems into a trace and +emits them to [OpenTelemetry]-compatible distributed tracing systems +for processing and visualization. The crate provides the following types: @@ -59,14 +59,14 @@ The crate provides the following types: ### Basic Usage ```rust -use opentelemetry::exporter::trace::stdout; +use opentelemetry::sdk::export::trace::stdout; use tracing::{error, span}; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::Registry; fn main() { // Install a new OpenTelemetry trace pipeline - let (tracer, _uninstall) = stdout::new_pipeline().install(); + let tracer = stdout::new_pipeline().install_simple(); // Create a tracing layer with the configured tracer let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); From db738eccb771283519d9ccc37b1342006fb35c44 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 11 May 2022 14:41:55 -0700 Subject: [PATCH 009/142] attributes: fix incorrect default level in docs (#2119) This corrects contradictory information in the documentation --- in one section, we state that the default level is `INFO` (which is correct), but in the examples, we say it's `TRACE`, which is wrong. This commit fixes that. Fixes #2117 Signed-off-by: Eliza Weisman --- tracing-attributes/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index a1d2d09d21..48deea2785 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -93,7 +93,7 @@ mod expand; /// Instruments a function to create and enter a `tracing` [span] every time /// the function is called. /// -/// Unless overriden, a span with `info` level will be generated. +/// Unless overriden, a span with the [`INFO`] [level] will be generated. /// The generated span's name will be the name of the function. /// By default, all arguments to the function are included as fields on the /// span. Arguments that are `tracing` [primitive types] implementing the @@ -420,7 +420,7 @@ mod expand; /// } /// ``` /// The return value event will have the same level as the span generated by `#[instrument]`. -/// By default, this will be `TRACE`, but if the level is overridden, the event will be at the same +/// By default, this will be [`INFO`], but if the level is overridden, the event will be at the same /// level. /// /// **Note**: if the function returns a `Result`, `ret` will record returned values if and From f90b4dc97a269453c0949293e2cc6ad7c408fdfd Mon Sep 17 00:00:00 2001 From: David Barsky Date: Mon, 16 May 2022 13:24:43 -0400 Subject: [PATCH 010/142] chore: backport #2126 (#2127) --- tracing-subscriber/src/fmt/fmt_layer.rs | 4 ++-- tracing-subscriber/src/fmt/format/mod.rs | 4 ++-- tracing-subscriber/src/fmt/mod.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tracing-subscriber/src/fmt/fmt_layer.rs b/tracing-subscriber/src/fmt/fmt_layer.rs index 5e94fd0c71..21992e7809 100644 --- a/tracing-subscriber/src/fmt/fmt_layer.rs +++ b/tracing-subscriber/src/fmt/fmt_layer.rs @@ -442,7 +442,7 @@ where } /// Sets whether or not the [thread ID] of the current thread is displayed - /// when formatting events + /// when formatting events. /// /// [thread ID]: std::thread::ThreadId pub fn with_thread_ids(self, display_thread_ids: bool) -> Layer, W> { @@ -453,7 +453,7 @@ where } /// Sets whether or not the [name] of the current thread is displayed - /// when formatting events + /// when formatting events. /// /// [name]: std::thread#naming-threads pub fn with_thread_names( diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs index 0090b571d7..ec79ac1402 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -748,7 +748,7 @@ impl Format { } /// Sets whether or not the [thread ID] of the current thread is displayed - /// when formatting events + /// when formatting events. /// /// [thread ID]: std::thread::ThreadId pub fn with_thread_ids(self, display_thread_id: bool) -> Format { @@ -759,7 +759,7 @@ impl Format { } /// Sets whether or not the [name] of the current thread is displayed - /// when formatting events + /// when formatting events. /// /// [name]: std::thread#naming-threads pub fn with_thread_names(self, display_thread_name: bool) -> Format { diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index eb2e7a252f..8470cc1c0f 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -666,7 +666,7 @@ where } /// Sets whether or not the [name] of the current thread is displayed - /// when formatting events + /// when formatting events. /// /// [name]: std::thread#naming-threads pub fn with_thread_names( @@ -680,7 +680,7 @@ where } /// Sets whether or not the [thread ID] of the current thread is displayed - /// when formatting events + /// when formatting events. /// /// [thread ID]: std::thread::ThreadId pub fn with_thread_ids( From c744b2f8bebfd7b53a454096aa7044cf12df4802 Mon Sep 17 00:00:00 2001 From: Lily Mara <54288692+lilymara-onesignal@users.noreply.github.com> Date: Fri, 20 May 2022 12:58:12 -0700 Subject: [PATCH 011/142] opentelemetry: record error source chain (#2122) Previously error values were recorded using their `Debug` representation. They are now reported with their `Display` implementation. This is more in line with current best practices for error handling. This is a change in how error values are recorded in the opentelemetry adapter. For a given field `x` that contains an error type, record an additional dynamic field `x.chain` that contains an array of all errors in the source chain. This allows users to determine where a high-level error originated. ## Motivation Rust's `Error` type includes a `source` method which allows library authors to compose errors on top of one another in a way that indicates how errors originated down to the OS level. Surfacing this information to the users will allow them to determine why errors are occurring with more detail. ## Solution Walking the `source` chain until there are no errors left and storing this at a new field called `{fieldname}.chain` allows operators to determine the causal chain of complex errors. --- tracing-opentelemetry/src/layer.rs | 99 ++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs index 0c21caab2f..bdaf1c32fa 100644 --- a/tracing-opentelemetry/src/layer.rs +++ b/tracing-opentelemetry/src/layer.rs @@ -4,6 +4,7 @@ use opentelemetry::{ Context as OtelContext, Key, KeyValue, Value, }; use std::any::TypeId; +use std::borrow::Cow; use std::fmt; use std::marker; use std::time::{Instant, SystemTime}; @@ -253,6 +254,27 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> { _ => self.record(Key::new(field.name()).string(format!("{:?}", value))), } } + + /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s + /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_error( + &mut self, + field: &tracing_core::Field, + value: &(dyn std::error::Error + 'static), + ) { + let mut chain = Vec::new(); + let mut next_err = value.source(); + + while let Some(err) = next_err { + chain.push(Cow::Owned(err.to_string())); + next_err = err.source(); + } + + self.record(Key::new(field.name()).string(value.to_string())); + self.record(Key::new(format!("{}.chain", field.name())).array(chain)); + } } impl OpenTelemetryLayer @@ -684,6 +706,7 @@ mod tests { use crate::OtelData; use opentelemetry::trace::{noop, SpanKind, TraceFlags}; use std::borrow::Cow; + use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::time::SystemTime; use tracing_subscriber::prelude::*; @@ -896,4 +919,80 @@ mod tests { assert!(keys.contains(&"idle_ns")); assert!(keys.contains(&"busy_ns")); } + + #[test] + fn records_error_fields() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); + + use std::error::Error; + use std::fmt::Display; + + #[derive(Debug)] + struct DynError { + msg: &'static str, + source: Option>, + } + + impl Display for DynError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.msg) + } + } + impl Error for DynError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match &self.source { + Some(source) => Some(source), + None => None, + } + } + } + + let err = DynError { + msg: "user error", + source: Some(Box::new(DynError { + msg: "intermediate error", + source: Some(Box::new(DynError { + msg: "base error", + source: None, + })), + })), + }; + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!( + "request", + error = &err as &(dyn std::error::Error + 'static) + ); + }); + + let attributes = tracer + .0 + .lock() + .unwrap() + .as_ref() + .unwrap() + .builder + .attributes + .as_ref() + .unwrap() + .clone(); + + let key_values = attributes + .into_iter() + .map(|attr| (attr.key.as_str().to_owned(), attr.value)) + .collect::>(); + + assert_eq!(key_values["error"].as_str(), "user error"); + assert_eq!( + key_values["error.chain"], + Value::Array( + vec![ + Cow::Borrowed("intermediate error"), + Cow::Borrowed("base error") + ] + .into() + ) + ); + } } From a29fbc6a36865c0c791cd807dbaf16c56027df82 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Mon, 6 Jun 2022 11:11:05 -0700 Subject: [PATCH 012/142] tracing-core: add documentation on `DefaultCallsite` (#2149) This adds a small note on the use of `DefaultCallsite` to the `tracing-core` docs. I wanted to take care of this before publishing a new `tracing-core` release. --- tracing-core/src/callsite.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tracing-core/src/callsite.rs b/tracing-core/src/callsite.rs index 55728821f6..573125a89a 100644 --- a/tracing-core/src/callsite.rs +++ b/tracing-core/src/callsite.rs @@ -68,6 +68,22 @@ //! configuration changes infrequently, it may be more efficient than calling //! [`Subscriber::enabled`] frequently. //! +//! # Implementing Callsites +//! +//! In most cases, instrumenting code using `tracing` should *not* require +//! implementing the [`Callsite`] trait directly. When using the [`tracing` +//! crate's macros][macros] or the [`#[instrument]` attribute][instrument], a +//! `Callsite` is automatically generated. +//! +//! However, code which provides alternative forms of `tracing` instrumentation +//! may need to interact with the callsite system directly. If +//! instrumentation-side code needs to produce a `Callsite` to emit spans or +//! events, the [`DefaultCallsite`] struct provided in this module is a +//! ready-made `Callsite` implementation that is suitable for most uses. When +//! possible, the use of `DefaultCallsite` should be preferred over implementing +//! [`Callsite`] for user types, as `DefaultCallsite` may benefit from +//! additional performance optimizations. +//! //! [^1]: Returned by the [`Subscriber::register_callsite`][`register_callsite`] //! method. //! @@ -80,6 +96,8 @@ //! [sometimes]: crate::subscriber::Interest::sometimes //! [never]: crate::subscriber::Interest::never //! [`Dispatch`]: crate::dispatch::Dispatch +//! [macros]: https://docs.rs/tracing/latest/tracing/#macros +//! [instrument]: https://docs.rs/tracing/latest/tracing/attr.instrument.html use crate::stdlib::{ any::TypeId, fmt, From 6c9bc9b49d9ea627a43da7e9f05e5328c44c2baf Mon Sep 17 00:00:00 2001 From: hubertbudzynski <44027774+hubertbudzynski@users.noreply.github.com> Date: Wed, 27 Apr 2022 18:04:46 +0200 Subject: [PATCH 013/142] opentelemetry: fix event source locations (#2099) Fixes: #2094 ## Motivation Properly attach event's source locations. ## Solution Append the locations to events' attributes instead of span's attributes. --- tracing-opentelemetry/src/layer.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs index bdaf1c32fa..722d7ce804 100644 --- a/tracing-opentelemetry/src/layer.rs +++ b/tracing-opentelemetry/src/layer.rs @@ -602,8 +602,6 @@ where } if self.event_location { - let builder_attrs = builder.attributes.get_or_insert(Vec::new()); - #[cfg(not(feature = "tracing-log"))] let normalized_meta: Option> = None; let (file, module) = match &normalized_meta { @@ -618,13 +616,19 @@ where }; if let Some(file) = file { - builder_attrs.push(KeyValue::new("code.filepath", file)); + otel_event + .attributes + .push(KeyValue::new("code.filepath", file)); } if let Some(module) = module { - builder_attrs.push(KeyValue::new("code.namespace", module)); + otel_event + .attributes + .push(KeyValue::new("code.namespace", module)); } if let Some(line) = meta.line() { - builder_attrs.push(KeyValue::new("code.lineno", line as i64)); + otel_event + .attributes + .push(KeyValue::new("code.lineno", line as i64)); } } From 1fbf90a2d454a0593184d8d57e0b3a3950f27e7b Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 5 May 2022 20:54:44 +0200 Subject: [PATCH 014/142] subscriber: don't enable `tracing-core` features by default (#2107) This attempts to address #2106 by disabling the `tracing-core` crate's default features in `tracing-subscriber`'s dependency. Now, `tracing-core`'s optional "alloc" feature is only enabled when "tracing-subscriber/alloc" is enabled. Closes #2106 --- tracing-subscriber/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index b28f498615..9109efc9ce 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -38,7 +38,7 @@ valuable = ["tracing-core/valuable", "valuable_crate", "valuable-serde", "tracin local-time = ["time/local-offset"] [dependencies] -tracing-core = { path = "../tracing-core", version = "0.1.22" } +tracing-core = { path = "../tracing-core", version = "0.1.22", default-features = false } # only required by the filter feature tracing = { optional = true, path = "../tracing", version = "0.1", default-features = false } From 586da9bc81a14021b1543e6cb4fa752576f3340e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 May 2022 13:48:08 -0700 Subject: [PATCH 015/142] chore(deps): update tokio-test requirement from 0.2.0 to 0.3.0 (#1379) Updates the requirements on [tokio-test](https://github.com/tokio-rs/tokio) to permit the latest version. - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-0.2.0...tokio-0.3.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Eliza Weisman --- tracing-attributes/Cargo.toml | 2 +- tracing-futures/Cargo.toml | 2 +- tracing-mock/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 8bc4a96069..85e51f6ffe 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -46,7 +46,7 @@ quote = "1" [dev-dependencies] tracing = { path = "../tracing", version = "0.1" } tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] } -tokio-test = { version = "0.2.0" } +tokio-test = { version = "0.3.0" } tracing-core = { path = "../tracing-core", version = "0.1"} async-trait = "0.1.44" diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml index 08afaa36bd..6347b97449 100644 --- a/tracing-futures/Cargo.toml +++ b/tracing-futures/Cargo.toml @@ -36,7 +36,7 @@ tokio = { version = "0.1", optional = true } [dev-dependencies] tokio = "0.1.22" -tokio-test = "0.2" +tokio-test = "0.3" tracing-core = { path = "../tracing-core", version = "0.1.2" } tracing-mock = { path = "../tracing-mock" } diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml index c54adac51d..3b74dced26 100644 --- a/tracing-mock/Cargo.toml +++ b/tracing-mock/Cargo.toml @@ -20,7 +20,7 @@ publish = false [dependencies] tracing = { path = "../tracing", version = "0.1", default-features = false } tracing-core = { path = "../tracing-core", version = "0.1", default-features = false } -tokio-test = { version = "0.2.0", optional = true } +tokio-test = { version = "0.3.0", optional = true } [package.metadata.docs.rs] all-features = true From e8f77541bd11abeea7682e73ddaed703198d6dc1 Mon Sep 17 00:00:00 2001 From: Max Davitt Date: Mon, 9 May 2022 17:58:53 -0400 Subject: [PATCH 016/142] journald: disable default features of tracing-subscriber (#1476) ## Motivation Closes #1465. ## Solution I'm just disabling the default features of `tracing-journald`'s dependency on `tracing-subscriber`. The original issue talked about the crate's dependencies more broadly but considering that the standard library is already depended upon I didn't think it made sense to change the `tracing-core` dependency's features (which are just `no_std` support). --- tracing-journald/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracing-journald/Cargo.toml b/tracing-journald/Cargo.toml index c5cea152d5..e4f5eca8a1 100644 --- a/tracing-journald/Cargo.toml +++ b/tracing-journald/Cargo.toml @@ -18,9 +18,9 @@ rust-version = "1.49.0" [dependencies] libc = "0.2.107" tracing-core = { path = "../tracing-core", version = "0.1.10" } -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3" } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry"] } [dev-dependencies] serde_json = "1.0.68" serde = { version = "1.0.130", features = ["derive"] } -tracing = { path = "../tracing", version = "0.1" } \ No newline at end of file +tracing = { path = "../tracing", version = "0.1" } From 5de809258e1c8c58e22ec7d84c11e975a17b2af4 Mon Sep 17 00:00:00 2001 From: George Malayil Philip Date: Tue, 10 May 2022 03:53:57 +0530 Subject: [PATCH 017/142] Add additional information to references of my_crate in env_filter docs. (#1088) Co-authored-by: Bryan Garza <1396101+bryangarza@users.noreply.github.com> --- tracing-subscriber/src/filter/env/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tracing-subscriber/src/filter/env/mod.rs b/tracing-subscriber/src/filter/env/mod.rs index dae20a6a2e..81a9ae2bde 100644 --- a/tracing-subscriber/src/filter/env/mod.rs +++ b/tracing-subscriber/src/filter/env/mod.rs @@ -449,6 +449,11 @@ impl EnvFilter { /// # Ok(()) /// # } /// ``` + /// In the above example, substitute `my_crate`, `module`, etc. with the + /// name your target crate/module is imported with. This might be + /// different from the package name in Cargo.toml (`-` is replaced by `_`). + /// Example, if the package name in your Cargo.toml is `MY-FANCY-LIB`, then + /// the corresponding Rust identifier would be `MY_FANCY_LIB`: pub fn add_directive(mut self, mut directive: Directive) -> Self { if !self.regex { directive.deregexify(); From 5a205d14290d03ad86c2711e5124410e0ddd6bb6 Mon Sep 17 00:00:00 2001 From: zz <48124374+zzhengzhuo@users.noreply.github.com> Date: Tue, 10 May 2022 08:13:29 +0800 Subject: [PATCH 018/142] fix opentelemetry example (#2110) Co-authored-by: Bryan Garza <1396101+bryangarza@users.noreply.github.com> --- examples/examples/opentelemetry.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/examples/examples/opentelemetry.rs b/examples/examples/opentelemetry.rs index 5c4b4c484a..fbc7469080 100644 --- a/examples/examples/opentelemetry.rs +++ b/examples/examples/opentelemetry.rs @@ -1,3 +1,4 @@ +use opentelemetry::global; use std::{error::Error, thread, time::Duration}; use tracing::{span, trace, warn}; use tracing_attributes::instrument; @@ -26,16 +27,20 @@ fn main() -> Result<(), Box> { .with(opentelemetry) .try_init()?; - let root = span!(tracing::Level::INFO, "app_start", work_units = 2); - let _enter = root.enter(); + { + let root = span!(tracing::Level::INFO, "app_start", work_units = 2); + let _enter = root.enter(); - let work_result = expensive_work(); + let work_result = expensive_work(); - span!(tracing::Level::INFO, "faster_work") - .in_scope(|| thread::sleep(Duration::from_millis(10))); + span!(tracing::Level::INFO, "faster_work") + .in_scope(|| thread::sleep(Duration::from_millis(10))); - warn!("About to exit!"); - trace!("status: {}", work_result); + warn!("About to exit!"); + trace!("status: {}", work_result); + } + + global::shutdown_tracer_provider(); Ok(()) } From 0e8767d9b212f056625407683b515afb9dae40f7 Mon Sep 17 00:00:00 2001 From: Tyson Nottingham Date: Wed, 11 May 2022 13:55:06 -0700 Subject: [PATCH 019/142] error: add missing backtick to `prelude` docs (#2120) --- tracing-error/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-error/src/lib.rs b/tracing-error/src/lib.rs index 88f5ae228d..b94205ceeb 100644 --- a/tracing-error/src/lib.rs +++ b/tracing-error/src/lib.rs @@ -221,7 +221,7 @@ pub use self::layer::ErrorLayer; pub mod prelude { //! The `tracing-error` prelude. //! - //! This brings into scope the `InstrumentError, `InstrumentResult`, and `ExtractSpanTrace` + //! This brings into scope the `InstrumentError`, `InstrumentResult`, and `ExtractSpanTrace` //! extension traits. These traits allow attaching `SpanTrace`s to errors and //! subsequently retrieving them from `dyn Error` trait objects. From 00fcb71e28cc89b7173bdf90939c839cf6b0a179 Mon Sep 17 00:00:00 2001 From: Devin Date: Fri, 20 May 2022 11:54:23 -0700 Subject: [PATCH 020/142] opentelemetry: enforce event_location for span tags (#2124) The `with_event_location(false)` method will now properly omit `code.*` tags from spans. ## Motivation Recently, I attempted to remove the `code.*` tags from opentelemetry tracing spans utilizing the [`with_event_location`] of OpenTelemetrySubscriber. This did not work as expected because the [`on_new_span`] doesn't account for the `event_location` boolean similar to how [`on_event`] does. ## Solution The change presented will expand the use of the `location` field to check before adding the `code.*` KeyValue attributes in `on_new_span`. In addition, `with_event_location` was renamed to `with_location`, as it now controls both span *and* event locations, and the `with_event_location` method has been deprecated. ## Testing Additional unit tests are included in [tracing-opentelemetry/src/subscriber.rs] to cover both boolean cases of `with_location`. [`with_event_location`]: https://github.com/tokio-rs/tracing/blob/master/tracing-opentelemetry/src/subscriber.rs#L343 [`on_new_span`]: https://github.com/tokio-rs/tracing/blob/master/tracing-opentelemetry/src/subscriber.rs#L448-L460 [`on_event`]: https://github.com/tokio-rs/tracing/blob/master/tracing-opentelemetry/src/subscriber.rs#L582 [tracing-opentelemetry/src/subscriber.rs]: https://github.com/tokio-rs/tracing/pull/2124/files#diff-69011e8b23dffcbe19b9b72e5ac54330a7871f424a90700ed7f5c5686daf431bR911-R975) --- tracing-opentelemetry/src/layer.rs | 102 +++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 14 deletions(-) diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs index 722d7ce804..9f2da0b601 100644 --- a/tracing-opentelemetry/src/layer.rs +++ b/tracing-opentelemetry/src/layer.rs @@ -28,7 +28,7 @@ const SPAN_STATUS_MESSAGE_FIELD: &str = "otel.status_message"; /// [tracing]: https://github.com/tokio-rs/tracing pub struct OpenTelemetryLayer { tracer: T, - event_location: bool, + location: bool, tracked_inactivity: bool, get_context: WithContext, _registry: marker::PhantomData, @@ -312,7 +312,7 @@ where pub fn new(tracer: T) -> Self { OpenTelemetryLayer { tracer, - event_location: true, + location: true, tracked_inactivity: true, get_context: WithContext(Self::get_context), _registry: marker::PhantomData, @@ -351,20 +351,32 @@ where { OpenTelemetryLayer { tracer, - event_location: self.event_location, + location: self.location, tracked_inactivity: self.tracked_inactivity, get_context: WithContext(OpenTelemetryLayer::::get_context), _registry: self._registry, } } + /// Sets whether or not span and event metadata should include detailed + /// location information, such as the file, module and line number. + /// + /// By default, locations are enabled. + pub fn with_location(self, location: bool) -> Self { + Self { location, ..self } + } + /// Sets whether or not event span's metadata should include detailed location /// information, such as the file, module and line number. /// /// By default, event locations are enabled. + #[deprecated( + since = "0.17.3", + note = "renamed to `OpenTelemetrySubscriber::with_location`" + )] pub fn with_event_location(self, event_location: bool) -> Self { Self { - event_location, + location: event_location, ..self } } @@ -467,18 +479,20 @@ where .attributes .get_or_insert(Vec::with_capacity(attrs.fields().len() + 3)); - let meta = attrs.metadata(); + if self.location { + let meta = attrs.metadata(); - if let Some(filename) = meta.file() { - builder_attrs.push(KeyValue::new("code.filepath", filename)); - } + if let Some(filename) = meta.file() { + builder_attrs.push(KeyValue::new("code.filepath", filename)); + } - if let Some(module) = meta.module_path() { - builder_attrs.push(KeyValue::new("code.namespace", module)); - } + if let Some(module) = meta.module_path() { + builder_attrs.push(KeyValue::new("code.namespace", module)); + } - if let Some(line) = meta.line() { - builder_attrs.push(KeyValue::new("code.lineno", line as i64)); + if let Some(line) = meta.line() { + builder_attrs.push(KeyValue::new("code.lineno", line as i64)); + } } attrs.record(&mut SpanAttributeVisitor(&mut builder)); @@ -601,7 +615,7 @@ where builder.status_code = Some(otel::StatusCode::Error); } - if self.event_location { + if self.location { #[cfg(not(feature = "tracing-log"))] let normalized_meta: Option> = None; let (file, module) = match &normalized_meta { @@ -999,4 +1013,64 @@ mod tests { ) ); } + + #[test] + fn includes_span_location() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry() + .with(layer().with_tracer(tracer.clone()).with_location(true)); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request"); + }); + + let attributes = tracer + .0 + .lock() + .unwrap() + .as_ref() + .unwrap() + .builder + .attributes + .as_ref() + .unwrap() + .clone(); + let keys = attributes + .iter() + .map(|attr| attr.key.as_str()) + .collect::>(); + assert!(keys.contains(&"code.filepath")); + assert!(keys.contains(&"code.namespace")); + assert!(keys.contains(&"code.lineno")); + } + + #[test] + fn excludes_span_location() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry() + .with(layer().with_tracer(tracer.clone()).with_location(false)); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request"); + }); + + let attributes = tracer + .0 + .lock() + .unwrap() + .as_ref() + .unwrap() + .builder + .attributes + .as_ref() + .unwrap() + .clone(); + let keys = attributes + .iter() + .map(|attr| attr.key.as_str()) + .collect::>(); + assert!(!keys.contains(&"code.filepath")); + assert!(!keys.contains(&"code.namespace")); + assert!(!keys.contains(&"code.lineno")); + } } From ebdafbd49832b3bec2a5b6d59df7af355d275d22 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Mon, 23 May 2022 09:05:51 -0700 Subject: [PATCH 021/142] appender: add initial set of benches (#2128) * appender: add initial set of benches This patch adds blocking and nonblocking benchmarks. This code is from an old PR (#703) that was never merged, and now ported to TOT so that it compiles. Co-authored-by: Zeki Sherif <9832640+zekisherif@users.noreply.github.com> * switch to no-op writers in benchmarks * fix macro resolution issue Co-authored-by: Zeki Sherif <9832640+zekisherif@users.noreply.github.com> Co-authored-by: David Barsky --- tracing-appender/Cargo.toml | 6 ++ tracing-appender/benches/bench.rs | 134 ++++++++++++++++++++++++++++++ tracing/src/macros.rs | 7 +- 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 tracing-appender/benches/bench.rs diff --git a/tracing-appender/Cargo.toml b/tracing-appender/Cargo.toml index c57932b503..2d02b38e95 100644 --- a/tracing-appender/Cargo.toml +++ b/tracing-appender/Cargo.toml @@ -32,6 +32,12 @@ default-features = false features = ["fmt", "std"] [dev-dependencies] + +criterion = { version = "0.3", default_features = false } tracing = { path = "../tracing", version = "0.1" } time = { version = "0.3", default-features = false, features = ["formatting", "parsing"] } tempfile = "3" + +[[bench]] +name = "bench" +harness = false \ No newline at end of file diff --git a/tracing-appender/benches/bench.rs b/tracing-appender/benches/bench.rs new file mode 100644 index 0000000000..e8ea4d75a6 --- /dev/null +++ b/tracing-appender/benches/bench.rs @@ -0,0 +1,134 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use std::{ + thread::{self, JoinHandle}, + time::Instant, +}; +use tracing::{event, Level}; +use tracing_appender::non_blocking; +use tracing_subscriber::fmt::MakeWriter; + +// a no-op writer is used in order to measure the overhead incurred by +// tracing-subscriber. +#[derive(Clone)] +struct NoOpWriter; + +impl NoOpWriter { + fn new() -> NoOpWriter { + NoOpWriter + } +} + +impl<'a> MakeWriter<'a> for NoOpWriter { + type Writer = NoOpWriter; + + fn make_writer(&self) -> Self::Writer { + self.clone() + } +} + +impl std::io::Write for NoOpWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + Ok(buf.len()) + } + + fn flush(&mut self) -> std::io::Result<()> { + Ok(()) + } +} + +fn synchronous_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("synchronous"); + group.bench_function("single_thread", |b| { + let subscriber = tracing_subscriber::fmt().with_writer(NoOpWriter::new()); + tracing::subscriber::with_default(subscriber.finish(), || { + b.iter(|| event!(Level::INFO, "event")) + }); + }); + + group.bench_function("multiple_writers", |b| { + b.iter_custom(|iters| { + let mut handles: Vec> = Vec::new(); + + let start = Instant::now(); + + let make_writer = NoOpWriter::new(); + let cloned_make_writer = make_writer.clone(); + + handles.push(thread::spawn(move || { + let subscriber = tracing_subscriber::fmt().with_writer(make_writer); + tracing::subscriber::with_default(subscriber.finish(), || { + for _ in 0..iters { + event!(Level::INFO, "event"); + } + }); + })); + + handles.push(thread::spawn(move || { + let subscriber = tracing_subscriber::fmt().with_writer(cloned_make_writer); + tracing::subscriber::with_default(subscriber.finish(), || { + for _ in 0..iters { + event!(Level::INFO, "event"); + } + }); + })); + + for handle in handles { + let _ = handle.join(); + } + + start.elapsed() + }); + }); +} + +fn non_blocking_benchmark(c: &mut Criterion) { + let mut group = c.benchmark_group("non_blocking"); + + group.bench_function("single_thread", |b| { + let (non_blocking, _guard) = non_blocking(NoOpWriter::new()); + let subscriber = tracing_subscriber::fmt().with_writer(non_blocking); + + tracing::subscriber::with_default(subscriber.finish(), || { + b.iter(|| event!(Level::INFO, "event")) + }); + }); + + group.bench_function("multiple_writers", |b| { + b.iter_custom(|iters| { + let (non_blocking, _guard) = non_blocking(NoOpWriter::new()); + + let mut handles: Vec> = Vec::new(); + + let start = Instant::now(); + + let cloned_make_writer = non_blocking.clone(); + + handles.push(thread::spawn(move || { + let subscriber = tracing_subscriber::fmt().with_writer(non_blocking); + tracing::subscriber::with_default(subscriber.finish(), || { + for _ in 0..iters { + event!(Level::INFO, "event"); + } + }); + })); + + handles.push(thread::spawn(move || { + let subscriber = tracing_subscriber::fmt().with_writer(cloned_make_writer); + tracing::subscriber::with_default(subscriber.finish(), || { + for _ in 0..iters { + event!(Level::INFO, "event"); + } + }); + })); + + for handle in handles { + let _ = handle.join(); + } + + start.elapsed() + }); + }); +} + +criterion_group!(benches, synchronous_benchmark, non_blocking_benchmark); +criterion_main!(benches); diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index bca42933b3..b134af6e81 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -832,6 +832,8 @@ macro_rules! event { /// } /// ``` /// +/// [`enabled!`]: crate::enabled +/// [`span_enabled!`]: crate::span_enabled #[macro_export] macro_rules! event_enabled { ($($rest:tt)*)=> ( @@ -864,6 +866,8 @@ macro_rules! event_enabled { /// } /// ``` /// +/// [`enabled!`]: crate::enabled +/// [`span_enabled!`]: crate::span_enabled #[macro_export] macro_rules! span_enabled { ($($rest:tt)*)=> ( @@ -959,7 +963,8 @@ macro_rules! span_enabled { /// [`Metadata`]: crate::Metadata /// [`is_event`]: crate::Metadata::is_event /// [`is_span`]: crate::Metadata::is_span -/// +/// [`enabled!`]: crate::enabled +/// [`span_enabled!`]: crate::span_enabled #[macro_export] macro_rules! enabled { (kind: $kind:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({ From bfdf8dc246f88e02e666d1eb4cd64edc2e20a259 Mon Sep 17 00:00:00 2001 From: jamen marz Date: Wed, 25 May 2022 10:31:06 -0600 Subject: [PATCH 022/142] docs: remove incorrect MSRV note (#2137) `tracing`'s lib.rs incorrectly states its MSRV; this commit removes it. --- tracing/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index fdd12bd3eb..65a4ebc951 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -119,8 +119,6 @@ //! tracing = "0.1" //! ``` //! -//! *Compiler support: [requires `rustc` 1.42+][msrv]* -//! //! ## Recording Spans and Events //! //! Spans and events are recorded using macros. From bc5ef8208df827b04b6c7055f783ec04010a01ef Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 25 May 2022 12:18:39 -0700 Subject: [PATCH 023/142] opentelemetry: add support for thread names/ids (#2134) OpenTelemetry has [semantic conventions][1] for reporting thread names and IDs on spans. This branch adds support for recording thread names and IDs according to these conventions. [1]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes Signed-off-by: Eliza Weisman --- tracing-opentelemetry/Cargo.toml | 1 + tracing-opentelemetry/src/layer.rs | 231 +++++++++++++++++++---------- 2 files changed, 150 insertions(+), 82 deletions(-) diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml index 788db1eb26..9e98b68f33 100644 --- a/tracing-opentelemetry/Cargo.toml +++ b/tracing-opentelemetry/Cargo.toml @@ -28,6 +28,7 @@ tracing = { path = "../tracing", version = "0.1", default-features = false, feat tracing-core = { path = "../tracing-core", version = "0.1" } tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry", "std"] } tracing-log = { path = "../tracing-log", version = "0.1", default-features = false, optional = true } +once_cell = "1" [dev-dependencies] async-trait = "0.1" diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs index 9f2da0b601..2c4749a1c1 100644 --- a/tracing-opentelemetry/src/layer.rs +++ b/tracing-opentelemetry/src/layer.rs @@ -1,4 +1,5 @@ use crate::{OtelData, PreSampledTracer}; +use once_cell::unsync; use opentelemetry::{ trace::{self as otel, noop, TraceContextExt}, Context as OtelContext, Key, KeyValue, Value, @@ -7,6 +8,7 @@ use std::any::TypeId; use std::borrow::Cow; use std::fmt; use std::marker; +use std::thread; use std::time::{Instant, SystemTime}; use tracing_core::span::{self, Attributes, Id, Record}; use tracing_core::{field, Event, Subscriber}; @@ -30,6 +32,7 @@ pub struct OpenTelemetryLayer { tracer: T, location: bool, tracked_inactivity: bool, + with_threads: bool, get_context: WithContext, _registry: marker::PhantomData, } @@ -314,6 +317,7 @@ where tracer, location: true, tracked_inactivity: true, + with_threads: true, get_context: WithContext(Self::get_context), _registry: marker::PhantomData, } @@ -353,23 +357,34 @@ where tracer, location: self.location, tracked_inactivity: self.tracked_inactivity, + with_threads: self.with_threads, get_context: WithContext(OpenTelemetryLayer::::get_context), _registry: self._registry, } } - /// Sets whether or not span and event metadata should include detailed - /// location information, such as the file, module and line number. + /// Sets whether or not span and event metadata should include OpenTelemetry + /// attributes with location information, such as the file, module and line number. + /// + /// These attributes follow the [OpenTelemetry semantic conventions for + /// source locations][conv]. /// /// By default, locations are enabled. + /// + /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes pub fn with_location(self, location: bool) -> Self { Self { location, ..self } } - /// Sets whether or not event span's metadata should include detailed location - /// information, such as the file, module and line number. + /// Sets whether or not span and event metadata should include OpenTelemetry + /// attributes with location information, such as the file, module and line number. + /// + /// These attributes follow the [OpenTelemetry semantic conventions for + /// source locations][conv]. + /// + /// By default, locations are enabled. /// - /// By default, event locations are enabled. + /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes #[deprecated( since = "0.17.3", note = "renamed to `OpenTelemetrySubscriber::with_location`" @@ -391,6 +406,20 @@ where } } + /// Sets whether or not spans record additional attributes for the thread + /// name and thread ID of the thread they were created on, following the + /// [OpenTelemetry semantic conventions for threads][conv]. + /// + /// By default, thread attributes are enabled. + /// + /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#general-thread-attributes + pub fn with_threads(self, threads: bool) -> Self { + Self { + with_threads: threads, + ..self + } + } + /// Retrieve the parent OpenTelemetry [`Context`] from the current tracing /// [`span`] through the [`Registry`]. This [`Context`] links spans to their /// parent for proper hierarchical visualization. @@ -443,6 +472,30 @@ where f(builder, &layer.tracer); } } + + fn extra_span_attrs(&self) -> usize { + let mut extra_attrs = 0; + if self.location { + extra_attrs += 3; + } + if self.with_threads { + extra_attrs += 2; + } + extra_attrs + } +} + +thread_local! { + static THREAD_ID: unsync::Lazy = unsync::Lazy::new(|| { + // OpenTelemetry's semantic conventions require the thread ID to be + // recorded as an integer, but `std::thread::ThreadId` does not expose + // the integer value on stable, so we have to convert it to a `usize` by + // parsing it. Since this requires allocating a `String`, store it in a + // thread local so we only have to do this once. + // TODO(eliza): once `std::thread::ThreadId::as_u64` is stabilized + // (https://github.com/rust-lang/rust/issues/67939), just use that. + thread_id_integer(thread::current().id()) + }); } impl Layer for OpenTelemetryLayer @@ -475,9 +528,9 @@ where builder.trace_id = Some(self.tracer.new_trace_id()); } - let builder_attrs = builder - .attributes - .get_or_insert(Vec::with_capacity(attrs.fields().len() + 3)); + let builder_attrs = builder.attributes.get_or_insert(Vec::with_capacity( + attrs.fields().len() + self.extra_span_attrs(), + )); if self.location { let meta = attrs.metadata(); @@ -495,6 +548,17 @@ where } } + if self.with_threads { + THREAD_ID.with(|id| builder_attrs.push(KeyValue::new("thread.id", **id as i64))); + if let Some(name) = std::thread::current().name() { + // TODO(eliza): it's a bummer that we have to allocate here, but + // we can't easily get the string as a `static`. it would be + // nice if `opentelemetry` could also take `Arc`s as + // `String` values... + builder_attrs.push(KeyValue::new("thread.name", name.to_owned())); + } + } + attrs.record(&mut SpanAttributeVisitor(&mut builder)); extensions.insert(OtelData { builder, parent_cx }); } @@ -718,15 +782,27 @@ impl Timings { } } +fn thread_id_integer(id: thread::ThreadId) -> u64 { + let thread_id = format!("{:?}", id); + thread_id + .trim_start_matches("ThreadId(") + .trim_end_matches(')') + .parse::() + .expect("thread ID should parse as an integer") +} + #[cfg(test)] mod tests { use super::*; use crate::OtelData; use opentelemetry::trace::{noop, SpanKind, TraceFlags}; - use std::borrow::Cow; - use std::collections::HashMap; - use std::sync::{Arc, Mutex}; - use std::time::SystemTime; + use std::{ + borrow::Cow, + collections::HashMap, + sync::{Arc, Mutex}, + thread, + time::SystemTime, + }; use tracing_subscriber::prelude::*; #[derive(Debug, Clone)] @@ -770,6 +846,14 @@ mod tests { } } + impl TestTracer { + fn with_data(&self, f: impl FnOnce(&OtelData) -> T) -> T { + let lock = self.0.lock().unwrap(); + let data = lock.as_ref().expect("no span data has been recorded yet"); + f(data) + } + } + #[derive(Debug, Clone)] struct TestSpan(otel::SpanContext); impl otel::Span for TestSpan { @@ -820,15 +904,7 @@ mod tests { tracing::debug_span!("request", otel.kind = %SpanKind::Server); }); - let recorded_kind = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .span_kind - .clone(); + let recorded_kind = tracer.with_data(|data| data.builder.span_kind.clone()); assert_eq!(recorded_kind, Some(otel::SpanKind::Server)) } @@ -840,14 +916,8 @@ mod tests { tracing::subscriber::with_default(subscriber, || { tracing::debug_span!("request", otel.status_code = ?otel::StatusCode::Ok); }); - let recorded_status_code = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .status_code; + + let recorded_status_code = tracer.with_data(|data| data.builder.status_code); assert_eq!(recorded_status_code, Some(otel::StatusCode::Ok)) } @@ -862,16 +932,7 @@ mod tests { tracing::debug_span!("request", otel.status_message = message); }); - let recorded_status_message = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .status_message - .clone(); - + let recorded_status_message = tracer.with_data(|data| data.builder.status_message.clone()); assert_eq!(recorded_status_message, Some(message.into())) } @@ -893,16 +954,8 @@ mod tests { tracing::debug_span!("request", otel.kind = %SpanKind::Server); }); - let recorded_trace_id = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .parent_cx - .span() - .span_context() - .trace_id(); + let recorded_trace_id = + tracer.with_data(|data| data.parent_cx.span().span_context().trace_id()); assert_eq!(recorded_trace_id, trace_id) } @@ -919,17 +972,7 @@ mod tests { tracing::debug_span!("request"); }); - let attributes = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .attributes - .as_ref() - .unwrap() - .clone(); + let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); let keys = attributes .iter() .map(|attr| attr.key.as_str()) @@ -1024,17 +1067,7 @@ mod tests { tracing::debug_span!("request"); }); - let attributes = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .attributes - .as_ref() - .unwrap() - .clone(); + let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); let keys = attributes .iter() .map(|attr| attr.key.as_str()) @@ -1054,17 +1087,7 @@ mod tests { tracing::debug_span!("request"); }); - let attributes = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .attributes - .as_ref() - .unwrap() - .clone(); + let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); let keys = attributes .iter() .map(|attr| attr.key.as_str()) @@ -1073,4 +1096,48 @@ mod tests { assert!(!keys.contains(&"code.namespace")); assert!(!keys.contains(&"code.lineno")); } + + #[test] + fn includes_thread() { + let thread = thread::current(); + let expected_name = thread + .name() + .map(|name| Value::String(Cow::Owned(name.to_owned()))); + let expected_id = Value::I64(thread_id_integer(thread.id()) as i64); + + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry() + .with(layer().with_tracer(tracer.clone()).with_threads(true)); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request"); + }); + + let attributes = tracer + .with_data(|data| data.builder.attributes.as_ref().unwrap().clone()) + .drain(..) + .map(|keyval| (keyval.key.as_str().to_string(), keyval.value)) + .collect::>(); + assert_eq!(attributes.get("thread.name"), expected_name.as_ref()); + assert_eq!(attributes.get("thread.id"), Some(&expected_id)); + } + + #[test] + fn excludes_thread() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry() + .with(layer().with_tracer(tracer.clone()).with_threads(false)); + + tracing::subscriber::with_default(subscriber, || { + tracing::debug_span!("request"); + }); + + let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); + let keys = attributes + .iter() + .map(|attr| attr.key.as_str()) + .collect::>(); + assert!(!keys.contains(&"thread.name")); + assert!(!keys.contains(&"thread.id")); + } } From 0130df7ca2f20b535c603b8c61b563e35df77258 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 31 May 2022 13:52:19 -0700 Subject: [PATCH 024/142] chore(deps): update parking_lot requirement from >= 0.9.0, <= 0.12 to >= 0.9.0, <= 0.13 (#2143) Updates the requirements on [parking_lot](https://github.com/Amanieu/parking_lot) to permit the latest version. - [Release notes](https://github.com/Amanieu/parking_lot/releases) - [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md) - [Commits](https://github.com/Amanieu/parking_lot/compare/0.12.0...0.12.1) --- updated-dependencies: - dependency-name: parking_lot dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tracing-subscriber/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 9109efc9ce..8f36c8f7eb 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -58,7 +58,7 @@ serde = { version = "1.0", optional = true } tracing-serde = { path = "../tracing-serde", version = "0.1.3", optional = true } # opt-in deps -parking_lot = { version = "0.12", optional = true } +parking_lot = { version = "0.13", optional = true } # registry sharded-slab = { version = "0.1.0", optional = true } From 29b265dd9c02f5435900521b5e55652578b21e3e Mon Sep 17 00:00:00 2001 From: James Liu Date: Mon, 6 Jun 2022 10:32:41 -0700 Subject: [PATCH 025/142] core, subscriber: migrate from `lazy_static` to `once_cell` (#2147) Replace `lazy_static` with `once_cell`. Fixes #2146. ## Motivation `lazy_static!`, while a declarative macro, is a macro nonetheless. It can add quite a bit of additional compilation time cost. `once_cell::sync::Lazy` does the same thing with generics, and can be used more flexibly (i.e. non-static lazily initialized values), and has been proposed to be added to `std` (see linked issue). I'm trying to reduce the compile time and dependency tree complexity of a dependent project: [bevy](https://bevyengine.org), which is using tracing. `lazy_static` and `once_cell` are both in our dependency tree and both end up doing the same thing. ## Solution Migrate to `once_cell`. --- examples/Cargo.toml | 2 +- examples/examples/hyper-echo.rs | 8 --- tracing-core/Cargo.toml | 4 +- tracing-core/src/callsite.rs | 11 ++- tracing-core/src/lib.rs | 4 -- tracing-flame/Cargo.toml | 3 +- tracing-flame/src/lib.rs | 6 +- tracing-log/Cargo.toml | 4 +- tracing-log/src/interest_cache.rs | 16 ++--- tracing-log/src/lib.rs | 14 ++-- tracing-subscriber/Cargo.toml | 6 +- .../src/filter/env/directive.rs | 70 +++++++++---------- 12 files changed, 66 insertions(+), 82 deletions(-) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 14d2ee34de..1f829940a0 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -32,7 +32,7 @@ futures = "0.3" tokio = { version = "1.1", features = ["full"] } # env-logger example -env_logger = "0.7" +env_logger = "0.9" # tower examples tower = { version = "0.4.4", features = ["full"] } diff --git a/examples/examples/hyper-echo.rs b/examples/examples/hyper-echo.rs index 3404a8d5e9..f0396d19cf 100644 --- a/examples/examples/hyper-echo.rs +++ b/examples/examples/hyper-echo.rs @@ -92,17 +92,9 @@ async fn echo(req: Request) -> Result, hyper::Error> { #[tokio::main] async fn main() -> Result<(), Box> { - use tracing_log::env_logger::BuilderExt; - let subscriber = tracing_subscriber::fmt() .with_max_level(Level::TRACE) .finish(); - let mut builder = env_logger::Builder::new(); - builder - .filter(Some("hyper_echo"), log::LevelFilter::Off) - .filter(Some("hyper"), log::LevelFilter::Trace) - .emit_traces() // from `tracing_log::env_logger::BuilderExt` - .try_init()?; tracing::subscriber::set_global_default(subscriber)?; let local_addr: std::net::SocketAddr = ([127, 0, 0, 1], 3000).into(); diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml index 77075846e7..cedffe7bfc 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -28,13 +28,13 @@ rust-version = "1.49.0" [features] default = ["std", "valuable/std"] -std = ["lazy_static"] +std = ["once_cell"] [badges] maintenance = { status = "actively-developed" } [dependencies] -lazy_static = { version = "1.0.2", optional = true } +once_cell = { version = "1.12", optional = true } [target.'cfg(tracing_unstable)'.dependencies] valuable = { version = "0.1.0", optional = true, default_features = false } diff --git a/tracing-core/src/callsite.rs b/tracing-core/src/callsite.rs index 573125a89a..87d084647b 100644 --- a/tracing-core/src/callsite.rs +++ b/tracing-core/src/callsite.rs @@ -253,6 +253,11 @@ static CALLSITES: Callsites = Callsites { static DISPATCHERS: Dispatchers = Dispatchers::new(); +#[cfg(feature = "std")] +static LOCKED_CALLSITES: once_cell::sync::Lazy>> = + once_cell::sync::Lazy::new(Default::default); + +#[cfg(not(feature = "std"))] crate::lazy_static! { static ref LOCKED_CALLSITES: Mutex> = Mutex::new(Vec::new()); } @@ -510,6 +515,7 @@ mod private { #[cfg(feature = "std")] mod dispatchers { use crate::dispatcher; + use once_cell::sync::Lazy; use std::sync::{ atomic::{AtomicBool, Ordering}, RwLock, RwLockReadGuard, RwLockWriteGuard, @@ -519,9 +525,8 @@ mod dispatchers { has_just_one: AtomicBool, } - crate::lazy_static! { - static ref LOCKED_DISPATCHERS: RwLock> = RwLock::new(Vec::new()); - } + static LOCKED_DISPATCHERS: Lazy>> = + Lazy::new(Default::default); pub(super) enum Rebuilder<'a> { JustOne, diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs index 7424a6cb3f..eceef7be22 100644 --- a/tracing-core/src/lib.rs +++ b/tracing-core/src/lib.rs @@ -254,10 +254,6 @@ macro_rules! metadata { }; } -// when `std` is enabled, use the `lazy_static` crate from crates.io -#[cfg(feature = "std")] -pub(crate) use lazy_static::lazy_static; - // Facade module: `no_std` uses spinlocks, `std` uses the mutexes in the standard library #[cfg(not(feature = "std"))] #[macro_use] diff --git a/tracing-flame/Cargo.toml b/tracing-flame/Cargo.toml index 5c6b9c0ba5..43ee62c922 100644 --- a/tracing-flame/Cargo.toml +++ b/tracing-flame/Cargo.toml @@ -28,7 +28,8 @@ smallvec = ["tracing-subscriber/smallvec"] [dependencies] tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry", "fmt"] } tracing = { path = "../tracing", version = "0.1.12", default-features = false, features = ["std"] } -lazy_static = "1.3.0" +once_cell = "1.12" + [dev-dependencies] tempfile = "3" diff --git a/tracing-flame/src/lib.rs b/tracing-flame/src/lib.rs index b37bae74c1..f7d670aa61 100644 --- a/tracing-flame/src/lib.rs +++ b/tracing-flame/src/lib.rs @@ -137,7 +137,7 @@ pub use error::Error; use error::Kind; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use std::cell::Cell; use std::fmt; use std::fmt::Write as _; @@ -158,9 +158,7 @@ use tracing_subscriber::Layer; mod error; -lazy_static! { - static ref START: Instant = Instant::now(); -} +static START: Lazy = Lazy::new(Instant::now); thread_local! { static LAST_EVENT: Cell = Cell::new(*START); diff --git a/tracing-log/Cargo.toml b/tracing-log/Cargo.toml index b482341eab..a774357788 100644 --- a/tracing-log/Cargo.toml +++ b/tracing-log/Cargo.toml @@ -27,8 +27,8 @@ interest-cache = ["lru", "ahash"] [dependencies] tracing-core = { path = "../tracing-core", version = "0.1.17"} log = { version = "0.4" } -lazy_static = "1.3.0" -env_logger = { version = "0.7", optional = true } +once_cell = "1.12" +env_logger = { version = "0.8", optional = true } lru = { version = "0.7.0", optional = true } ahash = { version = "0.7.4", optional = true } diff --git a/tracing-log/src/interest_cache.rs b/tracing-log/src/interest_cache.rs index fb3da875eb..aabf9ebaf7 100644 --- a/tracing-log/src/interest_cache.rs +++ b/tracing-log/src/interest_cache.rs @@ -1,6 +1,7 @@ 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}; @@ -140,12 +141,10 @@ static SENTINEL_METADATA: tracing_core::Metadata<'static> = tracing_core::Metada tracing_core::metadata::Kind::EVENT, ); -lazy_static::lazy_static! { - static ref CONFIG: Mutex = { - tracing_core::callsite::register(&SENTINEL_CALLSITE); - Mutex::new(InterestCacheConfig::disabled()) - }; -} +static CONFIG: Lazy> = Lazy::new(|| { + tracing_core::callsite::register(&SENTINEL_CALLSITE); + Mutex::new(InterestCacheConfig::disabled()) +}); thread_local! { static STATE: RefCell = { @@ -236,10 +235,7 @@ mod tests { fn lock_for_test() -> impl Drop { // We need to make sure only one test runs at a time. - - lazy_static::lazy_static! { - static ref LOCK: Mutex<()> = Mutex::new(()); - } + static LOCK: Lazy> = Lazy::new(Mutex::new); match LOCK.lock() { Ok(guard) => guard, diff --git a/tracing-log/src/lib.rs b/tracing-log/src/lib.rs index 09e9114a4c..44b3f1d32d 100644 --- a/tracing-log/src/lib.rs +++ b/tracing-log/src/lib.rs @@ -128,7 +128,7 @@ unused_parens, while_true )] -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use std::{fmt, io}; @@ -346,13 +346,11 @@ log_cs!( ErrorCallsite ); -lazy_static! { - static ref TRACE_FIELDS: Fields = Fields::new(&TRACE_CS); - static ref DEBUG_FIELDS: Fields = Fields::new(&DEBUG_CS); - static ref INFO_FIELDS: Fields = Fields::new(&INFO_CS); - static ref WARN_FIELDS: Fields = Fields::new(&WARN_CS); - static ref ERROR_FIELDS: Fields = Fields::new(&ERROR_CS); -} +static TRACE_FIELDS: Lazy = Lazy::new(|| Fields::new(&TRACE_CS)); +static DEBUG_FIELDS: Lazy = Lazy::new(|| Fields::new(&DEBUG_CS)); +static INFO_FIELDS: Lazy = Lazy::new(|| Fields::new(&INFO_CS)); +static WARN_FIELDS: Lazy = Lazy::new(|| Fields::new(&WARN_CS)); +static ERROR_FIELDS: Lazy = Lazy::new(|| Fields::new(&ERROR_CS)); fn level_to_cs(level: Level) -> (&'static dyn Callsite, &'static Fields) { match level { diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 8f36c8f7eb..3595318c46 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -27,7 +27,7 @@ rust-version = "1.49.0" default = ["smallvec", "fmt", "ansi", "tracing-log", "std"] alloc = [] std = ["alloc", "tracing-core/std"] -env-filter = ["matchers", "regex", "lazy_static", "tracing", "std", "thread_local"] +env-filter = ["matchers", "regex", "once_cell", "tracing", "std", "thread_local"] fmt = ["registry", "std"] ansi = ["fmt", "ansi_term"] registry = ["sharded-slab", "thread_local", "std"] @@ -45,7 +45,7 @@ tracing = { optional = true, path = "../tracing", version = "0.1", default-featu matchers = { optional = true, version = "0.1.0" } regex = { optional = true, version = "1", default-features = false, features = ["std"] } smallvec = { optional = true, version = "1.2.0" } -lazy_static = { optional = true, version = "1" } +once_cell = { optional = true, version = "1.12" } # fmt tracing-log = { path = "../tracing-log", version = "0.1.2", optional = true, default-features = false, features = ["log-tracer", "std"] } @@ -58,7 +58,7 @@ serde = { version = "1.0", optional = true } tracing-serde = { path = "../tracing-serde", version = "0.1.3", optional = true } # opt-in deps -parking_lot = { version = "0.13", optional = true } +parking_lot = { version = "0.12", optional = true } # registry sharded-slab = { version = "0.1.0", optional = true } diff --git a/tracing-subscriber/src/filter/env/directive.rs b/tracing-subscriber/src/filter/env/directive.rs index 3e993f6c92..f062e6ef93 100644 --- a/tracing-subscriber/src/filter/env/directive.rs +++ b/tracing-subscriber/src/filter/env/directive.rs @@ -4,7 +4,7 @@ use crate::filter::{ env::{field, FieldMap}, level::LevelFilter, }; -use lazy_static::lazy_static; +use once_cell::sync::Lazy; use regex::Regex; use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr}; use tracing_core::{span, Level, Metadata}; @@ -120,41 +120,39 @@ impl Directive { } pub(super) fn parse(from: &str, regex: bool) -> Result { - lazy_static! { - static ref DIRECTIVE_RE: Regex = Regex::new( - r"(?x) - ^(?P(?i:trace|debug|info|warn|error|off|[0-5]))$ | - # ^^^. - # `note: we match log level names case-insensitively - ^ - (?: # target name or span name - (?P[\w:-]+)|(?P\[[^\]]*\]) - ){1,2} - (?: # level or nothing - =(?P(?i:trace|debug|info|warn|error|off|[0-5]))? - # ^^^. - # `note: we match log level names case-insensitively - )? - $ - " - ) - .unwrap(); - static ref SPAN_PART_RE: Regex = - Regex::new(r#"(?P[^\]\{]+)?(?:\{(?P[^\}]*)\})?"#).unwrap(); - static ref FIELD_FILTER_RE: Regex = - // TODO(eliza): this doesn't _currently_ handle value matchers that include comma - // characters. We should fix that. - Regex::new(r#"(?x) - ( - # field name - [[:word:]][[[:word:]]\.]* - # value part (optional) - (?:=[^,]+)? - ) - # trailing comma or EOS - (?:,\s?|$) - "#).unwrap(); - } + static DIRECTIVE_RE: Lazy = Lazy::new(|| Regex::new( + r"(?x) + ^(?P(?i:trace|debug|info|warn|error|off|[0-5]))$ | + # ^^^. + # `note: we match log level names case-insensitively + ^ + (?: # target name or span name + (?P[\w:-]+)|(?P\[[^\]]*\]) + ){1,2} + (?: # level or nothing + =(?P(?i:trace|debug|info|warn|error|off|[0-5]))? + # ^^^. + # `note: we match log level names case-insensitively + )? + $ + " + ) + .unwrap()); + static SPAN_PART_RE: Lazy = + Lazy::new(|| Regex::new(r#"(?P[^\]\{]+)?(?:\{(?P[^\}]*)\})?"#).unwrap()); + static FIELD_FILTER_RE: Lazy = + // TODO(eliza): this doesn't _currently_ handle value matchers that include comma + // characters. We should fix that. + Lazy::new(|| Regex::new(r#"(?x) + ( + # field name + [[:word:]][[[:word:]]\.]* + # value part (optional) + (?:=[^,]+)? + ) + # trailing comma or EOS + (?:,\s?|$) + "#).unwrap()); let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?; From 1133a08f0d65e51f69c622ef07cbb1439a41a90a Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 7 Jun 2022 10:43:16 -0700 Subject: [PATCH 026/142] opentelemetry: prepare to release v0.17.3 (#2154) # 0.17.3 (June 7, 2022) This release adds support for emitting thread names and IDs to OpenTelemetry, as well as recording `std::error::Error` values in a structured manner with their source chain included. Additionally, this release fixes issues related to event and span source code locations. ### Added - `Layer::with_threads` to enable recording thread names/IDs according to [OpenTelemetry semantic conventions][thread-semconv] ([#2134]) - `Error::source` chain when recording `std::error::Error` values ([#2122]) - `Layer::with_location` method (replaces `Layer::with_event_location`) ([#2124]) ### Changed - `std::error::Error` values are now recorded using `fmt::Display` rather than `fmt::Debug` ([#2122]) ### Fixed - Fixed event source code locations overwriting the parent span's source location ([#2099]) - Fixed `Layer::with_event_location` not controlling whether locations are emitted for spans as well as events ([#2124]) ### Deprecated - `Layer::with_event_location`: renamed to `Layer::with_location`, as it now controls both span and event locations ([#2124]) Thanks to new contributors @lilymara-onesignal, @hubertbudzynski, and @DevinCarr for contributing to this release! [thread-semconv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes [#2134]: https://github.com/tokio-rs/tracing/pull/2134 [#2122]: https://github.com/tokio-rs/tracing/pull/2122 [#2124]: https://github.com/tokio-rs/tracing/pull/2124 [#2099]: https://github.com/tokio-rs/tracing/pull/2099 --- tracing-opentelemetry/CHANGELOG.md | 41 ++++++++++++++++++++++++++++++ tracing-opentelemetry/Cargo.toml | 2 +- tracing-opentelemetry/README.md | 4 +-- tracing-opentelemetry/src/lib.rs | 2 +- 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/tracing-opentelemetry/CHANGELOG.md b/tracing-opentelemetry/CHANGELOG.md index 967ea070d6..6bb1da3b5f 100644 --- a/tracing-opentelemetry/CHANGELOG.md +++ b/tracing-opentelemetry/CHANGELOG.md @@ -1,3 +1,44 @@ +# 0.17.3 (June 7, 2022) + +This release adds support for emitting thread names and IDs to OpenTelemetry, as +well as recording `std::error::Error` values in a structured manner with their +source chain included. Additionally, this release fixes issues related to event +and span source code locations. + +### Added + +- `Layer::with_threads` to enable recording thread names/IDs according to + [OpenTelemetry semantic conventions][thread-semconv] ([#2134]) +- `Error::source` chain when recording `std::error::Error` values ([#2122]) +- `Layer::with_location` method (replaces `Layer::with_event_location`) + ([#2124]) + +### Changed + +- `std::error::Error` values are now recorded using `fmt::Display` rather than + `fmt::Debug` ([#2122]) + +### Fixed + +- Fixed event source code locations overwriting the parent span's source + location ([#2099]) +- Fixed `Layer::with_event_location` not controlling whether locations are + emitted for spans as well as events ([#2124]) + +### Deprecated + +- `Layer::with_event_location`: renamed to `Layer::with_location`, as it now + controls both span and event locations ([#2124]) + +Thanks to new contributors @lilymara-onesignal, @hubertbudzynski, and @DevinCarr +for contributing to this release! + +[thread-semconv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes +[#2134]: https://github.com/tokio-rs/tracing/pull/2134 +[#2122]: https://github.com/tokio-rs/tracing/pull/2122 +[#2124]: https://github.com/tokio-rs/tracing/pull/2124 +[#2099]: https://github.com/tokio-rs/tracing/pull/2099 + # 0.17.2 (February 21, 2022) This release fixes [an issue][#1944] introduced in v0.17.1 where diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml index 9e98b68f33..2aa9e1ad69 100644 --- a/tracing-opentelemetry/Cargo.toml +++ b/tracing-opentelemetry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-opentelemetry" -version = "0.17.2" +version = "0.17.3" authors = [ "Julian Tescher ", "Tokio Contributors " diff --git a/tracing-opentelemetry/README.md b/tracing-opentelemetry/README.md index 9dcc115f68..d17c4ce6fa 100644 --- a/tracing-opentelemetry/README.md +++ b/tracing-opentelemetry/README.md @@ -17,9 +17,9 @@ Utilities for adding [OpenTelemetry] interoperability to [`tracing`]. [Documentation][docs-url] | [Chat][discord-url] [crates-badge]: https://img.shields.io/crates/v/tracing-opentelemetry.svg -[crates-url]: https://crates.io/crates/tracing-opentelemetry/0.17.2 +[crates-url]: https://crates.io/crates/tracing-opentelemetry/0.17.3 [docs-badge]: https://docs.rs/tracing-opentelemetry/badge.svg -[docs-url]: https://docs.rs/tracing-opentelemetry/0.17.2/tracing_opentelemetry +[docs-url]: https://docs.rs/tracing-opentelemetry/0.17.3/tracing_opentelemetry [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_opentelemetry [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg diff --git a/tracing-opentelemetry/src/lib.rs b/tracing-opentelemetry/src/lib.rs index f2bbdb8218..f493d5c2d6 100644 --- a/tracing-opentelemetry/src/lib.rs +++ b/tracing-opentelemetry/src/lib.rs @@ -92,7 +92,7 @@ //! #![deny(unreachable_pub)] #![cfg_attr(test, deny(warnings))] -#![doc(html_root_url = "https://docs.rs/tracing-opentelemetry/0.17.2")] +#![doc(html_root_url = "https://docs.rs/tracing-opentelemetry/0.17.3")] #![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/" From 2f23eccaca45cc80f7a17c7594bf806707ff7132 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 7 Jun 2022 22:12:06 +0200 Subject: [PATCH 027/142] core: add `ValueSet::len` and `Record::len` (#2152) ## Motivation This PR adds two new accessor functions that are useful for creating a structured serde implementation for tracing. This is a sort of "distilled" version of https://github.com/tokio-rs/tracing/pull/2113, based on the `v0.1.x` branch. As it is unlikely that "structured serde" will be 1:1 compatible with the existing JSON-based `tracing-serde` (or at least - I'm not sure how to do it in a reasonable amount of effort), these functions will allow me to make a separate crate to hold me over until breaking formatting changes are possible in `tracing-serde`. CC @hawkw, as we've discussed this pretty extensively --- tracing-core/src/field.rs | 15 ++++++++++++++- tracing-core/src/span.rs | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index 0bd8b667c7..ce8ebe58f4 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -935,6 +935,19 @@ impl<'a> ValueSet<'a> { } } + /// Returns the number of fields in this `ValueSet` that would be visited + /// by a given [visitor] to the [`ValueSet::record()`] method. + /// + /// [visitor]: Visit + /// [`ValueSet::record()`]: ValueSet::record() + pub fn len(&self) -> usize { + let my_callsite = self.callsite(); + self.values + .iter() + .filter(|(field, _)| field.callsite() == my_callsite) + .count() + } + /// Returns `true` if this `ValueSet` contains a value for the given `Field`. pub(crate) fn contains(&self, field: &Field) -> bool { field.callsite() == self.callsite() @@ -945,7 +958,7 @@ impl<'a> ValueSet<'a> { } /// Returns true if this `ValueSet` contains _no_ values. - pub(crate) fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool { let my_callsite = self.callsite(); self.values .iter() diff --git a/tracing-core/src/span.rs b/tracing-core/src/span.rs index cb75baf484..44738b2903 100644 --- a/tracing-core/src/span.rs +++ b/tracing-core/src/span.rs @@ -225,6 +225,14 @@ impl<'a> Record<'a> { self.values.record(visitor) } + /// Returns the number of fields that would be visited from this `Record` + /// when [`Record::record()`] is called + /// + /// [`Record::record()`]: Record::record() + pub fn len(&self) -> usize { + self.values.len() + } + /// Returns `true` if this `Record` contains a value for the given `Field`. pub fn contains(&self, field: &field::Field) -> bool { self.values.contains(field) From d6699fe31169209bd1f05c20759d5d584e1343c4 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 7 Jun 2022 14:18:32 -0700 Subject: [PATCH 028/142] core: prepare to release v0.1.27 (#2155) # 0.1.27 (June 7, 2022) This release of `tracing-core` introduces a new `DefaultCallsite` type, which can be used by instrumentation crates rather than implementing their own callsite types. Using `DefaultCallsite` may offer reduced overhead from callsite registration. ### Added - `DefaultCallsite`, a pre-written `Callsite` implementation for use in instrumentation crates ([#2083]) - `ValueSet::len` and `Record::len` methods returning the number of fields in a `ValueSet` or `Record` ([#2152]) ### Changed - Replaced `lazy_static` dependency with `once_cell` ([#2147]) ### Documented - Added documentation to the `callsite` module ([#2088], [#2149]) Thanks to new contributors @jamesmunns and @james7132 for contributing to this release! [#2083]: https://github.com/tokio-rs/tracing/pull/2083 [#2152]: https://github.com/tokio-rs/tracing/pull/2152 [#2147]: https://github.com/tokio-rs/tracing/pull/2147 [#2088]: https://github.com/tokio-rs/tracing/pull/2088 [#2149]: https://github.com/tokio-rs/tracing/pull/2149 --- tracing-core/CHANGELOG.md | 33 ++++++++++++++++++++++++++++++++- tracing-core/Cargo.toml | 2 +- tracing-core/README.md | 26 +++++++++++++------------- tracing/Cargo.toml | 2 +- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/tracing-core/CHANGELOG.md b/tracing-core/CHANGELOG.md index 1bb9aab529..2e6e12ec0e 100644 --- a/tracing-core/CHANGELOG.md +++ b/tracing-core/CHANGELOG.md @@ -1,9 +1,40 @@ +# 0.1.27 (June 7, 2022) + +This release of `tracing-core` introduces a new `DefaultCallsite` type, which +can be used by instrumentation crates rather than implementing their own +callsite types. Using `DefaultCallsite` may offer reduced overhead from callsite +registration. + +### Added + +- `DefaultCallsite`, a pre-written `Callsite` implementation for use in + instrumentation crates ([#2083]) +- `ValueSet::len` and `Record::len` methods returning the number of fields in a + `ValueSet` or `Record` ([#2152]) + +### Changed + +- Replaced `lazy_static` dependency with `once_cell` ([#2147]) + +### Documented + +- Added documentation to the `callsite` module ([#2088], [#2149]) + +Thanks to new contributors @jamesmunns and @james7132 for contributing to this +release! + +[#2083]: https://github.com/tokio-rs/tracing/pull/2083 +[#2152]: https://github.com/tokio-rs/tracing/pull/2152 +[#2147]: https://github.com/tokio-rs/tracing/pull/2147 +[#2088]: https://github.com/tokio-rs/tracing/pull/2088 +[#2149]: https://github.com/tokio-rs/tracing/pull/2149 + # 0.1.26 (April 14, 2022) This release adds a `Value` implementation for `Box` to allow recording boxed values more conveniently. In particular, this should improve the ergonomics of the implementations for `dyn std::error::Error` trait objects, -including those added in [v0.1.25]. +including those added in [v0.1.25]. ### Added diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml index cedffe7bfc..af03026726 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -8,7 +8,7 @@ name = "tracing-core" # - README.md # - Update CHANGELOG.md. # - Create "v0.1.x" git tag. -version = "0.1.26" +version = "0.1.27" authors = ["Tokio Contributors "] license = "MIT" readme = "README.md" diff --git a/tracing-core/README.md b/tracing-core/README.md index d841b15a0b..bfd9ad5e9f 100644 --- a/tracing-core/README.md +++ b/tracing-core/README.md @@ -16,9 +16,9 @@ Core primitives for application-level tracing. [Documentation][docs-url] | [Chat][discord-url] [crates-badge]: https://img.shields.io/crates/v/tracing-core.svg -[crates-url]: https://crates.io/crates/tracing-core/0.1.26 +[crates-url]: https://crates.io/crates/tracing-core/0.1.27 [docs-badge]: https://docs.rs/tracing-core/badge.svg -[docs-url]: https://docs.rs/tracing-core/0.1.26 +[docs-url]: https://docs.rs/tracing-core/0.1.27 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_core [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg @@ -79,22 +79,22 @@ The following crate feature flags are available: ```toml [dependencies] - tracing-core = { version = "0.1.26", default-features = false } + tracing-core = { version = "0.1.27", default-features = false } ``` **Note**:`tracing-core`'s `no_std` support requires `liballoc`. [`tracing`]: ../tracing -[`span::Id`]: https://docs.rs/tracing-core/0.1.26/tracing_core/span/struct.Id.html -[`Event`]: https://docs.rs/tracing-core/0.1.26/tracing_core/event/struct.Event.html -[`Subscriber`]: https://docs.rs/tracing-core/0.1.26/tracing_core/subscriber/trait.Subscriber.html -[`Metadata`]: https://docs.rs/tracing-core/0.1.26/tracing_core/metadata/struct.Metadata.html -[`Callsite`]: https://docs.rs/tracing-core/0.1.26/tracing_core/callsite/trait.Callsite.html -[`Field`]: https://docs.rs/tracing-core/0.1.26/tracing_core/field/struct.Field.html -[`FieldSet`]: https://docs.rs/tracing-core/0.1.26/tracing_core/field/struct.FieldSet.html -[`Value`]: https://docs.rs/tracing-core/0.1.26/tracing_core/field/trait.Value.html -[`ValueSet`]: https://docs.rs/tracing-core/0.1.26/tracing_core/field/struct.ValueSet.html -[`Dispatch`]: https://docs.rs/tracing-core/0.1.26/tracing_core/dispatcher/struct.Dispatch.html +[`span::Id`]: https://docs.rs/tracing-core/0.1.27/tracing_core/span/struct.Id.html +[`Event`]: https://docs.rs/tracing-core/0.1.27/tracing_core/event/struct.Event.html +[`Subscriber`]: https://docs.rs/tracing-core/0.1.27/tracing_core/subscriber/trait.Subscriber.html +[`Metadata`]: https://docs.rs/tracing-core/0.1.27/tracing_core/metadata/struct.Metadata.html +[`Callsite`]: https://docs.rs/tracing-core/0.1.27/tracing_core/callsite/trait.Callsite.html +[`Field`]: https://docs.rs/tracing-core/0.1.27/tracing_core/field/struct.Field.html +[`FieldSet`]: https://docs.rs/tracing-core/0.1.27/tracing_core/field/struct.FieldSet.html +[`Value`]: https://docs.rs/tracing-core/0.1.27/tracing_core/field/trait.Value.html +[`ValueSet`]: https://docs.rs/tracing-core/0.1.27/tracing_core/field/struct.ValueSet.html +[`Dispatch`]: https://docs.rs/tracing-core/0.1.27/tracing_core/dispatcher/struct.Dispatch.html ## Supported Rust Versions diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 558fd3332b..319b4eaab8 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -28,7 +28,7 @@ edition = "2018" rust-version = "1.49.0" [dependencies] -tracing-core = { path = "../tracing-core", version = "0.1.26", default-features = false } +tracing-core = { path = "../tracing-core", version = "0.1.27", default-features = false } log = { version = "0.4", optional = true } tracing-attributes = { path = "../tracing-attributes", version = "0.1.20", optional = true } cfg-if = "1.0.0" From cf98b732572b4b4ea8f5716094e55fa52f58e0ad Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 8 Jun 2022 08:50:00 -0700 Subject: [PATCH 029/142] tracing: prepare to releases v0.1.35 (#2156) # 0.1.35 (June 8, 2022) This release reduces the overhead of callsite registration by using new `tracing-core` APIs. ### Added - Use `DefaultCallsite` to reduce callsite registration overhead ([#2083]) ### Changed - `tracing-core`: updated to [0.1.27][core-0.1.27] [core-0.1.27]: https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.27 [#2088]: https://github.com/tokio-rs/tracing/pull/2083 --- tracing/CHANGELOG.md | 16 ++++++++++++++++ tracing/Cargo.toml | 2 +- tracing/README.md | 8 ++++---- tracing/src/lib.rs | 4 ++-- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/tracing/CHANGELOG.md b/tracing/CHANGELOG.md index 8720d98ad5..1c527f2de4 100644 --- a/tracing/CHANGELOG.md +++ b/tracing/CHANGELOG.md @@ -1,3 +1,19 @@ +# 0.1.35 (June 8, 2022) + +This release reduces the overhead of callsite registration by using new +`tracing-core` APIs. + +### Added + +- Use `DefaultCallsite` to reduce callsite registration overhead ([#2083]) + +### Changed + +- `tracing-core`: updated to [0.1.27][core-0.1.27] + +[core-0.1.27]: https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.27 +[#2088]: https://github.com/tokio-rs/tracing/pull/2083 + # 0.1.34 (April 14, 2022) This release includes bug fixes for the "log" support feature and for the use of diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 319b4eaab8..d32d34927b 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -8,7 +8,7 @@ name = "tracing" # - README.md # - Update CHANGELOG.md. # - Create "v0.1.x" git tag -version = "0.1.34" +version = "0.1.35" authors = ["Eliza Weisman ", "Tokio Contributors "] license = "MIT" readme = "README.md" diff --git a/tracing/README.md b/tracing/README.md index 6f1c07cc1f..5da141a3a5 100644 --- a/tracing/README.md +++ b/tracing/README.md @@ -16,9 +16,9 @@ Application-level tracing for Rust. [Documentation][docs-url] | [Chat][discord-url] [crates-badge]: https://img.shields.io/crates/v/tracing.svg -[crates-url]: https://crates.io/crates/tracing/0.1.34 +[crates-url]: https://crates.io/crates/tracing/0.1.35 [docs-badge]: https://docs.rs/tracing/badge.svg -[docs-url]: https://docs.rs/tracing/0.1.34 +[docs-url]: https://docs.rs/tracing/0.1.35 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg @@ -250,7 +250,7 @@ my_future is as long as the future's. The second, and preferred, option is through the -[`#[instrument]`](https://docs.rs/tracing/0.1.34/tracing/attr.instrument.html) +[`#[instrument]`](https://docs.rs/tracing/0.1.35/tracing/attr.instrument.html) attribute: ```rust @@ -297,7 +297,7 @@ span.in_scope(|| { // Dropping the span will close it, indicating that it has ended. ``` -The [`#[instrument]`](https://docs.rs/tracing/0.1.34/tracing/attr.instrument.html) attribute macro +The [`#[instrument]`](https://docs.rs/tracing/0.1.35/tracing/attr.instrument.html) attribute macro can reduce some of this boilerplate: ```rust diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 65a4ebc951..4743eba207 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -809,7 +809,7 @@ //! //! ```toml //! [dependencies] -//! tracing = { version = "0.1.34", default-features = false } +//! tracing = { version = "0.1.35", default-features = false } //! ``` //! //!

@@ -892,7 +892,7 @@
 //! [flags]: #crate-feature-flags
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
-#![doc(html_root_url = "https://docs.rs/tracing/0.1.34")]
+#![doc(html_root_url = "https://docs.rs/tracing/0.1.35")]
 #![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/"

From 5d90bdc48ebdd63a9dbe7868f1fe3f9ad0eaaeb4 Mon Sep 17 00:00:00 2001
From: Bryan Garza <1396101+bryangarza@users.noreply.github.com>
Date: Mon, 6 Jun 2022 11:53:01 -0700
Subject: [PATCH 030/142] opentelemetry: add more comments to example (#2140)

This patch adds a bit more context around why we are creating a smaller
scope for the spans, and also what happens when we call
`global::shutdown_tracer_provider()` (that comment was copied from
the`rust-opentelemetry` repo).

Co-authored-by: Eliza Weisman 
---
 examples/examples/opentelemetry.rs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/examples/examples/opentelemetry.rs b/examples/examples/opentelemetry.rs
index fbc7469080..b453c77cad 100644
--- a/examples/examples/opentelemetry.rs
+++ b/examples/examples/opentelemetry.rs
@@ -38,8 +38,11 @@ fn main() -> Result<(), Box> {
 
         warn!("About to exit!");
         trace!("status: {}", work_result);
-    }
+    } // Once this scope is closed, all spans inside are closed as well
 
+    // Shut down the current tracer provider. This will invoke the shutdown
+    // method on all span processors. span processors should export remaining
+    // spans before return.
     global::shutdown_tracer_provider();
 
     Ok(())

From e793b0b06a25058d102fe7b7e6741a5024c3efd7 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Sat, 11 Jun 2022 12:34:09 -0700
Subject: [PATCH 031/142] chore: `bin/publish` shell script fixes (#2162)

* fix use of `cargo --list` in bin/publish
* fix shellcheck lints in bin/publish
* set -euo pipefail
* fix cargo hack exit status check
* fix wrong emptystring args
* prompt before installing cargo-hack

Signed-off-by: Eliza Weisman 
---
 bin/publish | 75 +++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 56 insertions(+), 19 deletions(-)

diff --git a/bin/publish b/bin/publish
index 2fba506222..ee2a3ec88a 100755
--- a/bin/publish
+++ b/bin/publish
@@ -1,5 +1,4 @@
 #!/usr/bin/env bash
-set -e
 USAGE="Publish a new release of a tokio crate
 
 USAGE:
@@ -10,11 +9,15 @@ OPTIONS:
     -d, --dry-run   Perform a dry run (do not publish or tag the release)
     -h, --help      Show this help text and exit"
 
+set -euo pipefail
+
+cd "$(dirname "$0")"/..
+
 DRY_RUN=""
 VERBOSE=""
 
 err() {
-    echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2;
+    echo -e "\e[31m\e[1merror:\e[0m" "$@" 1>&2;
 }
 
 status() {
@@ -31,20 +34,40 @@ verify() {
         exit 1
     fi
 
-    if ! cargo list | grep -q "hack"; then
-        status "Installing" "cargo-hack"
-        cargo install cargo-hack
+    if ! cargo --list | grep -q "hack"; then
+        err "missing cargo-hack executable"
+        read -r -p "install it? [Y/n] " INPUT
+
+        case "$INPUT" in
+            [yY][eE][sS]|[yY])
+                status "Installing" "cargo-hack"
+                cargo install cargo-hack
+                ;;
+            [nN][oO]|[nN])
+                echo "okay, exiting"
+                exit 1
+                ;;
+            *)
+                err "invalid input $INPUT"
+                exit 1
+                ;;
+        esac
     fi
 
     status "Checking" "if $CRATE builds across feature combinations"
 
-    CARGO_HACK=(cargo hack check $VERBOSE --feature-powerset  --no-dev-deps)
+    CARGO_HACK=(cargo hack check --feature-powerset  --no-dev-deps)
+
+    if [[ "$VERBOSE" ]]; then
+        CARGO_HACK+=("$VERBOSE")
+    fi
+
     case "$CRATE" in
         tracing-subscriber)
             # for tracing-subscriber, don't test a complete powerset because
             # there are lots of feature flags
             INCLUDE_FEATURES=(fmt ansi json registry env-filter)
-            ${CARGO_HACK[@]} --include-features "${INCLUDE_FEATURES[*]}"
+            "${CARGO_HACK[@]}" --include-features "${INCLUDE_FEATURES[*]}"
             CARGO_HACK_STATUS="$?"
             ;;
         tracing)
@@ -58,17 +81,17 @@ verify() {
                 release_max_level_info release_max_level_debug
                 release_max_level_trace
             )
-            ${CARGO_HACK[@]} --exclude-features "${EXCLUDE_FEATURES[*]}"
+            "${CARGO_HACK[@]}" --exclude-features "${EXCLUDE_FEATURES[*]}"
             CARGO_HACK_STATUS="$?"
             ;;
         *)
-            ${CARGO_HACK[@]}
+            "${CARGO_HACK[@]}"
             CARGO_HACK_STATUS="$?"
             ;;
     esac
 
-    if "$CARGO_HACK_STATUS" ; then
-        err "$CRATE did not build with all feature combinations!"
+    if [[ "$CARGO_HACK_STATUS" != "0" ]] ; then
+        err "$CRATE did not build with all feature combinations (cargo hack exited with $CARGO_HACK_STATUS)!"
         exit 1
     fi
 
@@ -81,11 +104,25 @@ verify() {
 
 release() {
     status "Releasing" "$CRATE v$VERSION"
-    cargo package $VERBOSE
-    cargo publish $VERBOSE $DRY_RUN
+    local CARGO_PACKAGE=(cargo package)
+    local CARGO_PUBLISH=(cargo publish)
+
+    if [[ "$VERBOSE" ]]; then
+        CARGO_PACKAGE+=("$VERBOSE")
+        CARGO_PUBLISH+=("$VERBOSE")
+    fi
+
+    if [[ "$DRY_RUN" ]]; then
+        CARGO_PUBLISH+=("$DRY_RUN")
+    fi
+
+    "${CARGO_PACKAGE[@]}"
+    "${CARGO_PUBLISH[@]}"
+
+    cargo publish "$VERBOSE" "$DRY_RUN"
 
     status "Tagging" "$TAG"
-    if [ -n "$DRY_RUN" ]; then
+    if [[ "$DRY_RUN" ]]; then
         echo "# git tag $TAG && git push --tags"
     else
         git tag "$TAG" && git push --tags
@@ -111,9 +148,9 @@ case "$1" in
     exit 1
     ;;
     *) # crate or version
-    if [ -z "$CRATE" ]; then
+    if [[ -z "${CRATE+crate}" ]]; then
         CRATE="$1"
-    elif [ -z "$VERSION" ]; then
+    elif [[ -z "${VERSION+version}" ]]; then
         VERSION="$1"
     else
         err "unknown positional argument \"$1\""
@@ -126,19 +163,19 @@ esac
 done
 # set -- "${POSITIONAL[@]}"
 
-if [ -z "$VERSION" ]; then
+if [[ -z "${VERSION+version}" ]]; then
     err "no version specified!"
     HELP=1
 fi
 
-if [ -n "$CRATE" ]; then
+if [[ "${CRATE+crate}" ]]; then
     TAG="$CRATE-$VERSION"
 else
     err "no crate specified!"
     HELP=1
 fi
 
-if [ -n "$HELP" ]; then
+if [[ "${HELP+help}" ]]; then
     echo "$USAGE"
     exit 1
 fi

From 0d41b373e718917b22417edeecb430aeed07ac42 Mon Sep 17 00:00:00 2001
From: Jack Wrenn 
Date: Mon, 13 Jun 2022 18:50:52 -0400
Subject: [PATCH 032/142] core, subscriber: more `downcast_ref` & `is` methods
 (#2160)

Adds inherent `downcast_ref` and `is` inherent methods to:
  - `dyn Subscriber + Send`
  - `dyn Subscriber + Sync`
  - `dyn Subscriber + Send + Sync`
  - `Layered`

These additional implementations reduce the circumstances in which one
must cast to `dyn Subscriber` (which, previously, was the only type for
which `downcast_ref` and `is` were available).
---
 tracing-core/src/subscriber.rs          | 60 +++++++++++++++++++++++++
 tracing-subscriber/src/layer/layered.rs | 30 ++++++++++++-
 2 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/tracing-core/src/subscriber.rs b/tracing-core/src/subscriber.rs
index 501cce733d..3d6aaa3441 100644
--- a/tracing-core/src/subscriber.rs
+++ b/tracing-core/src/subscriber.rs
@@ -484,6 +484,66 @@ impl dyn Subscriber {
     }
 }
 
+impl dyn Subscriber + Send {
+    /// Returns `true` if this [`Subscriber`] is the same type as `T`.
+    pub fn is(&self) -> bool {
+        self.downcast_ref::().is_some()
+    }
+
+    /// Returns some reference to this [`Subscriber`] value if it is of type `T`,
+    /// or `None` if it isn't.
+    pub fn downcast_ref(&self) -> Option<&T> {
+        unsafe {
+            let raw = self.downcast_raw(TypeId::of::())?;
+            if raw.is_null() {
+                None
+            } else {
+                Some(&*(raw as *const _))
+            }
+        }
+    }
+}
+
+impl dyn Subscriber + Sync {
+    /// Returns `true` if this [`Subscriber`] is the same type as `T`.
+    pub fn is(&self) -> bool {
+        self.downcast_ref::().is_some()
+    }
+
+    /// Returns some reference to this `[`Subscriber`] value if it is of type `T`,
+    /// or `None` if it isn't.
+    pub fn downcast_ref(&self) -> Option<&T> {
+        unsafe {
+            let raw = self.downcast_raw(TypeId::of::())?;
+            if raw.is_null() {
+                None
+            } else {
+                Some(&*(raw as *const _))
+            }
+        }
+    }
+}
+
+impl dyn Subscriber + Send + Sync {
+    /// Returns `true` if this [`Subscriber`] is the same type as `T`.
+    pub fn is(&self) -> bool {
+        self.downcast_ref::().is_some()
+    }
+
+    /// Returns some reference to this [`Subscriber`] value if it is of type `T`,
+    /// or `None` if it isn't.
+    pub fn downcast_ref(&self) -> Option<&T> {
+        unsafe {
+            let raw = self.downcast_raw(TypeId::of::())?;
+            if raw.is_null() {
+                None
+            } else {
+                Some(&*(raw as *const _))
+            }
+        }
+    }
+}
+
 /// Indicates a [`Subscriber`]'s interest in a particular callsite.
 ///
 /// `Subscriber`s return an `Interest` from their [`register_callsite`] methods
diff --git a/tracing-subscriber/src/layer/layered.rs b/tracing-subscriber/src/layer/layered.rs
index 0c6658cb06..5c9b934e35 100644
--- a/tracing-subscriber/src/layer/layered.rs
+++ b/tracing-subscriber/src/layer/layered.rs
@@ -12,7 +12,11 @@ use crate::{
 };
 #[cfg(all(feature = "registry", feature = "std"))]
 use crate::{filter::FilterId, registry::Registry};
-use core::{any::TypeId, cmp, fmt, marker::PhantomData};
+use core::{
+    any::{Any, TypeId},
+    cmp, fmt,
+    marker::PhantomData,
+};
 
 /// A [`Subscriber`] composed of a `Subscriber` wrapped by one or more
 /// [`Layer`]s.
@@ -63,6 +67,30 @@ pub struct Layered {
 
 // === impl Layered ===
 
+impl Layered
+where
+    L: Layer,
+    S: Subscriber,
+{
+    /// Returns `true` if this [`Subscriber`] is the same type as `T`.
+    pub fn is(&self) -> bool {
+        self.downcast_ref::().is_some()
+    }
+
+    /// Returns some reference to this [`Subscriber`] value if it is of type `T`,
+    /// or `None` if it isn't.
+    pub fn downcast_ref(&self) -> Option<&T> {
+        unsafe {
+            let raw = self.downcast_raw(TypeId::of::())?;
+            if raw.is_null() {
+                None
+            } else {
+                Some(&*(raw as *const T))
+            }
+        }
+    }
+}
+
 impl Subscriber for Layered
 where
     L: Layer,

From edda806fc0ef16cc1d3dfb4b3728812afb396d6b Mon Sep 17 00:00:00 2001
From: Fredrik Enestad 
Date: Thu, 16 Jun 2022 20:57:04 +0200
Subject: [PATCH 033/142] docs: add tracing-logfmt to related crates (#2163)

Adds tracing-logfmt crate to the related crates section
---
 README.md          | 2 ++
 tracing/README.md  | 2 ++
 tracing/src/lib.rs | 2 ++
 3 files changed, 6 insertions(+)

diff --git a/README.md b/README.md
index ab4886821e..0fc9bc0797 100644
--- a/README.md
+++ b/README.md
@@ -402,6 +402,7 @@ are not maintained by the `tokio` project. These include:
 - [`tracing-forest`] provides a subscriber that preserves contextual coherence by 
   grouping together logs from the same spans during writing.
 - [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki].
+- [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.
 
 (if you're the maintainer of a `tracing` ecosystem crate not in this list,
 please let us know!)
@@ -439,6 +440,7 @@ please let us know!)
 [`tracing-forest`]: https://crates.io/crates/tracing-forest
 [`tracing-loki`]: https://crates.io/crates/tracing-loki
 [Grafana Loki]: https://grafana.com/oss/loki/
+[`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt
 
 **Note:** that some of the ecosystem crates are currently unreleased and
 undergoing active development. They may be less stable than `tracing` and
diff --git a/tracing/README.md b/tracing/README.md
index 5da141a3a5..1c7261c6ff 100644
--- a/tracing/README.md
+++ b/tracing/README.md
@@ -395,6 +395,7 @@ maintained by the `tokio` project. These include:
   framework for validating the behavior of `tracing` spans.
 - [`sentry-tracing`] provides a layer for reporting events and traces to [Sentry].
 - [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki].
+- [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.
 
 If you're the maintainer of a `tracing` ecosystem crate not listed above,
 please let us know! We'd love to add your project to the list!
@@ -424,6 +425,7 @@ please let us know! We'd love to add your project to the list!
 [Sentry]: https://sentry.io/welcome/
 [`tracing-loki`]: https://crates.io/crates/tracing-loki
 [Grafana Loki]: https://grafana.com/oss/loki/
+[`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt
 
 **Note:** that some of the ecosystem crates are currently unreleased and
 undergoing active development. They may be less stable than `tracing` and
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index 4743eba207..1ef96b54f8 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -743,6 +743,7 @@
 //!  - [`tracing-forest`] provides a subscriber that preserves contextual coherence by
 //!    grouping together logs from the same spans during writing.
 //!  - [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki].
+//!  - [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.
 //!
 //! If you're the maintainer of a `tracing` ecosystem crate not listed above,
 //! please let us know! We'd love to add your project to the list!
@@ -779,6 +780,7 @@
 //! [`tracing-forest`]: https://crates.io/crates/tracing-forest
 //! [`tracing-loki`]: https://crates.io/crates/tracing-loki
 //! [Grafana Loki]: https://grafana.com/oss/loki/
+//! [`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt
 //!
 //! 
 //!     Note: Some of these ecosystem crates are currently

From 3ad7f99f643306ef59e38f9c562ce649578eff20 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Thu, 16 Jun 2022 13:13:57 -0700
Subject: [PATCH 034/142] core: `impl field::Value for String` (#2164)

## Motivation

Currently, it is rather difficult to record `String`s as field values,
even though `&str` is a primitive `Value` type. For example, this code
does not currently compile:

```rust
let my_string = String::from("hello world!");
tracing::debug!(my_string);
```

Instead, it is necessary to explicitly call `String::as_str` or a
similar conversion method:

```rust
let my_string = String::from("hello world!");
tracing::debug!(my_string = my_string.as_str());
```

This is unfortunate, as it makes a fairly straightforward, commomplace
task (recording a `String` as a field) unnecessarily verbose.

## Solution

This branch adds an `impl Value for String` in `tracing-core`. The impl
simply calls `String::as_str` and then calls `record_str`. Because
`Value` takes an `&self`, and there are preexisting `impl
Value` for `&T` and `&mut T`, the string is not consumed, and `&String`
or `&mut String`s can also be used as `Value`s.

I've also added tests validating that this actually works.
---
 tracing-attributes/tests/fields.rs | 13 +++++++++++++
 tracing-core/src/field.rs          |  8 ++++++++
 tracing/tests/event.rs             | 24 ++++++++++++++++++++++++
 3 files changed, 45 insertions(+)

diff --git a/tracing-attributes/tests/fields.rs b/tracing-attributes/tests/fields.rs
index a31c1c2a84..c178fbb3d3 100644
--- a/tracing-attributes/tests/fields.rs
+++ b/tracing-attributes/tests/fields.rs
@@ -31,6 +31,11 @@ fn fn_clashy_expr_field2(s: &str) {
     let _ = s;
 }
 
+#[instrument(fields(s = &s))]
+fn fn_string(s: String) {
+    let _ = s;
+}
+
 #[derive(Debug)]
 struct HasField {
     my_field: &'static str,
@@ -134,6 +139,14 @@ fn empty_field() {
     });
 }
 
+#[test]
+fn string_field() {
+    let span = span::mock().with_field(mock("s").with_value(&"hello world").only());
+    run_test(span, || {
+        fn_string(String::from("hello world"));
+    });
+}
+
 fn run_test T, T>(span: NewSpan, fun: F) {
     let (subscriber, handle) = subscriber::mock()
         .new_span(span)
diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index ce8ebe58f4..df67566cda 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -116,6 +116,7 @@ use crate::stdlib::{
     hash::{Hash, Hasher},
     num,
     ops::Range,
+    string::String,
 };
 
 use self::private::ValidLen;
@@ -596,6 +597,13 @@ where
     }
 }
 
+impl crate::sealed::Sealed for String {}
+impl Value for String {
+    fn record(&self, key: &Field, visitor: &mut dyn Visit) {
+        visitor.record_str(key, self.as_str())
+    }
+}
+
 impl fmt::Debug for dyn Value {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // We are only going to be recording the field value, so we don't
diff --git a/tracing/tests/event.rs b/tracing/tests/event.rs
index ffb63c816b..61df19ad3c 100644
--- a/tracing/tests/event.rs
+++ b/tracing/tests/event.rs
@@ -474,3 +474,27 @@ fn option_ref_mut_values() {
 
     handle.assert_finished();
 }
+
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+#[test]
+fn string_field() {
+    let (subscriber, handle) = subscriber::mock()
+        .event(event::mock().with_fields(field::mock("my_string").with_value(&"hello").only()))
+        .event(
+            event::mock().with_fields(field::mock("my_string").with_value(&"hello world!").only()),
+        )
+        .done()
+        .run_with_handle();
+    with_default(subscriber, || {
+        let mut my_string = String::from("hello");
+
+        tracing::event!(Level::INFO, my_string);
+
+        // the string is not moved by using it as a field!
+        my_string.push_str(" world!");
+
+        tracing::event!(Level::INFO, my_string);
+    });
+
+    handle.assert_finished();
+}

From 374b646979614fd6ffbf173a1c1155025161bf80 Mon Sep 17 00:00:00 2001
From: Jack Wrenn 
Date: Thu, 16 Jun 2022 16:27:35 -0400
Subject: [PATCH 035/142] core: `impl Subscriber` for
 `Box`, `Arc` (#2161)

These broader impls supersede the previous impls where the inner type was a
`dyn Subscriber`. With these generic impls, you no longer must (but
still can, if you wish) cast the inner type of a boxed or arc'd
subscriber to `dyn Subscriber` to use it as a `Subscriber`.
---
 tracing-core/src/subscriber.rs | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/tracing-core/src/subscriber.rs b/tracing-core/src/subscriber.rs
index 3d6aaa3441..9c7e210b06 100644
--- a/tracing-core/src/subscriber.rs
+++ b/tracing-core/src/subscriber.rs
@@ -662,7 +662,10 @@ impl Subscriber for NoSubscriber {
     fn exit(&self, _span: &span::Id) {}
 }
 
-impl Subscriber for Box {
+impl Subscriber for Box
+where
+    S: Subscriber + ?Sized,
+{
     #[inline]
     fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
         self.as_ref().register_callsite(metadata)
@@ -739,7 +742,10 @@ impl Subscriber for Box {
     }
 }
 
-impl Subscriber for Arc {
+impl Subscriber for Arc
+where
+    S: Subscriber + ?Sized,
+{
     #[inline]
     fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
         self.as_ref().register_callsite(metadata)

From d3eb109e45a5078716345fccc05505f63ecea69f Mon Sep 17 00:00:00 2001
From: David Barsky 
Date: Fri, 17 Jun 2022 18:21:32 -0400
Subject: [PATCH 036/142] core: add support for recording 128-bit integers
 (#2166)

## Motivation

I've received a request at work to record 128-bit integers and realized
that we should probably support recording them.

## Solution

Added two methods to the `Visit` trait, `record_i128` and `record_u128`.
However, I didn't add the size conversions present for 64-bit integers,
as 128-bit integers are, at this time, more specialized.
---
 tracing-core/src/field.rs | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index df67566cda..c9869ce2ca 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -16,9 +16,9 @@
 //! will contain any fields attached to each event.
 //!
 //! `tracing` represents values as either one of a set of Rust primitives
-//! (`i64`, `u64`, `f64`, `bool`, and `&str`) or using a `fmt::Display` or
-//! `fmt::Debug` implementation. `Subscriber`s are provided these primitive
-//! value types as `dyn Value` trait objects.
+//! (`i64`, `u64`, `f64`, `i128`, `u128`, `bool`, and `&str`) or using a
+//! `fmt::Display` or `fmt::Debug` implementation. `Subscriber`s are provided
+//! these primitive value types as `dyn Value` trait objects.
 //!
 //! These trait objects can be formatted using `fmt::Debug`, but may also be
 //! recorded as typed data by calling the [`Value::record`] method on these
@@ -278,6 +278,16 @@ pub trait Visit {
         self.record_debug(field, &value)
     }
 
+    /// Visit a signed 128-bit integer value.
+    fn record_i128(&mut self, field: &Field, value: i128) {
+        self.record_debug(field, &value)
+    }
+
+    /// Visit an unsigned 128-bit integer value.
+    fn record_u128(&mut self, field: &Field, value: u128) {
+        self.record_debug(field, &value)
+    }
+
     /// Visit a boolean value.
     fn record_bool(&mut self, field: &Field, value: bool) {
         self.record_debug(field, &value)
@@ -490,6 +500,8 @@ impl_values! {
     record_u64(usize, u32, u16, u8 as u64),
     record_i64(i64),
     record_i64(isize, i32, i16, i8 as i64),
+    record_u128(u128),
+    record_i128(i128),
     record_bool(bool),
     record_f64(f64, f32 as f64)
 }

From f42e7e6dd2593ffdddf75f4ae48fa8ebdd649184 Mon Sep 17 00:00:00 2001
From: Christopher Durham 
Date: Tue, 21 Jun 2022 14:02:21 -0400
Subject: [PATCH 037/142] core, subscriber: add `event_enabled` to filter
 events on fields (#2008)

## Motivation

Allow filter layers to filter on the contents of events (see #2007).

## Solution

This branch adds a new `Subscriber::event_enabled` method, taking an
`Event` and returning `bool`. This is called before the
`Subscriber::event` method, and if it returns `false`,
`Subscriber::event` is not called.

For simple subscriber (e.g. not using `Layer`s), the `event_enabled`
method is not particulary necessary, as the subscriber can just skip the
`event` call. However, this branch also adds a new
`Layer::event_enabled` method, with the signature:
```rust
fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool;
```

This is called before `Layer::on_event`, and if `event_enabled`
returns `false`, `on_event` is not called (nor is `Subscriber::event`).
This allows filter `Layer`s to filter out an `Event` based on its
fields.

Closes #2007
---
 tracing-core/src/dispatcher.rs             |  4 +-
 tracing-core/src/subscriber.rs             | 26 +++++++
 tracing-subscriber/src/fmt/mod.rs          |  5 ++
 tracing-subscriber/src/layer/layered.rs    | 21 ++++++
 tracing-subscriber/src/layer/mod.rs        | 65 +++++++++++++++++
 tracing-subscriber/src/reload.rs           |  5 ++
 tracing-subscriber/tests/event_enabling.rs | 81 ++++++++++++++++++++++
 7 files changed, 206 insertions(+), 1 deletion(-)
 create mode 100644 tracing-subscriber/tests/event_enabling.rs

diff --git a/tracing-core/src/dispatcher.rs b/tracing-core/src/dispatcher.rs
index 741744437c..041722c366 100644
--- a/tracing-core/src/dispatcher.rs
+++ b/tracing-core/src/dispatcher.rs
@@ -511,7 +511,9 @@ impl Dispatch {
     /// [`event`]: super::subscriber::Subscriber::event
     #[inline]
     pub fn event(&self, event: &Event<'_>) {
-        self.subscriber.event(event)
+        if self.subscriber.event_enabled(event) {
+            self.subscriber.event(event);
+        }
     }
 
     /// Records that a span has been can_enter.
diff --git a/tracing-core/src/subscriber.rs b/tracing-core/src/subscriber.rs
index 9c7e210b06..a6f0834e2c 100644
--- a/tracing-core/src/subscriber.rs
+++ b/tracing-core/src/subscriber.rs
@@ -60,6 +60,9 @@ use crate::stdlib::{
 ///   See also the [documentation on the callsite registry][cs-reg] for details
 ///   on [`register_callsite`].
 ///
+/// - [`event_enabled`] is called once before every call to the [`event`]
+///   method. This can be used to implement filtering on events once their field
+///   values are known, but before any processing is done in the `event` method.
 /// - [`clone_span`] is called every time a span ID is cloned, and [`try_close`]
 ///   is called when a span ID is dropped. By default, these functions do
 ///   nothing. However, they can be used to implement reference counting for
@@ -75,6 +78,8 @@ use crate::stdlib::{
 /// [`clone_span`]: Subscriber::clone_span
 /// [`try_close`]: Subscriber::try_close
 /// [cs-reg]: crate::callsite#registering-callsites
+/// [`event`]: Subscriber::event
+/// [`event_enabled`]: Subscriber::event_enabled
 pub trait Subscriber: 'static {
     // === Span registry methods ==============================================
 
@@ -291,6 +296,17 @@ pub trait Subscriber: 'static {
     /// follow from _b_), it may silently do nothing.
     fn record_follows_from(&self, span: &span::Id, follows: &span::Id);
 
+    /// Determine if an [`Event`] should be recorded.
+    ///
+    /// By default, this returns `true` and `Subscriber`s can filter events in
+    /// [`event`][Self::event] without any penalty. However, when `event` is
+    /// more complicated, this can be used to determine if `event` should be
+    /// called at all, separating out the decision from the processing.
+    fn event_enabled(&self, event: &Event<'_>) -> bool {
+        let _ = event;
+        true
+    }
+
     /// Records that an [`Event`] has occurred.
     ///
     /// This method will be invoked when an Event is constructed by
@@ -696,6 +712,11 @@ where
         self.as_ref().record_follows_from(span, follows)
     }
 
+    #[inline]
+    fn event_enabled(&self, event: &Event<'_>) -> bool {
+        self.as_ref().event_enabled(event)
+    }
+
     #[inline]
     fn event(&self, event: &Event<'_>) {
         self.as_ref().event(event)
@@ -776,6 +797,11 @@ where
         self.as_ref().record_follows_from(span, follows)
     }
 
+    #[inline]
+    fn event_enabled(&self, event: &Event<'_>) -> bool {
+        self.as_ref().event_enabled(event)
+    }
+
     #[inline]
     fn event(&self, event: &Event<'_>) {
         self.as_ref().event(event)
diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs
index 8470cc1c0f..3c6a6ac40e 100644
--- a/tracing-subscriber/src/fmt/mod.rs
+++ b/tracing-subscriber/src/fmt/mod.rs
@@ -397,6 +397,11 @@ where
         self.inner.record_follows_from(span, follows)
     }
 
+    #[inline]
+    fn event_enabled(&self, event: &Event<'_>) -> bool {
+        self.inner.event_enabled(event)
+    }
+
     #[inline]
     fn event(&self, event: &Event<'_>) {
         self.inner.event(event);
diff --git a/tracing-subscriber/src/layer/layered.rs b/tracing-subscriber/src/layer/layered.rs
index 5c9b934e35..0160f99c1d 100644
--- a/tracing-subscriber/src/layer/layered.rs
+++ b/tracing-subscriber/src/layer/layered.rs
@@ -139,6 +139,16 @@ where
         self.layer.on_follows_from(span, follows, self.ctx());
     }
 
+    fn event_enabled(&self, event: &Event<'_>) -> bool {
+        if self.layer.event_enabled(event, self.ctx()) {
+            // if the outer layer enables the event, ask the inner subscriber.
+            self.inner.event_enabled(event)
+        } else {
+            // otherwise, the event is disabled by this layer
+            false
+        }
+    }
+
     fn event(&self, event: &Event<'_>) {
         self.inner.event(event);
         self.layer.on_event(event, self.ctx());
@@ -278,6 +288,17 @@ where
         self.layer.on_follows_from(span, follows, ctx);
     }
 
+    #[inline]
+    fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool {
+        if self.layer.event_enabled(event, ctx.clone()) {
+            // if the outer layer enables the event, ask the inner subscriber.
+            self.inner.event_enabled(event, ctx)
+        } else {
+            // otherwise, the event is disabled by this layer
+            false
+        }
+    }
+
     #[inline]
     fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
         self.inner.on_event(event, ctx.clone());
diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs
index d94742f0f2..a15870e691 100644
--- a/tracing-subscriber/src/layer/mod.rs
+++ b/tracing-subscriber/src/layer/mod.rs
@@ -415,6 +415,28 @@
 //! [`Interest::never()`] from its [`register_callsite`] method, filter
 //! evaluation will short-circuit and the span or event will be disabled.
 //!
+//! ### Enabling Interest
+//!
+//! Whenever an tracing event (or span) is emitted, it goes through a number of
+//! steps to determine how and how much it should be processed. The earlier an
+//! event is disabled, the less work has to be done to process the event, so
+//! `Layer`s that implement filtering should attempt to disable unwanted
+//! events as early as possible. In order, each event checks:
+//!
+//! - [`register_callsite`], once per callsite (roughly: once per time that
+//!   `event!` or `span!` is written in the source code; this is cached at the
+//!   callsite). See [`Subscriber::register_callsite`] and
+//!   [`tracing_core::callsite`] for a summary of how this behaves.
+//! - [`enabled`], once per emitted event (roughly: once per time that `event!`
+//!   or `span!` is *executed*), and only if `register_callsite` regesters an
+//!   [`Interest::sometimes`]. This is the main customization point to globally
+//!   filter events based on their [`Metadata`]. If an event can be disabled
+//!   based only on [`Metadata`], it should be, as this allows the construction
+//!   of the actual `Event`/`Span` to be skipped.
+//! - For events only (and not spans), [`event_enabled`] is called just before
+//!   processing the event. This gives layers one last chance to say that
+//!   an event should be filtered out, now that the event's fields are known.
+//!
 //! ## Per-Layer Filtering
 //!
 //! **Note**: per-layer filtering APIs currently require the [`"registry"` crate
@@ -634,6 +656,7 @@
 //! [the current span]: Context::current_span
 //! [`register_callsite`]: Layer::register_callsite
 //! [`enabled`]: Layer::enabled
+//! [`event_enabled`]: Layer::event_enabled
 //! [`on_enter`]: Layer::on_enter
 //! [`Layer::register_callsite`]: Layer::register_callsite
 //! [`Layer::enabled`]: Layer::enabled
@@ -832,6 +855,31 @@ where
     // seems like a good future-proofing measure as it may grow other methods later...
     fn on_follows_from(&self, _span: &span::Id, _follows: &span::Id, _ctx: Context<'_, S>) {}
 
+    /// Called before [`on_event`], to determine if `on_event` should be called.
+    ///
+    /// 
+ ///
+    ///
+    /// **Note**: This method determines whether an event is globally enabled,
+    /// *not* whether the individual `Layer` will be notified about the
+    /// event. This is intended to be used by `Layer`s that implement
+    /// filtering for the entire stack. `Layer`s which do not wish to be
+    /// notified about certain events but do not wish to globally disable them
+    /// should ignore those events in their [on_event][Self::on_event].
+    ///
+    /// 
+ /// + /// See [the trait-level documentation] for more information on filtering + /// with `Layer`s. + /// + /// [`on_event`]: Self::on_event + /// [`Interest`]: tracing_core::Interest + /// [the trait-level documentation]: #filtering-with-layers + #[inline] // collapse this to a constant please mrs optimizer + fn event_enabled(&self, _event: &Event<'_>, _ctx: Context<'_, S>) -> bool { + true + } + /// Notifies this layer that an event has occurred. fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {} @@ -1460,6 +1508,14 @@ where } } + #[inline] + fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool { + match self { + Some(ref inner) => inner.event_enabled(event, ctx), + None => false, + } + } + #[inline] fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { if let Some(ref inner) = self { @@ -1548,6 +1604,11 @@ feature! { self.deref().on_follows_from(span, follows, ctx) } + #[inline] + fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool { + self.deref().event_enabled(event, ctx) + } + #[inline] fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { self.deref().on_event(event, ctx) @@ -1629,6 +1690,10 @@ feature! { self.iter().all(|l| l.enabled(metadata, ctx.clone())) } + fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool { + self.iter().all(|l| l.event_enabled(event, ctx.clone())) + } + fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { for l in self { l.on_new_span(attrs, id, ctx.clone()); diff --git a/tracing-subscriber/src/reload.rs b/tracing-subscriber/src/reload.rs index 422917a413..7e88956ec9 100644 --- a/tracing-subscriber/src/reload.rs +++ b/tracing-subscriber/src/reload.rs @@ -134,6 +134,11 @@ where try_lock!(self.inner.read()).on_follows_from(span, follows, ctx) } + #[inline] + fn event_enabled(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) -> bool { + try_lock!(self.inner.read(), else return false).event_enabled(event, ctx) + } + #[inline] fn on_event(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_event(event, ctx) diff --git a/tracing-subscriber/tests/event_enabling.rs b/tracing-subscriber/tests/event_enabling.rs new file mode 100644 index 0000000000..8f67cfcbaf --- /dev/null +++ b/tracing-subscriber/tests/event_enabling.rs @@ -0,0 +1,81 @@ +#![cfg(feature = "registry")] + +use std::sync::{Arc, Mutex}; +use tracing::{subscriber::with_default, Event, Metadata, Subscriber}; +use tracing_subscriber::{layer::Context, prelude::*, registry, Layer}; + +struct TrackingLayer { + enabled: bool, + event_enabled_count: Arc>, + event_enabled: bool, + on_event_count: Arc>, +} + +impl Layer for TrackingLayer +where + C: Subscriber + Send + Sync + 'static, +{ + fn enabled(&self, _metadata: &Metadata<'_>, _ctx: Context<'_, C>) -> bool { + self.enabled + } + + fn event_enabled(&self, _event: &Event<'_>, _ctx: Context<'_, C>) -> bool { + *self.event_enabled_count.lock().unwrap() += 1; + self.event_enabled + } + + fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, C>) { + *self.on_event_count.lock().unwrap() += 1; + } +} + +#[test] +fn event_enabled_is_only_called_once() { + let event_enabled_count = Arc::new(Mutex::default()); + let count = event_enabled_count.clone(); + let subscriber = registry().with(TrackingLayer { + enabled: true, + event_enabled_count, + event_enabled: true, + on_event_count: Arc::new(Mutex::default()), + }); + with_default(subscriber, || { + tracing::error!("hiya!"); + }); + + assert_eq!(1, *count.lock().unwrap()); +} + +#[test] +fn event_enabled_not_called_when_not_enabled() { + let event_enabled_count = Arc::new(Mutex::default()); + let count = event_enabled_count.clone(); + let subscriber = registry().with(TrackingLayer { + enabled: false, + event_enabled_count, + event_enabled: true, + on_event_count: Arc::new(Mutex::default()), + }); + with_default(subscriber, || { + tracing::error!("hiya!"); + }); + + assert_eq!(0, *count.lock().unwrap()); +} + +#[test] +fn event_disabled_does_disable_event() { + let on_event_count = Arc::new(Mutex::default()); + let count = on_event_count.clone(); + let subscriber = registry().with(TrackingLayer { + enabled: true, + event_enabled_count: Arc::new(Mutex::default()), + event_enabled: false, + on_event_count, + }); + with_default(subscriber, || { + tracing::error!("hiya!"); + }); + + assert_eq!(0, *count.lock().unwrap()); +} From 67cfb5f3529962d43b2072a9f1c0c94d9dfe9b5a Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 21 Jun 2022 15:57:03 -0700 Subject: [PATCH 038/142] chore(ci): run MSRV checks with minimal versions (#2171) In many cases, new releases of a dependency can break compatibility with `tracing`'s minimum supported Rust version (MSRV). It shouldn't be necessary for a `tracing` crate to bump its MSRV when a dependency does, as users on older Rust versions should be able to depend on older versions of that crate explicitly and avoid bumping. Instead, we should probably just run our MSRV checks with minimal dependency versions. This way, we don't need to bump our MSRV when the latest version of a dependency does, unless we actually *need* to pick up that new version. This branch changes the `check_msrv` CI jobs to do that. I also did some minor dependency editing to actually make tracing build with `-Zminimal-versions`. Note that `tracing-futures` is currently excluded from the MSRV build because it depends on a really ancient version of Tokio that pulls in broken deps. We should probably drop support for Tokio 0.1 and release a new version of that crate, but for now, we have to skip it from the CI job temporarily. Signed-off-by: Eliza Weisman --- .github/workflows/check_msrv.yml | 59 ++++++++++++++++++++++++++------ tracing-futures/Cargo.toml | 4 +-- tracing-futures/src/lib.rs | 58 +++++++++++++++---------------- tracing-subscriber/Cargo.toml | 2 +- 4 files changed, 81 insertions(+), 42 deletions(-) diff --git a/.github/workflows/check_msrv.yml b/.github/workflows/check_msrv.yml index be257bfd30..9c345bb5b0 100644 --- a/.github/workflows/check_msrv.yml +++ b/.github/workflows/check_msrv.yml @@ -27,37 +27,76 @@ env: RUSTUP_MAX_RETRIES: 10 # Don't emit giant backtraces in the CI logs. RUST_BACKTRACE: short + MSRV: 1.49.0 + # TODO: remove this once tracing's MSRV is bumped. + APPENDER_MSRV: 1.53.0 jobs: check-msrv: # Run `cargo check` on our minimum supported Rust version (1.49.0). runs-on: ubuntu-latest steps: - - uses: actions/checkout@main - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@master + - name: "install Rust ${{ env.MSRV }}" + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.MSRV }} + profile: minimal + - name: "install Rust nightly" + uses: actions-rs/toolchain@v1 with: - toolchain: 1.49.0 + toolchain: nightly profile: minimal - override: true + - name: Select minimal versions + uses: actions-rs/cargo@v1 + with: + command: update + args: -Z minimal-versions + toolchain: nightly - name: Check uses: actions-rs/cargo@v1 with: command: check - args: --all --exclude=tracing-appender + # skip the following crates: + # - tracing-appender, as it has its own MSRV. + # TODO(eliza): remove this when appender is on the same MSRV as + # everything else + # - the examples, as they are not published & we don't care about + # MSRV support for them. + # - tracing-futures, as it depends on ancient tokio versions. + # TODO(eliza): remove this when the ancient tokio deps are dropped + args: >- + --workspace --all-features --locked + --exclude=tracing-appender + --exclude=tracing-examples + --exclude=tracing-futures + toolchain: ${{ env.MSRV }} # TODO: remove this once tracing's MSRV is bumped. check-msrv-appender: # Run `cargo check` on our minimum supported Rust version (1.53.0). runs-on: ubuntu-latest steps: - - uses: actions/checkout@main - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@master + - name: "install Rust ${{ env.APPENDER_MSRV }}" + uses: actions-rs/toolchain@v1 with: - toolchain: 1.53.0 + toolchain: ${{ env.APPENDER_MSRV }} profile: minimal - override: true + - name: "install Rust nightly" + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + profile: minimal + - name: Select minimal versions + uses: actions-rs/cargo@v1 + with: + command: update + args: -Z minimal-versions + toolchain: nightly - name: Check uses: actions-rs/cargo@v1 with: command: check - args: --lib=tracing-appender \ No newline at end of file + args: --all-features --locked -p tracing-appender + toolchain: ${{ env.APPENDER_MSRV }} \ No newline at end of file diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml index 6347b97449..196094414e 100644 --- a/tracing-futures/Cargo.toml +++ b/tracing-futures/Cargo.toml @@ -35,8 +35,8 @@ tokio-executor = { version = "0.1", optional = true } tokio = { version = "0.1", optional = true } [dev-dependencies] -tokio = "0.1.22" -tokio-test = "0.3" +tokio = "1" +tokio-test = "0.4" tracing-core = { path = "../tracing-core", version = "0.1.2" } tracing-mock = { path = "../tracing-mock" } diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs index addc624df8..2712c160ac 100644 --- a/tracing-futures/src/lib.rs +++ b/tracing-futures/src/lib.rs @@ -627,35 +627,35 @@ mod tests { handle.assert_finished(); } - #[test] - fn span_follows_future_onto_threadpool() { - let (subscriber, handle) = subscriber::mock() - .enter(span::mock().named("a")) - .enter(span::mock().named("b")) - .exit(span::mock().named("b")) - .enter(span::mock().named("b")) - .exit(span::mock().named("b")) - .drop_span(span::mock().named("b")) - .exit(span::mock().named("a")) - .drop_span(span::mock().named("a")) - .done() - .run_with_handle(); - let mut runtime = tokio::runtime::Runtime::new().unwrap(); - with_default(subscriber, || { - tracing::trace_span!("a").in_scope(|| { - let future = PollN::new_ok(2) - .instrument(tracing::trace_span!("b")) - .map(|_| { - tracing::trace_span!("c").in_scope(|| { - // "c" happens _outside_ of the instrumented future's - // span, so we don't expect it. - }) - }); - runtime.block_on(Box::new(future)).unwrap(); - }) - }); - handle.assert_finished(); - } + // #[test] + // fn span_follows_future_onto_threadpool() { + // let (subscriber, handle) = subscriber::mock() + // .enter(span::mock().named("a")) + // .enter(span::mock().named("b")) + // .exit(span::mock().named("b")) + // .enter(span::mock().named("b")) + // .exit(span::mock().named("b")) + // .drop_span(span::mock().named("b")) + // .exit(span::mock().named("a")) + // .drop_span(span::mock().named("a")) + // .done() + // .run_with_handle(); + // let mut runtime = tokio::runtime::Runtime::new().unwrap(); + // with_default(subscriber, || { + // tracing::trace_span!("a").in_scope(|| { + // let future = PollN::new_ok(2) + // .instrument(tracing::trace_span!("b")) + // .map(|_| { + // tracing::trace_span!("c").in_scope(|| { + // // "c" happens _outside_ of the instrumented future's + // // span, so we don't expect it. + // }) + // }); + // runtime.block_on(Box::new(future)).unwrap(); + // }) + // }); + // handle.assert_finished(); + // } } #[cfg(all(feature = "futures-03", feature = "std-future"))] diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 3595318c46..9fe05df14f 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -76,7 +76,7 @@ tracing-log = { path = "../tracing-log", version = "0.1.2" } criterion = { version = "0.3", default_features = false } regex = { version = "1", default-features = false, features = ["std"] } tracing-futures = { path = "../tracing-futures", version = "0.2", default-features = false, features = ["std-future", "std"] } -tokio = { version = "0.2", features = ["rt-core", "macros"] } +tokio = { version = "1", features = ["rt", "macros"] } # Enable the `time` crate's `macros` feature, for examples. time = { version = "0.3", features = ["formatting", "macros"] } From f7966bde765ca435ce120100dcc5d7368fe1a0d4 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 22 Jun 2022 15:43:25 -0700 Subject: [PATCH 039/142] core: remove vendored `lazy_static` on no-std (#2173) Currently, `no_std` targets use a vendored version of `lazy_static` that uses the `spin` crate's `Once` type, while the `std` target uses the `once_cell` crate's `Lazy` type. This is unfortunate, as the `lazy_static` macro has a different interface from the `Lazy` cell type. This increases the amount of code that differs based on whether or not `std` is enabled. This branch removes the vendored `lazy_static` macro and replaces it with a reimplementation of `once_cell::sync::Lazy` that uses `spin::Once` rather than `once_cell::sync::OnceCell` as the inner "once type". Now, all code can be written against a `Lazy` struct with the same interface, regardless of whether or not `std` is enabled. Signed-off-by: Eliza Weisman --- tracing-core/src/callsite.rs | 13 +--- tracing-core/src/lazy.rs | 76 +++++++++++++++++++ tracing-core/src/lazy_static/LICENSE | 26 ------- tracing-core/src/lazy_static/core_lazy.rs | 30 -------- tracing-core/src/lazy_static/mod.rs | 89 ----------------------- tracing-core/src/lib.rs | 5 +- tracing-core/src/stdlib.rs | 10 +-- 7 files changed, 85 insertions(+), 164 deletions(-) create mode 100644 tracing-core/src/lazy.rs delete mode 100644 tracing-core/src/lazy_static/LICENSE delete mode 100644 tracing-core/src/lazy_static/core_lazy.rs delete mode 100644 tracing-core/src/lazy_static/mod.rs diff --git a/tracing-core/src/callsite.rs b/tracing-core/src/callsite.rs index 87d084647b..d0fe1a3d6c 100644 --- a/tracing-core/src/callsite.rs +++ b/tracing-core/src/callsite.rs @@ -111,6 +111,7 @@ use crate::stdlib::{ }; use crate::{ dispatcher::Dispatch, + lazy::Lazy, metadata::{LevelFilter, Metadata}, subscriber::Interest, }; @@ -253,14 +254,7 @@ static CALLSITES: Callsites = Callsites { static DISPATCHERS: Dispatchers = Dispatchers::new(); -#[cfg(feature = "std")] -static LOCKED_CALLSITES: once_cell::sync::Lazy>> = - once_cell::sync::Lazy::new(Default::default); - -#[cfg(not(feature = "std"))] -crate::lazy_static! { - static ref LOCKED_CALLSITES: Mutex> = Mutex::new(Vec::new()); -} +static LOCKED_CALLSITES: Lazy>> = Lazy::new(Default::default); struct Callsites { list_head: AtomicPtr, @@ -514,8 +508,7 @@ mod private { #[cfg(feature = "std")] mod dispatchers { - use crate::dispatcher; - use once_cell::sync::Lazy; + use crate::{dispatcher, lazy::Lazy}; use std::sync::{ atomic::{AtomicBool, Ordering}, RwLock, RwLockReadGuard, RwLockWriteGuard, diff --git a/tracing-core/src/lazy.rs b/tracing-core/src/lazy.rs new file mode 100644 index 0000000000..4f004e6364 --- /dev/null +++ b/tracing-core/src/lazy.rs @@ -0,0 +1,76 @@ +#[cfg(feature = "std")] +pub(crate) use once_cell::sync::Lazy; + +#[cfg(not(feature = "std"))] +pub(crate) use self::spin::Lazy; + +#[cfg(not(feature = "std"))] +mod spin { + //! This is the `once_cell::sync::Lazy` type, but modified to use our + //! `spin::Once` type rather than `OnceCell`. This is used to replace + //! `once_cell::sync::Lazy` on `no-std` builds. + use crate::spin::Once; + use core::{cell::Cell, fmt, ops::Deref}; + + /// Re-implementation of `once_cell::sync::Lazy` on top of `spin::Once` + /// rather than `OnceCell`. + /// + /// This is used when the standard library is disabled. + pub(crate) struct Lazy T> { + cell: Once, + init: Cell>, + } + + impl fmt::Debug for Lazy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Lazy") + .field("cell", &self.cell) + .field("init", &"..") + .finish() + } + } + + // We never create a `&F` from a `&Lazy` so it is fine to not impl + // `Sync` for `F`. We do create a `&mut Option` in `force`, but this is + // properly synchronized, so it only happens once so it also does not + // contribute to this impl. + unsafe impl Sync for Lazy where Once: Sync {} + // auto-derived `Send` impl is OK. + + impl Lazy { + /// Creates a new lazy value with the given initializing function. + pub(crate) const fn new(init: F) -> Lazy { + Lazy { + cell: Once::new(), + init: Cell::new(Some(init)), + } + } + } + + impl T> Lazy { + /// Forces the evaluation of this lazy value and returns a reference to + /// the result. + /// + /// This is equivalent to the `Deref` impl, but is explicit. + pub(crate) fn force(this: &Lazy) -> &T { + this.cell.call_once(|| match this.init.take() { + Some(f) => f(), + None => panic!("Lazy instance has previously been poisoned"), + }) + } + } + + impl T> Deref for Lazy { + type Target = T; + fn deref(&self) -> &T { + Lazy::force(self) + } + } + + impl Default for Lazy { + /// Creates a new lazy value using `Default` as the initializing function. + fn default() -> Lazy { + Lazy::new(T::default) + } + } +} diff --git a/tracing-core/src/lazy_static/LICENSE b/tracing-core/src/lazy_static/LICENSE deleted file mode 100644 index 28e478827c..0000000000 --- a/tracing-core/src/lazy_static/LICENSE +++ /dev/null @@ -1,26 +0,0 @@ - -Copyright (c) 2010 The Rust Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/tracing-core/src/lazy_static/core_lazy.rs b/tracing-core/src/lazy_static/core_lazy.rs deleted file mode 100644 index c61d36202d..0000000000 --- a/tracing-core/src/lazy_static/core_lazy.rs +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016 lazy-static.rs Developers -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -use crate::spin::Once; - -pub(crate) struct Lazy(Once); - -impl Lazy { - pub(crate) const INIT: Self = Lazy(Once::INIT); - - #[inline(always)] - pub(crate) fn get(&'static self, builder: F) -> &T - where - F: FnOnce() -> T, - { - self.0.call_once(builder) - } -} - -#[macro_export] -#[doc(hidden)] -macro_rules! __lazy_static_create { - ($NAME:ident, $T:ty) => { - static $NAME: $crate::lazy_static::lazy::Lazy<$T> = $crate::lazy_static::lazy::Lazy::INIT; - }; -} diff --git a/tracing-core/src/lazy_static/mod.rs b/tracing-core/src/lazy_static/mod.rs deleted file mode 100644 index 78f0ae722b..0000000000 --- a/tracing-core/src/lazy_static/mod.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2016 lazy-static.rs Developers -// -// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be -// copied, modified, or distributed except according to those terms. - -/*! -A macro for declaring lazily evaluated statics. -Using this macro, it is possible to have `static`s that require code to be -executed at runtime in order to be initialized. -This includes anything requiring heap allocations, like vectors or hash maps, -as well as anything that requires function calls to be computed. -*/ - -#[path = "core_lazy.rs"] -pub(crate) mod lazy; - -#[doc(hidden)] -pub(crate) use core::ops::Deref as __Deref; - -#[macro_export] -#[doc(hidden)] -macro_rules! __lazy_static_internal { - // optional visibility restrictions are wrapped in `()` to allow for - // explicitly passing otherwise implicit information about private items - ($(#[$attr:meta])* ($($vis:tt)*) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { - $crate::__lazy_static_internal!(@MAKE TY, $(#[$attr])*, ($($vis)*), $N); - $crate::__lazy_static_internal!(@TAIL, $N : $T = $e); - $crate::lazy_static!($($t)*); - }; - (@TAIL, $N:ident : $T:ty = $e:expr) => { - impl $crate::lazy_static::__Deref for $N { - type Target = $T; - fn deref(&self) -> &$T { - #[inline(always)] - fn __static_ref_initialize() -> $T { $e } - - #[inline(always)] - fn __stability() -> &'static $T { - $crate::__lazy_static_create!(LAZY, $T); - LAZY.get(__static_ref_initialize) - } - __stability() - } - } - impl $crate::lazy_static::LazyStatic for $N { - fn initialize(lazy: &Self) { - let _ = &**lazy; - } - } - }; - // `vis` is wrapped in `()` to prevent parsing ambiguity - (@MAKE TY, $(#[$attr:meta])*, ($($vis:tt)*), $N:ident) => { - #[allow(missing_copy_implementations)] - #[allow(non_camel_case_types)] - #[allow(dead_code)] - $(#[$attr])* - $($vis)* struct $N {__private_field: ()} - #[doc(hidden)] - $($vis)* static $N: $N = $N {__private_field: ()}; - }; - () => () -} - -#[macro_export] -#[doc(hidden)] -macro_rules! lazy_static { - ($(#[$attr:meta])* static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { - // use `()` to explicitly forward the information about private items - $crate::__lazy_static_internal!($(#[$attr])* () static ref $N : $T = $e; $($t)*); - }; - ($(#[$attr:meta])* pub static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { - $crate::__lazy_static_internal!($(#[$attr])* (pub) static ref $N : $T = $e; $($t)*); - }; - ($(#[$attr:meta])* pub ($($vis:tt)+) static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { - $crate::__lazy_static_internal!($(#[$attr])* (pub ($($vis)+)) static ref $N : $T = $e; $($t)*); - }; - () => () -} - -/// Support trait for enabling a few common operation on lazy static values. -/// -/// This is implemented by each defined lazy static, and -/// used by the free functions in this crate. -pub(crate) trait LazyStatic { - #[doc(hidden)] - fn initialize(lazy: &Self); -} diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs index eceef7be22..c1f87b22f0 100644 --- a/tracing-core/src/lib.rs +++ b/tracing-core/src/lib.rs @@ -254,10 +254,7 @@ macro_rules! metadata { }; } -// Facade module: `no_std` uses spinlocks, `std` uses the mutexes in the standard library -#[cfg(not(feature = "std"))] -#[macro_use] -mod lazy_static; +pub(crate) mod lazy; // Trimmed-down vendored version of spin 0.5.2 (0387621) // Dependency of no_std lazy_static, not required in a std build diff --git a/tracing-core/src/stdlib.rs b/tracing-core/src/stdlib.rs index 4a1c17c2b8..741549519c 100644 --- a/tracing-core/src/stdlib.rs +++ b/tracing-core/src/stdlib.rs @@ -64,11 +64,11 @@ mod no_std { } impl Mutex { - pub(crate) fn new(data: T) -> Self { - Self { - inner: crate::spin::Mutex::new(data), - } - } + // pub(crate) fn new(data: T) -> Self { + // Self { + // inner: crate::spin::Mutex::new(data), + // } + // } pub(crate) fn lock(&self) -> Result, ()> { Ok(self.inner.lock()) From b007591ebf12c6d59b7e2c81d446d21298e543be Mon Sep 17 00:00:00 2001 From: Lily Mara <54288692+lilymara-onesignal@users.noreply.github.com> Date: Wed, 22 Jun 2022 20:20:51 -0700 Subject: [PATCH 040/142] opentelemetry: add semconv exception fields (#2135) When an error value is recorded, it should add the `exception.message` and `exception.stacktrace` fields from the opentelemetry semantic conventions to the span/event. Ideally the `exception.type` field would be provided also, but there is currently not a mechanism to determine the type name of a trait object. --- tracing-opentelemetry/src/layer.rs | 364 ++++++++++++++++++++++++----- 1 file changed, 302 insertions(+), 62 deletions(-) diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs index 2c4749a1c1..a722dfd185 100644 --- a/tracing-opentelemetry/src/layer.rs +++ b/tracing-opentelemetry/src/layer.rs @@ -23,6 +23,9 @@ const SPAN_KIND_FIELD: &str = "otel.kind"; const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code"; const SPAN_STATUS_MESSAGE_FIELD: &str = "otel.status_message"; +const FIELD_EXCEPTION_MESSAGE: &str = "exception.message"; +const FIELD_EXCEPTION_STACKTRACE: &str = "exception.stacktrace"; + /// An [OpenTelemetry] propagation layer for use in a project that uses /// [tracing]. /// @@ -33,6 +36,7 @@ pub struct OpenTelemetryLayer { location: bool, tracked_inactivity: bool, with_threads: bool, + exception_config: ExceptionFieldConfig, get_context: WithContext, _registry: marker::PhantomData, } @@ -110,20 +114,26 @@ fn str_to_status_code(s: &str) -> Option { } } -struct SpanEventVisitor<'a>(&'a mut otel::Event); +struct SpanEventVisitor<'a, 'b> { + event_builder: &'a mut otel::Event, + span_builder: Option<&'b mut otel::SpanBuilder>, + exception_config: ExceptionFieldConfig, +} -impl<'a> field::Visit for SpanEventVisitor<'a> { +impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { /// Record events on the underlying OpenTelemetry [`Span`] from `bool` values. /// /// [`Span`]: opentelemetry::trace::Span fn record_bool(&mut self, field: &field::Field, value: bool) { match field.name() { - "message" => self.0.name = value.to_string().into(), + "message" => self.event_builder.name = value.to_string().into(), // Skip fields that are actually log metadata that have already been handled #[cfg(feature = "tracing-log")] name if name.starts_with("log.") => (), name => { - self.0.attributes.push(KeyValue::new(name, value)); + self.event_builder + .attributes + .push(KeyValue::new(name, value)); } } } @@ -133,12 +143,14 @@ impl<'a> field::Visit for SpanEventVisitor<'a> { /// [`Span`]: opentelemetry::trace::Span fn record_f64(&mut self, field: &field::Field, value: f64) { match field.name() { - "message" => self.0.name = value.to_string().into(), + "message" => self.event_builder.name = value.to_string().into(), // Skip fields that are actually log metadata that have already been handled #[cfg(feature = "tracing-log")] name if name.starts_with("log.") => (), name => { - self.0.attributes.push(KeyValue::new(name, value)); + self.event_builder + .attributes + .push(KeyValue::new(name, value)); } } } @@ -148,12 +160,14 @@ impl<'a> field::Visit for SpanEventVisitor<'a> { /// [`Span`]: opentelemetry::trace::Span fn record_i64(&mut self, field: &field::Field, value: i64) { match field.name() { - "message" => self.0.name = value.to_string().into(), + "message" => self.event_builder.name = value.to_string().into(), // Skip fields that are actually log metadata that have already been handled #[cfg(feature = "tracing-log")] name if name.starts_with("log.") => (), name => { - self.0.attributes.push(KeyValue::new(name, value)); + self.event_builder + .attributes + .push(KeyValue::new(name, value)); } } } @@ -163,12 +177,12 @@ impl<'a> field::Visit for SpanEventVisitor<'a> { /// [`Span`]: opentelemetry::trace::Span fn record_str(&mut self, field: &field::Field, value: &str) { match field.name() { - "message" => self.0.name = value.to_string().into(), + "message" => self.event_builder.name = value.to_string().into(), // Skip fields that are actually log metadata that have already been handled #[cfg(feature = "tracing-log")] name if name.starts_with("log.") => (), name => { - self.0 + self.event_builder .attributes .push(KeyValue::new(name, value.to_string())); } @@ -181,25 +195,99 @@ impl<'a> field::Visit for SpanEventVisitor<'a> { /// [`Span`]: opentelemetry::trace::Span fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { match field.name() { - "message" => self.0.name = format!("{:?}", value).into(), + "message" => self.event_builder.name = format!("{:?}", value).into(), // Skip fields that are actually log metadata that have already been handled #[cfg(feature = "tracing-log")] name if name.starts_with("log.") => (), name => { - self.0 + self.event_builder .attributes .push(KeyValue::new(name, format!("{:?}", value))); } } } + + /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s + /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field + /// + /// [`Span`]: opentelemetry::trace::Span + fn record_error( + &mut self, + field: &tracing_core::Field, + value: &(dyn std::error::Error + 'static), + ) { + let mut chain = Vec::new(); + let mut next_err = value.source(); + + while let Some(err) = next_err { + chain.push(Cow::Owned(err.to_string())); + next_err = err.source(); + } + + let error_msg = value.to_string(); + + if self.exception_config.record { + self.event_builder + .attributes + .push(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone())); + + // NOTE: This is actually not the stacktrace of the exception. This is + // the "source chain". It represents the heirarchy of errors from the + // app level to the lowest level such as IO. It does not represent all + // of the callsites in the code that led to the error happening. + // `std::error::Error::backtrace` is a nightly-only API and cannot be + // used here until the feature is stabilized. + self.event_builder + .attributes + .push(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone())); + } + + if self.exception_config.propagate { + if let Some(span) = &mut self.span_builder { + if let Some(attrs) = span.attributes.as_mut() { + attrs.push(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone())); + + // NOTE: This is actually not the stacktrace of the exception. This is + // the "source chain". It represents the heirarchy of errors from the + // app level to the lowest level such as IO. It does not represent all + // of the callsites in the code that led to the error happening. + // `std::error::Error::backtrace` is a nightly-only API and cannot be + // used here until the feature is stabilized. + attrs.push(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone())); + } + } + } + + self.event_builder + .attributes + .push(Key::new(field.name()).string(error_msg)); + self.event_builder + .attributes + .push(Key::new(format!("{}.chain", field.name())).array(chain)); + } } -struct SpanAttributeVisitor<'a>(&'a mut otel::SpanBuilder); +/// Control over opentelemetry conventional exception fields +#[derive(Clone, Copy)] +struct ExceptionFieldConfig { + /// If an error value is recorded on an event/span, should the otel fields + /// be added + record: bool, + + /// If an error value is recorded on an event, should the otel fields be + /// added to the corresponding span + propagate: bool, +} + +struct SpanAttributeVisitor<'a> { + span_builder: &'a mut otel::SpanBuilder, + exception_config: ExceptionFieldConfig, +} impl<'a> SpanAttributeVisitor<'a> { fn record(&mut self, attribute: KeyValue) { - debug_assert!(self.0.attributes.is_some()); - if let Some(v) = self.0.attributes.as_mut() { + debug_assert!(self.span_builder.attributes.is_some()); + if let Some(v) = self.span_builder.attributes.as_mut() { v.push(attribute); } } @@ -232,10 +320,12 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> { /// [`Span`]: opentelemetry::trace::Span fn record_str(&mut self, field: &field::Field, value: &str) { match field.name() { - SPAN_NAME_FIELD => self.0.name = value.to_string().into(), - SPAN_KIND_FIELD => self.0.span_kind = str_to_span_kind(value), - SPAN_STATUS_CODE_FIELD => self.0.status_code = str_to_status_code(value), - SPAN_STATUS_MESSAGE_FIELD => self.0.status_message = Some(value.to_owned().into()), + SPAN_NAME_FIELD => self.span_builder.name = value.to_string().into(), + SPAN_KIND_FIELD => self.span_builder.span_kind = str_to_span_kind(value), + SPAN_STATUS_CODE_FIELD => self.span_builder.status_code = str_to_status_code(value), + SPAN_STATUS_MESSAGE_FIELD => { + self.span_builder.status_message = Some(value.to_owned().into()) + } _ => self.record(KeyValue::new(field.name(), value.to_string())), } } @@ -246,13 +336,15 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> { /// [`Span`]: opentelemetry::trace::Span fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { match field.name() { - SPAN_NAME_FIELD => self.0.name = format!("{:?}", value).into(), - SPAN_KIND_FIELD => self.0.span_kind = str_to_span_kind(&format!("{:?}", value)), + SPAN_NAME_FIELD => self.span_builder.name = format!("{:?}", value).into(), + SPAN_KIND_FIELD => { + self.span_builder.span_kind = str_to_span_kind(&format!("{:?}", value)) + } SPAN_STATUS_CODE_FIELD => { - self.0.status_code = str_to_status_code(&format!("{:?}", value)) + self.span_builder.status_code = str_to_status_code(&format!("{:?}", value)) } SPAN_STATUS_MESSAGE_FIELD => { - self.0.status_message = Some(format!("{:?}", value).into()) + self.span_builder.status_message = Some(format!("{:?}", value).into()) } _ => self.record(Key::new(field.name()).string(format!("{:?}", value))), } @@ -275,7 +367,21 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> { next_err = err.source(); } - self.record(Key::new(field.name()).string(value.to_string())); + let error_msg = value.to_string(); + + if self.exception_config.record { + self.record(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone())); + + // NOTE: This is actually not the stacktrace of the exception. This is + // the "source chain". It represents the heirarchy of errors from the + // app level to the lowest level such as IO. It does not represent all + // of the callsites in the code that led to the error happening. + // `std::error::Error::backtrace` is a nightly-only API and cannot be + // used here until the feature is stabilized. + self.record(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone())); + } + + self.record(Key::new(field.name()).string(error_msg)); self.record(Key::new(format!("{}.chain", field.name())).array(chain)); } } @@ -318,6 +424,10 @@ where location: true, tracked_inactivity: true, with_threads: true, + exception_config: ExceptionFieldConfig { + record: false, + propagate: false, + }, get_context: WithContext(Self::get_context), _registry: marker::PhantomData, } @@ -358,11 +468,57 @@ where location: self.location, tracked_inactivity: self.tracked_inactivity, with_threads: self.with_threads, + exception_config: self.exception_config, get_context: WithContext(OpenTelemetryLayer::::get_context), _registry: self._registry, } } + /// Sets whether or not span and event metadata should include OpenTelemetry + /// exception fields such as `exception.message` and `exception.backtrace` + /// when an `Error` value is recorded. If multiple error values are recorded + /// on the same span/event, only the most recently recorded error value will + /// show up under these fields. + /// + /// These attributes follow the [OpenTelemetry semantic conventions for + /// exceptions][conv]. + /// + /// By default, these fields are enabled + /// + /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ + pub fn with_exception_fields(self, exception_fields: bool) -> Self { + Self { + exception_config: ExceptionFieldConfig { + record: exception_fields, + ..self.exception_config + }, + ..self + } + } + + /// Sets whether or not reporting an `Error` value on an event will + /// propagate the OpenTelemetry exception fields such as `exception.message` + /// and `exception.backtrace` to the corresponding span. You do not need to + /// enable `with_exception_fields` in order to enable this. If multiple + /// error values are recorded on the same span/event, only the most recently + /// recorded error value will show up under these fields. + /// + /// These attributes follow the [OpenTelemetry semantic conventions for + /// exceptions][conv]. + /// + /// By default, this is enabled + /// + /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ + pub fn with_exception_field_propagation(self, exception_field_propagation: bool) -> Self { + Self { + exception_config: ExceptionFieldConfig { + propagate: exception_field_propagation, + ..self.exception_config + }, + ..self + } + } + /// Sets whether or not span and event metadata should include OpenTelemetry /// attributes with location information, such as the file, module and line number. /// @@ -559,7 +715,10 @@ where } } - attrs.record(&mut SpanAttributeVisitor(&mut builder)); + attrs.record(&mut SpanAttributeVisitor { + span_builder: &mut builder, + exception_config: self.exception_config, + }); extensions.insert(OtelData { builder, parent_cx }); } @@ -600,7 +759,10 @@ where let span = ctx.span(id).expect("Span not found, this is a bug"); let mut extensions = span.extensions_mut(); if let Some(data) = extensions.get_mut::() { - values.record(&mut SpanAttributeVisitor(&mut data.builder)); + values.record(&mut SpanAttributeVisitor { + span_builder: &mut data.builder, + exception_config: self.exception_config, + }); } } @@ -665,15 +827,23 @@ where #[cfg(not(feature = "tracing-log"))] let target = target.string(meta.target()); + let mut extensions = span.extensions_mut(); + let span_builder = extensions + .get_mut::() + .map(|data| &mut data.builder); + let mut otel_event = otel::Event::new( String::new(), SystemTime::now(), vec![Key::new("level").string(meta.level().as_str()), target], 0, ); - event.record(&mut SpanEventVisitor(&mut otel_event)); + event.record(&mut SpanEventVisitor { + event_builder: &mut otel_event, + span_builder, + exception_config: self.exception_config, + }); - let mut extensions = span.extensions_mut(); if let Some(OtelData { builder, .. }) = extensions.get_mut::() { if builder.status_code.is_none() && *meta.level() == tracing_core::Level::ERROR { builder.status_code = Some(otel::StatusCode::Error); @@ -799,6 +969,8 @@ mod tests { use std::{ borrow::Cow, collections::HashMap, + error::Error, + fmt::Display, sync::{Arc, Mutex}, thread, time::SystemTime, @@ -876,6 +1048,36 @@ mod tests { fn end_with_timestamp(&mut self, _timestamp: SystemTime) {} } + #[derive(Debug)] + struct TestDynError { + msg: &'static str, + source: Option>, + } + impl Display for TestDynError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.msg) + } + } + impl Error for TestDynError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + match &self.source { + Some(source) => Some(source), + None => None, + } + } + } + impl TestDynError { + fn new(msg: &'static str) -> Self { + Self { msg, source: None } + } + fn with_parent(self, parent_msg: &'static str) -> Self { + Self { + msg: parent_msg, + source: Some(Box::new(self)), + } + } + } + #[test] fn dynamic_span_names() { let dynamic_name = "GET http://example.com".to_string(); @@ -984,41 +1186,15 @@ mod tests { #[test] fn records_error_fields() { let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); - - use std::error::Error; - use std::fmt::Display; - - #[derive(Debug)] - struct DynError { - msg: &'static str, - source: Option>, - } - - impl Display for DynError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.msg) - } - } - impl Error for DynError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match &self.source { - Some(source) => Some(source), - None => None, - } - } - } + let subscriber = tracing_subscriber::registry().with( + layer() + .with_tracer(tracer.clone()) + .with_exception_fields(true), + ); - let err = DynError { - msg: "user error", - source: Some(Box::new(DynError { - msg: "intermediate error", - source: Some(Box::new(DynError { - msg: "base error", - source: None, - })), - })), - }; + let err = TestDynError::new("base error") + .with_parent("intermediate error") + .with_parent("user error"); tracing::subscriber::with_default(subscriber, || { tracing::debug_span!( @@ -1055,6 +1231,18 @@ mod tests { .into() ) ); + + assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); + assert_eq!( + key_values[FIELD_EXCEPTION_STACKTRACE], + Value::Array( + vec![ + Cow::Borrowed("intermediate error"), + Cow::Borrowed("base error") + ] + .into() + ) + ); } #[test] @@ -1140,4 +1328,56 @@ mod tests { assert!(!keys.contains(&"thread.name")); assert!(!keys.contains(&"thread.id")); } + + #[test] + fn propagates_error_fields_from_event_to_span() { + let tracer = TestTracer(Arc::new(Mutex::new(None))); + let subscriber = tracing_subscriber::registry().with( + layer() + .with_tracer(tracer.clone()) + .with_exception_field_propagation(true), + ); + + let err = TestDynError::new("base error") + .with_parent("intermediate error") + .with_parent("user error"); + + tracing::subscriber::with_default(subscriber, || { + let _guard = tracing::debug_span!("request",).entered(); + + tracing::error!( + error = &err as &(dyn std::error::Error + 'static), + "request error!" + ) + }); + + let attributes = tracer + .0 + .lock() + .unwrap() + .as_ref() + .unwrap() + .builder + .attributes + .as_ref() + .unwrap() + .clone(); + + let key_values = attributes + .into_iter() + .map(|attr| (attr.key.as_str().to_owned(), attr.value)) + .collect::>(); + + assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); + assert_eq!( + key_values[FIELD_EXCEPTION_STACKTRACE], + Value::Array( + vec![ + Cow::Borrowed("intermediate error"), + Cow::Borrowed("base error") + ] + .into() + ) + ); + } } From 42fc29eea159589e29debfc5be4e894424d005f6 Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Thu, 23 Jun 2022 10:34:25 -0700 Subject: [PATCH 041/142] subscriber: implement `FIlter` for `reload::Layer` (#2159) ## Motivation The `reload` layer doesn't (and can't) implement downcasting correctly, which breaks certain `Layer`s which require downcasting (such as `tracing-opentelemetry`'s `OpenTelemetryLayer`). ## Solution Most usages of `reload` (including mine) are just to change a `Filter`, so this PR implements `Filter` on the `reload::Layer` type to allow users to not need to wrap the whole `Filtered` layer. Another advantage of this is that the common-case critical sections are shortened Co-authored-by: Eliza Weisman --- .../src/filter/layer_filters/mod.rs | 2 +- tracing-subscriber/src/reload.rs | 108 ++++++++++++++---- tracing-subscriber/tests/reload.rs | 62 +++++++++- 3 files changed, 146 insertions(+), 26 deletions(-) diff --git a/tracing-subscriber/src/filter/layer_filters/mod.rs b/tracing-subscriber/src/filter/layer_filters/mod.rs index 9432cf1988..8949cfb5a8 100644 --- a/tracing-subscriber/src/filter/layer_filters/mod.rs +++ b/tracing-subscriber/src/filter/layer_filters/mod.rs @@ -529,7 +529,7 @@ impl Filtered { /// # /// # // specifying the Registry type is required /// # let _: &reload::Handle std::io::Stdout>, - /// # filter::LevelFilter, Registry>, _> + /// # filter::LevelFilter, Registry>, Registry> /// # = &reload_handle; /// # /// info!("This will be logged to stderr"); diff --git a/tracing-subscriber/src/reload.rs b/tracing-subscriber/src/reload.rs index 7e88956ec9..3d1743440e 100644 --- a/tracing-subscriber/src/reload.rs +++ b/tracing-subscriber/src/reload.rs @@ -1,10 +1,11 @@ //! Wrapper for a `Layer` to allow it to be dynamically reloaded. //! -//! This module provides a [`Layer` type] which wraps another type implementing -//! the [`Layer` trait], allowing the wrapped type to be replaced with another +//! This module provides a [`Layer` type] implementing the [`Layer` trait] or [`Filter` trait] +//! which wraps another type implementing the corresponding trait. This +//! allows the wrapped type to be replaced with another //! instance of that type at runtime. //! -//! This can be used in cases where a subset of `Subscriber` functionality +//! This can be used in cases where a subset of `Layer` or `Filter` functionality //! should be dynamically reconfigured, such as when filtering directives may //! change at runtime. Note that this layer introduces a (relatively small) //! amount of overhead, and should thus only be used as needed. @@ -52,6 +53,15 @@ //! info!("This will be logged"); //! ``` //! +//! ## Note +//! +//! The [`Layer`] implementation is unable to implement downcasting functionality, +//! so certain [`Layer`] will fail to downcast if wrapped in a `reload::Layer`. +//! +//! If you only want to be able to dynamically change the +//! `Filter` on a layer, prefer wrapping that `Filter` in the `reload::Layer`. +//! +//! [`Filter` trait]: crate::layer::Filter //! [`Layer` type]: Layer //! [`Layer` trait]: super::layer::Layer use crate::layer; @@ -68,7 +78,7 @@ use tracing_core::{ Event, Metadata, }; -/// Wraps a `Layer`, allowing it to be reloaded dynamically at runtime. +/// Wraps a `Layer` or `Filter`, allowing it to be reloaded dynamically at runtime. #[derive(Debug)] pub struct Layer { // TODO(eliza): this once used a `crossbeam_util::ShardedRwLock`. We may @@ -165,13 +175,57 @@ where } } -impl Layer +// ===== impl Filter ===== + +#[cfg(all(feature = "registry", feature = "std"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))] +impl crate::layer::Filter for Layer where - L: crate::Layer + 'static, + L: crate::layer::Filter + 'static, S: Subscriber, { - /// Wraps the given `Layer`, returning a `Layer` and a `Handle` that allows - /// the inner type to be modified at runtime. + #[inline] + fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + try_lock!(self.inner.read(), else return Interest::sometimes()).callsite_enabled(metadata) + } + + #[inline] + fn enabled(&self, metadata: &Metadata<'_>, ctx: &layer::Context<'_, S>) -> bool { + try_lock!(self.inner.read(), else return false).enabled(metadata, ctx) + } + + #[inline] + fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) { + try_lock!(self.inner.read()).on_new_span(attrs, id, ctx) + } + + #[inline] + fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) { + try_lock!(self.inner.read()).on_record(span, values, ctx) + } + + #[inline] + fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) { + try_lock!(self.inner.read()).on_enter(id, ctx) + } + + #[inline] + fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) { + try_lock!(self.inner.read()).on_exit(id, ctx) + } + + #[inline] + fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) { + try_lock!(self.inner.read()).on_close(id, ctx) + } +} + +impl Layer { + /// Wraps the given [`Layer`] or [`Filter`], returning a `reload::Layer` + /// and a `Handle` that allows the inner value to be modified at runtime. + /// + /// [`Layer`]: crate::layer::Layer + /// [`Filter`]: crate::layer::Filter pub fn new(inner: L) -> (Self, Handle) { let this = Self { inner: Arc::new(RwLock::new(inner)), @@ -181,7 +235,10 @@ where (this, handle) } - /// Returns a `Handle` that can be used to reload the wrapped `Layer`. + /// Returns a `Handle` that can be used to reload the wrapped [`Layer`] or [`Filter`]. + /// + /// [`Layer`]: crate::layer::Layer + /// [`Filter`]: crate::filter::Filter pub fn handle(&self) -> Handle { Handle { inner: Arc::downgrade(&self.inner), @@ -192,24 +249,27 @@ where // ===== impl Handle ===== -impl Handle -where - L: crate::Layer + 'static, - S: Subscriber, -{ - /// Replace the current layer with the provided `new_layer`. +impl Handle { + /// Replace the current [`Layer`] or [`Filter`] with the provided `new_value`. + /// + /// [`Handle::reload`] cannot be used with the [`Filtered`] layer; use + /// [`Handle::modify`] instead (see [this issue] for additional details). + /// + /// However, if the _only_ the [`Filter`] needs to be modified, use + /// `reload::Layer` to wrap the `Filter` directly. + /// + /// [`Layer`]: crate::layer::Layer + /// [`Filter`]: crate::layer::Filter + /// [`Filtered`]: crate::filter::Filtered /// - /// **Warning:** The [`Filtered`](crate::filter::Filtered) type currently can't be changed - /// at runtime via the [`Handle::reload`] method. - /// Use the [`Handle::modify`] method to change the filter instead. - /// (see ) - pub fn reload(&self, new_layer: impl Into) -> Result<(), Error> { + /// [this issue]: https://github.com/tokio-rs/tracing/issues/1629 + pub fn reload(&self, new_value: impl Into) -> Result<(), Error> { self.modify(|layer| { - *layer = new_layer.into(); + *layer = new_value.into(); }) } - /// Invokes a closure with a mutable reference to the current layer, + /// Invokes a closure with a mutable reference to the current layer or filter, /// allowing it to be modified in place. pub fn modify(&self, f: impl FnOnce(&mut L)) -> Result<(), Error> { let inner = self.inner.upgrade().ok_or(Error { @@ -226,7 +286,7 @@ where Ok(()) } - /// Returns a clone of the layer's current value if it still exists. + /// Returns a clone of the layer or filter's current value if it still exists. /// Otherwise, if the subscriber has been dropped, returns `None`. pub fn clone_current(&self) -> Option where @@ -235,7 +295,7 @@ where self.with_current(L::clone).ok() } - /// Invokes a closure with a borrowed reference to the current layer, + /// Invokes a closure with a borrowed reference to the current layer or filter, /// returning the result (or an error if the subscriber no longer exists). pub fn with_current(&self, f: impl FnOnce(&L) -> T) -> Result { let inner = self.inner.upgrade().ok_or(Error { diff --git a/tracing-subscriber/tests/reload.rs b/tracing-subscriber/tests/reload.rs index 5fe422e085..b8b6c2b461 100644 --- a/tracing-subscriber/tests/reload.rs +++ b/tracing-subscriber/tests/reload.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "reload")] +#![cfg(feature = "std")] use std::sync::atomic::{AtomicUsize, Ordering}; use tracing_core::{ span::{Attributes, Id, Record}, @@ -79,3 +79,63 @@ fn reload_handle() { assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 1); }) } + +#[test] +#[cfg(feature = "registry")] +fn reload_filter() { + struct NopLayer; + impl tracing_subscriber::Layer for NopLayer { + fn register_callsite(&self, _m: &Metadata<'_>) -> Interest { + Interest::sometimes() + } + + fn enabled(&self, _m: &Metadata<'_>, _: layer::Context<'_, S>) -> bool { + true + } + } + + static FILTER1_CALLS: AtomicUsize = AtomicUsize::new(0); + static FILTER2_CALLS: AtomicUsize = AtomicUsize::new(0); + + enum Filter { + One, + Two, + } + + impl tracing_subscriber::layer::Filter for Filter { + fn enabled(&self, m: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool { + println!("ENABLED: {:?}", m); + match self { + Filter::One => FILTER1_CALLS.fetch_add(1, Ordering::SeqCst), + Filter::Two => FILTER2_CALLS.fetch_add(1, Ordering::SeqCst), + }; + true + } + } + fn event() { + tracing::trace!("my event"); + } + + let (filter, handle) = Layer::new(Filter::One); + + let dispatcher = tracing_core::Dispatch::new( + tracing_subscriber::registry().with(NopLayer.with_filter(filter)), + ); + + tracing_core::dispatcher::with_default(&dispatcher, || { + assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 0); + assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 0); + + event(); + + assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 1); + assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 0); + + handle.reload(Filter::Two).expect("should reload"); + + event(); + + assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 1); + assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 1); + }) +} From 2c1af52aceecfbc95c958890e578a2c80cc02f61 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 23 Jun 2022 15:00:11 -0700 Subject: [PATCH 042/142] chore(ci): test workflow embetterments (#2176) This branch makes the following changes to the CI test workflows: - Consolidate all tests into a single workflow again. We had previously broken things out to allow restarting only some failed checks, but now GitHub Actions allows restarting individual jobs, which is much nicer, and we can combine everything into one workflow. - Gate starting any tests/checks on an initial `cargo check` run. This should mean that if code doesn't compile, we don't spin up a huge number of test jobs that all end up failing, and delaying other PRs' CI runs. - Use `cargo nextest` for running tests. This should make test runs a bit quicker, and also get us other nice features like retries for flaky tests. - Switch to `taiki-e/install-action` for installing stuff like `cargo-hack`, `nextest`, and `wasm-pack`. This is a bit nicer than just `curl`ing stuff. - Use a matrix for testing across toolchains/OSes, instead of having separate jobs. This reduces the complexity of the CI workflow a bit. Signed-off-by: Eliza Weisman --- .config/nextest.toml | 12 + .github/workflows/CI.yml | 358 +++++++++++++++------ .github/workflows/check_features.yml | 123 ------- .github/workflows/check_msrv.yml | 102 ------ tracing-futures/Cargo.toml | 6 +- tracing-futures/src/executor/futures_01.rs | 2 +- tracing-mock/Cargo.toml | 2 +- 7 files changed, 271 insertions(+), 334 deletions(-) create mode 100644 .config/nextest.toml delete mode 100644 .github/workflows/check_features.yml delete mode 100644 .github/workflows/check_msrv.yml diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 0000000000..776f4269ee --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,12 @@ +# recommended nextest profile for CI jobs (from +# https://nexte.st/book/configuration.html#profiles) +[profile.ci] +# Print out output for failing tests as soon as they fail, and also at the end +# of the run (for easy scrollability). +failure-output = "immediate-final" +# Do not cancel the test run on the first failure. +fail-fast = false + +# TODO(eliza): uncomment this when we can get nicer JUnit output from nextest... +# [profile.ci.junit] +# path = "junit.xml" \ No newline at end of file diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7b91a9d934..d3ebdec9eb 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -26,10 +26,16 @@ env: RUSTUP_MAX_RETRIES: 10 # Don't emit giant backtraces in the CI logs. RUST_BACKTRACE: short + MSRV: 1.49.0 + # TODO: remove this once tracing's MSRV is bumped. + APPENDER_MSRV: 1.53.0 jobs: + ### check jobs ### + check: # Run `cargo check` first to ensure that the pushed code at least compiles. + name: cargo check runs-on: ubuntu-latest steps: - uses: actions/checkout@main @@ -44,7 +50,74 @@ jobs: command: check args: --all --tests --benches + style: + # Check style. + name: cargo fmt + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: rustfmt + profile: minimal + override: true + - name: rustfmt + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + warnings: + # Check for any warnings. This is informational and thus is allowed to fail. + runs-on: ubuntu-latest + needs: check + steps: + - uses: actions/checkout@main + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: clippy + profile: minimal + - name: Clippy + uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --all --examples --tests --benches -- -D warnings + + minimal-versions: + # Check for minimal-versions errors where a dependency is too + # underconstrained to build on the minimal supported version of all + # dependencies in the dependency graph. + name: cargo check (-Zminimal-versions) + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + profile: minimal + override: true + - name: install cargo-hack + uses: taiki-e/install-action@cargo-hack + - name: "check --all-features -Z minimal-versions" + run: | + # Remove dev-dependencies from Cargo.toml to prevent the next `cargo update` + # from determining minimal versions based on dev-dependencies. + cargo hack --remove-dev-deps --workspace + # Update Cargo.lock to minimal version dependencies. + cargo update -Z minimal-versions + cargo hack check \ + --package tracing \ + --package tracing-core \ + --package tracing-subscriber \ + --all-features --ignore-private + cargo-hack: + needs: check + name: cargo check (feature combinations) runs-on: ubuntu-latest strategy: matrix: @@ -59,10 +132,9 @@ jobs: - tracing-macros - tracing-serde - tracing-tower - # tracing and tracing-subscriber have too many features to be checked by - # cargo-hack --feature-powerset, combinatorics there is exploding. - #- tracing - #- tracing-subscriber + - tracing-opentelemetry + - tracing + - tracing-subscriber steps: - uses: actions/checkout@main - uses: actions-rs/toolchain@v1 @@ -70,21 +142,130 @@ jobs: toolchain: stable profile: minimal override: true - - name: Install cargo-hack - run: | - curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin + + - name: install cargo-hack + uses: taiki-e/install-action@cargo-hack - name: cargo hack check working-directory: ${{ matrix.subcrate }} - run: cargo hack check --feature-powerset --no-dev-deps + # tracing and tracing-subscriber have too many features to be checked by + # cargo-hack --feature-powerset with all features in the powerset, so + # exclude some + run: | + CARGO_HACK=(cargo hack check --feature-powerset --no-dev-deps) + case "${{ matrix.subcrate }}" in + tracing) + EXCLUDE_FEATURES=( + max_level_off max_level_error max_level_warn max_level_info + max_level_debug max_level_trace release_max_level_off + release_max_level_error release_max_level_warn + release_max_level_info release_max_level_debug + release_max_level_trace + ) + ${CARGO_HACK[@]} --exclude-features "${EXCLUDE_FEATURES[*]}" + ;; + tracing-subscriber) + INCLUDE_FEATURES=(fmt ansi json registry env-filter) + ${CARGO_HACK[@]} --include-features "${INCLUDE_FEATURES[*]}" + ;; + *) + ${CARGO_HACK[@]} + ;; + esac + shell: bash + + check-msrv: + # Run `cargo check` on our minimum supported Rust version (1.49.0). + name: "cargo check (MSRV on ubuntu-latest)" + needs: check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: "install Rust ${{ env.MSRV }}" + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.MSRV }} + profile: minimal + - name: "install Rust nightly" + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + profile: minimal + - name: Select minimal versions + uses: actions-rs/cargo@v1 + with: + command: update + args: -Z minimal-versions + toolchain: nightly + - name: Check + uses: actions-rs/cargo@v1 + with: + command: check + # skip the following crates: + # - tracing-appender, as it has its own MSRV. + # TODO(eliza): remove this when appender is on the same MSRV as + # everything else + # - the examples, as they are not published & we don't care about + # MSRV support for them. + # - tracing-futures, as it depends on ancient tokio versions. + # TODO(eliza): remove this when the ancient tokio deps are dropped + args: >- + --workspace --all-features --locked + --exclude=tracing-appender + --exclude=tracing-examples + --exclude=tracing-futures + toolchain: ${{ env.MSRV }} - test-versions: - # Test against the stable, beta, and nightly Rust toolchains on ubuntu-latest. + # TODO: remove this once tracing's MSRV is bumped. + check-msrv-appender: + # Run `cargo check` on our minimum supported Rust version (1.53.0). + name: "cargo check (tracing-appender MSRV)" needs: check runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: "install Rust ${{ env.APPENDER_MSRV }}" + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ env.APPENDER_MSRV }} + profile: minimal + - name: "install Rust nightly" + uses: actions-rs/toolchain@v1 + with: + toolchain: nightly + profile: minimal + - name: Select minimal versions + uses: actions-rs/cargo@v1 + with: + command: update + args: -Z minimal-versions + toolchain: nightly + - name: Check + uses: actions-rs/cargo@v1 + with: + command: check + args: --all-features --locked -p tracing-appender + toolchain: ${{ env.APPENDER_MSRV }} + + ### test jobs ############################################################# + + test: + # Test against stable Rust across macOS, Windows, and Linux, and against + # beta and nightly rust on Ubuntu. + name: "cargo test (${{ matrix.rust }} on ${{ matrix.os }})" + needs: check strategy: matrix: + # test all Rust versions on ubuntu-latest + os: [ubuntu-latest] rust: [stable, beta, nightly] + # test stable Rust on Windows and MacOS as well + include: + - rust: stable + os: windows-latest + - rust: stable + os: macos-latest fail-fast: false + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@main - uses: actions-rs/toolchain@v1 @@ -92,13 +273,24 @@ jobs: toolchain: ${{ matrix.rust }} profile: minimal override: true + - name: install cargo-nextest + uses: taiki-e/install-action@nextest - name: Run tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --all + run: cargo nextest run --profile ci --workspace + # TODO(eliza): punt on this for now because the generated JUnit report is + # missing some fields that this action needs to give good output. + # - name: Publish Test Report + # uses: mikepenz/action-junit-report@v3 + # if: always() # always run even if the previous step fails + # with: + # report_paths: 'target/nextest/ci/junit.xml' + # check_name: "cargo test (Rust ${{ matrix.rust }} on ${{ matrix.os }})" + # check_title_template: "{{SUITE_NAME}}::{{TEST_NAME}}" + - name: Run doctests + run: cargo test --doc --workspace test-build-wasm: + name: build tests (wasm) needs: check runs-on: ubuntu-latest strategy: @@ -132,6 +324,7 @@ jobs: args: --no-run -p ${{ matrix.subcrate }} test-wasm: + name: cargo test (wasm) needs: check runs-on: ubuntu-latest strategy: @@ -146,34 +339,16 @@ jobs: toolchain: stable override: true - name: install test runner for wasm - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + uses: taiki-e/install-action@wasm-pack - name: run wasm tests run: cd ${{ matrix.subcrate }} && wasm-pack test --node - test-os: - # Test against stable Rust across macOS, Windows, and Linux. - needs: check - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, windows-latest, macOS-latest] - steps: - - uses: actions/checkout@main - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - - name: Run tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --all - - test-unstable: - # Test the `tracing-unstable` cfg flags - env: - RUSTFLAGS: "--cfg tracing_unstable" + test-features-stable: + # Feature flag tests that run on stable Rust. + # TODO(david): once tracing's MSRV goes up to Rust 1.51, we should be able to switch to + # using cargo's V2 feature resolver (https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions) + # and avoid cd'ing into each crate's directory. + name: cargo test (feature-specific) needs: check runs-on: ubuntu-latest steps: @@ -183,71 +358,46 @@ jobs: toolchain: stable profile: minimal override: true - - name: Run tests - uses: actions-rs/cargo@v1 - with: - command: test - args: --all --features "valuable" + - name: "Test log support" + run: cargo test + working-directory: "tracing/test-log-support" + - name: "Test static max level" + run: cargo test + working-directory: "tracing/test_static_max_level_features" + - name: "Test static max level (release)" + run: cargo test --release + working-directory: "tracing/test_static_max_level_features" + - name: "Test tracing-core no-std support" + run: cargo test --no-default-features + working-directory: tracing + - name: "Test tracing no-std support" + run: cargo test --no-default-features + working-directory: tracing + # this skips running doctests under the `--no-default-features` flag, + # as rustdoc isn't aware of cargo's feature flags. + - name: "Test tracing-subscriber no-std support" + run: cargo test --lib --tests --no-default-features + working-directory: tracing-subscriber + - name: "Test tracing-subscriber with liballoc only" + run: cargo test --lib --tests --no-default-features --features "alloc" + working-directory: tracing-subscriber + - name: "Test tracing-subscriber with no default features" + run: cargo test --lib --tests --no-default-features --features "std" + working-directory: tracing-subscriber - style: - # Check style. - needs: check + # all required checks except for the main test run (which we only require + # specific matrix combinations from) + all_required: + name: "all systems go!" runs-on: ubuntu-latest + needs: + - style + - minimal-versions + - cargo-hack + - check-msrv + - check-msrv-appender + - test-build-wasm + - test-wasm + - test-features-stable steps: - - uses: actions/checkout@main - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: rustfmt - profile: minimal - override: true - - name: rustfmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - warnings: - # Check for any warnings. This is informational and thus is allowed to fail. - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@main - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: clippy - profile: minimal - - name: Clippy - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --all --examples --tests --benches -- -D warnings - - minimal-versions: - # Check for minimal-versions errors where a dependency is too - # underconstrained to build on the minimal supported version of all - # dependencies in the dependency graph. - name: minimal-versions - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - profile: minimal - override: true - - name: Install cargo-hack - run: | - curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin - - name: "check --all-features -Z minimal-versions" - run: | - # Remove dev-dependencies from Cargo.toml to prevent the next `cargo update` - # from determining minimal versions based on dev-dependencies. - cargo hack --remove-dev-deps --workspace - # Update Cargo.lock to minimal version dependencies. - cargo update -Z minimal-versions - cargo hack check \ - --package tracing \ - --package tracing-core \ - --package tracing-subscriber \ - --all-features --ignore-private + - run: exit 0 diff --git a/.github/workflows/check_features.yml b/.github/workflows/check_features.yml deleted file mode 100644 index 817492905a..0000000000 --- a/.github/workflows/check_features.yml +++ /dev/null @@ -1,123 +0,0 @@ -name: Check Features - -on: - push: - branches: - - master - - "v0.1.x" - pull_request: {} - -env: - # Disable incremental compilation. - # - # Incremental compilation is useful as part of an edit-build-test-edit cycle, - # as it lets the compiler avoid recompiling code that hasn't changed. However, - # on CI, we're not making small edits; we're almost always building the entire - # project from scratch. Thus, incremental compilation on CI actually - # introduces *additional* overhead to support making future builds - # faster...but no future builds will ever occur in any given CI environment. - # - # See https://matklad.github.io/2021/09/04/fast-rust-builds.html#ci-workflow - # for details. - CARGO_INCREMENTAL: 0 - # Allow more retries for network requests in cargo (downloading crates) and - # rustup (installing toolchains). This should help to reduce flaky CI failures - # from transient network timeouts or other issues. - CARGO_NET_RETRY: 10 - RUSTUP_MAX_RETRIES: 10 - # Don't emit giant backtraces in the CI logs. - RUST_BACKTRACE: short - -jobs: - cargo-hack: - runs-on: ubuntu-latest - strategy: - matrix: - # cargo hack --feature-powerset will have a significant permutation - # number, we can't just use --all as it increases the runtime - # further than what we would like to - subcrate: - - tracing-attributes - - tracing-core - - tracing-futures - - tracing-log - - tracing-macros - - tracing-serde - - tracing-tower - - tracing-opentelemetry - - tracing - - tracing-subscriber - steps: - - uses: actions/checkout@main - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - - name: Install cargo-hack - run: | - curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin - - name: cargo hack check - working-directory: ${{ matrix.subcrate }} - # tracing and tracing-subscriber have too many features to be checked by - # cargo-hack --feature-powerset with all features in the powerset, so - # exclude some - run: | - CARGO_HACK=(cargo hack check --feature-powerset --no-dev-deps) - case "${{ matrix.subcrate }}" in - tracing) - EXCLUDE_FEATURES=( - max_level_off max_level_error max_level_warn max_level_info - max_level_debug max_level_trace release_max_level_off - release_max_level_error release_max_level_warn - release_max_level_info release_max_level_debug - release_max_level_trace - ) - ${CARGO_HACK[@]} --exclude-features "${EXCLUDE_FEATURES[*]}" - ;; - tracing-subscriber) - INCLUDE_FEATURES=(fmt ansi json registry env-filter) - ${CARGO_HACK[@]} --include-features "${INCLUDE_FEATURES[*]}" - ;; - *) - ${CARGO_HACK[@]} - ;; - esac - shell: bash - - features-stable: - # Feature flag tests that run on stable Rust. - # TODO(david): once tracing's MSRV goes up to Rust 1.51, we should be able to switch to - # using cargo's V2 feature resolver (https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions) - # and avoid cd'ing into each crate's directory. - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@main - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - - name: "Test log support" - run: cargo test - working-directory: "tracing/test-log-support" - - name: "Test static max level" - run: cargo test - working-directory: "tracing/test_static_max_level_features" - - name: "Test tracing-core no-std support" - run: cargo test --no-default-features - working-directory: tracing-core - - name: "Test tracing no-std support" - run: cargo test --lib --tests --no-default-features - working-directory: tracing - # this skips running doctests under the `--no-default-features` flag, - # as rustdoc isn't aware of cargo's feature flags. - - name: "Test tracing-subscriber no-std support" - run: cargo test --lib --tests --no-default-features - working-directory: tracing-subscriber - - name: "Test tracing-subscriber with liballoc only" - run: cargo test --lib --tests --no-default-features --features "alloc" - working-directory: tracing-subscriber - - name: "Test tracing-subscriber with no default features" - run: cargo test --lib --tests --no-default-features --features "std" - working-directory: tracing-subscriber diff --git a/.github/workflows/check_msrv.yml b/.github/workflows/check_msrv.yml deleted file mode 100644 index 9c345bb5b0..0000000000 --- a/.github/workflows/check_msrv.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: Check MSRV - -on: - push: - branches: - - master - - "v0.1.x" - pull_request: {} - -env: - # Disable incremental compilation. - # - # Incremental compilation is useful as part of an edit-build-test-edit cycle, - # as it lets the compiler avoid recompiling code that hasn't changed. However, - # on CI, we're not making small edits; we're almost always building the entire - # project from scratch. Thus, incremental compilation on CI actually - # introduces *additional* overhead to support making future builds - # faster...but no future builds will ever occur in any given CI environment. - # - # See https://matklad.github.io/2021/09/04/fast-rust-builds.html#ci-workflow - # for details. - CARGO_INCREMENTAL: 0 - # Allow more retries for network requests in cargo (downloading crates) and - # rustup (installing toolchains). This should help to reduce flaky CI failures - # from transient network timeouts or other issues. - CARGO_NET_RETRY: 10 - RUSTUP_MAX_RETRIES: 10 - # Don't emit giant backtraces in the CI logs. - RUST_BACKTRACE: short - MSRV: 1.49.0 - # TODO: remove this once tracing's MSRV is bumped. - APPENDER_MSRV: 1.53.0 - -jobs: - check-msrv: - # Run `cargo check` on our minimum supported Rust version (1.49.0). - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - name: "install Rust ${{ env.MSRV }}" - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ env.MSRV }} - profile: minimal - - name: "install Rust nightly" - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - profile: minimal - - name: Select minimal versions - uses: actions-rs/cargo@v1 - with: - command: update - args: -Z minimal-versions - toolchain: nightly - - name: Check - uses: actions-rs/cargo@v1 - with: - command: check - # skip the following crates: - # - tracing-appender, as it has its own MSRV. - # TODO(eliza): remove this when appender is on the same MSRV as - # everything else - # - the examples, as they are not published & we don't care about - # MSRV support for them. - # - tracing-futures, as it depends on ancient tokio versions. - # TODO(eliza): remove this when the ancient tokio deps are dropped - args: >- - --workspace --all-features --locked - --exclude=tracing-appender - --exclude=tracing-examples - --exclude=tracing-futures - toolchain: ${{ env.MSRV }} - - # TODO: remove this once tracing's MSRV is bumped. - check-msrv-appender: - # Run `cargo check` on our minimum supported Rust version (1.53.0). - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@master - - name: "install Rust ${{ env.APPENDER_MSRV }}" - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ env.APPENDER_MSRV }} - profile: minimal - - name: "install Rust nightly" - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - profile: minimal - - name: Select minimal versions - uses: actions-rs/cargo@v1 - with: - command: update - args: -Z minimal-versions - toolchain: nightly - - name: Check - uses: actions-rs/cargo@v1 - with: - command: check - args: --all-features --locked -p tracing-appender - toolchain: ${{ env.APPENDER_MSRV }} \ No newline at end of file diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml index 196094414e..874d31d363 100644 --- a/tracing-futures/Cargo.toml +++ b/tracing-futures/Cargo.toml @@ -23,6 +23,7 @@ default = ["std-future", "std"] futures-01 = ["futures_01", "std"] futures-03 = ["std-future", "futures", "futures-task", "std"] std-future = ["pin-project-lite"] +tokio = ["tokio_01"] std = ["tracing/std"] [dependencies] @@ -32,13 +33,12 @@ futures-task = { version = "0.3", optional = true } pin-project-lite = { version = "0.2.4", optional = true } tracing = { path = "../tracing", version = "0.1", default-features = false } tokio-executor = { version = "0.1", optional = true } -tokio = { version = "0.1", optional = true } +tokio_01 = { package = "tokio", version = "0.1", optional = true } [dev-dependencies] -tokio = "1" tokio-test = "0.4" tracing-core = { path = "../tracing-core", version = "0.1.2" } -tracing-mock = { path = "../tracing-mock" } +tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] } [badges] maintenance = { status = "actively-developed" } diff --git a/tracing-futures/src/executor/futures_01.rs b/tracing-futures/src/executor/futures_01.rs index a2fddc36ab..56ba6e3c42 100644 --- a/tracing-futures/src/executor/futures_01.rs +++ b/tracing-futures/src/executor/futures_01.rs @@ -44,7 +44,7 @@ pub use self::tokio::*; mod tokio { use crate::{Instrument, Instrumented, WithDispatch}; use futures_01::Future; - use tokio::{ + use tokio_01::{ executor::{Executor, SpawnError, TypedExecutor}, runtime::{current_thread, Runtime, TaskExecutor}, }; diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml index 3b74dced26..8afbd5d344 100644 --- a/tracing-mock/Cargo.toml +++ b/tracing-mock/Cargo.toml @@ -20,7 +20,7 @@ publish = false [dependencies] tracing = { path = "../tracing", version = "0.1", default-features = false } tracing-core = { path = "../tracing-core", version = "0.1", default-features = false } -tokio-test = { version = "0.3.0", optional = true } +tokio-test = { version = "0.4", optional = true } [package.metadata.docs.rs] all-features = true From 2dc1f391763f12b0ed215b165951da4f8551b988 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 23 Jun 2022 15:07:47 -0700 Subject: [PATCH 043/142] chore(ci): also run CI on pushes to v0.1.x Signed-off-by: Eliza Weisman --- .github/workflows/CI.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index d3ebdec9eb..7abbdbfc77 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - "v0.1.x" pull_request: {} env: From af699c9b5d09972e94a4e45ebbf4d3a39010554c Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 23 Jun 2022 15:37:17 -0700 Subject: [PATCH 044/142] chore(ci): whoops, wrong commands for no-std tests Signed-off-by: Eliza Weisman --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7abbdbfc77..2529218a14 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -370,9 +370,9 @@ jobs: working-directory: "tracing/test_static_max_level_features" - name: "Test tracing-core no-std support" run: cargo test --no-default-features - working-directory: tracing + working-directory: tracing-core - name: "Test tracing no-std support" - run: cargo test --no-default-features + run: cargo test --lib --no-default-features working-directory: tracing # this skips running doctests under the `--no-default-features` flag, # as rustdoc isn't aware of cargo's feature flags. From 542c34dcf0b4ea5806d1e70e14ec97632dfefedb Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 23 Jun 2022 17:35:21 -0700 Subject: [PATCH 045/142] chore: prepare to release `tracing-core` v0.1.28 (#2177) # 0.1.28 (June 23, 2022) This release of `tracing-core` adds new `Value` implementations, including one for `String`, to allow recording `&String` as a value without having to call `as_str()` or similar, and for 128-bit integers (`i128` and `u128`). In addition, it adds new methods and trait implementations for `Subscriber`s. ### Added - `Value` implementation for `String` ([#2164]) - `Value` implementation for `u128` and `i28` ([#2166]) - `downcast_ref` and `is` methods for `dyn Subscriber + Sync`, `dyn Subscriber + Send`, and `dyn Subscriber + Send + Sync` ([#2160]) - `Subscriber::event_enabled` method to enable filtering based on `Event` field values ([#2008]) - `Subscriber` implementation for `Box` and `Arc` ([#2161]) Thanks to @jswrenn and @CAD97 for contributing to this release! [#2164]: https://github.com/tokio-rs/tracing/pull/2164 [#2166]: https://github.com/tokio-rs/tracing/pull/2166 [#2160]: https://github.com/tokio-rs/tracing/pull/2160 [#2008]: https://github.com/tokio-rs/tracing/pull/2008 [#2161]: https://github.com/tokio-rs/tracing/pull/2161 --- tracing-core/CHANGELOG.md | 26 ++++++++++++++++++++++++++ tracing-core/Cargo.toml | 2 +- tracing-core/README.md | 26 +++++++++++++------------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/tracing-core/CHANGELOG.md b/tracing-core/CHANGELOG.md index 2e6e12ec0e..c6afcb0776 100644 --- a/tracing-core/CHANGELOG.md +++ b/tracing-core/CHANGELOG.md @@ -1,3 +1,29 @@ +# 0.1.28 (June 23, 2022) + +This release of `tracing-core` adds new `Value` implementations, including one +for `String`, to allow recording `&String` as a value without having to call +`as_str()` or similar, and for 128-bit integers (`i128` and `u128`). In +addition, it adds new methods and trait implementations for `Subscriber`s. + +### Added + +- `Value` implementation for `String` ([#2164]) +- `Value` implementation for `u128` and `i28` ([#2166]) +- `downcast_ref` and `is` methods for `dyn Subscriber + Sync`, + `dyn Subscriber + Send`, and `dyn Subscriber + Send + Sync` ([#2160]) +- `Subscriber::event_enabled` method to enable filtering based on `Event` field + values ([#2008]) +- `Subscriber` implementation for `Box` and + `Arc` ([#2161]) + +Thanks to @jswrenn and @CAD97 for contributing to this release! + +[#2164]: https://github.com/tokio-rs/tracing/pull/2164 +[#2166]: https://github.com/tokio-rs/tracing/pull/2166 +[#2160]: https://github.com/tokio-rs/tracing/pull/2160 +[#2008]: https://github.com/tokio-rs/tracing/pull/2008 +[#2161]: https://github.com/tokio-rs/tracing/pull/2161 + # 0.1.27 (June 7, 2022) This release of `tracing-core` introduces a new `DefaultCallsite` type, which diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml index af03026726..b6bdaea29b 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -8,7 +8,7 @@ name = "tracing-core" # - README.md # - Update CHANGELOG.md. # - Create "v0.1.x" git tag. -version = "0.1.27" +version = "0.1.28" authors = ["Tokio Contributors "] license = "MIT" readme = "README.md" diff --git a/tracing-core/README.md b/tracing-core/README.md index bfd9ad5e9f..f3afec07ed 100644 --- a/tracing-core/README.md +++ b/tracing-core/README.md @@ -16,9 +16,9 @@ Core primitives for application-level tracing. [Documentation][docs-url] | [Chat][discord-url] [crates-badge]: https://img.shields.io/crates/v/tracing-core.svg -[crates-url]: https://crates.io/crates/tracing-core/0.1.27 +[crates-url]: https://crates.io/crates/tracing-core/0.1.28 [docs-badge]: https://docs.rs/tracing-core/badge.svg -[docs-url]: https://docs.rs/tracing-core/0.1.27 +[docs-url]: https://docs.rs/tracing-core/0.1.28 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_core [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg @@ -79,22 +79,22 @@ The following crate feature flags are available: ```toml [dependencies] - tracing-core = { version = "0.1.27", default-features = false } + tracing-core = { version = "0.1.28", default-features = false } ``` **Note**:`tracing-core`'s `no_std` support requires `liballoc`. [`tracing`]: ../tracing -[`span::Id`]: https://docs.rs/tracing-core/0.1.27/tracing_core/span/struct.Id.html -[`Event`]: https://docs.rs/tracing-core/0.1.27/tracing_core/event/struct.Event.html -[`Subscriber`]: https://docs.rs/tracing-core/0.1.27/tracing_core/subscriber/trait.Subscriber.html -[`Metadata`]: https://docs.rs/tracing-core/0.1.27/tracing_core/metadata/struct.Metadata.html -[`Callsite`]: https://docs.rs/tracing-core/0.1.27/tracing_core/callsite/trait.Callsite.html -[`Field`]: https://docs.rs/tracing-core/0.1.27/tracing_core/field/struct.Field.html -[`FieldSet`]: https://docs.rs/tracing-core/0.1.27/tracing_core/field/struct.FieldSet.html -[`Value`]: https://docs.rs/tracing-core/0.1.27/tracing_core/field/trait.Value.html -[`ValueSet`]: https://docs.rs/tracing-core/0.1.27/tracing_core/field/struct.ValueSet.html -[`Dispatch`]: https://docs.rs/tracing-core/0.1.27/tracing_core/dispatcher/struct.Dispatch.html +[`span::Id`]: https://docs.rs/tracing-core/0.1.28/tracing_core/span/struct.Id.html +[`Event`]: https://docs.rs/tracing-core/0.1.28/tracing_core/event/struct.Event.html +[`Subscriber`]: https://docs.rs/tracing-core/0.1.28/tracing_core/subscriber/trait.Subscriber.html +[`Metadata`]: https://docs.rs/tracing-core/0.1.28/tracing_core/metadata/struct.Metadata.html +[`Callsite`]: https://docs.rs/tracing-core/0.1.28/tracing_core/callsite/trait.Callsite.html +[`Field`]: https://docs.rs/tracing-core/0.1.28/tracing_core/field/struct.Field.html +[`FieldSet`]: https://docs.rs/tracing-core/0.1.28/tracing_core/field/struct.FieldSet.html +[`Value`]: https://docs.rs/tracing-core/0.1.28/tracing_core/field/trait.Value.html +[`ValueSet`]: https://docs.rs/tracing-core/0.1.28/tracing_core/field/struct.ValueSet.html +[`Dispatch`]: https://docs.rs/tracing-core/0.1.28/tracing_core/dispatcher/struct.Dispatch.html ## Supported Rust Versions From 181bdbe1d8f6222aaff6dea17267d0779d07caf3 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 24 Jun 2022 10:25:01 -0700 Subject: [PATCH 046/142] tracing: actually run `global_subscriber` benchmark Seems like this was never actually being run due me being dumb and forgetting to add it to the Cargo.toml. Whoops! --- tracing/Cargo.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index d32d34927b..6e122de01e 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -75,6 +75,10 @@ harness = false name = "no_subscriber" harness = false +[[bench]] +name = "global_subscriber" +harness = false + [badges] maintenance = { status = "actively-developed" } From ebea0e4cc0838ea1438cbcbb693eb135606169be Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 24 Jun 2022 13:30:36 -0700 Subject: [PATCH 047/142] tracing: reorganize benchmarks for comparability (#2178) Currently, `tracing`'s benchmark suite benchmarks the same behaviors (e.g. creating a span, recording an event, etc) across a handful of cases: with no default dispatcher, with a global default, and with a scoped (thread-local) default. We use criterion's `benchmark_group` to represent each kind of dispatcher, and `bench_function` for each behavior being measured. This is actually kind of backwards relative to how Criterion is _supposed_ to be used. `bench_function` is intended for comparing different implementations of the *same* thing, with the `benchmark_group` representing what's being compared. If we inverted the structure of these benchmarks, Criterion would give us nicer plots that would allow comparing the performance of each dispatch type on the same task. This PR reorganizes the benchmarks so that each behavior being tested (such as entering a span or recording an event) is a `benchmark_group`, and each dispatch type (none, global, or scoped) is a `bench_function` within that group. Now, Criterion will generate plots for each group comparing the performance of each dispatch type in that benchmark. For example, we now get nice comparisons like this: ![image](https://user-images.githubusercontent.com/2796466/175659314-835664ac-a8cf-4b07-91ee-f8cfee77bfbb.png) Unfortunately, this required splitting each benchmark type out into its own file. This is because, once we set the global default dispatcher within one benchmark group, it would remain set for the entire lifetime of the process --- there would be no way to test something else with no global default. But, I think this is fine, even though it means we now have a bunch of tiny files: it also allows us to run an individual benchmark against every combination of dispatch types, without having to run unrelated benches. This is potentially useful if (for example) someone is changing only the code for recording events, and not spans. --- tracing/Cargo.toml | 30 +++- tracing/benches/baseline.rs | 24 ++++ tracing/benches/dispatch_get_clone.rs | 15 ++ tracing/benches/dispatch_get_ref.rs | 16 +++ tracing/benches/empty_span.rs | 43 ++++++ tracing/benches/enter_span.rs | 16 +++ tracing/benches/event.rs | 12 ++ tracing/benches/global_subscriber.rs | 136 ------------------ tracing/benches/no_subscriber.rs | 101 -------------- tracing/benches/shared.rs | 160 ++++++++++++++++++++++ tracing/benches/span_fields.rs | 23 ++++ tracing/benches/span_no_fields.rs | 13 ++ tracing/benches/span_repeated.rs | 20 +++ tracing/benches/subscriber.rs | 189 -------------------------- 14 files changed, 369 insertions(+), 429 deletions(-) create mode 100644 tracing/benches/baseline.rs create mode 100644 tracing/benches/dispatch_get_clone.rs create mode 100644 tracing/benches/dispatch_get_ref.rs create mode 100644 tracing/benches/empty_span.rs create mode 100644 tracing/benches/enter_span.rs create mode 100644 tracing/benches/event.rs delete mode 100644 tracing/benches/global_subscriber.rs delete mode 100644 tracing/benches/no_subscriber.rs create mode 100644 tracing/benches/shared.rs create mode 100644 tracing/benches/span_fields.rs create mode 100644 tracing/benches/span_no_fields.rs create mode 100644 tracing/benches/span_repeated.rs delete mode 100644 tracing/benches/subscriber.rs diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 6e122de01e..be4139245d 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -68,15 +68,39 @@ attributes = ["tracing-attributes"] valuable = ["tracing-core/valuable"] [[bench]] -name = "subscriber" +name = "baseline" harness = false [[bench]] -name = "no_subscriber" +name = "dispatch_get_clone" harness = false [[bench]] -name = "global_subscriber" +name = "dispatch_get_ref" +harness = false + +[[bench]] +name = "empty_span" +harness = false + +[[bench]] +name = "enter_span" +harness = false + +[[bench]] +name = "event" +harness = false + +[[bench]] +name = "span_fields" +harness = false + +[[bench]] +name = "span_no_fields" +harness = false + +[[bench]] +name = "span_repeated" harness = false [badges] diff --git a/tracing/benches/baseline.rs b/tracing/benches/baseline.rs new file mode 100644 index 0000000000..93c14f422c --- /dev/null +++ b/tracing/benches/baseline.rs @@ -0,0 +1,24 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +fn bench(c: &mut Criterion) { + use std::sync::atomic::{AtomicUsize, Ordering}; + + let mut group = c.benchmark_group("comparison"); + group.bench_function("relaxed_load", |b| { + let foo = AtomicUsize::new(1); + b.iter(|| black_box(foo.load(Ordering::Relaxed))); + }); + group.bench_function("acquire_load", |b| { + let foo = AtomicUsize::new(1); + b.iter(|| black_box(foo.load(Ordering::Acquire))) + }); + group.bench_function("log", |b| { + b.iter(|| { + log::log!(log::Level::Info, "log"); + }) + }); + group.finish(); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/tracing/benches/dispatch_get_clone.rs b/tracing/benches/dispatch_get_clone.rs new file mode 100644 index 0000000000..15577c6969 --- /dev/null +++ b/tracing/benches/dispatch_get_clone.rs @@ -0,0 +1,15 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +mod shared; + +fn bench(c: &mut Criterion) { + shared::for_all_dispatches(&mut c.benchmark_group("Dispatch::get_clone"), |b| { + b.iter(|| { + let current = tracing::dispatcher::get_default(|current| current.clone()); + black_box(current); + }) + }); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/tracing/benches/dispatch_get_ref.rs b/tracing/benches/dispatch_get_ref.rs new file mode 100644 index 0000000000..a59c343795 --- /dev/null +++ b/tracing/benches/dispatch_get_ref.rs @@ -0,0 +1,16 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +mod shared; + +fn bench(c: &mut Criterion) { + shared::for_all_dispatches(&mut c.benchmark_group("Dispatch::get_ref"), |b| { + b.iter(|| { + tracing::dispatcher::get_default(|current| { + black_box(¤t); + }) + }) + }); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/tracing/benches/empty_span.rs b/tracing/benches/empty_span.rs new file mode 100644 index 0000000000..fb38b08e15 --- /dev/null +++ b/tracing/benches/empty_span.rs @@ -0,0 +1,43 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; + +mod shared; + +fn bench(c: &mut Criterion) { + let mut group = c.benchmark_group("empty_span"); + shared::for_all_dispatches(&mut group, |b| { + b.iter(|| { + let span = tracing::span::Span::none(); + black_box(&span); + }) + }); + group.bench_function("baseline_struct", |b| { + b.iter(|| { + let span = FakeEmptySpan::new(); + black_box(&span); + }) + }); +} + +struct FakeEmptySpan { + inner: Option<(usize, std::sync::Arc<()>)>, + meta: Option<&'static ()>, +} + +impl FakeEmptySpan { + fn new() -> Self { + Self { + inner: None, + meta: None, + } + } +} + +impl Drop for FakeEmptySpan { + fn drop(&mut self) { + black_box(&self.inner); + black_box(&self.meta); + } +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/tracing/benches/enter_span.rs b/tracing/benches/enter_span.rs new file mode 100644 index 0000000000..757350a539 --- /dev/null +++ b/tracing/benches/enter_span.rs @@ -0,0 +1,16 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use tracing::{span, Level}; + +mod shared; + +fn bench(c: &mut Criterion) { + shared::for_all_dispatches(&mut c.benchmark_group("enter_span"), |b| { + let span = span!(Level::TRACE, "span"); + b.iter(|| { + let _span = span.enter(); + }) + }); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/tracing/benches/event.rs b/tracing/benches/event.rs new file mode 100644 index 0000000000..1649325482 --- /dev/null +++ b/tracing/benches/event.rs @@ -0,0 +1,12 @@ +use criterion::{criterion_group, criterion_main, Criterion}; + +mod shared; + +fn bench(c: &mut Criterion) { + shared::for_all_recording(&mut c.benchmark_group("event"), |b| { + b.iter(|| tracing::info!("hello world!")) + }); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/tracing/benches/global_subscriber.rs b/tracing/benches/global_subscriber.rs deleted file mode 100644 index 83519610a8..0000000000 --- a/tracing/benches/global_subscriber.rs +++ /dev/null @@ -1,136 +0,0 @@ -use std::fmt::Write; - -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use tracing::Level; - -use tracing::{span, Event, Id, Metadata}; - -/// A subscriber that is enabled but otherwise does nothing. -struct EnabledSubscriber; - -impl tracing::Subscriber for EnabledSubscriber { - fn new_span(&self, span: &span::Attributes<'_>) -> Id { - let _ = span; - Id::from_u64(0xDEAD_FACE) - } - - fn event(&self, event: &Event<'_>) { - let _ = event; - } - - fn record(&self, span: &Id, values: &span::Record<'_>) { - let _ = (span, values); - } - - fn record_follows_from(&self, span: &Id, follows: &Id) { - let _ = (span, follows); - } - - fn enabled(&self, metadata: &Metadata<'_>) -> bool { - let _ = metadata; - true - } - - fn enter(&self, span: &Id) { - let _ = span; - } - - fn exit(&self, span: &Id) { - let _ = span; - } -} - -const NOP_LOGGER: NopLogger = NopLogger; - -struct NopLogger; - -impl log::Log for NopLogger { - fn enabled(&self, _metadata: &log::Metadata) -> bool { - true - } - - fn log(&self, record: &log::Record) { - if self.enabled(record.metadata()) { - let mut this = self; - let _ = write!(this, "{}", record.args()); - } - } - - fn flush(&self) {} -} - -impl Write for &NopLogger { - fn write_str(&mut self, s: &str) -> std::fmt::Result { - black_box(s); - Ok(()) - } -} - -const N_SPANS: usize = 100; - -fn criterion_benchmark(c: &mut Criterion) { - let mut c = c.benchmark_group("global/subscriber"); - let _ = tracing::subscriber::set_global_default(EnabledSubscriber); - let _ = log::set_logger(&NOP_LOGGER); - log::set_max_level(log::LevelFilter::Trace); - c.bench_function("span_no_fields", |b| b.iter(|| span!(Level::TRACE, "span"))); - - c.bench_function("event", |b| { - b.iter(|| { - tracing::event!(Level::TRACE, "hello"); - }) - }); - - c.bench_function("enter_span", |b| { - let span = span!(Level::TRACE, "span"); - #[allow(clippy::unit_arg)] - b.iter(|| black_box(span.in_scope(|| {}))) - }); - - c.bench_function("span_repeatedly", |b| { - #[inline] - fn mk_span(i: u64) -> tracing::Span { - span!(Level::TRACE, "span", i = i) - } - - let n = black_box(N_SPANS); - b.iter(|| (0..n).fold(mk_span(0), |_, i| mk_span(i as u64))) - }); - - c.bench_function("span_with_fields", |b| { - b.iter(|| { - span!( - Level::TRACE, - "span", - foo = "foo", - bar = "bar", - baz = 3, - quuux = tracing::field::debug(0.99) - ) - }); - }); -} - -fn bench_dispatch(c: &mut Criterion) { - let mut group = c.benchmark_group("global/dispatch"); - let _ = tracing::subscriber::set_global_default(EnabledSubscriber); - let _ = log::set_logger(&NOP_LOGGER); - log::set_max_level(log::LevelFilter::Trace); - group.bench_function("get_ref", |b| { - b.iter(|| { - tracing::dispatcher::get_default(|current| { - black_box(¤t); - }) - }) - }); - group.bench_function("get_clone", |b| { - b.iter(|| { - let current = tracing::dispatcher::get_default(|current| current.clone()); - black_box(current); - }) - }); - group.finish(); -} - -criterion_group!(benches, criterion_benchmark, bench_dispatch); -criterion_main!(benches); diff --git a/tracing/benches/no_subscriber.rs b/tracing/benches/no_subscriber.rs deleted file mode 100644 index e0f82b56a0..0000000000 --- a/tracing/benches/no_subscriber.rs +++ /dev/null @@ -1,101 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use tracing::Level; - -struct FakeEmptySpan { - inner: Option<(usize, std::sync::Arc<()>)>, - meta: Option<&'static ()>, -} - -impl FakeEmptySpan { - fn new() -> Self { - Self { - inner: None, - meta: None, - } - } -} - -impl Drop for FakeEmptySpan { - fn drop(&mut self) { - black_box(&self.inner); - black_box(&self.meta); - } -} - -fn bench_no_subscriber(c: &mut Criterion) { - use std::sync::atomic::{AtomicUsize, Ordering}; - - let mut group = c.benchmark_group("no_subscriber"); - - group.bench_function("span", |b| { - b.iter(|| { - let span = tracing::span!(Level::TRACE, "span"); - black_box(&span); - }) - }); - group.bench_function("span_enter", |b| { - b.iter(|| { - let span = tracing::span!(Level::TRACE, "span"); - let _e = span.enter(); - }) - }); - group.bench_function("empty_span", |b| { - b.iter(|| { - let span = tracing::span::Span::none(); - black_box(&span); - }); - }); - group.bench_function("empty_struct", |b| { - b.iter(|| { - let span = FakeEmptySpan::new(); - black_box(&span); - }) - }); - group.bench_function("event", |b| { - b.iter(|| { - tracing::event!(Level::TRACE, "hello"); - }) - }); - group.bench_function("relaxed_load", |b| { - let foo = AtomicUsize::new(1); - b.iter(|| black_box(foo.load(Ordering::Relaxed))); - }); - group.bench_function("acquire_load", |b| { - let foo = AtomicUsize::new(1); - b.iter(|| black_box(foo.load(Ordering::Acquire))) - }); - group.bench_function("log", |b| { - b.iter(|| { - log::log!(log::Level::Info, "log"); - }) - }); - group.finish(); -} - -fn bench_fields(c: &mut Criterion) { - let mut group = c.benchmark_group("no_subscriber_field"); - group.bench_function("span", |b| { - b.iter(|| { - black_box(tracing::span!( - Level::TRACE, - "span", - foo = tracing::field::display(format!("bar {:?}", 2)) - )); - }) - }); - group.bench_function("event", |b| { - b.iter(|| { - tracing::event!( - Level::TRACE, - foo = tracing::field::display(format!("bar {:?}", 2)) - ); - }) - }); - group.bench_function("log", |b| { - b.iter(|| log::log!(log::Level::Trace, "{}", format!("bar {:?}", 2))) - }); - group.finish(); -} - -criterion_group!(benches, bench_no_subscriber, bench_fields); -criterion_main!(benches); diff --git a/tracing/benches/shared.rs b/tracing/benches/shared.rs new file mode 100644 index 0000000000..56508c4ab7 --- /dev/null +++ b/tracing/benches/shared.rs @@ -0,0 +1,160 @@ +#![allow(dead_code)] +use criterion::{black_box, measurement::WallTime, Bencher}; +use tracing::{field, span, Event, Id, Metadata}; + +use std::{ + fmt::{self, Write}, + sync::{Mutex, MutexGuard}, +}; + +pub fn for_all_recording( + group: &mut criterion::BenchmarkGroup<'_, WallTime>, + mut iter: impl FnMut(&mut Bencher<'_, WallTime>), +) { + // first, run benchmarks with no subscriber + group.bench_function("none", &mut iter); + + // then, run benchmarks with a scoped default subscriber + tracing::subscriber::with_default(EnabledSubscriber, || { + group.bench_function("scoped", &mut iter) + }); + + let subscriber = VisitingSubscriber(Mutex::new(String::from(""))); + tracing::subscriber::with_default(subscriber, || { + group.bench_function("scoped_recording", &mut iter); + }); + + // finally, set a global default subscriber, and run the benchmarks again. + tracing::subscriber::set_global_default(EnabledSubscriber) + .expect("global default should not have already been set!"); + let _ = log::set_logger(&NOP_LOGGER); + log::set_max_level(log::LevelFilter::Trace); + group.bench_function("global", &mut iter); +} + +pub fn for_all_dispatches( + group: &mut criterion::BenchmarkGroup<'_, WallTime>, + mut iter: impl FnMut(&mut Bencher<'_, WallTime>), +) { + // first, run benchmarks with no subscriber + group.bench_function("none", &mut iter); + + // then, run benchmarks with a scoped default subscriber + tracing::subscriber::with_default(EnabledSubscriber, || { + group.bench_function("scoped", &mut iter) + }); + + // finally, set a global default subscriber, and run the benchmarks again. + tracing::subscriber::set_global_default(EnabledSubscriber) + .expect("global default should not have already been set!"); + let _ = log::set_logger(&NOP_LOGGER); + log::set_max_level(log::LevelFilter::Trace); + group.bench_function("global", &mut iter); +} + +const NOP_LOGGER: NopLogger = NopLogger; + +struct NopLogger; + +impl log::Log for NopLogger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + if self.enabled(record.metadata()) { + let mut this = self; + let _ = write!(this, "{}", record.args()); + } + } + + fn flush(&self) {} +} + +impl Write for &NopLogger { + fn write_str(&mut self, s: &str) -> std::fmt::Result { + black_box(s); + Ok(()) + } +} + +/// Simulates a subscriber that records span data. +struct VisitingSubscriber(Mutex); + +struct Visitor<'a>(MutexGuard<'a, String>); + +impl<'a> field::Visit for Visitor<'a> { + fn record_debug(&mut self, _field: &field::Field, value: &dyn fmt::Debug) { + let _ = write!(&mut *self.0, "{:?}", value); + } +} + +impl tracing::Subscriber for VisitingSubscriber { + fn new_span(&self, span: &span::Attributes<'_>) -> Id { + let mut visitor = Visitor(self.0.lock().unwrap()); + span.record(&mut visitor); + Id::from_u64(0xDEAD_FACE) + } + + fn record(&self, _span: &Id, values: &span::Record<'_>) { + let mut visitor = Visitor(self.0.lock().unwrap()); + values.record(&mut visitor); + } + + fn event(&self, event: &Event<'_>) { + let mut visitor = Visitor(self.0.lock().unwrap()); + event.record(&mut visitor); + } + + fn record_follows_from(&self, span: &Id, follows: &Id) { + let _ = (span, follows); + } + + fn enabled(&self, metadata: &Metadata<'_>) -> bool { + let _ = metadata; + true + } + + fn enter(&self, span: &Id) { + let _ = span; + } + + fn exit(&self, span: &Id) { + let _ = span; + } +} + +/// A subscriber that is enabled but otherwise does nothing. +struct EnabledSubscriber; + +impl tracing::Subscriber for EnabledSubscriber { + fn new_span(&self, span: &span::Attributes<'_>) -> Id { + let _ = span; + Id::from_u64(0xDEAD_FACE) + } + + fn event(&self, event: &Event<'_>) { + let _ = event; + } + + fn record(&self, span: &Id, values: &span::Record<'_>) { + let _ = (span, values); + } + + fn record_follows_from(&self, span: &Id, follows: &Id) { + let _ = (span, follows); + } + + fn enabled(&self, metadata: &Metadata<'_>) -> bool { + let _ = metadata; + true + } + + fn enter(&self, span: &Id) { + let _ = span; + } + + fn exit(&self, span: &Id) { + let _ = span; + } +} diff --git a/tracing/benches/span_fields.rs b/tracing/benches/span_fields.rs new file mode 100644 index 0000000000..5ad8289826 --- /dev/null +++ b/tracing/benches/span_fields.rs @@ -0,0 +1,23 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use tracing::{span, Level}; + +mod shared; + +fn bench(c: &mut Criterion) { + shared::for_all_recording(&mut c.benchmark_group("span_fields"), |b| { + b.iter(|| { + let span = span!( + Level::TRACE, + "span", + foo = "foo", + bar = "bar", + baz = 3, + quuux = tracing::field::debug(0.99) + ); + criterion::black_box(span) + }) + }); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/tracing/benches/span_no_fields.rs b/tracing/benches/span_no_fields.rs new file mode 100644 index 0000000000..8a1ff6e041 --- /dev/null +++ b/tracing/benches/span_no_fields.rs @@ -0,0 +1,13 @@ +use criterion::{criterion_group, criterion_main, Criterion}; +use tracing::{span, Level}; + +mod shared; + +fn bench(c: &mut Criterion) { + shared::for_all_recording(&mut c.benchmark_group("span_no_fields"), |b| { + b.iter(|| span!(Level::TRACE, "span")) + }); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/tracing/benches/span_repeated.rs b/tracing/benches/span_repeated.rs new file mode 100644 index 0000000000..4c6ac409d8 --- /dev/null +++ b/tracing/benches/span_repeated.rs @@ -0,0 +1,20 @@ +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use tracing::{span, Level}; + +mod shared; + +fn bench(c: &mut Criterion) { + shared::for_all_recording(&mut c.benchmark_group("span_repeated"), |b| { + let n = black_box(N_SPANS); + b.iter(|| (0..n).fold(mk_span(0), |_, i| mk_span(i as u64))) + }); +} + +#[inline] +fn mk_span(i: u64) -> tracing::Span { + span!(Level::TRACE, "span", i = i) +} + +const N_SPANS: usize = 100; +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/tracing/benches/subscriber.rs b/tracing/benches/subscriber.rs deleted file mode 100644 index c6418010f4..0000000000 --- a/tracing/benches/subscriber.rs +++ /dev/null @@ -1,189 +0,0 @@ -use criterion::{black_box, criterion_group, criterion_main, Criterion}; -use tracing::Level; - -use std::{ - fmt, - sync::{Mutex, MutexGuard}, -}; -use tracing::{field, span, Event, Id, Metadata}; - -/// A subscriber that is enabled but otherwise does nothing. -struct EnabledSubscriber; - -impl tracing::Subscriber for EnabledSubscriber { - fn new_span(&self, span: &span::Attributes<'_>) -> Id { - let _ = span; - Id::from_u64(0xDEAD_FACE) - } - - fn event(&self, event: &Event<'_>) { - let _ = event; - } - - fn record(&self, span: &Id, values: &span::Record<'_>) { - let _ = (span, values); - } - - fn record_follows_from(&self, span: &Id, follows: &Id) { - let _ = (span, follows); - } - - fn enabled(&self, metadata: &Metadata<'_>) -> bool { - let _ = metadata; - true - } - - fn enter(&self, span: &Id) { - let _ = span; - } - - fn exit(&self, span: &Id) { - let _ = span; - } -} - -/// Simulates a subscriber that records span data. -struct VisitingSubscriber(Mutex); - -struct Visitor<'a>(MutexGuard<'a, String>); - -impl<'a> field::Visit for Visitor<'a> { - fn record_debug(&mut self, _field: &field::Field, value: &dyn fmt::Debug) { - use std::fmt::Write; - let _ = write!(&mut *self.0, "{:?}", value); - } -} - -impl tracing::Subscriber for VisitingSubscriber { - fn new_span(&self, span: &span::Attributes<'_>) -> Id { - let mut visitor = Visitor(self.0.lock().unwrap()); - span.record(&mut visitor); - Id::from_u64(0xDEAD_FACE) - } - - fn record(&self, _span: &Id, values: &span::Record<'_>) { - let mut visitor = Visitor(self.0.lock().unwrap()); - values.record(&mut visitor); - } - - fn event(&self, event: &Event<'_>) { - let mut visitor = Visitor(self.0.lock().unwrap()); - event.record(&mut visitor); - } - - fn record_follows_from(&self, span: &Id, follows: &Id) { - let _ = (span, follows); - } - - fn enabled(&self, metadata: &Metadata<'_>) -> bool { - let _ = metadata; - true - } - - fn enter(&self, span: &Id) { - let _ = span; - } - - fn exit(&self, span: &Id) { - let _ = span; - } -} - -const N_SPANS: usize = 100; - -fn criterion_benchmark(c: &mut Criterion) { - c.bench_function("span_no_fields", |b| { - tracing::subscriber::with_default(EnabledSubscriber, || { - b.iter(|| span!(Level::TRACE, "span")) - }); - }); - - c.bench_function("enter_span", |b| { - tracing::subscriber::with_default(EnabledSubscriber, || { - let span = span!(Level::TRACE, "span"); - #[allow(clippy::unit_arg)] - b.iter(|| black_box(span.in_scope(|| {}))) - }); - }); - - c.bench_function("span_repeatedly", |b| { - #[inline] - fn mk_span(i: u64) -> tracing::Span { - span!(Level::TRACE, "span", i = i) - } - - let n = black_box(N_SPANS); - tracing::subscriber::with_default(EnabledSubscriber, || { - b.iter(|| (0..n).fold(mk_span(0), |_, i| mk_span(i as u64))) - }); - }); - - c.bench_function("span_with_fields", |b| { - tracing::subscriber::with_default(EnabledSubscriber, || { - b.iter(|| { - span!( - Level::TRACE, - "span", - foo = "foo", - bar = "bar", - baz = 3, - quuux = tracing::field::debug(0.99) - ) - }) - }); - }); - - c.bench_function("span_with_fields_record", |b| { - let subscriber = VisitingSubscriber(Mutex::new(String::from(""))); - tracing::subscriber::with_default(subscriber, || { - b.iter(|| { - span!( - Level::TRACE, - "span", - foo = "foo", - bar = "bar", - baz = 3, - quuux = tracing::field::debug(0.99) - ) - }) - }); - }); -} - -fn bench_dispatch(c: &mut Criterion) { - let mut group = c.benchmark_group("dispatch"); - group.bench_function("no_dispatch_get_ref", |b| { - b.iter(|| { - tracing::dispatcher::get_default(|current| { - black_box(¤t); - }) - }) - }); - group.bench_function("no_dispatch_get_clone", |b| { - b.iter(|| { - let current = tracing::dispatcher::get_default(|current| current.clone()); - black_box(current); - }) - }); - group.bench_function("get_ref", |b| { - tracing::subscriber::with_default(EnabledSubscriber, || { - b.iter(|| { - tracing::dispatcher::get_default(|current| { - black_box(¤t); - }) - }) - }) - }); - group.bench_function("get_clone", |b| { - tracing::subscriber::with_default(EnabledSubscriber, || { - b.iter(|| { - let current = tracing::dispatcher::get_default(|current| current.clone()); - black_box(current); - }) - }) - }); - group.finish(); -} - -criterion_group!(benches, criterion_benchmark, bench_dispatch); -criterion_main!(benches); From d86d2a4b7e87dbe1c2efaf39162b68381a085f9b Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 29 Jun 2022 12:41:08 -0700 Subject: [PATCH 048/142] subscriber: prepare to release v0.3.12 (#2187) # 0.3.12 (Jun 29, 2022) This release of `tracing-subscriber` adds a new `Layer::event_enabled` method, which allows `Layer`s to filter events *after* their field values are recorded; a `Filter` implementation for `reload::Layer`, to make using `reload` with per-layer filtering more ergonomic, and additional inherent method downcasting APIs for the `Layered` type. In addition, it includes dependency updates, and minor fixes for documentation and feature flagging. ### Added - **layer**: `Layer::event_enabled` method, which can be implemented to filter events based on their field values (#2008) - **reload**: `Filter` implementation for `reload::Layer` (#2159) - **layer**: `Layered::downcast_ref` and `Layered::is` inherent methods (#2160) ### Changed - **parking_lot**: Updated dependency on `parking_lot` to 0.13.0 (#2143) - Replaced `lazy_static` dependency with `once_cell` (#2147) ### Fixed - Don't enable `tracing-core` features by default (#2107) - Several documentation link and typo fixes (#2064, #2068, #2077, #2161, #1088) Thanks to @ben0x539, @jamesmunns, @georgemp, @james7132, @jswrenn, @CAD97, and @guswynn for contributing to this release! --- tracing-subscriber/CHANGELOG.md | 42 +++++++++++++++++++++++++++++++++ tracing-subscriber/Cargo.toml | 2 +- tracing-subscriber/README.md | 2 +- tracing-subscriber/src/lib.rs | 2 +- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/tracing-subscriber/CHANGELOG.md b/tracing-subscriber/CHANGELOG.md index d88970191e..bf816a6071 100644 --- a/tracing-subscriber/CHANGELOG.md +++ b/tracing-subscriber/CHANGELOG.md @@ -1,3 +1,45 @@ +# 0.3.12 (Jun 29, 2022) + +This release of `tracing-subscriber` adds a new `Layer::event_enabled` method, +which allows `Layer`s to filter events *after* their field values are recorded; +a `Filter` implementation for `reload::Layer`, to make using `reload` with +per-layer filtering more ergonomic, and additional inherent method downcasting +APIs for the `Layered` type. In addition, it includes dependency updates, and +minor fixes for documentation and feature flagging. + +### Added + +- **layer**: `Layer::event_enabled` method, which can be implemented to filter + events based on their field values ([#2008]) +- **reload**: `Filter` implementation for `reload::Layer` ([#2159]) +- **layer**: `Layered::downcast_ref` and `Layered::is` inherent methods + ([#2160]) + +### Changed + +- **parking_lot**: Updated dependency on `parking_lot` to 0.13.0 ([#2143]) +- Replaced `lazy_static` dependency with `once_cell` ([#2147]) + +### Fixed + +- Don't enable `tracing-core` features by default ([#2107]) +- Several documentation link and typo fixes ([#2064], [#2068], #[2077], [#2161], + [#1088]) + +Thanks to @ben0x539, @jamesmunns, @georgemp, @james7132, @jswrenn, @CAD97, and +@guswynn for contributing to this release! + +[#2008]: https://github.com/tokio-rs/tracing/pull/2008 +[#2159]: https://github.com/tokio-rs/tracing/pull/2159 +[#2160]: https://github.com/tokio-rs/tracing/pull/2160 +[#2143]: https://github.com/tokio-rs/tracing/pull/2143 +[#2107]: https://github.com/tokio-rs/tracing/pull/2107 +[#2064]: https://github.com/tokio-rs/tracing/pull/2064 +[#2068]: https://github.com/tokio-rs/tracing/pull/2068 +[#2077]: https://github.com/tokio-rs/tracing/pull/2077 +[#2161]: https://github.com/tokio-rs/tracing/pull/2161 +[#1088]: https://github.com/tokio-rs/tracing/pull/1088 + # 0.3.11 (Apr 9, 2022) This is a bugfix release for the `Filter` implementation for `EnvFilter` added diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 9fe05df14f..60f7efda91 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-subscriber" -version = "0.3.11" +version = "0.3.12" authors = [ "Eliza Weisman ", "David Barsky ", diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md index e6c04ff7ac..21f7ebe19d 100644 --- a/tracing-subscriber/README.md +++ b/tracing-subscriber/README.md @@ -21,7 +21,7 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers. [crates-badge]: https://img.shields.io/crates/v/tracing-subscriber.svg [crates-url]: https://crates.io/crates/tracing-subscriber [docs-badge]: https://docs.rs/tracing-subscriber/badge.svg -[docs-url]: https://docs.rs/tracing-subscriber/0.3.11 +[docs-url]: https://docs.rs/tracing-subscriber/0.3.12 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_subscriber [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 25f2cea332..1a78f22840 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -160,7 +160,7 @@ //! [`time` crate]: https://crates.io/crates/time //! [`libstd`]: std //! [`liballoc`]: alloc -#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.11")] +#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.12")] #![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/" From b4680b1fd0419d522c853fb06eee6809b07aafbc Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 30 Jun 2022 11:27:49 -0700 Subject: [PATCH 049/142] subscriber: update minimum `tracing-core` dep to 0.1.28 (#2190) ## Motivation PR #2008 added a new method to the `tracing_core::Subscriber` trait, and modified `tracing-subscriber` to and implement that method. However, that PR did *not* increase the minimum `tracing-core` dependency for the `tracing-subscriber` crate. This means that if a dependent of `tracing-subscriber` updates *only* `tracing-subscriber` dependency version (e.g. by running `cargo update -p tracing-subscriber`), it will not also update its `tracing-core` version to one that contains the new method, and `tracing-subscriber` will fail to compile. This is a common occurrence with projects using Dependabot, for example. ## Solution This commit updates `tracing-subscriber`'s minimum `tracing-core` dep to 0.1.28. Once this merges, I'll release 0.3.13 of `tracing-subscriber` and yank 0.3.12. --- tracing-subscriber/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 60f7efda91..1fcf14a4a6 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -38,7 +38,7 @@ valuable = ["tracing-core/valuable", "valuable_crate", "valuable-serde", "tracin local-time = ["time/local-offset"] [dependencies] -tracing-core = { path = "../tracing-core", version = "0.1.22", default-features = false } +tracing-core = { path = "../tracing-core", version = "0.1.28", default-features = false } # only required by the filter feature tracing = { optional = true, path = "../tracing", version = "0.1", default-features = false } From 7f62097b42dc745ad0e66f91b0dd3ada22ac91c2 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 30 Jun 2022 13:12:26 -0700 Subject: [PATCH 050/142] subscriber: fix clippy lints (#2191) This fixes a Clippy lint for explicitly calling `drop` on a value without a `Drop` impl, and a lint for `let` bindings whose value is `()`. Signed-off-by: Eliza Weisman --- tracing-opentelemetry/benches/trace.rs | 4 +--- tracing-subscriber/src/layer/layered.rs | 2 +- tracing-subscriber/tests/registry_with_subscriber.rs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tracing-opentelemetry/benches/trace.rs b/tracing-opentelemetry/benches/trace.rs index e8316309e5..8dbc96eaa5 100644 --- a/tracing-opentelemetry/benches/trace.rs +++ b/tracing-opentelemetry/benches/trace.rs @@ -75,9 +75,7 @@ where let span = ctx.span(&id).expect("Span not found, this is a bug"); let mut extensions = span.extensions_mut(); - if let Some(no_data) = extensions.remove::() { - drop(no_data) - } + extensions.remove::(); } } diff --git a/tracing-subscriber/src/layer/layered.rs b/tracing-subscriber/src/layer/layered.rs index 0160f99c1d..805ec13dc8 100644 --- a/tracing-subscriber/src/layer/layered.rs +++ b/tracing-subscriber/src/layer/layered.rs @@ -435,7 +435,7 @@ where // (rather than calling into the inner type), clear the current // per-layer filter interest state. #[cfg(feature = "registry")] - drop(filter::FilterState::take_interest()); + filter::FilterState::take_interest(); return outer; } diff --git a/tracing-subscriber/tests/registry_with_subscriber.rs b/tracing-subscriber/tests/registry_with_subscriber.rs index 3f8d99b1d9..50d2f551d6 100644 --- a/tracing-subscriber/tests/registry_with_subscriber.rs +++ b/tracing-subscriber/tests/registry_with_subscriber.rs @@ -4,7 +4,7 @@ use tracing_subscriber::prelude::*; #[tokio::test] async fn future_with_subscriber() { - let _default = tracing_subscriber::registry().init(); + tracing_subscriber::registry().init(); let span = tracing::info_span!("foo"); let _e = span.enter(); let span = tracing::info_span!("bar"); From 2fdbaaa1e8532ee197df2642d810034f1181c8f1 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 30 Jun 2022 13:33:38 -0700 Subject: [PATCH 051/142] subscriber: prepare to release v0.3.13 (#2192) # 0.3.13 (Jun 30, 2022) This release of `tracing-subscriber` fixes a compilation failure due to an incorrect `tracing-core` dependency that was introduced in v0.3.12. ### Changed - **tracing_core**: Updated minimum dependency version to 0.1.28 (#2190) --- tracing-subscriber/CHANGELOG.md | 11 +++++++++++ tracing-subscriber/Cargo.toml | 2 +- tracing-subscriber/README.md | 2 +- tracing-subscriber/src/lib.rs | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/tracing-subscriber/CHANGELOG.md b/tracing-subscriber/CHANGELOG.md index bf816a6071..922e1f2ba2 100644 --- a/tracing-subscriber/CHANGELOG.md +++ b/tracing-subscriber/CHANGELOG.md @@ -1,3 +1,14 @@ +# 0.3.13 (Jun 30, 2022) + +This release of `tracing-subscriber` fixes a compilation failure due to an +incorrect `tracing-core` dependency that was introduced in v0.3.12. + +### Changed + +- **tracing_core**: Updated minimum dependency version to 0.1.28 ([#2190]) + +[#2190]: https://github.com/tokio-rs/tracing/pull/2190 + # 0.3.12 (Jun 29, 2022) This release of `tracing-subscriber` adds a new `Layer::event_enabled` method, diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 1fcf14a4a6..a0ce2eec42 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-subscriber" -version = "0.3.12" +version = "0.3.13" authors = [ "Eliza Weisman ", "David Barsky ", diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md index 21f7ebe19d..e0f9e27c07 100644 --- a/tracing-subscriber/README.md +++ b/tracing-subscriber/README.md @@ -21,7 +21,7 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers. [crates-badge]: https://img.shields.io/crates/v/tracing-subscriber.svg [crates-url]: https://crates.io/crates/tracing-subscriber [docs-badge]: https://docs.rs/tracing-subscriber/badge.svg -[docs-url]: https://docs.rs/tracing-subscriber/0.3.12 +[docs-url]: https://docs.rs/tracing-subscriber/0.3.13 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_subscriber [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 1a78f22840..23bb4436d7 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -160,7 +160,7 @@ //! [`time` crate]: https://crates.io/crates/time //! [`libstd`]: std //! [`liballoc`]: alloc -#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.12")] +#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.13")] #![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/" From 0012a4131adb0713ce2cf49b16fd1bb47c9ba22c Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Thu, 30 Jun 2022 18:39:50 -0400 Subject: [PATCH 052/142] subscriber: `::event_enabled` should be `true` (#2193) ## Motivation This is wrong. ## Solution Make it unwrong. As described by the documentation on `Layer::event_enabled`, the return value sets the global filtering of an event. This code used to say that that `Option::::None` existing in the layer stack should disable any layer in the stack seeing events --- tracing-subscriber/src/layer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs index a15870e691..808d890f3c 100644 --- a/tracing-subscriber/src/layer/mod.rs +++ b/tracing-subscriber/src/layer/mod.rs @@ -1512,7 +1512,7 @@ where fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool { match self { Some(ref inner) => inner.event_enabled(event, ctx), - None => false, + None => true, } } From e0b3f79ed9bc2bdf6125aa3d57919c26139a5a7e Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Thu, 30 Jun 2022 17:15:07 -0700 Subject: [PATCH 053/142] subscriber: fix `max_level_hint` for empty `Option`/`Vec` `Layer` impls (#2195) ## Motivation These are incorrect: currently, when you have a `None` layer, the `None` hint it returns causes the default `TRACE` to win, which is inaccurate. Similarly, `Vec` should default to `OFF` if it has no `Layer`s in it ## Solution Change the default hints to `Some(OFF)` Co-authored-by: Eliza Weisman --- tracing-subscriber/src/layer/mod.rs | 9 +++- tracing-subscriber/tests/layer_filters/vec.rs | 7 ++++ tracing-subscriber/tests/option.rs | 41 +++++++++++++++++++ tracing-subscriber/tests/vec.rs | 19 +++++++++ 4 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 tracing-subscriber/tests/option.rs create mode 100644 tracing-subscriber/tests/vec.rs diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs index 808d890f3c..24b8533234 100644 --- a/tracing-subscriber/src/layer/mod.rs +++ b/tracing-subscriber/src/layer/mod.rs @@ -1490,7 +1490,11 @@ where fn max_level_hint(&self) -> Option { match self { Some(ref inner) => inner.max_level_hint(), - None => None, + None => { + // There is no inner layer, so this layer will + // never enable anything. + Some(LevelFilter::OFF) + } } } @@ -1701,7 +1705,8 @@ feature! { } fn max_level_hint(&self) -> Option { - let mut max_level = LevelFilter::ERROR; + // Default to `OFF` if there are no inner layers. + let mut max_level = LevelFilter::OFF; for l in self { // NOTE(eliza): this is slightly subtle: if *any* layer // returns `None`, we have to return `None`, assuming there is diff --git a/tracing-subscriber/tests/layer_filters/vec.rs b/tracing-subscriber/tests/layer_filters/vec.rs index 77675a5f94..87244e4ab5 100644 --- a/tracing-subscriber/tests/layer_filters/vec.rs +++ b/tracing-subscriber/tests/layer_filters/vec.rs @@ -111,3 +111,10 @@ fn all_filtered_max_level_hint() { assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::DEBUG)); } + +#[test] +fn empty_vec() { + // Just a None means everything is off + let subscriber = tracing_subscriber::registry().with(Vec::::new()); + assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::OFF)); +} diff --git a/tracing-subscriber/tests/option.rs b/tracing-subscriber/tests/option.rs new file mode 100644 index 0000000000..738cc0a6c5 --- /dev/null +++ b/tracing-subscriber/tests/option.rs @@ -0,0 +1,41 @@ +#![cfg(feature = "registry")] +use tracing::level_filters::LevelFilter; +use tracing::Subscriber; +use tracing_subscriber::prelude::*; + +// This test is just used to compare to the tests below +#[test] +fn just_layer() { + let subscriber = tracing_subscriber::registry().with(LevelFilter::INFO); + assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::INFO)); +} + +#[test] +fn subscriber_and_option_some_layer() { + let subscriber = tracing_subscriber::registry() + .with(LevelFilter::INFO) + .with(Some(LevelFilter::DEBUG)); + assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::DEBUG)); +} + +#[test] +fn subscriber_and_option_none_layer() { + // None means the other layer takes control + let subscriber = tracing_subscriber::registry() + .with(LevelFilter::ERROR) + .with(None::); + assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::ERROR)); +} + +#[test] +fn just_option_some_layer() { + // Just a None means everything is off + let subscriber = tracing_subscriber::registry().with(None::); + assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::OFF)); +} + +#[test] +fn just_option_none_layer() { + let subscriber = tracing_subscriber::registry().with(Some(LevelFilter::ERROR)); + assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::ERROR)); +} diff --git a/tracing-subscriber/tests/vec.rs b/tracing-subscriber/tests/vec.rs new file mode 100644 index 0000000000..92abf0bff9 --- /dev/null +++ b/tracing-subscriber/tests/vec.rs @@ -0,0 +1,19 @@ +#![cfg(feature = "registry")] +use tracing::level_filters::LevelFilter; +use tracing::Subscriber; +use tracing_subscriber::prelude::*; + +#[test] +fn just_empty_vec() { + // Just a None means everything is off + let subscriber = tracing_subscriber::registry().with(Vec::::new()); + assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::OFF)); +} + +#[test] +fn layer_and_empty_vec() { + let subscriber = tracing_subscriber::registry() + .with(LevelFilter::INFO) + .with(Vec::::new()); + assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::INFO)); +} From 4a3f2991bfe8ed65abd7622273b3d3055e4dea3e Mon Sep 17 00:00:00 2001 From: Thomas Braun <38082993+tbraun96@users.noreply.github.com> Date: Fri, 1 Jul 2022 12:47:52 -0400 Subject: [PATCH 054/142] attributes: ensure `res` and `err` events inherit `target` (#2184) ## Motivation Currently, when an `#[instrument]` attribute has an overridden target, the events generated by `ret` and `err` arguments do not inherit that target. For example, if I write ```rust #[tracing::instrument(target = "some_target", ret) fn do_stuff() -> Something { // ... } ``` the `do_stuff` span will have the target "some_target", but the return value event generated by `ret` will have the current module path as its target, and there is no way to change the return value event's target. ## Solution This branch changes the macro expansion for `#[instrument]` with the `ret` and/or `err` arguments so that an overridden target is propagated to the events generated by `ret` and `err`. Fixes #2183 --- tracing-attributes/Cargo.toml | 1 + tracing-attributes/src/expand.rs | 16 +++++++++------ tracing-attributes/src/lib.rs | 3 +++ tracing-attributes/tests/err.rs | 33 +++++++++++++++++++++++++++++++ tracing-attributes/tests/ret.rs | 34 ++++++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 6 deletions(-) diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 85e51f6ffe..5a75ec0513 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -46,6 +46,7 @@ quote = "1" [dev-dependencies] tracing = { path = "../tracing", version = "0.1" } tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["env-filter"] } tokio-test = { version = "0.3.0" } tracing-core = { path = "../tracing-core", version = "0.1"} async-trait = "0.1.44" diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index b563d4bbfe..81ee6ed906 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -200,19 +200,23 @@ fn gen_block( )) })(); + let target = args.target(); + let err_event = match args.err_mode { Some(FormatMode::Default) | Some(FormatMode::Display) => { - Some(quote!(tracing::error!(error = %e))) + Some(quote!(tracing::error!(target: #target, error = %e))) } - Some(FormatMode::Debug) => Some(quote!(tracing::error!(error = ?e))), + Some(FormatMode::Debug) => Some(quote!(tracing::error!(target: #target, error = ?e))), _ => None, }; let ret_event = match args.ret_mode { - Some(FormatMode::Display) => Some(quote!(tracing::event!(#level, return = %x))), - Some(FormatMode::Default) | Some(FormatMode::Debug) => { - Some(quote!(tracing::event!(#level, return = ?x))) - } + Some(FormatMode::Display) => Some(quote!( + tracing::event!(target: #target, #level, return = %x) + )), + Some(FormatMode::Default) | Some(FormatMode::Debug) => Some(quote!( + tracing::event!(target: #target, #level, return = ?x) + )), _ => None, }; diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 48deea2785..40bd41076e 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -461,6 +461,9 @@ mod expand; /// } /// ``` /// +/// If a `target` is specified, both the `ret` and `err` arguments will emit outputs to +/// the declared target (or the default channel if `target` is not specified). +/// /// The `ret` and `err` arguments can be combined in order to record an event if a /// function returns [`Result::Ok`] or [`Result::Err`]: /// diff --git a/tracing-attributes/tests/err.rs b/tracing-attributes/tests/err.rs index f2706f3084..9e6d6b78c3 100644 --- a/tracing-attributes/tests/err.rs +++ b/tracing-attributes/tests/err.rs @@ -2,6 +2,8 @@ use tracing::subscriber::with_default; use tracing::Level; use tracing_attributes::instrument; use tracing_mock::*; +use tracing_subscriber::filter::EnvFilter; +use tracing_subscriber::layer::SubscriberExt; use std::convert::TryFrom; use std::num::TryFromIntError; @@ -198,3 +200,34 @@ fn test_err_display_default() { with_default(subscriber, || err().ok()); handle.assert_finished(); } + +#[test] +fn test_err_custom_target() { + let filter: EnvFilter = "my_target=error".parse().expect("filter should parse"); + let span = span::mock().named("error_span").with_target("my_target"); + + let (subscriber, handle) = subscriber::mock() + .new_span(span.clone()) + .enter(span.clone()) + .event( + event::mock() + .at_level(Level::ERROR) + .with_target("my_target"), + ) + .exit(span.clone()) + .drop_span(span) + .done() + .run_with_handle(); + + let subscriber = subscriber.with(filter); + + with_default(subscriber, || { + let error_span = tracing::error_span!(target: "my_target", "error_span"); + + { + let _enter = error_span.enter(); + tracing::error!(target: "my_target", "This should display") + } + }); + handle.assert_finished(); +} diff --git a/tracing-attributes/tests/ret.rs b/tracing-attributes/tests/ret.rs index 01879dfd2d..cfd2de10d3 100644 --- a/tracing-attributes/tests/ret.rs +++ b/tracing-attributes/tests/ret.rs @@ -4,12 +4,19 @@ use tracing_mock::*; use tracing::{subscriber::with_default, Level}; use tracing_attributes::instrument; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::EnvFilter; #[instrument(ret)] fn ret() -> i32 { 42 } +#[instrument(target = "my_target", ret)] +fn ret_with_target() -> i32 { + 42 +} + #[test] fn test() { let span = span::mock().named("ret"); @@ -30,6 +37,33 @@ fn test() { handle.assert_finished(); } +#[test] +fn test_custom_target() { + let filter: EnvFilter = "my_target=info".parse().expect("filter should parse"); + let span = span::mock() + .named("ret_with_target") + .with_target("my_target"); + + let (subscriber, handle) = subscriber::mock() + .new_span(span.clone()) + .enter(span.clone()) + .event( + event::mock() + .with_fields(field::mock("return").with_value(&tracing::field::debug(42))) + .at_level(Level::INFO) + .with_target("my_target"), + ) + .exit(span.clone()) + .drop_span(span) + .done() + .run_with_handle(); + + let subscriber = subscriber.with(filter); + + with_default(subscriber, ret_with_target); + handle.assert_finished(); +} + #[instrument(level = "warn", ret)] fn ret_warn() -> i32 { 42 From 2659ccf72fa3b2790279fb15d51b1266e7d21bf5 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 1 Jul 2022 10:21:44 -0700 Subject: [PATCH 055/142] opentelemetry: fix exception fields defaults in docs (#2200) ## Motivation Currently, the `tracing-opentelemetry` docs indicate that the support for OpenTelemetry's exception semantic conventions added in #2135 is enabled by default. However, this is not the case --- this feature was changed to opt-in rather than opt-out prior to merging PR #2135. ## Solution This branch updates the docs to state that these features are disabled by default, rather than enabled by default. --- tracing-opentelemetry/src/layer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs index a722dfd185..533fe19406 100644 --- a/tracing-opentelemetry/src/layer.rs +++ b/tracing-opentelemetry/src/layer.rs @@ -483,7 +483,7 @@ where /// These attributes follow the [OpenTelemetry semantic conventions for /// exceptions][conv]. /// - /// By default, these fields are enabled + /// By default, these attributes are not recorded. /// /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ pub fn with_exception_fields(self, exception_fields: bool) -> Self { @@ -506,7 +506,7 @@ where /// These attributes follow the [OpenTelemetry semantic conventions for /// exceptions][conv]. /// - /// By default, this is enabled + /// By default, these attributes are not propagated to the span. /// /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ pub fn with_exception_field_propagation(self, exception_field_propagation: bool) -> Self { From 9638587480b3f3c59a858ec78a9ea94103b746b8 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 1 Jul 2022 10:37:10 -0700 Subject: [PATCH 056/142] subscriber: prepare to release v0.3.14 (#2201) # 0.3.14 (Jul 1, 2022) This release fixes multiple filtering bugs in the `Layer` implementations for `Option` and `Vec`. ### Fixed - **layer**: `Layer::event_enabled` implementation for `Option>` returning `false` when the `Option` is `None`, disabling all events globally ([#2193]) - **layer**: `Layer::max_level_hint` implementation for `Option>` incorrectly disabling max level filtering when the option is `None` ([#2195]) - **layer**: `Layer::max_level_hint` implementation for `Vec>` returning `LevelFilter::ERROR` rather than `LevelFilter::OFF` when the `Vec` is empty ([#2195]) Thanks to @CAD97 and @guswynn for contributing to this release! [#2193]: https://github.com/tokio-rs/tracing/pull/2193 [#2195]: https://github.com/tokio-rs/tracing/pull/2195 --- tracing-subscriber/CHANGELOG.md | 25 +++++++++++++++++++++++-- tracing-subscriber/Cargo.toml | 2 +- tracing-subscriber/README.md | 2 +- tracing-subscriber/src/lib.rs | 2 +- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/tracing-subscriber/CHANGELOG.md b/tracing-subscriber/CHANGELOG.md index 922e1f2ba2..35747566e5 100644 --- a/tracing-subscriber/CHANGELOG.md +++ b/tracing-subscriber/CHANGELOG.md @@ -1,4 +1,25 @@ -# 0.3.13 (Jun 30, 2022) +# 0.3.14 (Jul 1, 2022) + +This release fixes multiple filtering bugs in the `Layer` implementations for +`Option` and `Vec`. + +### Fixed + +- **layer**: `Layer::event_enabled` implementation for `Option>` + returning `false` when the `Option` is `None`, disabling all events globally + ([#2193]) +- **layer**: `Layer::max_level_hint` implementation for `Option>` + incorrectly disabling max level filtering when the option is `None` ([#2195]) +- **layer**: `Layer::max_level_hint` implementation for `Vec>` + returning `LevelFilter::ERROR` rather than `LevelFilter::OFF` when the `Vec` + is empty ([#2195]) + +Thanks to @CAD97 and @guswynn for contributing to this release! + +[#2193]: https://github.com/tokio-rs/tracing/pull/2193 +[#2195]: https://github.com/tokio-rs/tracing/pull/2195 + +# 0.3.13 (Jun 30, 2022) (YANKED) This release of `tracing-subscriber` fixes a compilation failure due to an incorrect `tracing-core` dependency that was introduced in v0.3.12. @@ -9,7 +30,7 @@ incorrect `tracing-core` dependency that was introduced in v0.3.12. [#2190]: https://github.com/tokio-rs/tracing/pull/2190 -# 0.3.12 (Jun 29, 2022) +# 0.3.12 (Jun 29, 2022) (YANKED) This release of `tracing-subscriber` adds a new `Layer::event_enabled` method, which allows `Layer`s to filter events *after* their field values are recorded; diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index a0ce2eec42..95c6740c64 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-subscriber" -version = "0.3.13" +version = "0.3.14" authors = [ "Eliza Weisman ", "David Barsky ", diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md index e0f9e27c07..95242b37ed 100644 --- a/tracing-subscriber/README.md +++ b/tracing-subscriber/README.md @@ -21,7 +21,7 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers. [crates-badge]: https://img.shields.io/crates/v/tracing-subscriber.svg [crates-url]: https://crates.io/crates/tracing-subscriber [docs-badge]: https://docs.rs/tracing-subscriber/badge.svg -[docs-url]: https://docs.rs/tracing-subscriber/0.3.13 +[docs-url]: https://docs.rs/tracing-subscriber/0.3.14 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_subscriber [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 23bb4436d7..4bd61000e8 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -160,7 +160,7 @@ //! [`time` crate]: https://crates.io/crates/time //! [`libstd`]: std //! [`liballoc`]: alloc -#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.13")] +#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.14")] #![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/" From beb227b25ed8e6b9fcfe26a66f8bffa39cfa1355 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 1 Jul 2022 10:53:11 -0700 Subject: [PATCH 057/142] chore(bin): fix `bin/publish` script running `cargo publish` twice The second invocation would always fail, since that version had already been published. This command failing would cause the script to exit with an error, and thus fail to push the release tag. This commit fixes this by removing the second `cargo publish`. --- bin/publish | 2 -- 1 file changed, 2 deletions(-) diff --git a/bin/publish b/bin/publish index ee2a3ec88a..939f3a32ed 100755 --- a/bin/publish +++ b/bin/publish @@ -119,8 +119,6 @@ release() { "${CARGO_PACKAGE[@]}" "${CARGO_PUBLISH[@]}" - cargo publish "$VERBOSE" "$DRY_RUN" - status "Tagging" "$TAG" if [[ "$DRY_RUN" ]]; then echo "# git tag $TAG && git push --tags" From 6752895ae416a03960853bf2a0f8cece89ff74c3 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 1 Jul 2022 11:00:49 -0700 Subject: [PATCH 058/142] opentelemetry: prepare to release v0.17.4 (#2202) # 0.17.4 (July 1, 2022) This release adds optional support for recording `std::error::Error`s using OpenTelemetry's [semantic conventions for exceptions][exn-semconv]. ### Added - `Layer::with_exception_fields` to enable emitting `exception.message` and `exception.backtrace` semantic-convention fields when an `Error` is recorded as a span or event field ([#2135]) - `Layer::with_exception_field_propagation` to enable setting `exception.message` and `exception.backtrace` semantic-convention fields on the current span when an event with an `Error` field is recorded ([#2135]) Thanks to @lilymara-onesignal for contributing to this release! [thread-semconv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ [#2135]: https://github.com/tokio-rs/tracing/pull/2135 --- tracing-opentelemetry/CHANGELOG.md | 19 +++++++++++++++++++ tracing-opentelemetry/Cargo.toml | 2 +- tracing-opentelemetry/README.md | 4 ++-- tracing-opentelemetry/src/lib.rs | 2 +- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/tracing-opentelemetry/CHANGELOG.md b/tracing-opentelemetry/CHANGELOG.md index 6bb1da3b5f..6fb43d55db 100644 --- a/tracing-opentelemetry/CHANGELOG.md +++ b/tracing-opentelemetry/CHANGELOG.md @@ -1,3 +1,22 @@ +# 0.17.4 (July 1, 2022) + +This release adds optional support for recording `std::error::Error`s using +[OpenTelemetry's semantic conventions for exceptions][exn-semconv]. + +### Added + +- `Layer::with_exception_fields` to enable emitting `exception.message` and + `exception.backtrace` semantic-convention fields when an `Error` is recorded + as a span or event field ([#2135]) +- `Layer::with_exception_field_propagation` to enable setting `exception.message` and + `exception.backtrace` semantic-convention fields on the current span when an + event with an `Error` field is recorded ([#2135]) + +Thanks to @lilymara-onesignal for contributing to this release! + +[thread-semconv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ +[#2135]: https://github.com/tokio-rs/tracing/pull/2135 + # 0.17.3 (June 7, 2022) This release adds support for emitting thread names and IDs to OpenTelemetry, as diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml index 2aa9e1ad69..1781eb99f3 100644 --- a/tracing-opentelemetry/Cargo.toml +++ b/tracing-opentelemetry/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-opentelemetry" -version = "0.17.3" +version = "0.17.4" authors = [ "Julian Tescher ", "Tokio Contributors " diff --git a/tracing-opentelemetry/README.md b/tracing-opentelemetry/README.md index d17c4ce6fa..6640754483 100644 --- a/tracing-opentelemetry/README.md +++ b/tracing-opentelemetry/README.md @@ -17,9 +17,9 @@ Utilities for adding [OpenTelemetry] interoperability to [`tracing`]. [Documentation][docs-url] | [Chat][discord-url] [crates-badge]: https://img.shields.io/crates/v/tracing-opentelemetry.svg -[crates-url]: https://crates.io/crates/tracing-opentelemetry/0.17.3 +[crates-url]: https://crates.io/crates/tracing-opentelemetry/0.17.4 [docs-badge]: https://docs.rs/tracing-opentelemetry/badge.svg -[docs-url]: https://docs.rs/tracing-opentelemetry/0.17.3/tracing_opentelemetry +[docs-url]: https://docs.rs/tracing-opentelemetry/0.17.4/tracing_opentelemetry [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_opentelemetry [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg diff --git a/tracing-opentelemetry/src/lib.rs b/tracing-opentelemetry/src/lib.rs index f493d5c2d6..872afc8a44 100644 --- a/tracing-opentelemetry/src/lib.rs +++ b/tracing-opentelemetry/src/lib.rs @@ -92,7 +92,7 @@ //! #![deny(unreachable_pub)] #![cfg_attr(test, deny(warnings))] -#![doc(html_root_url = "https://docs.rs/tracing-opentelemetry/0.17.3")] +#![doc(html_root_url = "https://docs.rs/tracing-opentelemetry/0.17.4")] #![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/" From 9c4bd4319986b2711dd9606c618b4e3f061f0f2b Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 1 Jul 2022 11:15:48 -0700 Subject: [PATCH 059/142] attributes: prepare to release v0.1.22 (#2203) # 0.1.22 (July 1, 2022) This release fixes an issue where using the `err` or `ret` arguments to `#[instrument]` along with an overridden target, such as ```rust #[instrument(target = "...", err, ret)] ``` would not propagate the overridden target to the events generated for errors/return values. ### Fixed - Error and return value events generated by `#[instrument(err)]` or `#[instrument(ret)]` not inheriting an overridden target (#2184) - Incorrect default level in documentation (#2119) Thanks to new contributor @tbraun96 for contributing to this release! --- tracing-attributes/CHANGELOG.md | 23 +++++++++++++++++++++++ tracing-attributes/Cargo.toml | 2 +- tracing-attributes/README.md | 4 ++-- tracing-attributes/src/lib.rs | 4 ++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/tracing-attributes/CHANGELOG.md b/tracing-attributes/CHANGELOG.md index a52baa7d2b..4fcc81f294 100644 --- a/tracing-attributes/CHANGELOG.md +++ b/tracing-attributes/CHANGELOG.md @@ -1,3 +1,26 @@ +# 0.1.22 (July 1, 2022) + +This release fixes an issue where using the `err` or `ret` arguments to +`#[instrument]` along with an overridden target, such as + +```rust +#[instrument(target = "...", err, ret)] +``` + +would not propagate the overridden target to the events generated for +errors/return values. + +### Fixed + +- Error and return value events generated by `#[instrument(err)]` or + `#[instrument(ret)]` not inheriting an overridden target ([#2184]) +- Incorrect default level in documentation ([#2119]) + +Thanks to new contributor @tbraun96 for contributing to this release! + +[#2184]: https://github.com/tokio-rs/tracing/pull/2184 +[#2119]: https://github.com/tokio-rs/tracing/pull/2119 + # 0.1.21 (April 26, 2022) This release adds support for setting explicit parent and follows-from spans diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 5a75ec0513..c1673d65ba 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -8,7 +8,7 @@ name = "tracing-attributes" # - README.md # - Update CHANGELOG.md. # - Create "v0.1.x" git tag. -version = "0.1.21" +version = "0.1.22" authors = [ "Tokio Contributors ", "Eliza Weisman ", diff --git a/tracing-attributes/README.md b/tracing-attributes/README.md index 7d8009ac3c..6361794680 100644 --- a/tracing-attributes/README.md +++ b/tracing-attributes/README.md @@ -18,7 +18,7 @@ Macro attributes for application-level tracing. [crates-badge]: https://img.shields.io/crates/v/tracing-attributes.svg [crates-url]: https://crates.io/crates/tracing-attributes [docs-badge]: https://docs.rs/tracing-attributes/badge.svg -[docs-url]: https://docs.rs/tracing-attributes/0.1.21 +[docs-url]: https://docs.rs/tracing-attributes/0.1.22 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_attributes [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg @@ -47,7 +47,7 @@ First, add this to your `Cargo.toml`: ```toml [dependencies] -tracing-attributes = "0.1.21" +tracing-attributes = "0.1.22" ``` diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 40bd41076e..49b3c794a0 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -16,7 +16,7 @@ //! //! ```toml //! [dependencies] -//! tracing-attributes = "0.1.21" +//! tracing-attributes = "0.1.22" //! ``` //! //! The [`#[instrument]`][instrument] attribute can now be added to a function @@ -52,7 +52,7 @@ //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! -#![doc(html_root_url = "https://docs.rs/tracing-attributes/0.1.21")] +#![doc(html_root_url = "https://docs.rs/tracing-attributes/0.1.22")] #![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/" From d7139a5fc62d499b41c02896bd7247ba962e93ff Mon Sep 17 00:00:00 2001 From: Gus Wynn Date: Fri, 1 Jul 2022 12:39:36 -0700 Subject: [PATCH 060/142] subscriber: pass through `max_level_hint` in `reload` (#2204) ## Motivation When using a `reload` layer, the fast-path current level check doesn't work, as the `max_level_hint` is just `None`, which `rebuild_interest` interprets as `TRACE` ## Solution Pass through to the underlying layer/filter. On poisons, when already panicking, just return `None` --- tracing-subscriber/src/reload.rs | 12 ++++++++++- tracing-subscriber/tests/reload.rs | 32 +++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/tracing-subscriber/src/reload.rs b/tracing-subscriber/src/reload.rs index 3d1743440e..0c6c1c45c4 100644 --- a/tracing-subscriber/src/reload.rs +++ b/tracing-subscriber/src/reload.rs @@ -75,7 +75,7 @@ use std::{ use tracing_core::{ callsite, span, subscriber::{Interest, Subscriber}, - Event, Metadata, + Event, LevelFilter, Metadata, }; /// Wraps a `Layer` or `Filter`, allowing it to be reloaded dynamically at runtime. @@ -173,6 +173,11 @@ where fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_id_change(old, new, ctx) } + + #[inline] + fn max_level_hint(&self) -> Option { + try_lock!(self.inner.read(), else return None).max_level_hint() + } } // ===== impl Filter ===== @@ -218,6 +223,11 @@ where fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_close(id, ctx) } + + #[inline] + fn max_level_hint(&self) -> Option { + try_lock!(self.inner.read(), else return None).max_level_hint() + } } impl Layer { diff --git a/tracing-subscriber/tests/reload.rs b/tracing-subscriber/tests/reload.rs index b8b6c2b461..28662e2e6f 100644 --- a/tracing-subscriber/tests/reload.rs +++ b/tracing-subscriber/tests/reload.rs @@ -1,13 +1,16 @@ -#![cfg(feature = "std")] +#![cfg(feature = "registry")] use std::sync::atomic::{AtomicUsize, Ordering}; use tracing_core::{ span::{Attributes, Id, Record}, subscriber::Interest, - Event, Metadata, Subscriber, + Event, LevelFilter, Metadata, Subscriber, }; use tracing_subscriber::{layer, prelude::*, reload::*}; pub struct NopSubscriber; +fn event() { + tracing::info!("my event"); +} impl Subscriber for NopSubscriber { fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest { @@ -53,9 +56,13 @@ fn reload_handle() { }; true } - } - fn event() { - tracing::trace!("my event"); + + fn max_level_hint(&self) -> Option { + match self { + Filter::One => Some(LevelFilter::INFO), + Filter::Two => Some(LevelFilter::DEBUG), + } + } } let (layer, handle) = Layer::new(Filter::One); @@ -71,7 +78,9 @@ fn reload_handle() { assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 1); assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 0); + assert_eq!(LevelFilter::current(), LevelFilter::INFO); handle.reload(Filter::Two).expect("should reload"); + assert_eq!(LevelFilter::current(), LevelFilter::DEBUG); event(); @@ -81,7 +90,6 @@ fn reload_handle() { } #[test] -#[cfg(feature = "registry")] fn reload_filter() { struct NopLayer; impl tracing_subscriber::Layer for NopLayer { @@ -111,9 +119,13 @@ fn reload_filter() { }; true } - } - fn event() { - tracing::trace!("my event"); + + fn max_level_hint(&self) -> Option { + match self { + Filter::One => Some(LevelFilter::INFO), + Filter::Two => Some(LevelFilter::DEBUG), + } + } } let (filter, handle) = Layer::new(Filter::One); @@ -131,7 +143,9 @@ fn reload_filter() { assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 1); assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 0); + assert_eq!(LevelFilter::current(), LevelFilter::INFO); handle.reload(Filter::Two).expect("should reload"); + assert_eq!(LevelFilter::current(), LevelFilter::DEBUG); event(); From 65093b38a5335c38f6d8b6834b3ebd8959fe5ff6 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Tue, 12 Jul 2022 13:01:23 +0900 Subject: [PATCH 061/142] chore(ci): update actions/checkout action to v3 (#2213) Older versions use node 12 which is no longer supported (end-of-life on April 30, 2022). Also, @main/@master is not very good as they may run into unreleased breaking changes. --- .github/workflows/CI.yml | 22 +++++++++++----------- .github/workflows/audit.yml | 6 +++--- .github/workflows/release.yml | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2529218a14..43fd26fbc1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -39,7 +39,7 @@ jobs: name: cargo check runs-on: ubuntu-latest steps: - - uses: actions/checkout@main + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: toolchain: stable @@ -57,7 +57,7 @@ jobs: needs: check runs-on: ubuntu-latest steps: - - uses: actions/checkout@main + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: toolchain: stable @@ -75,7 +75,7 @@ jobs: runs-on: ubuntu-latest needs: check steps: - - uses: actions/checkout@main + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: toolchain: stable @@ -95,7 +95,7 @@ jobs: needs: check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: toolchain: nightly @@ -137,7 +137,7 @@ jobs: - tracing - tracing-subscriber steps: - - uses: actions/checkout@main + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: toolchain: stable @@ -180,7 +180,7 @@ jobs: needs: check runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - name: "install Rust ${{ env.MSRV }}" uses: actions-rs/toolchain@v1 with: @@ -223,7 +223,7 @@ jobs: needs: check runs-on: ubuntu-latest steps: - - uses: actions/checkout@master + - uses: actions/checkout@v3 - name: "install Rust ${{ env.APPENDER_MSRV }}" uses: actions-rs/toolchain@v1 with: @@ -268,7 +268,7 @@ jobs: fail-fast: false runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@main + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} @@ -312,7 +312,7 @@ jobs: - tracing-tower fail-fast: false steps: - - uses: actions/checkout@main + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: target: wasm32-unknown-unknown @@ -333,7 +333,7 @@ jobs: subcrate: - tracing steps: - - uses: actions/checkout@main + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: target: wasm32-unknown-unknown @@ -353,7 +353,7 @@ jobs: needs: check runs-on: ubuntu-latest steps: - - uses: actions/checkout@main + - uses: actions/checkout@v3 - uses: actions-rs/toolchain@v1 with: toolchain: stable diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml index 438cc500ef..f4d89ead1f 100644 --- a/.github/workflows/audit.yml +++ b/.github/workflows/audit.yml @@ -3,7 +3,7 @@ name: Security audit on: schedule: - cron: '0 0 * * *' - + env: # Disable incremental compilation. # @@ -29,7 +29,7 @@ jobs: security_audit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - uses: actions-rs/audit-check@v1 with: - token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 98a6d33c77..77a52ca806 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: if: github.repository_owner == 'tokio-rs' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: taiki-e/create-gh-release-action@v1 with: prefix: tracing(-[a-z]+)? From 7533872af0268656f8526765102195bfae043759 Mon Sep 17 00:00:00 2001 From: Chris Burgess <9002722+cgbur@users.noreply.github.com> Date: Tue, 12 Jul 2022 11:57:39 -0400 Subject: [PATCH 062/142] docs: remove hard coded version link from readme (#2208) Motivation: When a user clicks they they may accidentally get an old version of the crate. Its also out of sync with the current main readme. Solution: removed the hard coded links --- tracing/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracing/README.md b/tracing/README.md index 1c7261c6ff..4090c5d73b 100644 --- a/tracing/README.md +++ b/tracing/README.md @@ -16,9 +16,9 @@ Application-level tracing for Rust. [Documentation][docs-url] | [Chat][discord-url] [crates-badge]: https://img.shields.io/crates/v/tracing.svg -[crates-url]: https://crates.io/crates/tracing/0.1.35 +[crates-url]: https://crates.io/crates/tracing [docs-badge]: https://docs.rs/tracing/badge.svg -[docs-url]: https://docs.rs/tracing/0.1.35 +[docs-url]: https://docs.rs/tracing [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg From 21cdf08e15f41286dc46ab152878b9812a7dffb4 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Wed, 13 Jul 2022 21:13:14 +0200 Subject: [PATCH 063/142] appender: name spawned thread (#2219) ## Motivation I find it useful when debugging applications with lots of threads to easily identity them by their names. ## Solution Just name the thread as other crates such as `sentry-rust` are doing. Co-authored-by: Guillaume Desmottes --- tracing-appender/src/worker.rs | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/tracing-appender/src/worker.rs b/tracing-appender/src/worker.rs index 5508baca85..622cb2c61a 100644 --- a/tracing-appender/src/worker.rs +++ b/tracing-appender/src/worker.rs @@ -68,22 +68,25 @@ impl Worker { /// Creates a worker thread that processes a channel until it's disconnected pub(crate) fn worker_thread(mut self) -> std::thread::JoinHandle<()> { - thread::spawn(move || { - loop { - match self.work() { - Ok(WorkerState::Continue) | Ok(WorkerState::Empty) => {} - Ok(WorkerState::Shutdown) | Ok(WorkerState::Disconnected) => { - let _ = self.shutdown.recv(); - break; - } - Err(_) => { - // TODO: Expose a metric for IO Errors, or print to stderr + thread::Builder::new() + .name("tracing-appender".to_string()) + .spawn(move || { + loop { + match self.work() { + Ok(WorkerState::Continue) | Ok(WorkerState::Empty) => {} + Ok(WorkerState::Shutdown) | Ok(WorkerState::Disconnected) => { + let _ = self.shutdown.recv(); + break; + } + Err(_) => { + // TODO: Expose a metric for IO Errors, or print to stderr + } } } - } - if let Err(e) = self.writer.flush() { - eprintln!("Failed to flush. Error: {}", e); - } - }) + if let Err(e) = self.writer.flush() { + eprintln!("Failed to flush. Error: {}", e); + } + }) + .expect("failed to spawn `tracing-appender` non-blocking worker thread") } } From 8fbdb1cd6baf83ba9bc930683cbd1b2980252558 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Wed, 13 Jul 2022 19:27:16 -0400 Subject: [PATCH 064/142] tracing: allow owned values and fat pointers in `Span::record` (#2212) Previously, using `record("x", "y")` would give an error: ``` error[E0277]: the size for values of type `str` cannot be known at compilation time --> src/main.rs:3:22 | 243 | span.record("x", "y"); | ^^^^^^ doesn't have a size known at compile-time | = help: the trait `Sized` is not implemented for `str` note: required by a bound in `Span::record` --> /home/jnelson/.local/lib/cargo/registry/src/github.com-1ecc6299db9ec823/tracing-0.1.32/src/span.rs:1184:30 | 1184 | pub fn record(&self, field: &Q, value: &V) -> &Self | ^ required by this bound in `Span::record` ``` Now it works fine, as tested by the doc-example. This doesn't break any existing code, because there's a generic `impl Value for &T`: https://docs.rs/tracing/0.1.35/tracing/trait.Value.html#impl-Value-for-%26%27a%20T Co-authored-by: Eliza Weisman --- tracing/src/span.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tracing/src/span.rs b/tracing/src/span.rs index 21358eb276..58822f4d9b 100644 --- a/tracing/src/span.rs +++ b/tracing/src/span.rs @@ -1136,7 +1136,7 @@ impl Span { /// /// // Now, record a value for parting as well. /// // (note that the field name is passed as a string slice) - /// span.record("parting", &"goodbye world!"); + /// span.record("parting", "goodbye world!"); /// ``` /// However, it may also be used to record a _new_ value for a field whose /// value was already recorded: @@ -1154,7 +1154,7 @@ impl Span { /// } /// Err(_) => { /// // Things are no longer okay! - /// span.record("is_okay", &false); + /// span.record("is_okay", false); /// } /// } /// ``` @@ -1181,17 +1181,17 @@ impl Span { /// // Now, you try to record a value for a new field, `new_field`, which was not /// // declared as `Empty` or populated when you created `span`. /// // You won't get any error, but the assignment will have no effect! - /// span.record("new_field", &"interesting_value_you_really_need"); + /// span.record("new_field", "interesting_value_you_really_need"); /// /// // Instead, all fields that may be recorded after span creation should be declared up front, /// // using field::Empty when a value is not known, as we did for `parting`. /// // This `record` call will indeed replace field::Empty with "you will be remembered". - /// span.record("parting", &"you will be remembered"); + /// span.record("parting", "you will be remembered"); /// ``` /// /// [`field::Empty`]: super::field::Empty /// [`Metadata`]: super::Metadata - pub fn record(&self, field: &Q, value: &V) -> &Self + pub fn record(&self, field: &Q, value: V) -> &Self where Q: field::AsField, V: field::Value, @@ -1201,7 +1201,7 @@ impl Span { self.record_all( &meta .fields() - .value_set(&[(&field, Some(value as &dyn field::Value))]), + .value_set(&[(&field, Some(&value as &dyn field::Value))]), ); } } @@ -1614,4 +1614,10 @@ mod test { impl AssertSync for Span {} impl AssertSync for Entered<'_> {} impl AssertSync for EnteredSpan {} + + #[test] + fn test_record_backwards_compat() { + Span::current().record("some-key", &"some text"); + Span::current().record("some-key", &false); + } } From 463b699377d59cf1a03fd8380daca68f5c66e0e2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 14 Jul 2022 20:50:42 +0100 Subject: [PATCH 065/142] core: remove misleading dispatcher docs (#2220) There's no automated propagation of subscribers between spans, you have to do this manually. https://discord.com/channels/500028886025895936/997201342596657232/997210656677830778 Co-authored-by: Eliza Weisman --- tracing/src/subscriber.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tracing/src/subscriber.rs b/tracing/src/subscriber.rs index 343dc5914c..f55698d13f 100644 --- a/tracing/src/subscriber.rs +++ b/tracing/src/subscriber.rs @@ -5,12 +5,12 @@ pub use tracing_core::subscriber::*; #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub use tracing_core::dispatcher::DefaultGuard; -/// Sets this subscriber as the default for the duration of a closure. +/// Sets this [`Subscriber`] as the default for the current thread for the +/// duration of a closure. /// /// The default subscriber is used when creating a new [`Span`] or -/// [`Event`], _if no span is currently executing_. If a span is currently -/// executing, new spans or events are dispatched to the subscriber that -/// tagged that span, instead. +/// [`Event`]. +/// /// /// [`Span`]: super::span::Span /// [`Subscriber`]: super::subscriber::Subscriber @@ -43,13 +43,10 @@ where crate::dispatcher::set_global_default(crate::Dispatch::new(subscriber)) } -/// Sets the subscriber as the default for the duration of the lifetime of the -/// returned [`DefaultGuard`] +/// Sets the [`Subscriber`] as the default for the current thread for the +/// duration of the lifetime of the returned [`DefaultGuard`]. /// -/// The default subscriber is used when creating a new [`Span`] or -/// [`Event`], _if no span is currently executing_. If a span is currently -/// executing, new spans or events are dispatched to the subscriber that -/// tagged that span, instead. +/// The default subscriber is used when creating a new [`Span`] or [`Event`]. /// /// [`Span`]: super::span::Span /// [`Subscriber`]: super::subscriber::Subscriber From 28a0c99cd59106c5aff5b74bafb8465057751343 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Mon, 18 Jul 2022 12:21:25 -0700 Subject: [PATCH 066/142] appender: add a builder for constructing `RollingFileAppender`s (#2227) ## Motivation Several currently open PRs, such as #2225 and #2221, add new configuration parameters to the rolling file appender in `tracing-appender`. The best way to add new optional configuration settings without breaking existing APIs or creating a very large number of new constructors is to add a builder interface. ## Solution Since a number of PRs would all need to add the builder API, introducing potential conflicts, this branch _just_ adds the builder interface without adding any new configuration options. Once this merges, the existing in-flight PRs can be rebased onto this branch to use the builder interface without conflicting with each other. Also, the `Builder::build` method is fallible and returns a `Result`, rather than panicking. This is a relatively common pattern in Rust --- for example, `std::thread::Builder::spawn` returns a `Result` if a new thread cannot be spawned, while `std::thread::spawn` simply panics. This allows users to handle appender initialization errors gracefully without breaking the API of the existing `new` constructor. Fixes #1953 Signed-off-by: Eliza Weisman --- tracing-appender/Cargo.toml | 1 + tracing-appender/src/rolling.rs | 160 ++++++++++++++++------- tracing-appender/src/rolling/builder.rs | 167 ++++++++++++++++++++++++ 3 files changed, 280 insertions(+), 48 deletions(-) create mode 100644 tracing-appender/src/rolling/builder.rs diff --git a/tracing-appender/Cargo.toml b/tracing-appender/Cargo.toml index 2d02b38e95..9f4e52139b 100644 --- a/tracing-appender/Cargo.toml +++ b/tracing-appender/Cargo.toml @@ -24,6 +24,7 @@ rust-version = "1.53.0" crossbeam-channel = "0.5.0" time = { version = "0.3", default-features = false, features = ["formatting"] } parking_lot = { optional = true, version = "0.12.0" } +thiserror = "1" [dependencies.tracing-subscriber] path = "../tracing-subscriber" diff --git a/tracing-appender/src/rolling.rs b/tracing-appender/src/rolling.rs index db1099403f..d4b2ee6fcf 100644 --- a/tracing-appender/src/rolling.rs +++ b/tracing-appender/src/rolling.rs @@ -31,11 +31,14 @@ use std::{ fmt::{self, Debug}, fs::{self, File, OpenOptions}, io::{self, Write}, - path::Path, + path::{Path, PathBuf}, sync::atomic::{AtomicUsize, Ordering}, }; use time::{format_description, Duration, OffsetDateTime, Time}; +mod builder; +pub use builder::{Builder, InitError}; + /// A file appender with the ability to rotate log files at a fixed schedule. /// /// `RollingFileAppender` implements the [`std:io::Write` trait][write] and will @@ -98,8 +101,8 @@ pub struct RollingWriter<'a>(RwLockReadGuard<'a, File>); #[derive(Debug)] struct Inner { - log_directory: String, - log_filename_prefix: String, + log_directory: PathBuf, + log_filename_prefix: Option, rotation: Rotation, next_date: AtomicUsize, } @@ -122,8 +125,10 @@ impl RollingFileAppender { /// - [`Rotation::daily()`][daily], /// - [`Rotation::never()`][never()] /// + /// Additional parameters can be configured using [`RollingFileAppender::builder`]. /// /// # Examples + /// /// ```rust /// # fn docs() { /// use tracing_appender::rolling::{RollingFileAppender, Rotation}; @@ -133,16 +138,63 @@ impl RollingFileAppender { pub fn new( rotation: Rotation, directory: impl AsRef, - file_name_prefix: impl AsRef, + filename_prefix: impl AsRef, ) -> RollingFileAppender { + let filename_prefix = filename_prefix + .as_ref() + .to_str() + .expect("filename prefix must be a valid UTF-8 string"); + Self::builder() + .rotation(rotation) + .filename_prefix(filename_prefix) + .build(directory) + .expect("initializing rolling file appender failed") + } + + /// Returns a new [`Builder`] for configuring a `RollingFileAppender`. + /// + /// The builder interface can be used to set additional configuration + /// parameters when constructing a new appender. + /// + /// Unlike [`RollingFileAppender::new`], the [`Builder::build`] method + /// returns a `Result` rather than panicking when the appender cannot be + /// initialized. Therefore, the builder interface can also be used when + /// appender initialization errors should be handled gracefully. + /// + /// # Examples + /// + /// ```rust + /// # fn docs() { + /// use tracing_appender::rolling::{RollingFileAppender, Rotation}; + /// + /// let file_appender = RollingFileAppender::builder() + /// .rotation(Rotation::HOURLY) // rotate log files once every hour + /// .filename_prefix("myapp") // log file names will be prefixed with `myapp.` + /// .build("/var/log") // try to build an appender that stores log files in `/var/log` + /// .expect("initializing rolling file appender failed"); + /// # drop(file_appender); + /// # } + /// ``` + #[must_use] + pub fn builder() -> Builder { + Builder::new() + } + + fn from_builder(builder: &Builder, directory: impl AsRef) -> Result { + let Builder { + ref rotation, + ref prefix, + } = builder; + let filename_prefix = prefix.clone(); + let directory = directory.as_ref().to_path_buf(); let now = OffsetDateTime::now_utc(); - let (state, writer) = Inner::new(now, rotation, directory, file_name_prefix); - Self { + let (state, writer) = Inner::new(now, rotation.clone(), directory, filename_prefix)?; + Ok(Self { state, writer, #[cfg(test)] now: Box::new(OffsetDateTime::now_utc), - } + }) } #[inline] @@ -428,35 +480,42 @@ impl Rotation { } } - pub(crate) fn join_date(&self, filename: &str, date: &OffsetDateTime) -> String { - match *self { + pub(crate) fn join_date(&self, filename: Option<&str>, date: &OffsetDateTime) -> String { + let date = match *self { Rotation::MINUTELY => { let format = format_description::parse("[year]-[month]-[day]-[hour]-[minute]") .expect("Unable to create a formatter; this is a bug in tracing-appender"); - - let date = date - .format(&format) - .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender"); - format!("{}.{}", filename, date) + date.format(&format) + .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender") } Rotation::HOURLY => { let format = format_description::parse("[year]-[month]-[day]-[hour]") .expect("Unable to create a formatter; this is a bug in tracing-appender"); - - let date = date - .format(&format) - .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender"); - format!("{}.{}", filename, date) + date.format(&format) + .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender") } Rotation::DAILY => { let format = format_description::parse("[year]-[month]-[day]") .expect("Unable to create a formatter; this is a bug in tracing-appender"); - let date = date - .format(&format) - .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender"); - format!("{}.{}", filename, date) + date.format(&format) + .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender") + } + Rotation::NEVER => { + // If there's a name prefix, use that. + if let Some(filename) = filename { + return filename.to_owned(); + } + + // Otherwise, just use the date. + let format = format_description::parse("[year]-[month]-[day]") + .expect("Unable to create a formatter; this is a bug in tracing-appender"); + date.format(&format) + .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender") } - Rotation::NEVER => filename.to_string(), + }; + match filename { + Some(filename) => format!("{}.{}", filename, date), + None => date, } } } @@ -480,20 +539,16 @@ impl Inner { now: OffsetDateTime, rotation: Rotation, directory: impl AsRef, - file_name_prefix: impl AsRef, - ) -> (Self, RwLock) { - let log_directory = directory.as_ref().to_str().unwrap(); - let log_filename_prefix = file_name_prefix.as_ref().to_str().unwrap(); - - let filename = rotation.join_date(log_filename_prefix, &now); + log_filename_prefix: Option, + ) -> Result<(Self, RwLock), builder::InitError> { + let log_directory = directory.as_ref().to_path_buf(); + let filename = rotation.join_date(log_filename_prefix.as_deref(), &now); let next_date = rotation.next_date(&now); - let writer = RwLock::new( - create_writer(log_directory, &filename).expect("failed to create appender"), - ); + let writer = RwLock::new(create_writer(log_directory.as_ref(), &filename)?); let inner = Inner { - log_directory: log_directory.to_string(), - log_filename_prefix: log_filename_prefix.to_string(), + log_directory, + log_filename_prefix, next_date: AtomicUsize::new( next_date .map(|date| date.unix_timestamp() as usize) @@ -501,11 +556,13 @@ impl Inner { ), rotation, }; - (inner, writer) + Ok((inner, writer)) } fn refresh_writer(&self, now: OffsetDateTime, file: &mut File) { - let filename = self.rotation.join_date(&self.log_filename_prefix, &now); + let filename = self + .rotation + .join_date(self.log_filename_prefix.as_deref(), &now); match create_writer(&self.log_directory, &filename) { Ok(new_file) => { @@ -552,20 +609,22 @@ impl Inner { } } -fn create_writer(directory: &str, filename: &str) -> io::Result { - let path = Path::new(directory).join(filename); +fn create_writer(directory: &Path, filename: &str) -> Result { + let path = directory.join(filename); let mut open_options = OpenOptions::new(); open_options.append(true).create(true); let new_file = open_options.open(path.as_path()); if new_file.is_err() { if let Some(parent) = path.parent() { - fs::create_dir_all(parent)?; - return open_options.open(path); + fs::create_dir_all(parent).map_err(InitError::ctx("failed to create log directory"))?; + return open_options + .open(path) + .map_err(InitError::ctx("failed to create initial log file")); } } - new_file + new_file.map_err(InitError::ctx("failed to create initial log file")) } #[cfg(test)] @@ -673,19 +732,19 @@ mod test { let now = OffsetDateTime::parse("2020-02-01 10:01:00 +00:00:00", &format).unwrap(); // per-minute - let path = Rotation::MINUTELY.join_date("app.log", &now); + let path = Rotation::MINUTELY.join_date(Some("app.log"), &now); assert_eq!("app.log.2020-02-01-10-01", path); // per-hour - let path = Rotation::HOURLY.join_date("app.log", &now); + let path = Rotation::HOURLY.join_date(Some("app.log"), &now); assert_eq!("app.log.2020-02-01-10", path); // per-day - let path = Rotation::DAILY.join_date("app.log", &now); + let path = Rotation::DAILY.join_date(Some("app.log"), &now); assert_eq!("app.log.2020-02-01", path); // never - let path = Rotation::NEVER.join_date("app.log", &now); + let path = Rotation::NEVER.join_date(Some("app.log"), &now); assert_eq!("app.log", path); } @@ -702,8 +761,13 @@ mod test { let now = OffsetDateTime::parse("2020-02-01 10:01:00 +00:00:00", &format).unwrap(); let directory = tempfile::tempdir().expect("failed to create tempdir"); - let (state, writer) = - Inner::new(now, Rotation::HOURLY, directory.path(), "test_make_writer"); + let (state, writer) = Inner::new( + now, + Rotation::HOURLY, + directory.path(), + Some("test_make_writer".to_string()), + ) + .unwrap(); let clock = Arc::new(Mutex::new(now)); let now = { diff --git a/tracing-appender/src/rolling/builder.rs b/tracing-appender/src/rolling/builder.rs new file mode 100644 index 0000000000..82161f0cc6 --- /dev/null +++ b/tracing-appender/src/rolling/builder.rs @@ -0,0 +1,167 @@ +use super::{RollingFileAppender, Rotation}; +use std::{io, path::Path}; +use thiserror::Error; + +/// A [builder] for configuring [`RollingFileAppender`]s. +/// +/// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html +#[derive(Debug)] +pub struct Builder { + pub(super) rotation: Rotation, + pub(super) prefix: Option, +} + +/// Errors returned by [`Builder::build`]. +#[derive(Error, Debug)] +#[error("{context}: {source}")] +pub struct InitError { + context: &'static str, + #[source] + source: io::Error, +} + +impl InitError { + pub(crate) fn ctx(context: &'static str) -> impl FnOnce(io::Error) -> Self { + move |source| Self { context, source } + } +} + +impl Builder { + /// Returns a new `Builder` for configuring a [`RollingFileAppender`], with + /// the default parameters. + /// + /// # Default Values + /// + /// The default values for the builder are: + /// + /// | Parameter | Default Value | Notes | + /// | :-------- | :------------ | :---- | + /// | [`rotation`] | [`Rotation::NEVER`] | By default, log files will never be rotated. | + /// | [`filename_prefix`] | `""` | By default, log file names will not have a prefix. | + /// + /// [`rotation`]: Self::rotation + /// [`filename_prefix`]: Self::filename_prefix + #[must_use] + pub const fn new() -> Self { + Self { + rotation: Rotation::NEVER, + prefix: None, + } + } + + /// Sets the [rotation strategy] for log files. + /// + /// By default, this is [`Rotation::NEVER`]. + /// + /// # Examples + /// + /// ``` + /// # fn docs() { + /// use tracing_appender::rolling::{Rotation, RollingFileAppender}; + /// + /// let appender = RollingFileAppender::builder() + /// .rotation(Rotation::HOURLY) // rotate log files once every hour + /// // ... + /// .build("/var/log") + /// .expect("failed to initialize rolling file appender"); + /// + /// # drop(appender) + /// # } + /// ``` + /// + /// [rotation strategy]: Rotation + #[must_use] + pub fn rotation(self, rotation: Rotation) -> Self { + Self { rotation, ..self } + } + + /// Sets the prefix for log filenames. The prefix is output before the + /// timestamp in the file name, and if it is non-empty, it is followed by a + /// dot (`.`). + /// + /// By default, log files do not have a prefix. + /// + /// # Examples + /// + /// Setting a prefix: + /// + /// ``` + /// use tracing_appender::rolling::RollingFileAppender; + /// + /// # fn docs() { + /// let appender = RollingFileAppender::builder() + /// .filename_prefix("myapp.log") // log files will have names like "myapp.log.2019-01-01" + /// // ... + /// .build("/var/log") + /// .expect("failed to initialize rolling file appender"); + /// # drop(appender) + /// # } + /// ``` + /// + /// No prefix: + /// + /// ``` + /// use tracing_appender::rolling::RollingFileAppender; + /// + /// # fn docs() { + /// let appender = RollingFileAppender::builder() + /// .filename_prefix("") // log files will have names like "2019-01-01" + /// // ... + /// .build("/var/log") + /// .expect("failed to initialize rolling file appender"); + /// # drop(appender) + /// # } + /// ``` + /// + /// [rotation strategy]: Rotation + #[must_use] + pub fn filename_prefix(self, prefix: impl Into) -> Self { + let prefix = prefix.into(); + // If the configured prefix is the empty string, then don't include a + // separator character. + let prefix = if prefix.is_empty() { + None + } else { + Some(prefix) + }; + Self { prefix, ..self } + } + + /// Builds a new [`RollingFileAppender`] with the configured parameters, + /// emitting log files to the provided directory. + /// + /// Unlike [`RollingFileAppender::new`], this returns a `Result` rather than + /// panicking when the appender cannot be initialized. + /// + /// # Examples + /// + /// ``` + /// use tracing_appender::rolling::{Rotation, RollingFileAppender}; + /// + /// # fn docs() { + /// let appender = RollingFileAppender::builder() + /// .rotation(Rotation::DAILY) // rotate log files once per day + /// .filename_prefix("myapp.log") // log files will have names like "myapp.log.2019-01-01" + /// .build("/var/log/myapp") // write log files to the '/var/log/myapp' directory + /// .expect("failed to initialize rolling file appender"); + /// # drop(appender); + /// # } + /// ``` + /// + /// This is equivalent to + /// ``` + /// # fn docs() { + /// let appender = tracing_appender::rolling::daily("myapp.log", "/var/log/myapp"); + /// # drop(appender); + /// # } + /// ``` + pub fn build(&self, directory: impl AsRef) -> Result { + RollingFileAppender::from_builder(self, directory) + } +} + +impl Default for Builder { + fn default() -> Self { + Self::new() + } +} From 398a6ecf89f8c29a6cd5fee6282a829431b0357a Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 19 Jul 2022 18:36:59 -0400 Subject: [PATCH 067/142] core: implement `PartialEq`, `Eq` for `Metadata`, `FieldSet` (#2229) A `FieldSet` is equal to another `FieldSet` if they share the same callsite and fields (provided in the same order). This ensures that a `Field` applicable to one `FieldSet` is applicable to any equal `FieldSet`. A `Metadata` is equal to another `Metadata` if all of their contained metadata is exactly equal. This change manually re-implements `PartialEq` and `Eq` for `Metadata` and `FieldSet` to define their equality strictly in terms of callsite equality. In debug builds, the equality of these types' other fields is also checked. Documentation is added to both `Metadata` and `FieldSet` explaining this behavior. The expectation that, in a well-behaving application, `Metadata` and `FieldSet`s with equal callsites will be otherwise equal is documented on `Callsite::metadata`. This is not a breaking change, as previous releases did not define equality for `Metadata` or `FieldSet`. The `Callsite` trait remains safe, as this expectation is not (yet) a safety-critical property. --- tracing-core/src/callsite.rs | 9 ++++ tracing-core/src/field.rs | 44 ++++++++++++++++++ tracing-core/src/metadata.rs | 89 ++++++++++++++++++++++++++++-------- 3 files changed, 124 insertions(+), 18 deletions(-) diff --git a/tracing-core/src/callsite.rs b/tracing-core/src/callsite.rs index d0fe1a3d6c..bc7405ffa0 100644 --- a/tracing-core/src/callsite.rs +++ b/tracing-core/src/callsite.rs @@ -137,6 +137,15 @@ pub trait Callsite: Sync { /// Returns the [metadata] associated with the callsite. /// + ///
+ ///
+    ///
+    /// **Note:** Implementations of this method should not produce [`Metadata`]
+    /// that share the same callsite [`Identifier`] but otherwise differ in any
+    /// way (e.g., have different `name`s).
+    ///
+    /// 
+ /// /// [metadata]: super::metadata::Metadata fn metadata(&self) -> &Metadata<'_>; diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index c9869ce2ca..e103c75a9d 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -145,6 +145,16 @@ pub struct Field { pub struct Empty; /// Describes the fields present on a span. +/// +/// ## Equality +/// +/// In well-behaved applications, two `FieldSet`s [initialized] with equal +/// [callsite identifiers] will have identical fields. Consequently, in release +/// builds, [`FieldSet::eq`] *only* checks that its arguments have equal +/// callsites. However, the equality of field names is checked in debug builds. +/// +/// [initialized]: Self::new +/// [callsite identifiers]: callsite::Identifier pub struct FieldSet { /// The names of each field on the described span. names: &'static [&'static str], @@ -911,6 +921,40 @@ impl fmt::Display for FieldSet { } } +impl Eq for FieldSet {} + +impl PartialEq for FieldSet { + fn eq(&self, other: &Self) -> bool { + if core::ptr::eq(&self, &other) { + true + } else if cfg!(not(debug_assertions)) { + // In a well-behaving application, two `FieldSet`s can be assumed to + // be totally equal so long as they share the same callsite. + self.callsite == other.callsite + } else { + // However, when debug-assertions are enabled, do NOT assume that + // the application is well-behaving; check every the field names of + // each `FieldSet` for equality. + + // `FieldSet` is destructured here to ensure a compile-error if the + // fields of `FieldSet` change. + let Self { + names: lhs_names, + callsite: lhs_callsite, + } = self; + + let Self { + names: rhs_names, + callsite: rhs_callsite, + } = &other; + + // Check callsite equality first, as it is probably cheaper to do + // than str equality. + lhs_callsite == rhs_callsite && lhs_names == rhs_names + } + } +} + // ===== impl Iter ===== impl Iterator for Iter { diff --git a/tracing-core/src/metadata.rs b/tracing-core/src/metadata.rs index 47b9388a41..a154419a74 100644 --- a/tracing-core/src/metadata.rs +++ b/tracing-core/src/metadata.rs @@ -35,28 +35,25 @@ use crate::stdlib::{ /// _significantly_ lower than that of creating the actual span. Therefore, /// filtering is based on metadata, rather than on the constructed span. /// -///
-///     Note: Although instances of Metadata
-///     cannot be compared directly, they provide a method
-///     id, returning
-///     an opaque callsite
-///     identifierwhich uniquely identifies the callsite where the metadata
-///     originated. This can be used to determine if two Metadata
-///     correspond to the same callsite.
-/// 
+/// ## Equality +/// +/// In well-behaved applications, two `Metadata` with equal +/// [callsite identifiers] will be equal in all other ways (i.e., have the same +/// `name`, `target`, etc.). Consequently, in release builds, [`Metadata::eq`] +/// *only* checks that its arguments have equal callsites. However, the equality +/// of `Metadata`'s other fields is checked in debug builds. /// /// [span]: super::span /// [event]: super::event -/// [name]: Metadata::name() -/// [target]: Metadata::target() -/// [fields]: Metadata::fields() -/// [verbosity level]: Metadata::level() -/// [file name]: Metadata::file() -/// [line number]: Metadata::line() -/// [module path]: Metadata::module_path() +/// [name]: Self::name +/// [target]: Self::target +/// [fields]: Self::fields +/// [verbosity level]: Self::level +/// [file name]: Self::file +/// [line number]: Self::line +/// [module path]: Self::module_path /// [`Subscriber`]: super::subscriber::Subscriber -/// [`id`]: Metadata::id -/// [callsite identifier]: super::callsite::Identifier +/// [callsite identifiers]: Self::callsite pub struct Metadata<'a> { /// The name of the span described by this metadata. name: &'static str, @@ -443,6 +440,62 @@ impl fmt::Debug for Kind { } } +impl<'a> Eq for Metadata<'a> {} + +impl<'a> PartialEq for Metadata<'a> { + #[inline] + fn eq(&self, other: &Self) -> bool { + if core::ptr::eq(&self, &other) { + true + } else if cfg!(not(debug_assertions)) { + // In a well-behaving application, two `Metadata` can be assumed to + // be totally equal so long as they share the same callsite. + self.callsite() == other.callsite() + } else { + // However, when debug-assertions are enabled, do not assume that + // the application is well-behaving; check every field of `Metadata` + // for equality. + + // `Metadata` is destructured here to ensure a compile-error if the + // fields of `Metadata` change. + let Metadata { + name: lhs_name, + target: lhs_target, + level: lhs_level, + module_path: lhs_module_path, + file: lhs_file, + line: lhs_line, + fields: lhs_fields, + kind: lhs_kind, + } = self; + + let Metadata { + name: rhs_name, + target: rhs_target, + level: rhs_level, + module_path: rhs_module_path, + file: rhs_file, + line: rhs_line, + fields: rhs_fields, + kind: rhs_kind, + } = &other; + + // The initial comparison of callsites is purely an optimization; + // it can be removed without affecting the overall semantics of the + // expression. + self.callsite() == other.callsite() + && lhs_name == rhs_name + && lhs_target == rhs_target + && lhs_level == rhs_level + && lhs_module_path == rhs_module_path + && lhs_file == rhs_file + && lhs_line == rhs_line + && lhs_fields == rhs_fields + && lhs_kind == rhs_kind + } + } +} + // ===== impl Level ===== impl Level { From 290eff2e4df4951d89cf287bb22265399d030471 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 20 Jul 2022 12:36:20 -0700 Subject: [PATCH 068/142] subscriber: prepare to release v0.3.15 (#2237) # 0.3.15 (Jul 20, 2022) This release fixes a bug where the `reload` layer would fail to pass through `max_level_hint` to the underlying layer, potentially breaking filtering. ### Fixed - **reload**: pass through `max_level_hint` to the inner `Layer` ([#2204]) Thanks to @guswynn for contributing to this release! [#2204]: https://github.com/tokio-rs/tracing/pull/2204 --- tracing-subscriber/CHANGELOG.md | 13 +++++++++++++ tracing-subscriber/Cargo.toml | 2 +- tracing-subscriber/README.md | 2 +- tracing-subscriber/src/lib.rs | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/tracing-subscriber/CHANGELOG.md b/tracing-subscriber/CHANGELOG.md index 35747566e5..e2ac4e7c31 100644 --- a/tracing-subscriber/CHANGELOG.md +++ b/tracing-subscriber/CHANGELOG.md @@ -1,3 +1,16 @@ +# 0.3.15 (Jul 20, 2022) + +This release fixes a bug where the `reload` layer would fail to pass through +`max_level_hint` to the underlying layer, potentially breaking filtering. + +### Fixed + +- **reload**: pass through `max_level_hint` to the inner `Layer` ([#2204]) + +Thanks to @guswynn for contributing to this release! + +[#2204]: https://github.com/tokio-rs/tracing/pull/2204 + # 0.3.14 (Jul 1, 2022) This release fixes multiple filtering bugs in the `Layer` implementations for diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 95c6740c64..8d2d1691c0 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-subscriber" -version = "0.3.14" +version = "0.3.15" authors = [ "Eliza Weisman ", "David Barsky ", diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md index 95242b37ed..ba2b4e1c33 100644 --- a/tracing-subscriber/README.md +++ b/tracing-subscriber/README.md @@ -21,7 +21,7 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers. [crates-badge]: https://img.shields.io/crates/v/tracing-subscriber.svg [crates-url]: https://crates.io/crates/tracing-subscriber [docs-badge]: https://docs.rs/tracing-subscriber/badge.svg -[docs-url]: https://docs.rs/tracing-subscriber/0.3.14 +[docs-url]: https://docs.rs/tracing-subscriber/0.3.15 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_subscriber [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 4bd61000e8..9de70a90d1 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -160,7 +160,7 @@ //! [`time` crate]: https://crates.io/crates/time //! [`libstd`]: std //! [`liballoc`]: alloc -#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.14")] +#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.15")] #![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/" From e4a6b5745f748ad165e5bb49d44428bd68580600 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Mon, 25 Jul 2022 09:44:27 -0700 Subject: [PATCH 069/142] subscriber: if error occurs when formatting event, write error to Writer (#2102) Motivation: When `Format_event::format_event(...)` returns an error, we are currently silently dropping that Event. https://github.com/tokio-rs/valuable/issues/88 explains one such case in which this was encountered (due to a bug in valuable-serde). We want to be made aware whenever an Event is dropped. Solution: Write to the Writer with an error message to let the user know that we were unable to format a specific event. If writing to the Writer fails, we fall back to writing to stderr. We are not emitting an actual tracing Event, to avoid the risk of a cycle (the new Event could trigger the same formatting error again). Resolves #1965. Co-authored-by: Eliza Weisman Co-authored-by: David Barsky --- tracing-subscriber/src/fmt/fmt_layer.rs | 106 +++++++++++++++++++++++- tracing-subscriber/src/fmt/mod.rs | 20 ++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/tracing-subscriber/src/fmt/fmt_layer.rs b/tracing-subscriber/src/fmt/fmt_layer.rs index 21992e7809..6e4e2ac0bb 100644 --- a/tracing-subscriber/src/fmt/fmt_layer.rs +++ b/tracing-subscriber/src/fmt/fmt_layer.rs @@ -70,6 +70,7 @@ pub struct Layer< fmt_event: E, fmt_span: format::FmtSpanConfig, is_ansi: bool, + log_internal_errors: bool, _inner: PhantomData, } @@ -119,6 +120,7 @@ where fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, _inner: self._inner, } } @@ -148,6 +150,7 @@ where fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, _inner: self._inner, } } @@ -180,6 +183,7 @@ impl Layer { fmt_event: self.fmt_event, fmt_span: self.fmt_span, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, make_writer, _inner: self._inner, } @@ -263,6 +267,7 @@ impl Layer { fmt_event: self.fmt_event, fmt_span: self.fmt_span, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, make_writer: TestWriter::default(), _inner: self._inner, } @@ -278,6 +283,24 @@ impl Layer { } } + /// Sets whether to write errors from [`FormatEvent`] to the writer. + /// Defaults to true. + /// + /// By default, `fmt::Layer` will write any `FormatEvent`-internal errors to + /// the writer. These errors are unlikely and will only occur if there is a + /// bug in the `FormatEvent` implementation or its dependencies. + /// + /// If writing to the writer fails, the error message is printed to stderr + /// as a fallback. + /// + /// [`FormatEvent`]: crate::fmt::FormatEvent + pub fn log_internal_errors(self, log_internal_errors: bool) -> Self { + Self { + log_internal_errors, + ..self + } + } + /// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`]. /// /// This sets the [`MakeWriter`] that the layer being built will use to write events. @@ -306,6 +329,7 @@ impl Layer { fmt_event: self.fmt_event, fmt_span: self.fmt_span, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, make_writer: f(self.make_writer), _inner: self._inner, } @@ -337,6 +361,7 @@ where fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, _inner: self._inner, } } @@ -349,6 +374,7 @@ where fmt_span: self.fmt_span.without_time(), make_writer: self.make_writer, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, _inner: self._inner, } } @@ -477,6 +503,7 @@ where fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, _inner: self._inner, } } @@ -491,6 +518,7 @@ where fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, _inner: self._inner, } } @@ -521,6 +549,7 @@ where make_writer: self.make_writer, // always disable ANSI escapes in JSON mode! is_ansi: false, + log_internal_errors: self.log_internal_errors, _inner: self._inner, } } @@ -587,6 +616,7 @@ impl Layer { fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, _inner: self._inner, } } @@ -617,6 +647,7 @@ impl Layer { fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, + log_internal_errors: self.log_internal_errors, _inner: self._inner, } } @@ -630,6 +661,7 @@ impl Default for Layer { fmt_span: format::FmtSpanConfig::default(), make_writer: io::stdout, is_ansi: cfg!(feature = "ansi"), + log_internal_errors: false, _inner: PhantomData, } } @@ -749,6 +781,11 @@ where { fields.was_ansi = self.is_ansi; extensions.insert(fields); + } else { + eprintln!( + "[tracing-subscriber] Unable to format the following event, ignoring: {:?}", + attrs + ); } } @@ -895,7 +932,20 @@ where .is_ok() { let mut writer = self.make_writer.make_writer_for(event.metadata()); - let _ = io::Write::write_all(&mut writer, buf.as_bytes()); + let res = io::Write::write_all(&mut writer, buf.as_bytes()); + if self.log_internal_errors { + if let Err(e) = res { + eprintln!("[tracing-subscriber] Unable to write an event to the Writer for this Subscriber! Error: {}\n", e); + } + } + } else if self.log_internal_errors { + let err_msg = format!("Unable to format the following event. Name: {}; Fields: {:?}\n", + event.metadata().name(), event.fields()); + let mut writer = self.make_writer.make_writer_for(event.metadata()); + let res = io::Write::write_all(&mut writer, err_msg.as_bytes()); + if let Err(e) = res { + eprintln!("[tracing-subscriber] Unable to write an \"event formatting error\" to the Writer for this Subscriber! Error: {}\n", e); + } } buf.clear(); @@ -1192,6 +1242,60 @@ mod test { re.replace_all(s.as_str(), "timing").to_string() } + #[test] + fn format_error_print_to_stderr() { + struct AlwaysError; + + impl std::fmt::Debug for AlwaysError { + fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + Err(std::fmt::Error) + } + } + + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt::Subscriber::builder() + .with_writer(make_writer.clone()) + .with_level(false) + .with_ansi(false) + .with_timer(MockTime) + .finish(); + + with_default(subscriber, || { + tracing::info!(?AlwaysError); + }); + let actual = sanitize_timings(make_writer.get_string()); + + // Only assert the start because the line number and callsite may change. + let expected = concat!("Unable to format the following event. Name: event ", file!(), ":"); + assert!(actual.as_str().starts_with(expected), "\nactual = {}\nshould start with expected = {}\n", actual, expected); + } + + #[test] + fn format_error_ignore_if_log_internal_errors_is_false() { + struct AlwaysError; + + impl std::fmt::Debug for AlwaysError { + fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + Err(std::fmt::Error) + } + } + + let make_writer = MockMakeWriter::default(); + let subscriber = crate::fmt::Subscriber::builder() + .with_writer(make_writer.clone()) + .with_level(false) + .with_ansi(false) + .with_timer(MockTime) + .log_internal_errors(false) + .finish(); + + with_default(subscriber, || { + tracing::info!(?AlwaysError); + }); + let actual = sanitize_timings(make_writer.get_string()); + assert_eq!("", actual.as_str()); + } + #[test] fn synthesize_span_none() { let make_writer = MockMakeWriter::default(); diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index 3c6a6ac40e..70c6db579b 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -465,7 +465,7 @@ impl Default for SubscriberBuilder { SubscriberBuilder { filter: Subscriber::DEFAULT_MAX_LEVEL, inner: Default::default(), - } + }.log_internal_errors(true) } } @@ -620,6 +620,24 @@ where } } + /// Sets whether to write errors from [`FormatEvent`] to the writer. + /// Defaults to true. + /// + /// By default, `fmt::Layer` will write any `FormatEvent`-internal errors to + /// the writer. These errors are unlikely and will only occur if there is a + /// bug in the `FormatEvent` implementation or its dependencies. + /// + /// If writing to the writer fails, the error message is printed to stderr + /// as a fallback. + /// + /// [`FormatEvent`]: crate::fmt::FormatEvent + pub fn log_internal_errors(self, log_internal_errors: bool) -> SubscriberBuilder, F, W> { + SubscriberBuilder { + inner: self.inner.log_internal_errors(log_internal_errors), + ..self + } + } + /// Sets whether or not an event's target is displayed. pub fn with_target( self, From a3868af664fb593a86667b105658eacf4b08ed90 Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Tue, 26 Jul 2022 18:27:58 -0400 Subject: [PATCH 070/142] chore: fix minimal-versions correctness (#2246) ## Motivation Fix minimal-versions failure. ## Solution Upgrade all the dependencies to their most recent semver-compatible version, adjusting back down as necessary for MSRV. Essentially a cherry-pick of #2231, but redone by hand. ## Tests - `cargo minimal-versions msrv verify -- cargo check --all-features` - `cargo minimal-versions msrv verify -- cargo check --no-default-features` ## Methodology - `cargo update && cargo upgrade --to-lockfile` - Identify [a bug](https://github.com/killercup/cargo-edit/issues/750) and manually resolve it - loop; upgrade transitive deps - `cargo minimal-versions check --all-features` - Identify failing dep - `cargo minimal-versions tree -i dep --all-features` - Find the closest dependency leading to pulling in `dep` - `cargo add fixdep --optional` to force a more recent more-minimal-versions-correct version - loop; downgrade to msrv - `cargo minimal-versions msrv verify -- cargo check --all-features` - Identify failing dep - `cargo minimal-versions tree -i dep --all-features` - Find the version that supports MSRV from lib.rs - `cargo upgrade dep@msrv` --- examples/Cargo.toml | 48 ++++++++++++++--------------- tracing-appender/Cargo.toml | 16 +++++----- tracing-attributes/Cargo.toml | 10 +++--- tracing-core/Cargo.toml | 6 ++-- tracing-error/Cargo.toml | 4 +-- tracing-flame/Cargo.toml | 6 ++-- tracing-futures/Cargo.toml | 22 +++++++------ tracing-journald/Cargo.toml | 12 ++++---- tracing-journald/src/lib.rs | 4 +-- tracing-log/Cargo.toml | 16 +++++----- tracing-macros/Cargo.toml | 6 ++-- tracing-mock/Cargo.toml | 9 ++++-- tracing-opentelemetry/Cargo.toml | 20 +++++++----- tracing-serde/Cargo.toml | 6 ++-- tracing-subscriber/Cargo.toml | 36 +++++++++++----------- tracing-tower/Cargo.toml | 14 ++++----- tracing/Cargo.toml | 14 ++++----- tracing/test-log-support/Cargo.toml | 2 +- 18 files changed, 131 insertions(+), 120 deletions(-) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 1f829940a0..28a12f4456 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -11,57 +11,57 @@ default = [] [dev-dependencies] # tracing crates -tracing = { path = "../tracing", version = "0.1" } -tracing-core = { path = "../tracing-core", version = "0.1" } +tracing = { path = "../tracing", version = "0.1.35" } +tracing-core = { path = "../tracing-core", version = "0.1.28" } tracing-error = { path = "../tracing-error" } tracing-flame = { path = "../tracing-flame" } tracing-tower = { version = "0.1.0", path = "../tracing-tower" } -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["json", "env-filter"] } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", features = ["json", "env-filter"] } tracing-futures = { version = "0.2.1", path = "../tracing-futures", features = ["futures-01"] } -tracing-attributes = { path = "../tracing-attributes", version = "0.1.2" } -tracing-log = { path = "../tracing-log", version = "0.1.1", features = ["env_logger"] } +tracing-attributes = { path = "../tracing-attributes", version = "0.1.22" } +tracing-log = { path = "../tracing-log", version = "0.1.3", features = ["env_logger"] } tracing-serde = { path = "../tracing-serde" } tracing-opentelemetry = { path = "../tracing-opentelemetry" } tracing-journald = { path = "../tracing-journald" } -tracing-appender = { path = "../tracing-appender", version = "0.2" } +tracing-appender = { path = "../tracing-appender", version = "0.2.0" } # serde example -serde_json = "1.0" +serde_json = "1.0.82" -futures = "0.3" -tokio = { version = "1.1", features = ["full"] } +futures = "0.3.21" +tokio = { version = "1.20.1", features = ["full"] } # env-logger example -env_logger = "0.9" +env_logger = "0.9.0" # tower examples -tower = { version = "0.4.4", features = ["full"] } -http = "0.2" -hyper = { version = "0.14.11", features = ["full"] } -rand = "0.7" +tower = { version = "0.4.13", features = ["full"] } +http = "0.2.8" +hyper = { version = "0.14.20", features = ["full"] } +rand = "0.7.3" bytes = "1" -argh = "0.1.5" +argh = "0.1.8" # sloggish example -ansi_term = "0.12" -humantime = "2.0" -log = "0.4" +ansi_term = "0.12.1" +humantime = "2.1.0" +log = "0.4.17" # inferno example -inferno = "0.11.0" +inferno = "0.11.6" tempfile = "3" # opentelemetry example -opentelemetry = { version = "0.17", default-features = false, features = ["trace"] } -opentelemetry-jaeger = "0.16" +opentelemetry = { version = "0.17.0", default-features = false, features = ["trace"] } +opentelemetry-jaeger = "0.16.0" # fmt examples snafu = "0.6.10" -thiserror = "1.0.26" +thiserror = "1.0.31" # valuable examples valuable = { version = "0.1.0", features = ["derive"] } [target.'cfg(tracing_unstable)'.dependencies] -tracing-core = { path = "../tracing-core", version = "0.1", features = ["valuable"]} -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["json", "env-filter", "valuable"]} +tracing-core = { path = "../tracing-core", version = "0.1.28", features = ["valuable"]} +tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", features = ["json", "env-filter", "valuable"]} diff --git a/tracing-appender/Cargo.toml b/tracing-appender/Cargo.toml index 9f4e52139b..c7716a2564 100644 --- a/tracing-appender/Cargo.toml +++ b/tracing-appender/Cargo.toml @@ -21,24 +21,24 @@ edition = "2018" rust-version = "1.53.0" [dependencies] -crossbeam-channel = "0.5.0" -time = { version = "0.3", default-features = false, features = ["formatting"] } -parking_lot = { optional = true, version = "0.12.0" } +crossbeam-channel = "0.5.6" +time = { version = "0.3.2", default-features = false, features = ["formatting"] } +parking_lot = { optional = true, version = "0.12.1" } thiserror = "1" [dependencies.tracing-subscriber] path = "../tracing-subscriber" -version = "0.3" +version = "0.3.0" default-features = false features = ["fmt", "std"] [dev-dependencies] -criterion = { version = "0.3", default_features = false } -tracing = { path = "../tracing", version = "0.1" } -time = { version = "0.3", default-features = false, features = ["formatting", "parsing"] } +criterion = { version = "0.3.6", default-features = false } +tracing = { path = "../tracing", version = "0.1.35" } +time = { version = "0.3.2", default-features = false, features = ["formatting", "parsing"] } tempfile = "3" [[bench]] name = "bench" -harness = false \ No newline at end of file +harness = false diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index c1673d65ba..5d8915331f 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -40,16 +40,16 @@ async-await = [] [dependencies] proc-macro2 = "1" -syn = { version = "1.0.43", default-features = false, features = ["full", "parsing", "printing", "visit", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] } +syn = { version = "1.0.98", default-features = false, features = ["full", "parsing", "printing", "visit", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] } quote = "1" [dev-dependencies] -tracing = { path = "../tracing", version = "0.1" } +tracing = { path = "../tracing", version = "0.1.35" } tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] } -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["env-filter"] } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", features = ["env-filter"] } tokio-test = { version = "0.3.0" } -tracing-core = { path = "../tracing-core", version = "0.1"} -async-trait = "0.1.44" +tracing-core = { path = "../tracing-core", version = "0.1.28"} +async-trait = "0.1.56" [badges] maintenance = { status = "experimental" } diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml index b6bdaea29b..b43482fade 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -34,10 +34,10 @@ std = ["once_cell"] maintenance = { status = "actively-developed" } [dependencies] -once_cell = { version = "1.12", optional = true } +once_cell = { version = "1.13.0", optional = true } [target.'cfg(tracing_unstable)'.dependencies] -valuable = { version = "0.1.0", optional = true, default_features = false } +valuable = { version = "0.1.0", optional = true, default-features = false } [package.metadata.docs.rs] all-features = true @@ -45,4 +45,4 @@ all-features = true rustdoc-args = ["--cfg", "docsrs", "--cfg", "tracing_unstable"] # it's necessary to _also_ pass `--cfg tracing_unstable` to rustc, or else # dependencies will not be enabled, and the docs build will fail. -rustc-args = ["--cfg", "tracing_unstable"] \ No newline at end of file +rustc-args = ["--cfg", "tracing_unstable"] diff --git a/tracing-error/Cargo.toml b/tracing-error/Cargo.toml index 655b04da1c..d3c66713b7 100644 --- a/tracing-error/Cargo.toml +++ b/tracing-error/Cargo.toml @@ -39,8 +39,8 @@ default = ["traced-error"] traced-error = [] [dependencies] -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry", "fmt"] } -tracing = { path = "../tracing", version = "0.1.12", default-features = false, features = ["std"] } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", default-features = false, features = ["registry", "fmt"] } +tracing = { path = "../tracing", version = "0.1.35", default-features = false, features = ["std"] } [badges] maintenance = { status = "experimental" } diff --git a/tracing-flame/Cargo.toml b/tracing-flame/Cargo.toml index 43ee62c922..d69ea7afb1 100644 --- a/tracing-flame/Cargo.toml +++ b/tracing-flame/Cargo.toml @@ -26,9 +26,9 @@ default = ["smallvec"] smallvec = ["tracing-subscriber/smallvec"] [dependencies] -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry", "fmt"] } -tracing = { path = "../tracing", version = "0.1.12", default-features = false, features = ["std"] } -once_cell = "1.12" +tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", default-features = false, features = ["registry", "fmt"] } +tracing = { path = "../tracing", version = "0.1.35", default-features = false, features = ["std"] } +once_cell = "1.13.0" [dev-dependencies] diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml index 874d31d363..ba00ec5c12 100644 --- a/tracing-futures/Cargo.toml +++ b/tracing-futures/Cargo.toml @@ -27,17 +27,21 @@ tokio = ["tokio_01"] std = ["tracing/std"] [dependencies] -futures_01 = { package = "futures", version = "0.1", optional = true } -futures = { version = "0.3.0", optional = true } -futures-task = { version = "0.3", optional = true } -pin-project-lite = { version = "0.2.4", optional = true } -tracing = { path = "../tracing", version = "0.1", default-features = false } -tokio-executor = { version = "0.1", optional = true } -tokio_01 = { package = "tokio", version = "0.1", optional = true } +futures_01 = { package = "futures", version = "0.1.31", optional = true } +futures = { version = "0.3.21", optional = true } +futures-task = { version = "0.3.21", optional = true } +pin-project-lite = { version = "0.2.9", optional = true } +tracing = { path = "../tracing", version = "0.1.35", default-features = false } +tokio-executor = { version = "0.1.10", optional = true } +tokio_01 = { package = "tokio", version = "0.1.22", optional = true } + +# Fix minimal-versions +tokio-threadpool = { version = "0.1.18", optional = true } +mio = { version = "0.6.23", optional = true } [dev-dependencies] -tokio-test = "0.4" -tracing-core = { path = "../tracing-core", version = "0.1.2" } +tokio-test = "0.4.2" +tracing-core = { path = "../tracing-core", version = "0.1.28" } tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] } [badges] diff --git a/tracing-journald/Cargo.toml b/tracing-journald/Cargo.toml index e4f5eca8a1..790fec4959 100644 --- a/tracing-journald/Cargo.toml +++ b/tracing-journald/Cargo.toml @@ -16,11 +16,11 @@ keywords = ["tracing", "journald"] rust-version = "1.49.0" [dependencies] -libc = "0.2.107" -tracing-core = { path = "../tracing-core", version = "0.1.10" } -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry"] } +libc = "0.2.126" +tracing-core = { path = "../tracing-core", version = "0.1.28" } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", default-features = false, features = ["registry"] } [dev-dependencies] -serde_json = "1.0.68" -serde = { version = "1.0.130", features = ["derive"] } -tracing = { path = "../tracing", version = "0.1" } +serde_json = "1.0.82" +serde = { version = "1.0.140", features = ["derive"] } +tracing = { path = "../tracing", version = "0.1.35" } diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs index a8e1278266..c91fbddd19 100644 --- a/tracing-journald/src/lib.rs +++ b/tracing-journald/src/lib.rs @@ -156,7 +156,7 @@ impl Layer { #[cfg(not(unix))] fn send_payload(&self, _opayload: &[u8]) -> io::Result<()> { Err(io::Error::new( - io::ErrorKind::Unsupported, + io::ErrorKind::Other, "journald not supported on non-Unix", )) } @@ -177,7 +177,7 @@ impl Layer { #[cfg(all(unix, not(target_os = "linux")))] fn send_large_payload(&self, _payload: &[u8]) -> io::Result { Err(io::Error::new( - io::ErrorKind::Unsupported, + io::ErrorKind::Other, "Large payloads not supported on non-Linux OS", )) } diff --git a/tracing-log/Cargo.toml b/tracing-log/Cargo.toml index a774357788..b295da9646 100644 --- a/tracing-log/Cargo.toml +++ b/tracing-log/Cargo.toml @@ -25,17 +25,17 @@ trace-logger = [] interest-cache = ["lru", "ahash"] [dependencies] -tracing-core = { path = "../tracing-core", version = "0.1.17"} -log = { version = "0.4" } -once_cell = "1.12" -env_logger = { version = "0.8", optional = true } -lru = { version = "0.7.0", optional = true } -ahash = { version = "0.7.4", optional = true } +tracing-core = { path = "../tracing-core", version = "0.1.28"} +log = { version = "0.4.17" } +once_cell = "1.13.0" +env_logger = { version = "0.8.4", optional = true } +lru = { version = "0.7.7", optional = true } +ahash = { version = "0.7.6", optional = true } [dev-dependencies] -tracing = { path = "../tracing", version = "0.1"} +tracing = { path = "../tracing", version = "0.1.35"} tracing-subscriber = { path = "../tracing-subscriber" } -criterion = { version = "0.3", default_features = false } +criterion = { version = "0.3.6", default-features = false } [badges] maintenance = { status = "actively-maintained" } diff --git a/tracing-macros/Cargo.toml b/tracing-macros/Cargo.toml index 5643103da3..8680720b88 100644 --- a/tracing-macros/Cargo.toml +++ b/tracing-macros/Cargo.toml @@ -18,11 +18,11 @@ license = "MIT" rust-version = "1.49.0" [dependencies] -tracing = "0.1.18" +tracing = "0.1.35" [dev-dependencies] -tracing-log = "0.1" -env_logger = "0.7" +tracing-log = "0.1.3" +env_logger = "0.7.1" [badges] maintenance = { status = "experimental" } diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml index 8afbd5d344..02aabbfeaa 100644 --- a/tracing-mock/Cargo.toml +++ b/tracing-mock/Cargo.toml @@ -18,9 +18,12 @@ rust-version = "1.49.0" publish = false [dependencies] -tracing = { path = "../tracing", version = "0.1", default-features = false } -tracing-core = { path = "../tracing-core", version = "0.1", default-features = false } -tokio-test = { version = "0.4", optional = true } +tracing = { path = "../tracing", version = "0.1.35", default-features = false } +tracing-core = { path = "../tracing-core", version = "0.1.28", default-features = false } +tokio-test = { version = "0.4.2", optional = true } + +# Fix minimal-versions; tokio-test fails with otherwise acceptable 0.1.0 +tokio-stream = { version = "0.1.9", optional = true } [package.metadata.docs.rs] all-features = true diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml index 1781eb99f3..ee53821fc7 100644 --- a/tracing-opentelemetry/Cargo.toml +++ b/tracing-opentelemetry/Cargo.toml @@ -23,17 +23,21 @@ rust-version = "1.46.0" default = ["tracing-log"] [dependencies] -opentelemetry = { version = "0.17", default-features = false, features = ["trace"] } -tracing = { path = "../tracing", version = "0.1", default-features = false, features = ["std"] } -tracing-core = { path = "../tracing-core", version = "0.1" } -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry", "std"] } -tracing-log = { path = "../tracing-log", version = "0.1", default-features = false, optional = true } +opentelemetry = { version = "0.17.0", default-features = false, features = ["trace"] } +tracing = { path = "../tracing", version = "0.1.35", default-features = false, features = ["std"] } +tracing-core = { path = "../tracing-core", version = "0.1.28" } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", default-features = false, features = ["registry", "std"] } +tracing-log = { path = "../tracing-log", version = "0.1.3", default-features = false, optional = true } once_cell = "1" +# Fix minimal-versions +async-trait = { version = "0.1.56", optional = true } +thiserror = { version = "1.0.31", optional = true } + [dev-dependencies] -async-trait = "0.1" -criterion = { version = "0.3", default_features = false } -opentelemetry-jaeger = "0.16" +async-trait = "0.1.56" +criterion = { version = "0.3.6", default-features = false } +opentelemetry-jaeger = "0.16.0" [lib] bench = false diff --git a/tracing-serde/Cargo.toml b/tracing-serde/Cargo.toml index de51570abd..b3b0f940b8 100644 --- a/tracing-serde/Cargo.toml +++ b/tracing-serde/Cargo.toml @@ -23,14 +23,14 @@ valuable = ["valuable_crate", "valuable-serde", "tracing-core/valuable"] [dependencies] serde = "1" -tracing-core = { path = "../tracing-core", version = "0.1.22"} +tracing-core = { path = "../tracing-core", version = "0.1.28"} [dev-dependencies] serde_json = "1" [target.'cfg(tracing_unstable)'.dependencies] -valuable_crate = { package = "valuable", version = "0.1.0", optional = true, default_features = false } -valuable-serde = { version = "0.1.0", optional = true, default_features = false } +valuable_crate = { package = "valuable", version = "0.1.0", optional = true, default-features = false } +valuable-serde = { version = "0.1.0", optional = true, default-features = false } [badges] maintenance = { status = "experimental" } diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 8d2d1691c0..3ed2e0db5e 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -41,44 +41,44 @@ local-time = ["time/local-offset"] tracing-core = { path = "../tracing-core", version = "0.1.28", default-features = false } # only required by the filter feature -tracing = { optional = true, path = "../tracing", version = "0.1", default-features = false } +tracing = { optional = true, path = "../tracing", version = "0.1.35", default-features = false } matchers = { optional = true, version = "0.1.0" } regex = { optional = true, version = "1", default-features = false, features = ["std"] } -smallvec = { optional = true, version = "1.2.0" } -once_cell = { optional = true, version = "1.12" } +smallvec = { optional = true, version = "1.9.0" } +once_cell = { optional = true, version = "1.13.0" } # fmt -tracing-log = { path = "../tracing-log", version = "0.1.2", optional = true, default-features = false, features = ["log-tracer", "std"] } -ansi_term = { version = "0.12", optional = true } -time = { version = "0.3", features = ["formatting"], optional = true } +tracing-log = { path = "../tracing-log", version = "0.1.3", optional = true, default-features = false, features = ["log-tracer", "std"] } +ansi_term = { version = "0.12.1", optional = true } +time = { version = "0.3.2", features = ["formatting"], optional = true } # only required by the json feature -serde_json = { version = "1.0", optional = true } -serde = { version = "1.0", optional = true } +serde_json = { version = "1.0.82", optional = true } +serde = { version = "1.0.140", optional = true } tracing-serde = { path = "../tracing-serde", version = "0.1.3", optional = true } # opt-in deps -parking_lot = { version = "0.12", optional = true } +parking_lot = { version = "0.12.1", optional = true } # registry -sharded-slab = { version = "0.1.0", optional = true } +sharded-slab = { version = "0.1.4", optional = true } thread_local = { version = "1.1.4", optional = true } [target.'cfg(tracing_unstable)'.dependencies] -valuable_crate = { package = "valuable", version = "0.1.0", optional = true, default_features = false } -valuable-serde = { version = "0.1.0", optional = true, default_features = false } +valuable_crate = { package = "valuable", version = "0.1.0", optional = true, default-features = false } +valuable-serde = { version = "0.1.0", optional = true, default-features = false } [dev-dependencies] -tracing = { path = "../tracing", version = "0.1" } +tracing = { path = "../tracing", version = "0.1.35" } tracing-mock = { path = "../tracing-mock" } -log = "0.4" -tracing-log = { path = "../tracing-log", version = "0.1.2" } -criterion = { version = "0.3", default_features = false } +log = "0.4.17" +tracing-log = { path = "../tracing-log", version = "0.1.3" } +criterion = { version = "0.3.6", default-features = false } regex = { version = "1", default-features = false, features = ["std"] } -tracing-futures = { path = "../tracing-futures", version = "0.2", default-features = false, features = ["std-future", "std"] } +tracing-futures = { path = "../tracing-futures", version = "0.2.0", default-features = false, features = ["std-future", "std"] } tokio = { version = "1", features = ["rt", "macros"] } # Enable the `time` crate's `macros` feature, for examples. -time = { version = "0.3", features = ["formatting", "macros"] } +time = { version = "0.3.2", features = ["formatting", "macros"] } [badges] maintenance = { status = "experimental" } diff --git a/tracing-tower/Cargo.toml b/tracing-tower/Cargo.toml index 05e15dc090..1ea0a572ea 100644 --- a/tracing-tower/Cargo.toml +++ b/tracing-tower/Cargo.toml @@ -25,14 +25,14 @@ tower-make = [ ] [dependencies] -tracing = { path = "../tracing", version = "0.1", default-features = false, features = ["std"] } +tracing = { path = "../tracing", version = "0.1.35", default-features = false, features = ["std"] } tracing-futures = { version = "0.2.1", path = "../tracing-futures", features = ["std-future"] } -futures = "0.3" -tower-service = "0.3" -tower-layer = { version = "0.3", optional = true } -tower_make = { package = "tower-make", version = "0.3", optional = true } -pin-project-lite = { version = "0.2.4", optional = true } -http = { version = "0.2", optional = true } +futures = "0.3.21" +tower-service = "0.3.2" +tower-layer = { version = "0.3.1", optional = true } +tower_make = { package = "tower-make", version = "0.3.0", optional = true } +pin-project-lite = { version = "0.2.9", optional = true } +http = { version = "0.2.8", optional = true } [badges] maintenance = { status = "experimental" } diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index be4139245d..43d42cc306 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -28,15 +28,15 @@ edition = "2018" rust-version = "1.49.0" [dependencies] -tracing-core = { path = "../tracing-core", version = "0.1.27", default-features = false } -log = { version = "0.4", optional = true } -tracing-attributes = { path = "../tracing-attributes", version = "0.1.20", optional = true } +tracing-core = { path = "../tracing-core", version = "0.1.28", default-features = false } +log = { version = "0.4.17", optional = true } +tracing-attributes = { path = "../tracing-attributes", version = "0.1.22", optional = true } cfg-if = "1.0.0" -pin-project-lite = "0.2" +pin-project-lite = "0.2.9" [dev-dependencies] -criterion = { version = "0.3", default_features = false } -log = "0.4" +criterion = { version = "0.3.6", default-features = false } +log = "0.4.17" tracing-mock = { path = "../tracing-mock" } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] @@ -112,4 +112,4 @@ all-features = true rustdoc-args = ["--cfg", "docsrs", "--cfg", "tracing_unstable"] # it's necessary to _also_ pass `--cfg tracing_unstable` to rustc, or else # dependencies will not be enabled, and the docs build will fail. -rustc-args = ["--cfg", "tracing_unstable"] \ No newline at end of file +rustc-args = ["--cfg", "tracing_unstable"] diff --git a/tracing/test-log-support/Cargo.toml b/tracing/test-log-support/Cargo.toml index 68675928c3..c59f67a515 100644 --- a/tracing/test-log-support/Cargo.toml +++ b/tracing/test-log-support/Cargo.toml @@ -8,4 +8,4 @@ edition = "2018" [dependencies] tracing = { path = "..", features = ["log", "log-always"] } -log = { version = "0.4", features = ["std"] } +log = { version = "0.4.0", features = ["std"] } From b6762daf52dca469d0d09389b1348014de6dd2ee Mon Sep 17 00:00:00 2001 From: Chris Burgess <9002722+cgbur@users.noreply.github.com> Date: Thu, 28 Jul 2022 14:56:03 -0400 Subject: [PATCH 071/142] subscriber: correct fmt::init() documentation (#2224) ## Motivation Previously the documentation for `fmt::init()` was misleading. It stated that it was shorthand for `fmt().init()`. This lead to confusion as users would expect the same behavior from both. However `fmt::init()` would, whether you used the env-filter feature or not, rely on RUST_LOG to set the tracing level. `fmt().init()` does not do this and it must be set with a specific configuration via `with_env_filter`. ## Solution The documentation has been updated to no longer state that it is 1:1 shorthand for the other. The documentation now specifically points out that you must be using the `env-filter` feature and gives a correct example to mimic the `fmt::init()` behavior using `fmt().init()`. Fixes #2217 Fixes #1329 Co-authored-by: Eliza Weisman --- tracing-subscriber/src/fmt/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index 70c6db579b..9103ce8f16 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -1203,13 +1203,19 @@ pub fn try_init() -> Result<(), Box> { /// Install a global tracing subscriber that listens for events and /// filters based on the value of the [`RUST_LOG` environment variable]. /// +/// The configuration of the subscriber initialized by this function +/// depends on what [feature flags](crate#feature-flags) are enabled. +/// /// If the `tracing-log` feature is enabled, this will also install /// the LogTracer to convert `Log` records into `tracing` `Event`s. /// -/// This is shorthand for +/// If the `env-filter` feature is enabled, this is shorthand for /// /// ```rust -/// tracing_subscriber::fmt().init() +/// # use tracing_subscriber::EnvFilter; +/// tracing_subscriber::fmt() +/// .with_env_filter(EnvFilter::from_default_env()) +/// .init(); /// ``` /// /// # Panics From 522437d51cc3785b64c5533e14c0c1a0c993202f Mon Sep 17 00:00:00 2001 From: Charles Date: Thu, 21 Jul 2022 14:18:23 -0400 Subject: [PATCH 072/142] appender: add `Builder::filename_suffix` parameter (#2225) ## Motivation The `RollingFileAppender` currently only supports a filename suffix. A lot of editors have support for log files using the `.log` extension. It would be nice to be able to configure what gets added after the date. ## Solution - Add a `Builder::filename_suffix` method, taking a string. - If the string is non-empty, this gets appended to the filename after the date. - This isn't an `AsRef` because it's not supposed to be a `Path` - Update the date appending logic to handle cases when the suffix or prefix is empty - Split each part with a `.` so the final output would be `prefix.date.suffix` - Make sure to remove unnecessary `.` when a prefix or suffix is empty - Add tests related to those changes ## Notes It would probably be nicer to simply have a completely configurable file name format, but I went with the easiest approach that achieved what I needed. Closes #1477 --- tracing-appender/src/rolling.rs | 130 +++++++++++++++--------- tracing-appender/src/rolling/builder.rs | 54 ++++++++++ 2 files changed, 138 insertions(+), 46 deletions(-) diff --git a/tracing-appender/src/rolling.rs b/tracing-appender/src/rolling.rs index d4b2ee6fcf..8e9597c29a 100644 --- a/tracing-appender/src/rolling.rs +++ b/tracing-appender/src/rolling.rs @@ -103,6 +103,7 @@ pub struct RollingWriter<'a>(RwLockReadGuard<'a, File>); struct Inner { log_directory: PathBuf, log_filename_prefix: Option, + log_filename_suffix: Option, rotation: Rotation, next_date: AtomicUsize, } @@ -170,6 +171,7 @@ impl RollingFileAppender { /// let file_appender = RollingFileAppender::builder() /// .rotation(Rotation::HOURLY) // rotate log files once every hour /// .filename_prefix("myapp") // log file names will be prefixed with `myapp.` + /// .filename_suffix("log") // log file names will be suffixed with `.log` /// .build("/var/log") // try to build an appender that stores log files in `/var/log` /// .expect("initializing rolling file appender failed"); /// # drop(file_appender); @@ -184,11 +186,17 @@ impl RollingFileAppender { let Builder { ref rotation, ref prefix, + ref suffix, } = builder; - let filename_prefix = prefix.clone(); let directory = directory.as_ref().to_path_buf(); let now = OffsetDateTime::now_utc(); - let (state, writer) = Inner::new(now, rotation.clone(), directory, filename_prefix)?; + let (state, writer) = Inner::new( + now, + rotation.clone(), + directory, + prefix.clone(), + suffix.clone(), + )?; Ok(Self { state, writer, @@ -480,42 +488,31 @@ impl Rotation { } } - pub(crate) fn join_date(&self, filename: Option<&str>, date: &OffsetDateTime) -> String { - let date = match *self { - Rotation::MINUTELY => { - let format = format_description::parse("[year]-[month]-[day]-[hour]-[minute]") - .expect("Unable to create a formatter; this is a bug in tracing-appender"); - date.format(&format) - .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender") - } - Rotation::HOURLY => { - let format = format_description::parse("[year]-[month]-[day]-[hour]") - .expect("Unable to create a formatter; this is a bug in tracing-appender"); - date.format(&format) - .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender") - } - Rotation::DAILY => { - let format = format_description::parse("[year]-[month]-[day]") - .expect("Unable to create a formatter; this is a bug in tracing-appender"); - date.format(&format) - .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender") - } - Rotation::NEVER => { - // If there's a name prefix, use that. - if let Some(filename) = filename { - return filename.to_owned(); - } - - // Otherwise, just use the date. - let format = format_description::parse("[year]-[month]-[day]") - .expect("Unable to create a formatter; this is a bug in tracing-appender"); - date.format(&format) - .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender") - } - }; - match filename { - Some(filename) => format!("{}.{}", filename, date), - None => date, + pub(crate) fn join_date( + &self, + filename: Option<&str>, + date: &OffsetDateTime, + suffix: Option<&str>, + ) -> String { + let format = match *self { + Rotation::MINUTELY => format_description::parse("[year]-[month]-[day]-[hour]-[minute]"), + Rotation::HOURLY => format_description::parse("[year]-[month]-[day]-[hour]"), + Rotation::DAILY => format_description::parse("[year]-[month]-[day]"), + Rotation::NEVER => format_description::parse("[year]-[month]-[day]"), + } + .expect("Unable to create a formatter; this is a bug in tracing-appender"); + let date = date + .format(&format) + .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender"); + + match (self, filename, suffix) { + (&Rotation::NEVER, Some(filename), None) => filename.to_string(), + (&Rotation::NEVER, Some(filename), Some(suffix)) => format!("{}.{}", filename, suffix), + (&Rotation::NEVER, None, Some(suffix)) => suffix.to_string(), + (_, Some(filename), Some(suffix)) => format!("{}.{}.{}", filename, date, suffix), + (_, Some(filename), None) => format!("{}.{}", filename, date), + (_, None, Some(suffix)) => format!("{}.{}", date, suffix), + (_, None, None) => date, } } } @@ -540,15 +537,21 @@ impl Inner { rotation: Rotation, directory: impl AsRef, log_filename_prefix: Option, + log_filename_suffix: Option, ) -> Result<(Self, RwLock), builder::InitError> { let log_directory = directory.as_ref().to_path_buf(); - let filename = rotation.join_date(log_filename_prefix.as_deref(), &now); + let filename = rotation.join_date( + log_filename_prefix.as_deref(), + &now, + log_filename_suffix.as_deref(), + ); let next_date = rotation.next_date(&now); let writer = RwLock::new(create_writer(log_directory.as_ref(), &filename)?); let inner = Inner { log_directory, log_filename_prefix, + log_filename_suffix, next_date: AtomicUsize::new( next_date .map(|date| date.unix_timestamp() as usize) @@ -560,9 +563,11 @@ impl Inner { } fn refresh_writer(&self, now: OffsetDateTime, file: &mut File) { - let filename = self - .rotation - .join_date(self.log_filename_prefix.as_deref(), &now); + let filename = self.rotation.join_date( + self.log_filename_prefix.as_deref(), + &now, + self.log_filename_suffix.as_deref(), + ); match create_writer(&self.log_directory, &filename) { Ok(new_file) => { @@ -732,19 +737,51 @@ mod test { let now = OffsetDateTime::parse("2020-02-01 10:01:00 +00:00:00", &format).unwrap(); // per-minute - let path = Rotation::MINUTELY.join_date(Some("app.log"), &now); + let path = Rotation::MINUTELY.join_date(Some("app.log"), &now, None); assert_eq!("app.log.2020-02-01-10-01", path); // per-hour - let path = Rotation::HOURLY.join_date(Some("app.log"), &now); + let path = Rotation::HOURLY.join_date(Some("app.log"), &now, None); assert_eq!("app.log.2020-02-01-10", path); // per-day - let path = Rotation::DAILY.join_date(Some("app.log"), &now); + let path = Rotation::DAILY.join_date(Some("app.log"), &now, None); assert_eq!("app.log.2020-02-01", path); // never - let path = Rotation::NEVER.join_date(Some("app.log"), &now); + let path = Rotation::NEVER.join_date(Some("app.log"), &now, None); + assert_eq!("app.log", path); + + // per-minute with suffix + let path = Rotation::MINUTELY.join_date(Some("app"), &now, Some("log")); + assert_eq!("app.2020-02-01-10-01.log", path); + + // per-hour with suffix + let path = Rotation::HOURLY.join_date(Some("app"), &now, Some("log")); + assert_eq!("app.2020-02-01-10.log", path); + + // per-day with suffix + let path = Rotation::DAILY.join_date(Some("app"), &now, Some("log")); + assert_eq!("app.2020-02-01.log", path); + + // never with suffix + let path = Rotation::NEVER.join_date(Some("app"), &now, Some("log")); + assert_eq!("app.log", path); + + // per-minute without prefix + let path = Rotation::MINUTELY.join_date(None, &now, Some("app.log")); + assert_eq!("2020-02-01-10-01.app.log", path); + + // per-hour without prefix + let path = Rotation::HOURLY.join_date(None, &now, Some("app.log")); + assert_eq!("2020-02-01-10.app.log", path); + + // per-day without prefix + let path = Rotation::DAILY.join_date(None, &now, Some("app.log")); + assert_eq!("2020-02-01.app.log", path); + + // never without prefix + let path = Rotation::NEVER.join_date(None, &now, Some("app.log")); assert_eq!("app.log", path); } @@ -766,6 +803,7 @@ mod test { Rotation::HOURLY, directory.path(), Some("test_make_writer".to_string()), + None, ) .unwrap(); diff --git a/tracing-appender/src/rolling/builder.rs b/tracing-appender/src/rolling/builder.rs index 82161f0cc6..ea5d39cd5f 100644 --- a/tracing-appender/src/rolling/builder.rs +++ b/tracing-appender/src/rolling/builder.rs @@ -9,6 +9,7 @@ use thiserror::Error; pub struct Builder { pub(super) rotation: Rotation, pub(super) prefix: Option, + pub(super) suffix: Option, } /// Errors returned by [`Builder::build`]. @@ -46,6 +47,7 @@ impl Builder { Self { rotation: Rotation::NEVER, prefix: None, + suffix: None, } } @@ -127,6 +129,58 @@ impl Builder { Self { prefix, ..self } } + /// Sets the suffix for log filenames. The suffix is output after the + /// timestamp in the file name, and if it is non-empty, it is preceded by a + /// dot (`.`). + /// + /// By default, log files do not have a suffix. + /// + /// # Examples + /// + /// Setting a suffix: + /// + /// ``` + /// use tracing_appender::rolling::RollingFileAppender; + /// + /// # fn docs() { + /// let appender = RollingFileAppender::builder() + /// .filename_suffix("myapp.log") // log files will have names like "2019-01-01.myapp.log" + /// // ... + /// .build("/var/log") + /// .expect("failed to initialize rolling file appender"); + /// # drop(appender) + /// # } + /// ``` + /// + /// No suffix: + /// + /// ``` + /// use tracing_appender::rolling::RollingFileAppender; + /// + /// # fn docs() { + /// let appender = RollingFileAppender::builder() + /// .filename_suffix("") // log files will have names like "2019-01-01" + /// // ... + /// .build("/var/log") + /// .expect("failed to initialize rolling file appender"); + /// # drop(appender) + /// # } + /// ``` + /// + /// [rotation strategy]: Rotation + #[must_use] + pub fn filename_suffix(self, suffix: impl Into) -> Self { + let suffix = suffix.into(); + // If the configured suffix is the empty string, then don't include a + // separator character. + let suffix = if suffix.is_empty() { + None + } else { + Some(suffix) + }; + Self { suffix, ..self } + } + /// Builds a new [`RollingFileAppender`] with the configured parameters, /// emitting log files to the provided directory. /// From 1b89aa950ac623b3157afeac3dd827976b7a556a Mon Sep 17 00:00:00 2001 From: Kartavya Vashishtha Date: Fri, 22 Jul 2022 00:08:11 +0530 Subject: [PATCH 073/142] subscriber: mark builders as must_use (#2239) ## Motivation Builders not marked `#[must_use]` can not be initialized sometimes, causing silent failures. Eg. ```rust fn main() { tracing_subscriber::fmt(); tracing::info!("hello"); } ``` won't print anything. ## Solution Added `#[must_use]` to builder types in the tracing-subscriber crate. --- tracing-subscriber/src/filter/env/builder.rs | 1 + tracing-subscriber/src/fmt/mod.rs | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tracing-subscriber/src/filter/env/builder.rs b/tracing-subscriber/src/filter/env/builder.rs index 36b5205431..7bc65484ea 100644 --- a/tracing-subscriber/src/filter/env/builder.rs +++ b/tracing-subscriber/src/filter/env/builder.rs @@ -11,6 +11,7 @@ use tracing::level_filters::STATIC_MAX_LEVEL; /// /// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html #[derive(Debug, Clone)] +#[must_use] pub struct Builder { regex: bool, env: Option, diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index 9103ce8f16..025e17504d 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -243,6 +243,7 @@ pub type Formatter< /// Configures and constructs `Subscriber`s. #[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] #[derive(Debug)] +#[must_use] pub struct SubscriberBuilder< N = format::DefaultFields, E = format::Format, @@ -465,7 +466,8 @@ impl Default for SubscriberBuilder { SubscriberBuilder { filter: Subscriber::DEFAULT_MAX_LEVEL, inner: Default::default(), - }.log_internal_errors(true) + } + .log_internal_errors(true) } } @@ -626,12 +628,15 @@ where /// By default, `fmt::Layer` will write any `FormatEvent`-internal errors to /// the writer. These errors are unlikely and will only occur if there is a /// bug in the `FormatEvent` implementation or its dependencies. - /// + /// /// If writing to the writer fails, the error message is printed to stderr /// as a fallback. - /// + /// /// [`FormatEvent`]: crate::fmt::FormatEvent - pub fn log_internal_errors(self, log_internal_errors: bool) -> SubscriberBuilder, F, W> { + pub fn log_internal_errors( + self, + log_internal_errors: bool, + ) -> SubscriberBuilder, F, W> { SubscriberBuilder { inner: self.inner.log_internal_errors(log_internal_errors), ..self From 219d926dcfcb78b30d4ac493ace99dead504eadb Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Mon, 11 Jul 2022 18:09:21 -0700 Subject: [PATCH 074/142] opentelemetry: support publishing metrics (#2185) Motivation: Currently, there is no way to publish metrics via tracing. Solution: Update the tracing-opentelemetry crate to publish metrics for event fields that contain specific prefixes in the name. Right now, we lazily instantiate and store one metrics object per-callsite, but a future improvement that we should add to tracing itself is the ability to store data per-callsite, so that we don't have to do a HashMap lookup each time we want to publish a metric. Co-authored-by: Eliza Weisman Co-authored-by: David Barsky --- tracing-opentelemetry/Cargo.toml | 5 +- tracing-opentelemetry/src/lib.rs | 5 + tracing-opentelemetry/src/metrics.rs | 352 ++++++++++++++++++ .../tests/metrics_publishing.rs | 283 ++++++++++++++ 4 files changed, 644 insertions(+), 1 deletion(-) create mode 100644 tracing-opentelemetry/src/metrics.rs create mode 100644 tracing-opentelemetry/tests/metrics_publishing.rs diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml index ee53821fc7..0a7d05cd3e 100644 --- a/tracing-opentelemetry/Cargo.toml +++ b/tracing-opentelemetry/Cargo.toml @@ -23,7 +23,7 @@ rust-version = "1.46.0" default = ["tracing-log"] [dependencies] -opentelemetry = { version = "0.17.0", default-features = false, features = ["trace"] } +opentelemetry = { version = "0.17.0", default-features = false, features = ["metrics", "trace"] } tracing = { path = "../tracing", version = "0.1.35", default-features = false, features = ["std"] } tracing-core = { path = "../tracing-core", version = "0.1.28" } tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", default-features = false, features = ["registry", "std"] } @@ -38,6 +38,9 @@ thiserror = { version = "1.0.31", optional = true } async-trait = "0.1.56" criterion = { version = "0.3.6", default-features = false } opentelemetry-jaeger = "0.16.0" +futures-util = { version = "0.3", default-features = false } +tokio = { version = "1", features = ["full"] } +tokio-stream = "0.1" [lib] bench = false diff --git a/tracing-opentelemetry/src/lib.rs b/tracing-opentelemetry/src/lib.rs index 872afc8a44..c3a3992587 100644 --- a/tracing-opentelemetry/src/lib.rs +++ b/tracing-opentelemetry/src/lib.rs @@ -99,6 +99,9 @@ )] #![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))] +/// Implementation of the trace::Layer trait; publishes OpenTelemetry metrics. +mod metrics; + /// Implementation of the trace::Layer as a source of OpenTelemetry data. mod layer; /// Span extension which enables OpenTelemetry context management. @@ -107,6 +110,8 @@ mod span_ext; mod tracer; pub use layer::{layer, OpenTelemetryLayer}; + +pub use metrics::MetricsLayer; pub use span_ext::OpenTelemetrySpanExt; pub use tracer::PreSampledTracer; diff --git a/tracing-opentelemetry/src/metrics.rs b/tracing-opentelemetry/src/metrics.rs new file mode 100644 index 0000000000..50a98cc1e9 --- /dev/null +++ b/tracing-opentelemetry/src/metrics.rs @@ -0,0 +1,352 @@ +use std::{collections::HashMap, fmt, sync::RwLock}; +use tracing::{field::Visit, Subscriber}; +use tracing_core::Field; + +use opentelemetry::{ + metrics::{Counter, Meter, MeterProvider, UpDownCounter, ValueRecorder}, + sdk::metrics::PushController, +}; +use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer}; + +const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); +const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry"; + +const METRIC_PREFIX_MONOTONIC_COUNTER: &str = "monotonic_counter."; +const METRIC_PREFIX_COUNTER: &str = "counter."; +const METRIC_PREFIX_VALUE: &str = "value."; +const I64_MAX: u64 = i64::MAX as u64; + +#[derive(Default)] +pub(crate) struct Instruments { + u64_counter: MetricsMap>, + f64_counter: MetricsMap>, + i64_up_down_counter: MetricsMap>, + f64_up_down_counter: MetricsMap>, + u64_value_recorder: MetricsMap>, + i64_value_recorder: MetricsMap>, + f64_value_recorder: MetricsMap>, +} + +type MetricsMap = RwLock>; + +#[derive(Copy, Clone, Debug)] +pub(crate) enum InstrumentType { + CounterU64(u64), + CounterF64(f64), + UpDownCounterI64(i64), + UpDownCounterF64(f64), + ValueRecorderU64(u64), + ValueRecorderI64(i64), + ValueRecorderF64(f64), +} + +impl Instruments { + pub(crate) fn update_metric( + &self, + meter: &Meter, + instrument_type: InstrumentType, + metric_name: &'static str, + ) { + fn update_or_insert( + map: &MetricsMap, + name: &'static str, + insert: impl FnOnce() -> T, + update: impl FnOnce(&T), + ) { + { + let lock = map.read().unwrap(); + if let Some(metric) = lock.get(name) { + update(metric); + return; + } + } + + // that metric did not already exist, so we have to acquire a write lock to + // create it. + let mut lock = map.write().unwrap(); + // handle the case where the entry was created while we were waiting to + // acquire the write lock + let metric = lock.entry(name).or_insert_with(insert); + update(metric) + } + + match instrument_type { + InstrumentType::CounterU64(value) => { + update_or_insert( + &self.u64_counter, + metric_name, + || meter.u64_counter(metric_name).init(), + |ctr| ctr.add(value, &[]), + ); + } + InstrumentType::CounterF64(value) => { + update_or_insert( + &self.f64_counter, + metric_name, + || meter.f64_counter(metric_name).init(), + |ctr| ctr.add(value, &[]), + ); + } + InstrumentType::UpDownCounterI64(value) => { + update_or_insert( + &self.i64_up_down_counter, + metric_name, + || meter.i64_up_down_counter(metric_name).init(), + |ctr| ctr.add(value, &[]), + ); + } + InstrumentType::UpDownCounterF64(value) => { + update_or_insert( + &self.f64_up_down_counter, + metric_name, + || meter.f64_up_down_counter(metric_name).init(), + |ctr| ctr.add(value, &[]), + ); + } + InstrumentType::ValueRecorderU64(value) => { + update_or_insert( + &self.u64_value_recorder, + metric_name, + || meter.u64_value_recorder(metric_name).init(), + |rec| rec.record(value, &[]), + ); + } + InstrumentType::ValueRecorderI64(value) => { + update_or_insert( + &self.i64_value_recorder, + metric_name, + || meter.i64_value_recorder(metric_name).init(), + |rec| rec.record(value, &[]), + ); + } + InstrumentType::ValueRecorderF64(value) => { + update_or_insert( + &self.f64_value_recorder, + metric_name, + || meter.f64_value_recorder(metric_name).init(), + |rec| rec.record(value, &[]), + ); + } + }; + } +} + +pub(crate) struct MetricVisitor<'a> { + pub(crate) instruments: &'a Instruments, + pub(crate) meter: &'a Meter, +} + +impl<'a> Visit for MetricVisitor<'a> { + fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) { + // Do nothing + } + + fn record_u64(&mut self, field: &Field, value: u64) { + if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) { + self.instruments.update_metric( + self.meter, + InstrumentType::CounterU64(value), + metric_name, + ); + } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) { + if value <= I64_MAX { + self.instruments.update_metric( + self.meter, + InstrumentType::UpDownCounterI64(value as i64), + metric_name, + ); + } else { + eprintln!( + "[tracing-opentelemetry]: Received Counter metric, but \ + provided u64: {} is greater than i64::MAX. Ignoring \ + this metric.", + value + ); + } + } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_VALUE) { + self.instruments.update_metric( + self.meter, + InstrumentType::ValueRecorderU64(value), + metric_name, + ); + } + } + + fn record_f64(&mut self, field: &Field, value: f64) { + if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) { + self.instruments.update_metric( + self.meter, + InstrumentType::CounterF64(value), + metric_name, + ); + } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) { + self.instruments.update_metric( + self.meter, + InstrumentType::UpDownCounterF64(value), + metric_name, + ); + } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_VALUE) { + self.instruments.update_metric( + self.meter, + InstrumentType::ValueRecorderF64(value), + metric_name, + ); + } + } + + fn record_i64(&mut self, field: &Field, value: i64) { + if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) { + self.instruments.update_metric( + self.meter, + InstrumentType::CounterU64(value as u64), + metric_name, + ); + } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) { + self.instruments.update_metric( + self.meter, + InstrumentType::UpDownCounterI64(value), + metric_name, + ); + } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_VALUE) { + self.instruments.update_metric( + self.meter, + InstrumentType::ValueRecorderI64(value), + metric_name, + ); + } + } +} + +/// A layer that publishes metrics via the OpenTelemetry SDK. +/// +/// # Usage +/// +/// No configuration is needed for this Layer, as it's only responsible for +/// pushing data out to the `opentelemetry` family of crates. For example, when +/// using `opentelemetry-otlp`, that crate will provide its own set of +/// configuration options for setting up the duration metrics will be collected +/// before exporting to the OpenTelemetry Collector, aggregation of data points, +/// etc. +/// +/// ```no_run +/// use tracing_opentelemetry::MetricsLayer; +/// use tracing_subscriber::layer::SubscriberExt; +/// use tracing_subscriber::Registry; +/// # use opentelemetry::sdk::metrics::PushController; +/// +/// // Constructing a PushController is out-of-scope for the docs here, but there +/// // are examples in the opentelemetry repository. See: +/// // https://github.com/open-telemetry/opentelemetry-rust/blob/c13a11e62a68eacd8c41a0742a0d097808e28fbd/examples/basic-otlp/src/main.rs#L39-L53 +/// # let push_controller: PushController = unimplemented!(); +/// +/// let opentelemetry_metrics = MetricsLayer::new(push_controller); +/// let subscriber = Registry::default().with(opentelemetry_metrics); +/// tracing::subscriber::set_global_default(subscriber).unwrap(); +/// ``` +/// +/// To publish a new metric, add a key-value pair to your `tracing::Event` that +/// contains following prefixes: +/// - `monotonic_counter.` (non-negative numbers): Used when the counter should +/// only ever increase +/// - `counter.`: Used when the counter can go up or down +/// - `value.`: Used for discrete data points (i.e., summing them does not make +/// semantic sense) +/// +/// Examples: +/// ``` +/// # use tracing::info; +/// info!(monotonic_counter.foo = 1); +/// info!(monotonic_counter.bar = 1.1); +/// +/// info!(counter.baz = 1); +/// info!(counter.baz = -1); +/// info!(counter.xyz = 1.1); +/// +/// info!(value.qux = 1); +/// info!(value.abc = -1); +/// info!(value.def = 1.1); +/// ``` +/// +/// # Mixing data types +/// +/// ## Floating-point numbers +/// +/// Do not mix floating point and non-floating point numbers for the same +/// metric. If a floating point number will be used for a given metric, be sure +/// to cast any other usages of that metric to a floating point number. +/// +/// Do this: +/// ``` +/// # use tracing::info; +/// info!(monotonic_counter.foo = 1_f64); +/// info!(monotonic_counter.foo = 1.1); +/// ``` +/// +/// This is because all data published for a given metric name must be the same +/// numeric type. +/// +/// ## Integers +/// +/// Positive and negative integers can be mixed freely. The instrumentation +/// provided by `tracing` assumes that all integers are `i64` unless explicitly +/// cast to something else. In the case that an integer *is* cast to `u64`, this +/// subscriber will handle the conversion internally. +/// +/// For example: +/// ``` +/// # use tracing::info; +/// // The subscriber receives an i64 +/// info!(counter.baz = 1); +/// +/// // The subscriber receives an i64 +/// info!(counter.baz = -1); +/// +/// // The subscriber receives a u64, but casts it to i64 internally +/// info!(counter.baz = 1_u64); +/// +/// // The subscriber receives a u64, but cannot cast it to i64 because of +/// // overflow. An error is printed to stderr, and the metric is dropped. +/// info!(counter.baz = (i64::MAX as u64) + 1) +/// ``` +/// +/// # Implementation Details +/// +/// `MetricsLayer` holds a set of maps, with each map corresponding to a +/// type of metric supported by OpenTelemetry. These maps are populated lazily. +/// The first time that a metric is emitted by the instrumentation, a `Metric` +/// instance will be created and added to the corresponding map. This means that +/// any time a metric is emitted by the instrumentation, one map lookup has to +/// be performed. +/// +/// In the future, this can be improved by associating each `Metric` instance to +/// its callsite, eliminating the need for any maps. +pub struct MetricsLayer { + meter: Meter, + instruments: Instruments, +} + +impl MetricsLayer { + /// Create a new instance of MetricsLayer. + pub fn new(push_controller: PushController) -> Self { + let meter = push_controller + .provider() + .meter(INSTRUMENTATION_LIBRARY_NAME, Some(CARGO_PKG_VERSION)); + MetricsLayer { + meter, + instruments: Default::default(), + } + } +} + +impl Layer for MetricsLayer +where + S: Subscriber + for<'span> LookupSpan<'span>, +{ + fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) { + let mut metric_visitor = MetricVisitor { + instruments: &self.instruments, + meter: &self.meter, + }; + event.record(&mut metric_visitor); + } +} diff --git a/tracing-opentelemetry/tests/metrics_publishing.rs b/tracing-opentelemetry/tests/metrics_publishing.rs new file mode 100644 index 0000000000..419558363f --- /dev/null +++ b/tracing-opentelemetry/tests/metrics_publishing.rs @@ -0,0 +1,283 @@ +use async_trait::async_trait; +use futures_util::{Stream, StreamExt as _}; +use opentelemetry::{ + metrics::{Descriptor, InstrumentKind}, + metrics::{Number, NumberKind}, + sdk::{ + export::{ + metrics::{ + CheckpointSet, ExportKind, ExportKindFor, ExportKindSelector, + Exporter as MetricsExporter, Points, Sum, + }, + trace::{SpanData, SpanExporter}, + }, + metrics::{ + aggregators::{ArrayAggregator, SumAggregator}, + selectors::simple::Selector, + }, + }, + Key, Value, +}; +use std::cmp::Ordering; +use std::time::Duration; +use tracing::Subscriber; +use tracing_opentelemetry::MetricsLayer; +use tracing_subscriber::prelude::*; + +const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); +const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry"; + +#[tokio::test] +async fn u64_counter_is_exported() { + let subscriber = init_subscriber( + "hello_world".to_string(), + InstrumentKind::Counter, + NumberKind::U64, + Number::from(1_u64), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::info!(monotonic_counter.hello_world = 1_u64); + }); +} + +#[tokio::test] +async fn u64_counter_is_exported_i64_at_instrumentation_point() { + let subscriber = init_subscriber( + "hello_world2".to_string(), + InstrumentKind::Counter, + NumberKind::U64, + Number::from(1_u64), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::info!(monotonic_counter.hello_world2 = 1_i64); + }); +} + +#[tokio::test] +async fn f64_counter_is_exported() { + let subscriber = init_subscriber( + "float_hello_world".to_string(), + InstrumentKind::Counter, + NumberKind::F64, + Number::from(1.000000123_f64), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::info!(monotonic_counter.float_hello_world = 1.000000123_f64); + }); +} + +#[tokio::test] +async fn i64_up_down_counter_is_exported() { + let subscriber = init_subscriber( + "pebcak".to_string(), + InstrumentKind::UpDownCounter, + NumberKind::I64, + Number::from(-5_i64), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::info!(counter.pebcak = -5_i64); + }); +} + +#[tokio::test] +async fn i64_up_down_counter_is_exported_u64_at_instrumentation_point() { + let subscriber = init_subscriber( + "pebcak2".to_string(), + InstrumentKind::UpDownCounter, + NumberKind::I64, + Number::from(5_i64), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::info!(counter.pebcak2 = 5_u64); + }); +} + +#[tokio::test] +async fn f64_up_down_counter_is_exported() { + let subscriber = init_subscriber( + "pebcak_blah".to_string(), + InstrumentKind::UpDownCounter, + NumberKind::F64, + Number::from(99.123_f64), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::info!(counter.pebcak_blah = 99.123_f64); + }); +} + +#[tokio::test] +async fn u64_value_is_exported() { + let subscriber = init_subscriber( + "abcdefg".to_string(), + InstrumentKind::ValueRecorder, + NumberKind::U64, + Number::from(9_u64), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::info!(value.abcdefg = 9_u64); + }); +} + +#[tokio::test] +async fn i64_value_is_exported() { + let subscriber = init_subscriber( + "abcdefg_auenatsou".to_string(), + InstrumentKind::ValueRecorder, + NumberKind::I64, + Number::from(-19_i64), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::info!(value.abcdefg_auenatsou = -19_i64); + }); +} + +#[tokio::test] +async fn f64_value_is_exported() { + let subscriber = init_subscriber( + "abcdefg_racecar".to_string(), + InstrumentKind::ValueRecorder, + NumberKind::F64, + Number::from(777.0012_f64), + ); + + tracing::subscriber::with_default(subscriber, || { + tracing::info!(value.abcdefg_racecar = 777.0012_f64); + }); +} + +fn init_subscriber( + expected_metric_name: String, + expected_instrument_kind: InstrumentKind, + expected_number_kind: NumberKind, + expected_value: Number, +) -> impl Subscriber + 'static { + let exporter = TestExporter { + expected_metric_name, + expected_instrument_kind, + expected_number_kind, + expected_value, + }; + + let push_controller = opentelemetry::sdk::metrics::controllers::push( + Selector::Exact, + ExportKindSelector::Stateless, + exporter, + tokio::spawn, + delayed_interval, + ) + .build(); + + tracing_subscriber::registry().with(MetricsLayer::new(push_controller)) +} + +#[derive(Clone, Debug)] +struct TestExporter { + expected_metric_name: String, + expected_instrument_kind: InstrumentKind, + expected_number_kind: NumberKind, + expected_value: Number, +} + +#[async_trait] +impl SpanExporter for TestExporter { + async fn export( + &mut self, + mut _batch: Vec, + ) -> opentelemetry::sdk::export::trace::ExportResult { + Ok(()) + } +} + +impl MetricsExporter for TestExporter { + fn export(&self, checkpoint_set: &mut dyn CheckpointSet) -> opentelemetry::metrics::Result<()> { + checkpoint_set.try_for_each(self, &mut |record| { + assert_eq!(self.expected_metric_name, record.descriptor().name()); + assert_eq!( + self.expected_instrument_kind, + *record.descriptor().instrument_kind() + ); + assert_eq!( + self.expected_number_kind, + *record.descriptor().number_kind() + ); + let number = match self.expected_instrument_kind { + InstrumentKind::Counter | InstrumentKind::UpDownCounter => record + .aggregator() + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .sum() + .unwrap(), + InstrumentKind::ValueRecorder => record + .aggregator() + .unwrap() + .as_any() + .downcast_ref::() + .unwrap() + .points() + .unwrap()[0] + .clone(), + _ => panic!( + "InstrumentKind {:?} not currently supported!", + self.expected_instrument_kind + ), + }; + assert_eq!( + Ordering::Equal, + number + .partial_cmp(&NumberKind::U64, &self.expected_value) + .unwrap() + ); + + // The following are the same regardless of the individual metric. + assert_eq!( + INSTRUMENTATION_LIBRARY_NAME, + record.descriptor().instrumentation_library().name + ); + assert_eq!( + CARGO_PKG_VERSION, + record.descriptor().instrumentation_version().unwrap() + ); + assert_eq!( + Value::String("unknown_service".into()), + record + .resource() + .get(Key::new("service.name".to_string())) + .unwrap() + ); + + opentelemetry::metrics::Result::Ok(()) + }) + } +} + +impl ExportKindFor for TestExporter { + fn export_kind_for(&self, _descriptor: &Descriptor) -> ExportKind { + // I don't think the value here makes a difference since + // we are just testing a single metric. + ExportKind::Cumulative + } +} + +// From opentelemetry::sdk::util:: +// For some reason I can't pull it in from the other crate, it gives +// could not find `util` in `sdk` +/// Helper which wraps `tokio::time::interval` and makes it return a stream +fn tokio_interval_stream(period: std::time::Duration) -> tokio_stream::wrappers::IntervalStream { + tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(period)) +} + +// https://github.com/open-telemetry/opentelemetry-rust/blob/2585d109bf90d53d57c91e19c758dca8c36f5512/examples/basic-otlp/src/main.rs#L34-L37 +// Skip first immediate tick from tokio, not needed for async_std. +fn delayed_interval(duration: Duration) -> impl Stream { + tokio_interval_stream(duration).skip(0) +} From 22d09f62bce3abefb53c073788f188c5aadb5f56 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 21 Jul 2022 15:46:12 -0700 Subject: [PATCH 075/142] opentelemetry: feature-flag `MetricsLayer` (#2234) In the upstream `opentelemetry` crate, the `trace` and `metrics` features are gated by separate feature flags. This allows users who are only using OpenTelemetry for tracing, or who are only using it for metrics, to pick and choose what they depend on. Currently, the release version of `tracing-opentelemetry` only provides tracing functionality, and therefore, it only depends on `opentelemetry` with the `trace` feature enabled. However, the metrics support added in #2185 adds a dependency on the `opentelemetry/metrics` feature. This is currently always enabled. We should probably follow the same approach as upstream `opentelemetry`, and allow enabling/disabling metrics and tracing separately. This branch adds a `metrics` feature to `tracing-opentelemetry`, and makes the `MetricsLayer` from #2185 gated on the `metrics` feature. This feature flag is on by default, like the upstream `opentelemetry/metrics` feature, but it can be disabled using `default-features = false`. We should probably do something similar for the tracing components of the crate, and make them gated on a `trace` feature flag, but adding a feature flag to released APIs is not semver-compatible, so we should save that until the next breaking release. --- tracing-opentelemetry/Cargo.toml | 12 +++++++--- tracing-opentelemetry/README.md | 6 +++++ tracing-opentelemetry/src/lib.rs | 22 +++++++++++++++++-- tracing-opentelemetry/src/metrics.rs | 2 ++ .../tests/metrics_publishing.rs | 1 + 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml index 0a7d05cd3e..efe2de0ab9 100644 --- a/tracing-opentelemetry/Cargo.toml +++ b/tracing-opentelemetry/Cargo.toml @@ -20,15 +20,17 @@ edition = "2018" rust-version = "1.46.0" [features] -default = ["tracing-log"] +default = ["tracing-log", "metrics"] +# Enables support for exporting OpenTelemetry metrics +metrics = ["opentelemetry/metrics"] [dependencies] -opentelemetry = { version = "0.17.0", default-features = false, features = ["metrics", "trace"] } +opentelemetry = { version = "0.17.0", default-features = false, features = ["trace"] } tracing = { path = "../tracing", version = "0.1.35", default-features = false, features = ["std"] } tracing-core = { path = "../tracing-core", version = "0.1.28" } tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", default-features = false, features = ["registry", "std"] } tracing-log = { path = "../tracing-log", version = "0.1.3", default-features = false, optional = true } -once_cell = "1" +once_cell = "1.13.0" # Fix minimal-versions async-trait = { version = "0.1.56", optional = true } @@ -48,3 +50,7 @@ bench = false [[bench]] name = "trace" harness = false + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] \ No newline at end of file diff --git a/tracing-opentelemetry/README.md b/tracing-opentelemetry/README.md index 6640754483..4fcb658cfa 100644 --- a/tracing-opentelemetry/README.md +++ b/tracing-opentelemetry/README.md @@ -101,6 +101,12 @@ $ firefox http://localhost:16686/ ![Jaeger UI](trace.png) +## Feature Flags + + - `metrics`: Enables the [`MetricsSubscriber`] type, a [subscriber] that + exports OpenTelemetry metrics from specifically-named events. This enables + the `metrics` feature flag on the `opentelemetry` crate. + ## Supported Rust Versions Tracing Opentelemetry is built against the latest stable release. The minimum diff --git a/tracing-opentelemetry/src/lib.rs b/tracing-opentelemetry/src/lib.rs index c3a3992587..5cd725d5a3 100644 --- a/tracing-opentelemetry/src/lib.rs +++ b/tracing-opentelemetry/src/lib.rs @@ -76,6 +76,13 @@ //! }); //! ``` //! +//! ## Feature Flags +//! +//! - `metrics`: Enables the [`MetricsSubscriber`] type, a [subscriber] that +//! exports OpenTelemetry metrics from specifically-named events. This enables +//! the `metrics` feature flag on the `opentelemetry` crate. *Enabled by +//! default*. +//! //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported @@ -90,6 +97,7 @@ //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! +//! [subscriber]: tracing_subscriber::subscribe #![deny(unreachable_pub)] #![cfg_attr(test, deny(warnings))] #![doc(html_root_url = "https://docs.rs/tracing-opentelemetry/0.17.4")] @@ -97,9 +105,18 @@ 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, deny(rustdoc::broken_intra_doc_links))] +#![cfg_attr( + docsrs, + // Allows displaying cfgs/feature flags in the documentation. + feature(doc_cfg, doc_auto_cfg), + // Allows adding traits to RustDoc's list of "notable traits" + feature(doc_notable_trait), + // Fail the docs build if any intra-docs links are broken + deny(rustdoc::broken_intra_doc_links), +)] -/// Implementation of the trace::Layer trait; publishes OpenTelemetry metrics. +/// Implementation of the trace::Subscriber trait; publishes OpenTelemetry metrics. +#[cfg(feature = "metrics")] mod metrics; /// Implementation of the trace::Layer as a source of OpenTelemetry data. @@ -111,6 +128,7 @@ mod tracer; pub use layer::{layer, OpenTelemetryLayer}; +#[cfg(feature = "metrics")] pub use metrics::MetricsLayer; pub use span_ext::OpenTelemetrySpanExt; pub use tracer::PreSampledTracer; diff --git a/tracing-opentelemetry/src/metrics.rs b/tracing-opentelemetry/src/metrics.rs index 50a98cc1e9..37df62c4b4 100644 --- a/tracing-opentelemetry/src/metrics.rs +++ b/tracing-opentelemetry/src/metrics.rs @@ -320,6 +320,8 @@ impl<'a> Visit for MetricVisitor<'a> { /// /// In the future, this can be improved by associating each `Metric` instance to /// its callsite, eliminating the need for any maps. +/// +#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] pub struct MetricsLayer { meter: Meter, instruments: Instruments, diff --git a/tracing-opentelemetry/tests/metrics_publishing.rs b/tracing-opentelemetry/tests/metrics_publishing.rs index 419558363f..9db53fcb3f 100644 --- a/tracing-opentelemetry/tests/metrics_publishing.rs +++ b/tracing-opentelemetry/tests/metrics_publishing.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "metrics")] use async_trait::async_trait; use futures_util::{Stream, StreamExt as _}; use opentelemetry::{ From d2ad8ab4f2ba42668d20c4548882538d7bcd54da Mon Sep 17 00:00:00 2001 From: Chris Connelly Date: Mon, 25 Jul 2022 18:08:05 +0100 Subject: [PATCH 076/142] subscriber: add `Targets::default_level` method (#2242) ## Motivation This makes it possible to fully "override" some base `Targets` filter with another (e.g. user-supplied) filter. Without some way to obtain the default, only explicit targets can be overridden (via `IntoIter` and friends). See also https://github.com/tokio-rs/tracing/issues/1790#issuecomment-999739222 ## Solution We can add a method to `Targets` that filters the underlying `DirectiveSet` for the default directive. This works because `DirectiveSet::add` will replace directives with the same `target`/`field_names`, which is always `None`/`vec![]` for the directive added by `with_default` (and in fact we are only concerned with `target`, since no other `Targets` API allows adding directives with a `None` target). Ideally the method would be named `default`, corresponding to `with_default`, however this conflicts with `Default::default` and so would be a breaking change (and harm ergonomics). `default_level` seemed a better name than `get_default`, since "getters" of this style are generally considered unidiomatic[citation needed]. Example usage: ```rust let mut filter = Targets::new().with_target("some_module", LevelFilter::INFO); // imagine this came from `RUST_LOG` or similar let override: Targets = "trace".parse().unwrap(); // merge the override if let Some(default) = override.default_level() { filter = filter.with_default(default); } for (target, level) in override.iter() { filter = filter.with_target(target, level); } ``` Closes #1790 --- tracing-subscriber/src/filter/targets.rs | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tracing-subscriber/src/filter/targets.rs b/tracing-subscriber/src/filter/targets.rs index 2a30d2db60..e1407114b5 100644 --- a/tracing-subscriber/src/filter/targets.rs +++ b/tracing-subscriber/src/filter/targets.rs @@ -277,6 +277,62 @@ impl Targets { self } + /// Returns the default level for this filter, if one is set. + /// + /// The default level is used to filter any spans or events with targets + /// that do not match any of the configured set of prefixes. + /// + /// The default level can be set for a filter either by using + /// [`with_default`](Self::with_default) or when parsing from a filter string that includes a + /// level without a target (e.g. `"trace"`). + /// + /// # Examples + /// + /// ``` + /// use tracing_subscriber::filter::{LevelFilter, Targets}; + /// + /// let filter = Targets::new().with_default(LevelFilter::INFO); + /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); + /// + /// let filter: Targets = "info".parse().unwrap(); + /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); + /// ``` + /// + /// The default level is `None` if no default is set: + /// + /// ``` + /// use tracing_subscriber::filter::Targets; + /// + /// let filter = Targets::new(); + /// assert_eq!(filter.default_level(), None); + /// + /// let filter: Targets = "my_crate=info".parse().unwrap(); + /// assert_eq!(filter.default_level(), None); + /// ``` + /// + /// Note that an unset default level (`None`) behaves like [`LevelFilter::OFF`] when the filter is + /// used, but it could also be set explicitly which may be useful to distinguish (such as when + /// merging multiple `Targets`). + /// + /// ``` + /// use tracing_subscriber::filter::{LevelFilter, Targets}; + /// + /// let filter = Targets::new().with_default(LevelFilter::OFF); + /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); + /// + /// let filter: Targets = "off".parse().unwrap(); + /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); + /// ``` + pub fn default_level(&self) -> Option { + self.0.directives().into_iter().find_map(|d| { + if d.target.is_none() { + Some(d.level) + } else { + None + } + }) + } + /// Returns an iterator over the [target]-[`LevelFilter`] pairs in this filter. /// /// The order of iteration is undefined. @@ -685,6 +741,21 @@ mod tests { ); } + #[test] + fn targets_default_level() { + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off"); + assert_eq!(filter.default_level(), None); + + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") + .with_default(LevelFilter::OFF); + assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); + + let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") + .with_default(LevelFilter::OFF) + .with_default(LevelFilter::INFO); + assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); + } + #[test] // `println!` is only available with `libstd`. #[cfg(feature = "std")] From a0824d398aa2511de28371d30dda9203360a6cf5 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Tue, 26 Jul 2022 18:24:01 -0400 Subject: [PATCH 077/142] subscriber: impl `LookupSpan` for `Box` and `Arc` (#2247) These implementations mirror those provided by tracing-core for `Subscriber` on `Box` and `Arc`. --- tracing-subscriber/src/layer/mod.rs | 4 +- tracing-subscriber/src/registry/mod.rs | 65 ++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs index 24b8533234..5122559ce5 100644 --- a/tracing-subscriber/src/layer/mod.rs +++ b/tracing-subscriber/src/layer/mod.rs @@ -685,7 +685,7 @@ use core::any::TypeId; feature! { #![feature = "alloc"] - use alloc::boxed::Box; + use alloc::{vec::Vec, boxed::Box}; use core::ops::{Deref, DerefMut}; } @@ -1568,8 +1568,6 @@ where feature! { #![any(feature = "std", feature = "alloc")] - #[cfg(not(feature = "std"))] - use alloc::vec::Vec; macro_rules! layer_impl_body { () => { diff --git a/tracing-subscriber/src/registry/mod.rs b/tracing-subscriber/src/registry/mod.rs index 38af53e8ad..0f9fe76a1a 100644 --- a/tracing-subscriber/src/registry/mod.rs +++ b/tracing-subscriber/src/registry/mod.rs @@ -230,6 +230,11 @@ pub struct Scope<'a, R> { feature! { #![any(feature = "alloc", feature = "std")] + use alloc::{ + boxed::Box, + sync::Arc + }; + #[cfg(not(feature = "smallvec"))] use alloc::vec::{self, Vec}; @@ -251,6 +256,66 @@ feature! { #[cfg(feature = "smallvec")] type SpanRefVecArray<'span, L> = [SpanRef<'span, L>; 16]; + impl<'a, S> LookupSpan<'a> for Arc + where + S: LookupSpan<'a>, + { + type Data = >::Data; + + fn span_data(&'a self, id: &Id) -> Option { + self.as_ref().span_data(id) + } + + fn span(&'a self, id: &Id) -> Option> + where + Self: Sized, + { + self.as_ref().span(id).map( + |SpanRef { + registry: _, + data, + #[cfg(feature = "registry")] + filter, + }| SpanRef { + registry: self, + data, + #[cfg(feature = "registry")] + filter, + }, + ) + } + } + + impl<'a, S> LookupSpan<'a> for Box + where + S: LookupSpan<'a>, + { + type Data = >::Data; + + fn span_data(&'a self, id: &Id) -> Option { + self.as_ref().span_data(id) + } + + fn span(&'a self, id: &Id) -> Option> + where + Self: Sized, + { + self.as_ref().span(id).map( + |SpanRef { + registry: _, + data, + #[cfg(feature = "registry")] + filter, + }| SpanRef { + registry: self, + data, + #[cfg(feature = "registry")] + filter, + }, + ) + } + } + impl<'a, R> Scope<'a, R> where R: LookupSpan<'a>, From 27ffce2e114ed1b3b5bec572e73943f6cc1dd4b3 Mon Sep 17 00:00:00 2001 From: Ilia Konnov Date: Wed, 27 Jul 2022 02:02:44 +0300 Subject: [PATCH 078/142] serde: implement `AsSerde` for `FieldSet` (#2241) ## Motivation I've wanted to serialize fieldset of current span. ## Solution Expose already existing `SerializeFieldSet` for users by implementing `AsSerde` for `FieldSet`. --- tracing-serde/src/lib.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tracing-serde/src/lib.rs b/tracing-serde/src/lib.rs index 3df9114f0a..f099853ec0 100644 --- a/tracing-serde/src/lib.rs +++ b/tracing-serde/src/lib.rs @@ -565,6 +565,14 @@ impl<'a> AsSerde<'a> for Level { } } +impl<'a> AsSerde<'a> for FieldSet { + type Serializable = SerializeFieldSet<'a>; + + fn as_serde(&'a self) -> Self::Serializable { + SerializeFieldSet(self) + } +} + impl<'a> self::sealed::Sealed for Event<'a> {} impl<'a> self::sealed::Sealed for Attributes<'a> {} @@ -577,6 +585,8 @@ impl<'a> self::sealed::Sealed for Record<'a> {} impl<'a> self::sealed::Sealed for Metadata<'a> {} +impl self::sealed::Sealed for FieldSet {} + mod sealed { pub trait Sealed {} } From 09da422a7680fd8ac7691e9b42ceec18624a04a4 Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Wed, 27 Jul 2022 15:17:44 -0400 Subject: [PATCH 079/142] subscriber: add `Filter::event_enabled` (#2245) ## Motivation Like for `Subscriber` and `Layer`, allow per-layer `Filter`s to filter based on event fields. ## Solution Add `Filter::event_enabled`, plumb it through the combinator implementations, and call it from `Filtered`. The bit I'm the least confident about is the check in `Registry`'s implementation, but I *think* it matches what `event` is doing and everything seems to function correctly. --- .../src/filter/layer_filters/combinator.rs | 27 ++++++++ .../src/filter/layer_filters/mod.rs | 35 +++++++++++ tracing-subscriber/src/layer/mod.rs | 20 ++++++ tracing-subscriber/src/registry/sharded.rs | 7 +++ .../tests/layer_filters/main.rs | 1 + .../tests/layer_filters/per_event.rs | 61 +++++++++++++++++++ 6 files changed, 151 insertions(+) create mode 100644 tracing-subscriber/tests/layer_filters/per_event.rs diff --git a/tracing-subscriber/src/filter/layer_filters/combinator.rs b/tracing-subscriber/src/filter/layer_filters/combinator.rs index e79de20870..669ddf63a7 100644 --- a/tracing-subscriber/src/filter/layer_filters/combinator.rs +++ b/tracing-subscriber/src/filter/layer_filters/combinator.rs @@ -137,6 +137,11 @@ where cmp::min(self.a.max_level_hint(), self.b.max_level_hint()) } + #[inline] + fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { + self.a.event_enabled(event, cx) && self.b.event_enabled(event, cx) + } + #[inline] fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { self.a.on_new_span(attrs, id, ctx.clone()); @@ -324,6 +329,11 @@ where Some(cmp::max(self.a.max_level_hint()?, self.b.max_level_hint()?)) } + #[inline] + fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { + self.a.event_enabled(event, cx) || self.b.event_enabled(event, cx) + } + #[inline] fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { self.a.on_new_span(attrs, id, ctx.clone()); @@ -393,7 +403,16 @@ where /// If the wrapped filter would enable a span or event, it will be disabled. If /// it would disable a span or event, that span or event will be enabled. /// + /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] + /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as + /// implementing that method is optional, and filters which do not implement + /// filtering on event field values will return `true` even for events that their + /// [`enabled`] method would disable. + /// /// [`Filter`]: crate::layer::Filter + /// [`enabled`]: crate::layer::Filter::enabled + /// [`event_enabled`]: crate::layer::Filter::event_enabled + /// [`callsite_enabled`]: crate::layer::Filter::callsite_enabled pub(crate) fn new(a: A) -> Self { Self { a, _s: PhantomData } } @@ -421,6 +440,14 @@ where None } + #[inline] + fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { + // Never disable based on event_enabled; we "disabled" it in `enabled`, + // so the `not` has already been applied and filtered this not out. + let _ = (event, cx); + true + } + #[inline] fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { self.a.on_new_span(attrs, id, ctx); diff --git a/tracing-subscriber/src/filter/layer_filters/mod.rs b/tracing-subscriber/src/filter/layer_filters/mod.rs index 8949cfb5a8..fbfa55a443 100644 --- a/tracing-subscriber/src/filter/layer_filters/mod.rs +++ b/tracing-subscriber/src/filter/layer_filters/mod.rs @@ -298,6 +298,17 @@ pub trait FilterExt: layer::Filter { /// Inverts `self`, returning a filter that enables spans and events only if /// `self` would *not* enable them. + /// + /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] + /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as + /// implementing that method is optional, and filters which do not implement + /// filtering on event field values will return `true` even for events that their + /// [`enabled`] method would disable. + /// + /// [`Filter`]: crate::subscribe::Filter + /// [`enabled`]: crate::subscribe::Filter::enabled + /// [`event_enabled`]: crate::subscribe::Filter::event_enabled + /// [`callsite_enabled`]: crate::subscribe::Filter::callsite_enabled fn not(self) -> combinator::Not where Self: Sized, @@ -643,6 +654,22 @@ where } } + fn event_enabled(&self, event: &Event<'_>, cx: Context<'_, S>) -> bool { + let cx = cx.with_filter(self.id()); + let enabled = FILTERING + .with(|filtering| filtering.and(self.id(), || self.filter.event_enabled(event, &cx))); + + if enabled { + // If the filter enabled this event, ask the wrapped subscriber if + // _it_ wants it --- it might have a global filter. + self.layer.event_enabled(event, cx) + } else { + // Otherwise, return `true`. See the comment in `enabled` for why this + // is necessary. + true + } + } + fn on_event(&self, event: &Event<'_>, cx: Context<'_, S>) { self.did_enable(|| { self.layer.on_event(event, cx.with_filter(self.id())); @@ -1014,6 +1041,14 @@ impl FilterState { } } + /// Run a second filtering pass, e.g. for Subscribe::event_enabled. + fn and(&self, filter: FilterId, f: impl FnOnce() -> bool) -> bool { + let map = self.enabled.get(); + let enabled = map.is_enabled(filter) && f(); + self.enabled.set(map.set(filter, enabled)); + enabled + } + /// Clears the current in-progress filter state. /// /// This resets the [`FilterMap`] and current [`Interest`] as well as diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs index 5122559ce5..753b6cb249 100644 --- a/tracing-subscriber/src/layer/mod.rs +++ b/tracing-subscriber/src/layer/mod.rs @@ -1352,6 +1352,26 @@ feature! { Interest::sometimes() } + /// Called before the filtered [`Layer]'s [`on_event`], to determine if + /// `on_event` should be called. + /// + /// This gives a chance to filter events based on their fields. Note, + /// however, that this *does not* override [`enabled`], and is not even + /// called if [`enabled`] returns `false`. + /// + /// ## Default Implementation + /// + /// By default, this method returns `true`, indicating that no events are + /// filtered out based on their fields. + /// + /// [`enabled`]: crate::layer::Filter::enabled + /// [`on_event`]: crate::layer::Layer::on_event + #[inline] // collapse this to a constant please mrs optimizer + fn event_enabled(&self, event: &Event<'_>, cx: &Context<'_, S>) -> bool { + let _ = (event, cx); + true + } + /// Returns an optional hint of the highest [verbosity level][level] that /// this `Filter` will enable. /// diff --git a/tracing-subscriber/src/registry/sharded.rs b/tracing-subscriber/src/registry/sharded.rs index b81d5fef83..7978997678 100644 --- a/tracing-subscriber/src/registry/sharded.rs +++ b/tracing-subscriber/src/registry/sharded.rs @@ -275,6 +275,13 @@ impl Subscriber for Registry { fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {} + fn event_enabled(&self, _event: &Event<'_>) -> bool { + if self.has_per_layer_filters() { + return FilterState::event_enabled(); + } + true + } + /// This is intentionally not implemented, as recording events /// is the responsibility of layers atop of this registry. fn event(&self, _: &Event<'_>) {} diff --git a/tracing-subscriber/tests/layer_filters/main.rs b/tracing-subscriber/tests/layer_filters/main.rs index 0233f063b9..10f06c24c6 100644 --- a/tracing-subscriber/tests/layer_filters/main.rs +++ b/tracing-subscriber/tests/layer_filters/main.rs @@ -5,6 +5,7 @@ use self::support::*; mod boxed; mod downcast_raw; mod filter_scopes; +mod per_event; mod targets; mod trees; mod vec; diff --git a/tracing-subscriber/tests/layer_filters/per_event.rs b/tracing-subscriber/tests/layer_filters/per_event.rs new file mode 100644 index 0000000000..9c785f9a23 --- /dev/null +++ b/tracing-subscriber/tests/layer_filters/per_event.rs @@ -0,0 +1,61 @@ +use crate::support::*; +use tracing::Level; +use tracing_subscriber::{field::Visit, layer::Filter, prelude::*}; + +struct FilterEvent; + +impl Filter for FilterEvent { + fn enabled( + &self, + _meta: &tracing::Metadata<'_>, + _cx: &tracing_subscriber::layer::Context<'_, S>, + ) -> bool { + true + } + + fn event_enabled( + &self, + event: &tracing::Event<'_>, + _cx: &tracing_subscriber::layer::Context<'_, S>, + ) -> bool { + struct ShouldEnable(bool); + impl Visit for ShouldEnable { + fn record_bool(&mut self, field: &tracing_core::Field, value: bool) { + if field.name() == "enable" { + self.0 = value; + } + } + + fn record_debug( + &mut self, + _field: &tracing_core::Field, + _value: &dyn core::fmt::Debug, + ) { + } + } + let mut should_enable = ShouldEnable(false); + event.record(&mut should_enable); + should_enable.0 + } +} + +#[test] +fn per_subscriber_event_field_filtering() { + let (expect, handle) = layer::mock() + .event(event::mock().at_level(Level::TRACE)) + .event(event::mock().at_level(Level::INFO)) + .done() + .run_with_handle(); + + let _subscriber = tracing_subscriber::registry() + .with(expect.with_filter(FilterEvent)) + .set_default(); + + tracing::trace!(enable = true, "hello trace"); + tracing::debug!("hello debug"); + tracing::info!(enable = true, "hello info"); + tracing::warn!(enable = false, "hello warn"); + tracing::error!("hello error"); + + handle.assert_finished(); +} From 6a0af278165088428838565d61379fac984cb60c Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Wed, 27 Jul 2022 16:46:11 -0400 Subject: [PATCH 080/142] subscriber: `Not` is `not`, not `or` (#2249) ## Motivation Doc typo. ## Solution Fix. --- tracing-subscriber/src/filter/layer_filters/combinator.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracing-subscriber/src/filter/layer_filters/combinator.rs b/tracing-subscriber/src/filter/layer_filters/combinator.rs index 669ddf63a7..745531de69 100644 --- a/tracing-subscriber/src/filter/layer_filters/combinator.rs +++ b/tracing-subscriber/src/filter/layer_filters/combinator.rs @@ -40,11 +40,11 @@ pub struct Or { /// If the wrapped filter would enable a span or event, it will be disabled. If /// it would disable a span or event, that span or event will be enabled. /// -/// This type is typically returned by the [`FilterExt::or`] method. See that +/// This type is typically returned by the [`FilterExt::not`] method. See that /// method's documentation for details. /// /// [`Filter`]: crate::layer::Filter -/// [`FilterExt::or`]: crate::filter::FilterExt::or +/// [`FilterExt::not`]: crate::filter::FilterExt::not pub struct Not { a: A, _s: PhantomData, From 250cb5b3c1ecb08e62145aee3637866776f375f6 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 27 Jul 2022 14:52:52 -0700 Subject: [PATCH 081/142] core: give `SetGlobalDefaultError` a useful `Debug` impl (#2250) ## Motivation When a global default dispatcher has already been set, the `dispatch::set_global_default` function fails with a `SetGlobalDefaultError`. The `fmt::Display` impl for this type includes a message explaining that the error indicates that a global default has already been set, but the `fmt::Debug` impl is derived, and just looks like this: ``` SetGlobalDefaultError { _no_construct: () } ``` which isn't particularly helpful. Unfortunately, when `unwrap()`ping or `expect()`ing a `Result`, the `fmt::Debug` implementation for the error type is used, rather than `fmt::Display`. This means that the error message will not explain to the user why setting the global default dispatcher failed, which is a bummer. ## Solution This branch replaces the derived `Debug` impl with a manually implemented one that prints the same message as the `Display` impl, but formatted like a tuple struct with a single string field. This avoids emitting `Debug` output that's *just* a textual human-readable message, rather than looking like Rust code, but ensures the message is visible to the user when writing code like ```rust tracing::dispatch::set_global_default(foo).unwrap(); ``` The mesasge now looks like: ``` SetGlobalDefaultError("a global default trace dispatcher has already been set") ``` which should be a bit easier to debug. --- tracing-core/src/dispatcher.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tracing-core/src/dispatcher.rs b/tracing-core/src/dispatcher.rs index 041722c366..a3661817c0 100644 --- a/tracing-core/src/dispatcher.rs +++ b/tracing-core/src/dispatcher.rs @@ -293,14 +293,21 @@ pub fn has_been_set() -> bool { } /// Returned if setting the global dispatcher fails. -#[derive(Debug)] pub struct SetGlobalDefaultError { _no_construct: (), } +impl fmt::Debug for SetGlobalDefaultError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("SetGlobalDefaultError") + .field(&Self::MESSAGE) + .finish() + } +} + impl fmt::Display for SetGlobalDefaultError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.pad("a global default trace dispatcher has already been set") + f.pad(Self::MESSAGE) } } @@ -308,6 +315,10 @@ impl fmt::Display for SetGlobalDefaultError { #[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl error::Error for SetGlobalDefaultError {} +impl SetGlobalDefaultError { + const MESSAGE: &'static str = "a global default trace dispatcher has already been set"; +} + /// Executes a closure with a reference to this thread's current [dispatcher]. /// /// Note that calls to `get_default` should not be nested; if this function is From 1462e1f0593ceac91d4d8d4e7539432ee73a342b Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Thu, 28 Jul 2022 17:50:41 -0400 Subject: [PATCH 082/142] subscriber: clarify `filter.not()` docs w.r.t. event_enabled (#2251) * Explain filter.not() w.r.t. event_enabled Co-authored-by: David Barsky Co-authored-by: Eliza Weisman --- .../src/filter/layer_filters/combinator.rs | 52 +++++++++++++++++-- .../src/filter/layer_filters/mod.rs | 52 +++++++++++++++++-- 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/tracing-subscriber/src/filter/layer_filters/combinator.rs b/tracing-subscriber/src/filter/layer_filters/combinator.rs index 745531de69..3934a13267 100644 --- a/tracing-subscriber/src/filter/layer_filters/combinator.rs +++ b/tracing-subscriber/src/filter/layer_filters/combinator.rs @@ -405,9 +405,55 @@ where /// /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as - /// implementing that method is optional, and filters which do not implement - /// filtering on event field values will return `true` even for events that their - /// [`enabled`] method would disable. + /// filters which do not implement filtering on event field values will return + /// the default `true` even for events that their [`enabled`] method disables. + /// + /// Consider a normal filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => on_span(), + /// SOMETIMES => if enabled() { on_span() }, + /// NEVER => (), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => on_event(), + /// SOMETIMES => if enabled() && event_enabled() { on_event() }, + /// NEVER => (), + /// } + /// ``` + /// + /// and an inverted filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_span() }, + /// NEVER => on_span(), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_event() }, + /// NEVER => on_event(), + /// } + /// ``` + /// + /// A proper inversion would do `!(enabled() && event_enabled())` (or + /// `!enabled() || !event_enabled()`), but because of the implicit `&&` + /// relation between `enabled` and `event_enabled`, it is difficult to + /// short circuit and not call the wrapped `event_enabled`. + /// + /// A combinator which remembers the result of `enabled` in order to call + /// `event_enabled` only when `enabled() == true` is possible, but requires + /// additional thread-local mutable state to support a very niche use case. + // + // Also, it'd mean the wrapped layer's `enabled()` always gets called and + // globally applied to events where it doesn't today, since we can't know + // what `event_enabled` will say until we have the event to call it with. /// /// [`Filter`]: crate::layer::Filter /// [`enabled`]: crate::layer::Filter::enabled diff --git a/tracing-subscriber/src/filter/layer_filters/mod.rs b/tracing-subscriber/src/filter/layer_filters/mod.rs index fbfa55a443..7c2ca4f640 100644 --- a/tracing-subscriber/src/filter/layer_filters/mod.rs +++ b/tracing-subscriber/src/filter/layer_filters/mod.rs @@ -301,9 +301,55 @@ pub trait FilterExt: layer::Filter { /// /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as - /// implementing that method is optional, and filters which do not implement - /// filtering on event field values will return `true` even for events that their - /// [`enabled`] method would disable. + /// filters which do not implement filtering on event field values will return + /// the default `true` even for events that their [`enabled`] method disables. + /// + /// Consider a normal filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => on_span(), + /// SOMETIMES => if enabled() { on_span() }, + /// NEVER => (), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => on_event(), + /// SOMETIMES => if enabled() && event_enabled() { on_event() }, + /// NEVER => (), + /// } + /// ``` + /// + /// and an inverted filter defined as: + /// + /// ```ignore (pseudo-code) + /// // for spans + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_span() }, + /// NEVER => on_span(), + /// } + /// // for events + /// match callsite_enabled() { + /// ALWAYS => (), + /// SOMETIMES => if !enabled() { on_event() }, + /// NEVER => on_event(), + /// } + /// ``` + /// + /// A proper inversion would do `!(enabled() && event_enabled())` (or + /// `!enabled() || !event_enabled()`), but because of the implicit `&&` + /// relation between `enabled` and `event_enabled`, it is difficult to + /// short circuit and not call the wrapped `event_enabled`. + /// + /// A combinator which remembers the result of `enabled` in order to call + /// `event_enabled` only when `enabled() == true` is possible, but requires + /// additional thread-local mutable state to support a very niche use case. + // + // Also, it'd mean the wrapped layer's `enabled()` always gets called and + // globally applied to events where it doesn't today, since we can't know + // what `event_enabled` will say until we have the event to call it with. /// /// [`Filter`]: crate::subscribe::Filter /// [`enabled`]: crate::subscribe::Filter::enabled From 275ededfc24f71c99c2d8dd8a0902b10136bbbe5 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 29 Jul 2022 12:45:09 -0700 Subject: [PATCH 083/142] core: prepare to release v0.1.29 (#2256) # 0.1.29 (July 29, 2022) This release of `tracing-core` adds `PartialEq` and `Eq` implementations for metadata types, and improves error messages when setting the global default subscriber fails. ### Added - `PartialEq` and `Eq` implementations for `Metadata` ([#2229]) - `PartialEq` and `Eq` implementations for `FieldSet` ([#2229]) ### Fixed - Fixed unhelpful `fmt::Debug` output for `dispatcher::SetGlobalDefaultError` ([#2250]) - Fixed compilation with `-Z minimal-versions` ([#2246]) Thanks to @jswrenn and @CAD97 for contributing to this release! [#2229]: https://github.com/tokio-rs/tracing/pull/2229 [#2246]: https://github.com/tokio-rs/tracing/pull/2246 [#2250]: https://github.com/tokio-rs/tracing/pull/2250 --- tracing-core/CHANGELOG.md | 23 +++++++++++++++++++++++ tracing-core/Cargo.toml | 2 +- tracing-core/README.md | 26 +++++++++++++------------- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/tracing-core/CHANGELOG.md b/tracing-core/CHANGELOG.md index c6afcb0776..2db8553c98 100644 --- a/tracing-core/CHANGELOG.md +++ b/tracing-core/CHANGELOG.md @@ -1,3 +1,26 @@ +# 0.1.29 (July 29, 2022) + +This release of `tracing-core` adds `PartialEq` and `Eq` implementations for +metadata types, and improves error messages when setting the global default +subscriber fails. + +### Added + +- `PartialEq` and `Eq` implementations for `Metadata` ([#2229]) +- `PartialEq` and `Eq` implementations for `FieldSet` ([#2229]) + +### Fixed + +- Fixed unhelpful `fmt::Debug` output for `dispatcher::SetGlobalDefaultError` + ([#2250]) +- Fixed compilation with `-Z minimal-versions` ([#2246]) + +Thanks to @jswrenn and @CAD97 for contributing to this release! + +[#2229]: https://github.com/tokio-rs/tracing/pull/2229 +[#2246]: https://github.com/tokio-rs/tracing/pull/2246 +[#2250]: https://github.com/tokio-rs/tracing/pull/2250 + # 0.1.28 (June 23, 2022) This release of `tracing-core` adds new `Value` implementations, including one diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml index b43482fade..8cb3bd9400 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -8,7 +8,7 @@ name = "tracing-core" # - README.md # - Update CHANGELOG.md. # - Create "v0.1.x" git tag. -version = "0.1.28" +version = "0.1.29" authors = ["Tokio Contributors "] license = "MIT" readme = "README.md" diff --git a/tracing-core/README.md b/tracing-core/README.md index f3afec07ed..c6f7b03337 100644 --- a/tracing-core/README.md +++ b/tracing-core/README.md @@ -16,9 +16,9 @@ Core primitives for application-level tracing. [Documentation][docs-url] | [Chat][discord-url] [crates-badge]: https://img.shields.io/crates/v/tracing-core.svg -[crates-url]: https://crates.io/crates/tracing-core/0.1.28 +[crates-url]: https://crates.io/crates/tracing-core/0.1.29 [docs-badge]: https://docs.rs/tracing-core/badge.svg -[docs-url]: https://docs.rs/tracing-core/0.1.28 +[docs-url]: https://docs.rs/tracing-core/0.1.29 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue [docs-master-url]: https://tracing-rs.netlify.com/tracing_core [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg @@ -79,22 +79,22 @@ The following crate feature flags are available: ```toml [dependencies] - tracing-core = { version = "0.1.28", default-features = false } + tracing-core = { version = "0.1.29", default-features = false } ``` **Note**:`tracing-core`'s `no_std` support requires `liballoc`. [`tracing`]: ../tracing -[`span::Id`]: https://docs.rs/tracing-core/0.1.28/tracing_core/span/struct.Id.html -[`Event`]: https://docs.rs/tracing-core/0.1.28/tracing_core/event/struct.Event.html -[`Subscriber`]: https://docs.rs/tracing-core/0.1.28/tracing_core/subscriber/trait.Subscriber.html -[`Metadata`]: https://docs.rs/tracing-core/0.1.28/tracing_core/metadata/struct.Metadata.html -[`Callsite`]: https://docs.rs/tracing-core/0.1.28/tracing_core/callsite/trait.Callsite.html -[`Field`]: https://docs.rs/tracing-core/0.1.28/tracing_core/field/struct.Field.html -[`FieldSet`]: https://docs.rs/tracing-core/0.1.28/tracing_core/field/struct.FieldSet.html -[`Value`]: https://docs.rs/tracing-core/0.1.28/tracing_core/field/trait.Value.html -[`ValueSet`]: https://docs.rs/tracing-core/0.1.28/tracing_core/field/struct.ValueSet.html -[`Dispatch`]: https://docs.rs/tracing-core/0.1.28/tracing_core/dispatcher/struct.Dispatch.html +[`span::Id`]: https://docs.rs/tracing-core/0.1.29/tracing_core/span/struct.Id.html +[`Event`]: https://docs.rs/tracing-core/0.1.29/tracing_core/event/struct.Event.html +[`Subscriber`]: https://docs.rs/tracing-core/0.1.29/tracing_core/subscriber/trait.Subscriber.html +[`Metadata`]: https://docs.rs/tracing-core/0.1.29/tracing_core/metadata/struct.Metadata.html +[`Callsite`]: https://docs.rs/tracing-core/0.1.29/tracing_core/callsite/trait.Callsite.html +[`Field`]: https://docs.rs/tracing-core/0.1.29/tracing_core/field/struct.Field.html +[`FieldSet`]: https://docs.rs/tracing-core/0.1.29/tracing_core/field/struct.FieldSet.html +[`Value`]: https://docs.rs/tracing-core/0.1.29/tracing_core/field/trait.Value.html +[`ValueSet`]: https://docs.rs/tracing-core/0.1.29/tracing_core/field/struct.ValueSet.html +[`Dispatch`]: https://docs.rs/tracing-core/0.1.29/tracing_core/dispatcher/struct.Dispatch.html ## Supported Rust Versions From 5a141ea9c16ccacf411b9b917a07e216ff5c6b4d Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 29 Jul 2022 14:17:47 -0700 Subject: [PATCH 084/142] tracing: prepare to release v0.1.36 (#2257) # 0.1.36 (July 29, 2022) This release adds support for owned values and fat pointers as arguments to the `Span::record` method, as well as updating the minimum `tracing-core` version and several documentation improvements. ### Fixed - Incorrect docs in `dispatcher::set_default` ([#2220]) - Compilation with `-Z minimal-versions` ([#2246]) ### Added - Support for owned values and fat pointers in `Span::record` ([#2212]) - Documentation improvements ([#2208], [#2163]) ### Changed - `tracing-core`: updated to [0.1.29][core-0.1.29] Thanks to @fredr, @cgbur, @jyn514, @matklad, and @CAD97 for contributing to this release! [core-0.1.29]: https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.29 [#2220]: https://github.com/tokio-rs/tracing/pull/2220 [#2246]: https://github.com/tokio-rs/tracing/pull/2246 [#2212]: https://github.com/tokio-rs/tracing/pull/2212 [#2208]: https://github.com/tokio-rs/tracing/pull/2208 [#2163]: https://github.com/tokio-rs/tracing/pull/2163 --- tracing/CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ tracing/Cargo.toml | 4 ++-- tracing/README.md | 4 ++-- tracing/src/lib.rs | 4 ++-- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/tracing/CHANGELOG.md b/tracing/CHANGELOG.md index 1c527f2de4..8b7d58808b 100644 --- a/tracing/CHANGELOG.md +++ b/tracing/CHANGELOG.md @@ -1,3 +1,33 @@ +# 0.1.36 (July 29, 2022) + +This release adds support for owned values and fat pointers as arguments to the +`Span::record` method, as well as updating the minimum `tracing-core` version +and several documentation improvements. + +### Fixed + +- Incorrect docs in `dispatcher::set_default` ([#2220]) +- Compilation with `-Z minimal-versions` ([#2246]) + +### Added + +- Support for owned values and fat pointers in `Span::record` ([#2212]) +- Documentation improvements ([#2208], [#2163]) + +### Changed + +- `tracing-core`: updated to [0.1.29][core-0.1.29] + +Thanks to @fredr, @cgbur, @jyn514, @matklad, and @CAD97 for contributing to this +release! + +[core-0.1.29]: https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.29 +[#2220]: https://github.com/tokio-rs/tracing/pull/2220 +[#2246]: https://github.com/tokio-rs/tracing/pull/2246 +[#2212]: https://github.com/tokio-rs/tracing/pull/2212 +[#2208]: https://github.com/tokio-rs/tracing/pull/2208 +[#2163]: https://github.com/tokio-rs/tracing/pull/2163 + # 0.1.35 (June 8, 2022) This release reduces the overhead of callsite registration by using new diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 43d42cc306..a738d24f15 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -8,7 +8,7 @@ name = "tracing" # - README.md # - Update CHANGELOG.md. # - Create "v0.1.x" git tag -version = "0.1.35" +version = "0.1.36" authors = ["Eliza Weisman ", "Tokio Contributors "] license = "MIT" readme = "README.md" @@ -28,7 +28,7 @@ edition = "2018" rust-version = "1.49.0" [dependencies] -tracing-core = { path = "../tracing-core", version = "0.1.28", default-features = false } +tracing-core = { path = "../tracing-core", version = "0.1.29", default-features = false } log = { version = "0.4.17", optional = true } tracing-attributes = { path = "../tracing-attributes", version = "0.1.22", optional = true } cfg-if = "1.0.0" diff --git a/tracing/README.md b/tracing/README.md index 4090c5d73b..16e671e05c 100644 --- a/tracing/README.md +++ b/tracing/README.md @@ -250,7 +250,7 @@ my_future is as long as the future's. The second, and preferred, option is through the -[`#[instrument]`](https://docs.rs/tracing/0.1.35/tracing/attr.instrument.html) +[`#[instrument]`](https://docs.rs/tracing/0.1.36/tracing/attr.instrument.html) attribute: ```rust @@ -297,7 +297,7 @@ span.in_scope(|| { // Dropping the span will close it, indicating that it has ended. ``` -The [`#[instrument]`](https://docs.rs/tracing/0.1.35/tracing/attr.instrument.html) attribute macro +The [`#[instrument]`](https://docs.rs/tracing/0.1.36/tracing/attr.instrument.html) attribute macro can reduce some of this boilerplate: ```rust diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 1ef96b54f8..7d60c7721d 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -811,7 +811,7 @@ //! //! ```toml //! [dependencies] -//! tracing = { version = "0.1.35", default-features = false } +//! tracing = { version = "0.1.36", default-features = false } //! ``` //! //!
@@ -894,7 +894,7 @@
 //! [flags]: #crate-feature-flags
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
-#![doc(html_root_url = "https://docs.rs/tracing/0.1.35")]
+#![doc(html_root_url = "https://docs.rs/tracing/0.1.36")]
 #![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/"

From 3a193f3d30e9be038fdc86b464b3a0148acc865a Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Fri, 16 Sep 2022 13:15:39 -0700
Subject: [PATCH 085/142] chore: fix `clippy::borrow_deref_ref` warnings

This fixes some relatively recent clippy lints.
---
 examples/examples/futures-proxy-server.rs | 2 +-
 tracing-attributes/src/expand.rs          | 2 +-
 tracing-error/src/error.rs                | 2 +-
 tracing-subscriber/src/fmt/writer.rs      | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/examples/examples/futures-proxy-server.rs b/examples/examples/futures-proxy-server.rs
index 4c181a2573..b036c18a54 100644
--- a/examples/examples/futures-proxy-server.rs
+++ b/examples/examples/futures-proxy-server.rs
@@ -86,7 +86,7 @@ pub struct Args {
     server_addr: SocketAddr,
 }
 
-#[derive(PartialEq, Debug)]
+#[derive(Eq, PartialEq, Debug)]
 pub enum LogFormat {
     Plain,
     Json,
diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs
index 81ee6ed906..7e385566c4 100644
--- a/tracing-attributes/src/expand.rs
+++ b/tracing-attributes/src/expand.rs
@@ -411,7 +411,7 @@ impl RecordType {
                 RecordType::Value
             }
             syn::Type::Reference(syn::TypeReference { elem, .. }) => {
-                RecordType::parse_from_ty(&*elem)
+                RecordType::parse_from_ty(elem)
             }
             _ => RecordType::Debug,
         }
diff --git a/tracing-error/src/error.rs b/tracing-error/src/error.rs
index fea7296257..f28d0c39fd 100644
--- a/tracing-error/src/error.rs
+++ b/tracing-error/src/error.rs
@@ -109,7 +109,7 @@ impl ErrorImpl {
         // uphold this is UB. since the `From` impl is parameterized over the original error type,
         // the function pointer we construct here will also retain the original type. therefore,
         // when this is consumed by the `error` method, it will be safe to call.
-        unsafe { &*(self.vtable.object_ref)(self) }
+        unsafe { (self.vtable.object_ref)(self) }
     }
 }
 
diff --git a/tracing-subscriber/src/fmt/writer.rs b/tracing-subscriber/src/fmt/writer.rs
index 4aacd6d540..3fe9455664 100644
--- a/tracing-subscriber/src/fmt/writer.rs
+++ b/tracing-subscriber/src/fmt/writer.rs
@@ -688,7 +688,7 @@ where
 {
     type Writer = &'a W;
     fn make_writer(&'a self) -> Self::Writer {
-        &*self
+        self
     }
 }
 

From 10a4b13ddcb36db07e79756c99642db552274740 Mon Sep 17 00:00:00 2001
From: Julian Tescher 
Date: Fri, 16 Sep 2022 13:29:35 -0700
Subject: [PATCH 086/142] opentelemetry: update otel to 0.18.0 (#2303)

## Motivation

Support the latest OpenTelemetry specification.

## Solution

Update `opentelemetry` to the latest `0.18.x` release. Breaking changes
in the metrics spec have removed value recorders and added histograms so
the metrics layer's `value.` prefix has been changed to `histogram.` and
behaves accordingly. Additionally the `PushController` configuration for
the metrics layer has been simplified to accept a `BasicController` that
can act in either push or pull modes. Finally trace sampling in the
sdk's `PreSampledTracer` impl has been updated to match the sampling
logic in https://github.com/open-telemetry/opentelemetry-rust/pull/839.

* Update MSRV to 1.56
* Update examples
* Fix async-trait dep
* Update msrv action

Co-authored-by: Eliza Weisman 
---
 .github/workflows/CI.yml                      |   1 +
 examples/Cargo.toml                           |   4 +-
 examples/examples/opentelemetry.rs            |   2 +-
 tracing-opentelemetry/Cargo.toml              |   8 +-
 tracing-opentelemetry/README.md               |   4 +-
 tracing-opentelemetry/src/layer.rs            | 140 +++++-----
 tracing-opentelemetry/src/lib.rs              |   4 +-
 tracing-opentelemetry/src/metrics.rs          |  93 ++++---
 tracing-opentelemetry/src/tracer.rs           |  26 +-
 .../tests/metrics_publishing.rs               | 248 +++++++++---------
 .../tests/trace_state_propagation.rs          |  21 +-
 11 files changed, 288 insertions(+), 263 deletions(-)

diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 43fd26fbc1..b7532f0639 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -214,6 +214,7 @@ jobs:
           --exclude=tracing-appender
           --exclude=tracing-examples
           --exclude=tracing-futures
+          --exclude=tracing-opentelemetry
         toolchain: ${{ env.MSRV }}
 
   # TODO: remove this once tracing's MSRV is bumped.
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index 28a12f4456..c08515bc79 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -52,8 +52,8 @@ inferno = "0.11.6"
 tempfile = "3"
 
 # opentelemetry example
-opentelemetry = { version = "0.17.0", default-features = false, features = ["trace"] }
-opentelemetry-jaeger = "0.16.0"
+opentelemetry = { version = "0.18.0", default-features = false, features = ["trace"] }
+opentelemetry-jaeger = "0.17.0"
 
 # fmt examples
 snafu = "0.6.10"
diff --git a/examples/examples/opentelemetry.rs b/examples/examples/opentelemetry.rs
index b453c77cad..e00cd3a500 100644
--- a/examples/examples/opentelemetry.rs
+++ b/examples/examples/opentelemetry.rs
@@ -19,7 +19,7 @@ fn main() -> Result<(), Box> {
     // Install an otel pipeline with a simple span processor that exports data one at a time when
     // spans end. See the `install_batch` option on each exporter's pipeline builder to see how to
     // export in batches.
-    let tracer = opentelemetry_jaeger::new_pipeline()
+    let tracer = opentelemetry_jaeger::new_agent_pipeline()
         .with_service_name("report_example")
         .install_simple()?;
     let opentelemetry = tracing_opentelemetry::layer().with_tracer(tracer);
diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml
index efe2de0ab9..b9142f1e4e 100644
--- a/tracing-opentelemetry/Cargo.toml
+++ b/tracing-opentelemetry/Cargo.toml
@@ -17,7 +17,7 @@ categories = [
 keywords = ["tracing", "opentelemetry", "jaeger", "zipkin", "async"]
 license = "MIT"
 edition = "2018"
-rust-version = "1.46.0"
+rust-version = "1.56.0"
 
 [features]
 default = ["tracing-log", "metrics"]
@@ -25,7 +25,7 @@ default = ["tracing-log", "metrics"]
 metrics = ["opentelemetry/metrics"]
 
 [dependencies]
-opentelemetry = { version = "0.17.0", default-features = false, features = ["trace"] }
+opentelemetry = { version = "0.18.0", default-features = false, features = ["trace"] }
 tracing = { path = "../tracing", version = "0.1.35", default-features = false, features = ["std"] }
 tracing-core = { path = "../tracing-core", version = "0.1.28" }
 tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", default-features = false, features = ["registry", "std"] }
@@ -39,7 +39,7 @@ thiserror = { version = "1.0.31", optional = true }
 [dev-dependencies]
 async-trait = "0.1.56"
 criterion = { version = "0.3.6", default-features = false }
-opentelemetry-jaeger = "0.16.0"
+opentelemetry-jaeger = "0.17.0"
 futures-util = { version = "0.3", default-features = false }
 tokio = { version = "1", features = ["full"] }
 tokio-stream = "0.1"
@@ -53,4 +53,4 @@ harness = false
 
 [package.metadata.docs.rs]
 all-features = true
-rustdoc-args = ["--cfg", "docsrs"]
\ No newline at end of file
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/tracing-opentelemetry/README.md b/tracing-opentelemetry/README.md
index 4fcb658cfa..9260d80636 100644
--- a/tracing-opentelemetry/README.md
+++ b/tracing-opentelemetry/README.md
@@ -50,7 +50,7 @@ The crate provides the following types:
 [`tracing`]: https://crates.io/crates/tracing
 [OpenTelemetry]: https://opentelemetry.io/
 
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.56+][msrv]*
 
 [msrv]: #supported-rust-versions
 
@@ -110,7 +110,7 @@ $ firefox http://localhost:16686/
 ## Supported Rust Versions
 
 Tracing Opentelemetry is built against the latest stable release. The minimum
-supported version is 1.46. The current Tracing version is not guaranteed to
+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
diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs
index 533fe19406..94304bbce8 100644
--- a/tracing-opentelemetry/src/layer.rs
+++ b/tracing-opentelemetry/src/layer.rs
@@ -1,11 +1,10 @@
 use crate::{OtelData, PreSampledTracer};
 use once_cell::unsync;
 use opentelemetry::{
-    trace::{self as otel, noop, TraceContextExt},
-    Context as OtelContext, Key, KeyValue, Value,
+    trace::{self as otel, noop, OrderMap, TraceContextExt},
+    Context as OtelContext, Key, KeyValue, StringValue, Value,
 };
 use std::any::TypeId;
-use std::borrow::Cow;
 use std::fmt;
 use std::marker;
 use std::thread;
@@ -105,12 +104,11 @@ fn str_to_span_kind(s: &str) -> Option {
     }
 }
 
-fn str_to_status_code(s: &str) -> Option {
+fn str_to_status(s: &str) -> otel::Status {
     match s {
-        s if s.eq_ignore_ascii_case("unset") => Some(otel::StatusCode::Unset),
-        s if s.eq_ignore_ascii_case("ok") => Some(otel::StatusCode::Ok),
-        s if s.eq_ignore_ascii_case("error") => Some(otel::StatusCode::Error),
-        _ => None,
+        s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok,
+        s if s.eq_ignore_ascii_case("error") => otel::Status::error(""),
+        _ => otel::Status::Unset,
     }
 }
 
@@ -220,7 +218,7 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
         let mut next_err = value.source();
 
         while let Some(err) = next_err {
-            chain.push(Cow::Owned(err.to_string()));
+            chain.push(StringValue::from(err.to_string()));
             next_err = err.source();
         }
 
@@ -245,7 +243,7 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
         if self.exception_config.propagate {
             if let Some(span) = &mut self.span_builder {
                 if let Some(attrs) = span.attributes.as_mut() {
-                    attrs.push(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone()));
+                    attrs.insert(Key::new(FIELD_EXCEPTION_MESSAGE), error_msg.clone().into());
 
                     // NOTE: This is actually not the stacktrace of the exception. This is
                     // the "source chain". It represents the heirarchy of errors from the
@@ -253,7 +251,10 @@ impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> {
                     // of the callsites in the code that led to the error happening.
                     // `std::error::Error::backtrace` is a nightly-only API and cannot be
                     // used here until the feature is stabilized.
-                    attrs.push(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone()));
+                    attrs.insert(
+                        Key::new(FIELD_EXCEPTION_STACKTRACE),
+                        Value::Array(chain.clone().into()),
+                    );
                 }
             }
         }
@@ -288,7 +289,7 @@ impl<'a> SpanAttributeVisitor<'a> {
     fn record(&mut self, attribute: KeyValue) {
         debug_assert!(self.span_builder.attributes.is_some());
         if let Some(v) = self.span_builder.attributes.as_mut() {
-            v.push(attribute);
+            v.insert(attribute.key, attribute.value);
         }
     }
 }
@@ -322,9 +323,9 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
         match field.name() {
             SPAN_NAME_FIELD => self.span_builder.name = value.to_string().into(),
             SPAN_KIND_FIELD => self.span_builder.span_kind = str_to_span_kind(value),
-            SPAN_STATUS_CODE_FIELD => self.span_builder.status_code = str_to_status_code(value),
+            SPAN_STATUS_CODE_FIELD => self.span_builder.status = str_to_status(value),
             SPAN_STATUS_MESSAGE_FIELD => {
-                self.span_builder.status_message = Some(value.to_owned().into())
+                self.span_builder.status = otel::Status::error(value.to_string())
             }
             _ => self.record(KeyValue::new(field.name(), value.to_string())),
         }
@@ -341,10 +342,10 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
                 self.span_builder.span_kind = str_to_span_kind(&format!("{:?}", value))
             }
             SPAN_STATUS_CODE_FIELD => {
-                self.span_builder.status_code = str_to_status_code(&format!("{:?}", value))
+                self.span_builder.status = str_to_status(&format!("{:?}", value))
             }
             SPAN_STATUS_MESSAGE_FIELD => {
-                self.span_builder.status_message = Some(format!("{:?}", value).into())
+                self.span_builder.status = otel::Status::error(format!("{:?}", value))
             }
             _ => self.record(Key::new(field.name()).string(format!("{:?}", value))),
         }
@@ -363,7 +364,7 @@ impl<'a> field::Visit for SpanAttributeVisitor<'a> {
         let mut next_err = value.source();
 
         while let Some(err) = next_err {
-            chain.push(Cow::Owned(err.to_string()));
+            chain.push(StringValue::from(err.to_string()));
             next_err = err.source();
         }
 
@@ -405,7 +406,7 @@ where
     /// use tracing_subscriber::Registry;
     ///
     /// // Create a jaeger exporter pipeline for a `trace_demo` service.
-    /// let tracer = opentelemetry_jaeger::new_pipeline()
+    /// let tracer = opentelemetry_jaeger::new_agent_pipeline()
     ///     .with_service_name("trace_demo")
     ///     .install_simple()
     ///     .expect("Error initializing Jaeger exporter");
@@ -446,7 +447,7 @@ where
     /// use tracing_subscriber::Registry;
     ///
     /// // Create a jaeger exporter pipeline for a `trace_demo` service.
-    /// let tracer = opentelemetry_jaeger::new_pipeline()
+    /// let tracer = opentelemetry_jaeger::new_agent_pipeline()
     ///     .with_service_name("trace_demo")
     ///     .install_simple()
     ///     .expect("Error initializing Jaeger exporter");
@@ -684,7 +685,7 @@ where
             builder.trace_id = Some(self.tracer.new_trace_id());
         }
 
-        let builder_attrs = builder.attributes.get_or_insert(Vec::with_capacity(
+        let builder_attrs = builder.attributes.get_or_insert(OrderMap::with_capacity(
             attrs.fields().len() + self.extra_span_attrs(),
         ));
 
@@ -692,26 +693,26 @@ where
             let meta = attrs.metadata();
 
             if let Some(filename) = meta.file() {
-                builder_attrs.push(KeyValue::new("code.filepath", filename));
+                builder_attrs.insert("code.filepath".into(), filename.into());
             }
 
             if let Some(module) = meta.module_path() {
-                builder_attrs.push(KeyValue::new("code.namespace", module));
+                builder_attrs.insert("code.namespace".into(), module.into());
             }
 
             if let Some(line) = meta.line() {
-                builder_attrs.push(KeyValue::new("code.lineno", line as i64));
+                builder_attrs.insert("code.lineno".into(), (line as i64).into());
             }
         }
 
         if self.with_threads {
-            THREAD_ID.with(|id| builder_attrs.push(KeyValue::new("thread.id", **id as i64)));
+            THREAD_ID.with(|id| builder_attrs.insert("thread.id".into(), (**id as i64).into()));
             if let Some(name) = std::thread::current().name() {
                 // TODO(eliza): it's a bummer that we have to allocate here, but
                 // we can't easily get the string as a `static`. it would be
                 // nice if `opentelemetry` could also take `Arc`s as
                 // `String` values...
-                builder_attrs.push(KeyValue::new("thread.name", name.to_owned()));
+                builder_attrs.insert("thread.name".into(), name.to_owned().into());
             }
         }
 
@@ -845,8 +846,10 @@ where
             });
 
             if let Some(OtelData { builder, .. }) = extensions.get_mut::() {
-                if builder.status_code.is_none() && *meta.level() == tracing_core::Level::ERROR {
-                    builder.status_code = Some(otel::StatusCode::Error);
+                if builder.status == otel::Status::Unset
+                    && *meta.level() == tracing_core::Level::ERROR
+                {
+                    builder.status = otel::Status::error("")
                 }
 
                 if self.location {
@@ -904,15 +907,14 @@ where
             if self.tracked_inactivity {
                 // Append busy/idle timings when enabled.
                 if let Some(timings) = extensions.get_mut::() {
-                    let busy_ns = KeyValue::new("busy_ns", timings.busy);
-                    let idle_ns = KeyValue::new("idle_ns", timings.idle);
-
-                    if let Some(ref mut attributes) = builder.attributes {
-                        attributes.push(busy_ns);
-                        attributes.push(idle_ns);
-                    } else {
-                        builder.attributes = Some(vec![busy_ns, idle_ns]);
-                    }
+                    let busy_ns = Key::new("busy_ns");
+                    let idle_ns = Key::new("idle_ns");
+
+                    let attributes = builder
+                        .attributes
+                        .get_or_insert_with(|| OrderMap::with_capacity(2));
+                    attributes.insert(busy_ns, timings.busy.into());
+                    attributes.insert(idle_ns, timings.idle.into());
                 }
             }
 
@@ -965,7 +967,10 @@ fn thread_id_integer(id: thread::ThreadId) -> u64 {
 mod tests {
     use super::*;
     use crate::OtelData;
-    use opentelemetry::trace::{noop, SpanKind, TraceFlags};
+    use opentelemetry::{
+        trace::{noop, TraceFlags},
+        StringValue,
+    };
     use std::{
         borrow::Cow,
         collections::HashMap,
@@ -1043,7 +1048,7 @@ mod tests {
             false
         }
         fn set_attribute(&mut self, _attribute: KeyValue) {}
-        fn set_status(&mut self, _code: otel::StatusCode, _message: String) {}
+        fn set_status(&mut self, _status: otel::Status) {}
         fn update_name>>(&mut self, _new_name: T) {}
         fn end_with_timestamp(&mut self, _timestamp: SystemTime) {}
     }
@@ -1103,7 +1108,7 @@ mod tests {
         let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
 
         tracing::subscriber::with_default(subscriber, || {
-            tracing::debug_span!("request", otel.kind = %SpanKind::Server);
+            tracing::debug_span!("request", otel.kind = "server");
         });
 
         let recorded_kind = tracer.with_data(|data| data.builder.span_kind.clone());
@@ -1116,11 +1121,19 @@ mod tests {
         let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone()));
 
         tracing::subscriber::with_default(subscriber, || {
-            tracing::debug_span!("request", otel.status_code = ?otel::StatusCode::Ok);
+            tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok);
         });
+        let recorded_status = tracer
+            .0
+            .lock()
+            .unwrap()
+            .as_ref()
+            .unwrap()
+            .builder
+            .status
+            .clone();
 
-        let recorded_status_code = tracer.with_data(|data| data.builder.status_code);
-        assert_eq!(recorded_status_code, Some(otel::StatusCode::Ok))
+        assert_eq!(recorded_status, otel::Status::Ok)
     }
 
     #[test]
@@ -1134,8 +1147,17 @@ mod tests {
             tracing::debug_span!("request", otel.status_message = message);
         });
 
-        let recorded_status_message = tracer.with_data(|data| data.builder.status_message.clone());
-        assert_eq!(recorded_status_message, Some(message.into()))
+        let recorded_status_message = tracer
+            .0
+            .lock()
+            .unwrap()
+            .as_ref()
+            .unwrap()
+            .builder
+            .status
+            .clone();
+
+        assert_eq!(recorded_status_message, otel::Status::error(message))
     }
 
     #[test]
@@ -1153,7 +1175,7 @@ mod tests {
         let _g = existing_cx.attach();
 
         tracing::subscriber::with_default(subscriber, || {
-            tracing::debug_span!("request", otel.kind = %SpanKind::Server);
+            tracing::debug_span!("request", otel.kind = "server");
         });
 
         let recorded_trace_id =
@@ -1177,7 +1199,7 @@ mod tests {
         let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
         let keys = attributes
             .iter()
-            .map(|attr| attr.key.as_str())
+            .map(|(key, _)| key.as_str())
             .collect::>();
         assert!(keys.contains(&"idle_ns"));
         assert!(keys.contains(&"busy_ns"));
@@ -1217,7 +1239,7 @@ mod tests {
 
         let key_values = attributes
             .into_iter()
-            .map(|attr| (attr.key.as_str().to_owned(), attr.value))
+            .map(|(key, value)| (key.as_str().to_owned(), value))
             .collect::>();
 
         assert_eq!(key_values["error"].as_str(), "user error");
@@ -1225,8 +1247,8 @@ mod tests {
             key_values["error.chain"],
             Value::Array(
                 vec![
-                    Cow::Borrowed("intermediate error"),
-                    Cow::Borrowed("base error")
+                    StringValue::from("intermediate error"),
+                    StringValue::from("base error")
                 ]
                 .into()
             )
@@ -1237,8 +1259,8 @@ mod tests {
             key_values[FIELD_EXCEPTION_STACKTRACE],
             Value::Array(
                 vec![
-                    Cow::Borrowed("intermediate error"),
-                    Cow::Borrowed("base error")
+                    StringValue::from("intermediate error"),
+                    StringValue::from("base error")
                 ]
                 .into()
             )
@@ -1258,7 +1280,7 @@ mod tests {
         let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
         let keys = attributes
             .iter()
-            .map(|attr| attr.key.as_str())
+            .map(|(key, _)| key.as_str())
             .collect::>();
         assert!(keys.contains(&"code.filepath"));
         assert!(keys.contains(&"code.namespace"));
@@ -1278,7 +1300,7 @@ mod tests {
         let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
         let keys = attributes
             .iter()
-            .map(|attr| attr.key.as_str())
+            .map(|(key, _)| key.as_str())
             .collect::>();
         assert!(!keys.contains(&"code.filepath"));
         assert!(!keys.contains(&"code.namespace"));
@@ -1290,7 +1312,7 @@ mod tests {
         let thread = thread::current();
         let expected_name = thread
             .name()
-            .map(|name| Value::String(Cow::Owned(name.to_owned())));
+            .map(|name| Value::String(name.to_owned().into()));
         let expected_id = Value::I64(thread_id_integer(thread.id()) as i64);
 
         let tracer = TestTracer(Arc::new(Mutex::new(None)));
@@ -1304,7 +1326,7 @@ mod tests {
         let attributes = tracer
             .with_data(|data| data.builder.attributes.as_ref().unwrap().clone())
             .drain(..)
-            .map(|keyval| (keyval.key.as_str().to_string(), keyval.value))
+            .map(|(key, value)| (key.as_str().to_string(), value))
             .collect::>();
         assert_eq!(attributes.get("thread.name"), expected_name.as_ref());
         assert_eq!(attributes.get("thread.id"), Some(&expected_id));
@@ -1323,7 +1345,7 @@ mod tests {
         let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone());
         let keys = attributes
             .iter()
-            .map(|attr| attr.key.as_str())
+            .map(|(key, _)| key.as_str())
             .collect::>();
         assert!(!keys.contains(&"thread.name"));
         assert!(!keys.contains(&"thread.id"));
@@ -1365,7 +1387,7 @@ mod tests {
 
         let key_values = attributes
             .into_iter()
-            .map(|attr| (attr.key.as_str().to_owned(), attr.value))
+            .map(|(key, value)| (key.as_str().to_owned(), value))
             .collect::>();
 
         assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error");
@@ -1373,8 +1395,8 @@ mod tests {
             key_values[FIELD_EXCEPTION_STACKTRACE],
             Value::Array(
                 vec![
-                    Cow::Borrowed("intermediate error"),
-                    Cow::Borrowed("base error")
+                    StringValue::from("intermediate error"),
+                    StringValue::from("base error")
                 ]
                 .into()
             )
diff --git a/tracing-opentelemetry/src/lib.rs b/tracing-opentelemetry/src/lib.rs
index 5cd725d5a3..d3bc5bbce3 100644
--- a/tracing-opentelemetry/src/lib.rs
+++ b/tracing-opentelemetry/src/lib.rs
@@ -9,7 +9,7 @@
 //! [OpenTelemetry]: https://opentelemetry.io
 //! [`tracing`]: https://github.com/tokio-rs/tracing
 //!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.56+][msrv]*
 //!
 //! [msrv]: #supported-rust-versions
 //!
@@ -86,7 +86,7 @@
 //! ## Supported Rust Versions
 //!
 //! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! 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
diff --git a/tracing-opentelemetry/src/metrics.rs b/tracing-opentelemetry/src/metrics.rs
index 37df62c4b4..91b9e63a8f 100644
--- a/tracing-opentelemetry/src/metrics.rs
+++ b/tracing-opentelemetry/src/metrics.rs
@@ -3,8 +3,9 @@ use tracing::{field::Visit, Subscriber};
 use tracing_core::Field;
 
 use opentelemetry::{
-    metrics::{Counter, Meter, MeterProvider, UpDownCounter, ValueRecorder},
-    sdk::metrics::PushController,
+    metrics::{Counter, Histogram, Meter, MeterProvider, UpDownCounter},
+    sdk::metrics::controllers::BasicController,
+    Context as OtelContext,
 };
 use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer};
 
@@ -13,7 +14,7 @@ const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry";
 
 const METRIC_PREFIX_MONOTONIC_COUNTER: &str = "monotonic_counter.";
 const METRIC_PREFIX_COUNTER: &str = "counter.";
-const METRIC_PREFIX_VALUE: &str = "value.";
+const METRIC_PREFIX_HISTOGRAM: &str = "histogram.";
 const I64_MAX: u64 = i64::MAX as u64;
 
 #[derive(Default)]
@@ -22,9 +23,9 @@ pub(crate) struct Instruments {
     f64_counter: MetricsMap>,
     i64_up_down_counter: MetricsMap>,
     f64_up_down_counter: MetricsMap>,
-    u64_value_recorder: MetricsMap>,
-    i64_value_recorder: MetricsMap>,
-    f64_value_recorder: MetricsMap>,
+    u64_histogram: MetricsMap>,
+    i64_histogram: MetricsMap>,
+    f64_histogram: MetricsMap>,
 }
 
 type MetricsMap = RwLock>;
@@ -35,14 +36,15 @@ pub(crate) enum InstrumentType {
     CounterF64(f64),
     UpDownCounterI64(i64),
     UpDownCounterF64(f64),
-    ValueRecorderU64(u64),
-    ValueRecorderI64(i64),
-    ValueRecorderF64(f64),
+    HistogramU64(u64),
+    HistogramI64(i64),
+    HistogramF64(f64),
 }
 
 impl Instruments {
     pub(crate) fn update_metric(
         &self,
+        cx: &OtelContext,
         meter: &Meter,
         instrument_type: InstrumentType,
         metric_name: &'static str,
@@ -76,7 +78,7 @@ impl Instruments {
                     &self.u64_counter,
                     metric_name,
                     || meter.u64_counter(metric_name).init(),
-                    |ctr| ctr.add(value, &[]),
+                    |ctr| ctr.add(cx, value, &[]),
                 );
             }
             InstrumentType::CounterF64(value) => {
@@ -84,7 +86,7 @@ impl Instruments {
                     &self.f64_counter,
                     metric_name,
                     || meter.f64_counter(metric_name).init(),
-                    |ctr| ctr.add(value, &[]),
+                    |ctr| ctr.add(cx, value, &[]),
                 );
             }
             InstrumentType::UpDownCounterI64(value) => {
@@ -92,7 +94,7 @@ impl Instruments {
                     &self.i64_up_down_counter,
                     metric_name,
                     || meter.i64_up_down_counter(metric_name).init(),
-                    |ctr| ctr.add(value, &[]),
+                    |ctr| ctr.add(cx, value, &[]),
                 );
             }
             InstrumentType::UpDownCounterF64(value) => {
@@ -100,31 +102,31 @@ impl Instruments {
                     &self.f64_up_down_counter,
                     metric_name,
                     || meter.f64_up_down_counter(metric_name).init(),
-                    |ctr| ctr.add(value, &[]),
+                    |ctr| ctr.add(cx, value, &[]),
                 );
             }
-            InstrumentType::ValueRecorderU64(value) => {
+            InstrumentType::HistogramU64(value) => {
                 update_or_insert(
-                    &self.u64_value_recorder,
+                    &self.u64_histogram,
                     metric_name,
-                    || meter.u64_value_recorder(metric_name).init(),
-                    |rec| rec.record(value, &[]),
+                    || meter.u64_histogram(metric_name).init(),
+                    |rec| rec.record(cx, value, &[]),
                 );
             }
-            InstrumentType::ValueRecorderI64(value) => {
+            InstrumentType::HistogramI64(value) => {
                 update_or_insert(
-                    &self.i64_value_recorder,
+                    &self.i64_histogram,
                     metric_name,
-                    || meter.i64_value_recorder(metric_name).init(),
-                    |rec| rec.record(value, &[]),
+                    || meter.i64_histogram(metric_name).init(),
+                    |rec| rec.record(cx, value, &[]),
                 );
             }
-            InstrumentType::ValueRecorderF64(value) => {
+            InstrumentType::HistogramF64(value) => {
                 update_or_insert(
-                    &self.f64_value_recorder,
+                    &self.f64_histogram,
                     metric_name,
-                    || meter.f64_value_recorder(metric_name).init(),
-                    |rec| rec.record(value, &[]),
+                    || meter.f64_histogram(metric_name).init(),
+                    |rec| rec.record(cx, value, &[]),
                 );
             }
         };
@@ -142,8 +144,10 @@ impl<'a> Visit for MetricVisitor<'a> {
     }
 
     fn record_u64(&mut self, field: &Field, value: u64) {
+        let cx = OtelContext::current();
         if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) {
             self.instruments.update_metric(
+                &cx,
                 self.meter,
                 InstrumentType::CounterU64(value),
                 metric_name,
@@ -151,6 +155,7 @@ impl<'a> Visit for MetricVisitor<'a> {
         } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) {
             if value <= I64_MAX {
                 self.instruments.update_metric(
+                    &cx,
                     self.meter,
                     InstrumentType::UpDownCounterI64(value as i64),
                     metric_name,
@@ -163,54 +168,63 @@ impl<'a> Visit for MetricVisitor<'a> {
                     value
                 );
             }
-        } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_VALUE) {
+        } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) {
             self.instruments.update_metric(
+                &cx,
                 self.meter,
-                InstrumentType::ValueRecorderU64(value),
+                InstrumentType::HistogramU64(value),
                 metric_name,
             );
         }
     }
 
     fn record_f64(&mut self, field: &Field, value: f64) {
+        let cx = OtelContext::current();
         if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) {
             self.instruments.update_metric(
+                &cx,
                 self.meter,
                 InstrumentType::CounterF64(value),
                 metric_name,
             );
         } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) {
             self.instruments.update_metric(
+                &cx,
                 self.meter,
                 InstrumentType::UpDownCounterF64(value),
                 metric_name,
             );
-        } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_VALUE) {
+        } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) {
             self.instruments.update_metric(
+                &cx,
                 self.meter,
-                InstrumentType::ValueRecorderF64(value),
+                InstrumentType::HistogramF64(value),
                 metric_name,
             );
         }
     }
 
     fn record_i64(&mut self, field: &Field, value: i64) {
+        let cx = OtelContext::current();
         if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) {
             self.instruments.update_metric(
+                &cx,
                 self.meter,
                 InstrumentType::CounterU64(value as u64),
                 metric_name,
             );
         } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) {
             self.instruments.update_metric(
+                &cx,
                 self.meter,
                 InstrumentType::UpDownCounterI64(value),
                 metric_name,
             );
-        } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_VALUE) {
+        } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) {
             self.instruments.update_metric(
+                &cx,
                 self.meter,
-                InstrumentType::ValueRecorderI64(value),
+                InstrumentType::HistogramI64(value),
                 metric_name,
             );
         }
@@ -232,14 +246,14 @@ impl<'a> Visit for MetricVisitor<'a> {
 /// use tracing_opentelemetry::MetricsLayer;
 /// use tracing_subscriber::layer::SubscriberExt;
 /// use tracing_subscriber::Registry;
-/// # use opentelemetry::sdk::metrics::PushController;
+/// # use opentelemetry::sdk::metrics::controllers::BasicController;
 ///
-/// // Constructing a PushController is out-of-scope for the docs here, but there
+/// // Constructing a BasicController is out-of-scope for the docs here, but there
 /// // are examples in the opentelemetry repository. See:
-/// // https://github.com/open-telemetry/opentelemetry-rust/blob/c13a11e62a68eacd8c41a0742a0d097808e28fbd/examples/basic-otlp/src/main.rs#L39-L53
-/// # let push_controller: PushController = unimplemented!();
+/// // https://github.com/open-telemetry/opentelemetry-rust/blob/d4b9befea04bcc7fc19319a6ebf5b5070131c486/examples/basic-otlp/src/main.rs#L35-L52
+/// # let controller: BasicController = unimplemented!();
 ///
-/// let opentelemetry_metrics =  MetricsLayer::new(push_controller);
+/// let opentelemetry_metrics =  MetricsLayer::new(controller);
 /// let subscriber = Registry::default().with(opentelemetry_metrics);
 /// tracing::subscriber::set_global_default(subscriber).unwrap();
 /// ```
@@ -329,10 +343,9 @@ pub struct MetricsLayer {
 
 impl MetricsLayer {
     /// Create a new instance of MetricsLayer.
-    pub fn new(push_controller: PushController) -> Self {
-        let meter = push_controller
-            .provider()
-            .meter(INSTRUMENTATION_LIBRARY_NAME, Some(CARGO_PKG_VERSION));
+    pub fn new(controller: BasicController) -> Self {
+        let meter =
+            controller.versioned_meter(INSTRUMENTATION_LIBRARY_NAME, Some(CARGO_PKG_VERSION), None);
         MetricsLayer {
             meter,
             instruments: Default::default(),
diff --git a/tracing-opentelemetry/src/tracer.rs b/tracing-opentelemetry/src/tracer.rs
index 9cd6d378d5..66c52fea21 100644
--- a/tracing-opentelemetry/src/tracer.rs
+++ b/tracing-opentelemetry/src/tracer.rs
@@ -1,9 +1,10 @@
-use opentelemetry::sdk::trace::{SamplingDecision, SamplingResult, Tracer, TracerProvider};
+use opentelemetry::sdk::trace::{Tracer, TracerProvider};
+use opentelemetry::trace::OrderMap;
 use opentelemetry::{
     trace as otel,
     trace::{
-        noop, SpanBuilder, SpanContext, SpanId, SpanKind, TraceContextExt, TraceFlags, TraceId,
-        TraceState,
+        noop, SamplingDecision, SamplingResult, SpanBuilder, SpanContext, SpanId, SpanKind,
+        TraceContextExt, TraceFlags, TraceId, TraceState,
     },
     Context as OtelContext,
 };
@@ -74,19 +75,18 @@ impl PreSampledTracer for Tracer {
         let builder = &mut data.builder;
 
         // Gather trace state
-        let (no_parent, trace_id, remote_parent, parent_trace_flags) =
-            current_trace_state(builder, parent_cx, &provider);
+        let (trace_id, parent_trace_flags) = current_trace_state(builder, parent_cx, &provider);
 
         // Sample or defer to existing sampling decisions
         let (flags, trace_state) = if let Some(result) = &builder.sampling_result {
             process_sampling_result(result, parent_trace_flags)
-        } else if no_parent || remote_parent {
+        } else {
             builder.sampling_result = Some(provider.config().sampler.should_sample(
                 Some(parent_cx),
                 trace_id,
                 &builder.name,
                 builder.span_kind.as_ref().unwrap_or(&SpanKind::Internal),
-                builder.attributes.as_deref().unwrap_or(&[]),
+                builder.attributes.as_ref().unwrap_or(&OrderMap::default()),
                 builder.links.as_deref().unwrap_or(&[]),
                 self.instrumentation_library(),
             ));
@@ -95,12 +95,6 @@ impl PreSampledTracer for Tracer {
                 builder.sampling_result.as_ref().unwrap(),
                 parent_trace_flags,
             )
-        } else {
-            // has parent that is local
-            Some((
-                parent_trace_flags,
-                parent_cx.span().span_context().trace_state().clone(),
-            ))
         }
         .unwrap_or_default();
 
@@ -126,18 +120,16 @@ fn current_trace_state(
     builder: &SpanBuilder,
     parent_cx: &OtelContext,
     provider: &TracerProvider,
-) -> (bool, TraceId, bool, TraceFlags) {
+) -> (TraceId, TraceFlags) {
     if parent_cx.has_active_span() {
         let span = parent_cx.span();
         let sc = span.span_context();
-        (false, sc.trace_id(), sc.is_remote(), sc.trace_flags())
+        (sc.trace_id(), sc.trace_flags())
     } else {
         (
-            true,
             builder
                 .trace_id
                 .unwrap_or_else(|| provider.config().id_generator.new_trace_id()),
-            false,
             Default::default(),
         )
     }
diff --git a/tracing-opentelemetry/tests/metrics_publishing.rs b/tracing-opentelemetry/tests/metrics_publishing.rs
index 9db53fcb3f..284005e538 100644
--- a/tracing-opentelemetry/tests/metrics_publishing.rs
+++ b/tracing-opentelemetry/tests/metrics_publishing.rs
@@ -1,26 +1,21 @@
-#![cfg(feature = "metrics")]
-use async_trait::async_trait;
-use futures_util::{Stream, StreamExt as _};
 use opentelemetry::{
-    metrics::{Descriptor, InstrumentKind},
-    metrics::{Number, NumberKind},
+    metrics::MetricsError,
     sdk::{
-        export::{
-            metrics::{
-                CheckpointSet, ExportKind, ExportKindFor, ExportKindSelector,
-                Exporter as MetricsExporter, Points, Sum,
-            },
-            trace::{SpanData, SpanExporter},
+        export::metrics::{
+            aggregation::{self, Histogram, Sum, TemporalitySelector},
+            InstrumentationLibraryReader,
         },
         metrics::{
-            aggregators::{ArrayAggregator, SumAggregator},
-            selectors::simple::Selector,
+            aggregators::{HistogramAggregator, SumAggregator},
+            controllers::BasicController,
+            processors,
+            sdk_api::{Descriptor, InstrumentKind, Number, NumberKind},
+            selectors,
         },
     },
-    Key, Value,
+    Context,
 };
 use std::cmp::Ordering;
-use std::time::Duration;
 use tracing::Subscriber;
 use tracing_opentelemetry::MetricsLayer;
 use tracing_subscriber::prelude::*;
@@ -30,7 +25,7 @@ const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry";
 
 #[tokio::test]
 async fn u64_counter_is_exported() {
-    let subscriber = init_subscriber(
+    let (subscriber, exporter) = init_subscriber(
         "hello_world".to_string(),
         InstrumentKind::Counter,
         NumberKind::U64,
@@ -40,11 +35,13 @@ async fn u64_counter_is_exported() {
     tracing::subscriber::with_default(subscriber, || {
         tracing::info!(monotonic_counter.hello_world = 1_u64);
     });
+
+    exporter.export().unwrap();
 }
 
 #[tokio::test]
 async fn u64_counter_is_exported_i64_at_instrumentation_point() {
-    let subscriber = init_subscriber(
+    let (subscriber, exporter) = init_subscriber(
         "hello_world2".to_string(),
         InstrumentKind::Counter,
         NumberKind::U64,
@@ -54,11 +51,13 @@ async fn u64_counter_is_exported_i64_at_instrumentation_point() {
     tracing::subscriber::with_default(subscriber, || {
         tracing::info!(monotonic_counter.hello_world2 = 1_i64);
     });
+
+    exporter.export().unwrap();
 }
 
 #[tokio::test]
 async fn f64_counter_is_exported() {
-    let subscriber = init_subscriber(
+    let (subscriber, exporter) = init_subscriber(
         "float_hello_world".to_string(),
         InstrumentKind::Counter,
         NumberKind::F64,
@@ -68,11 +67,13 @@ async fn f64_counter_is_exported() {
     tracing::subscriber::with_default(subscriber, || {
         tracing::info!(monotonic_counter.float_hello_world = 1.000000123_f64);
     });
+
+    exporter.export().unwrap();
 }
 
 #[tokio::test]
 async fn i64_up_down_counter_is_exported() {
-    let subscriber = init_subscriber(
+    let (subscriber, exporter) = init_subscriber(
         "pebcak".to_string(),
         InstrumentKind::UpDownCounter,
         NumberKind::I64,
@@ -82,11 +83,13 @@ async fn i64_up_down_counter_is_exported() {
     tracing::subscriber::with_default(subscriber, || {
         tracing::info!(counter.pebcak = -5_i64);
     });
+
+    exporter.export().unwrap();
 }
 
 #[tokio::test]
 async fn i64_up_down_counter_is_exported_u64_at_instrumentation_point() {
-    let subscriber = init_subscriber(
+    let (subscriber, exporter) = init_subscriber(
         "pebcak2".to_string(),
         InstrumentKind::UpDownCounter,
         NumberKind::I64,
@@ -96,11 +99,13 @@ async fn i64_up_down_counter_is_exported_u64_at_instrumentation_point() {
     tracing::subscriber::with_default(subscriber, || {
         tracing::info!(counter.pebcak2 = 5_u64);
     });
+
+    exporter.export().unwrap();
 }
 
 #[tokio::test]
 async fn f64_up_down_counter_is_exported() {
-    let subscriber = init_subscriber(
+    let (subscriber, exporter) = init_subscriber(
         "pebcak_blah".to_string(),
         InstrumentKind::UpDownCounter,
         NumberKind::F64,
@@ -110,13 +115,15 @@ async fn f64_up_down_counter_is_exported() {
     tracing::subscriber::with_default(subscriber, || {
         tracing::info!(counter.pebcak_blah = 99.123_f64);
     });
+
+    exporter.export().unwrap();
 }
 
 #[tokio::test]
-async fn u64_value_is_exported() {
-    let subscriber = init_subscriber(
+async fn u64_histogram_is_exported() {
+    let (subscriber, exporter) = init_subscriber(
         "abcdefg".to_string(),
-        InstrumentKind::ValueRecorder,
+        InstrumentKind::Histogram,
         NumberKind::U64,
         Number::from(9_u64),
     );
@@ -124,13 +131,15 @@ async fn u64_value_is_exported() {
     tracing::subscriber::with_default(subscriber, || {
         tracing::info!(value.abcdefg = 9_u64);
     });
+
+    exporter.export().unwrap();
 }
 
 #[tokio::test]
-async fn i64_value_is_exported() {
-    let subscriber = init_subscriber(
+async fn i64_histogram_is_exported() {
+    let (subscriber, exporter) = init_subscriber(
         "abcdefg_auenatsou".to_string(),
-        InstrumentKind::ValueRecorder,
+        InstrumentKind::Histogram,
         NumberKind::I64,
         Number::from(-19_i64),
     );
@@ -138,13 +147,15 @@ async fn i64_value_is_exported() {
     tracing::subscriber::with_default(subscriber, || {
         tracing::info!(value.abcdefg_auenatsou = -19_i64);
     });
+
+    exporter.export().unwrap();
 }
 
 #[tokio::test]
-async fn f64_value_is_exported() {
-    let subscriber = init_subscriber(
+async fn f64_histogram_is_exported() {
+    let (subscriber, exporter) = init_subscriber(
         "abcdefg_racecar".to_string(),
-        InstrumentKind::ValueRecorder,
+        InstrumentKind::Histogram,
         NumberKind::F64,
         Number::from(777.0012_f64),
     );
@@ -152,6 +163,8 @@ async fn f64_value_is_exported() {
     tracing::subscriber::with_default(subscriber, || {
         tracing::info!(value.abcdefg_racecar = 777.0012_f64);
     });
+
+    exporter.export().unwrap();
 }
 
 fn init_subscriber(
@@ -159,24 +172,25 @@ fn init_subscriber(
     expected_instrument_kind: InstrumentKind,
     expected_number_kind: NumberKind,
     expected_value: Number,
-) -> impl Subscriber + 'static {
+) -> (impl Subscriber + 'static, TestExporter) {
+    let controller = opentelemetry::sdk::metrics::controllers::basic(processors::factory(
+        selectors::simple::histogram(vec![-10.0, 100.0]),
+        aggregation::cumulative_temporality_selector(),
+    ))
+    .build();
+
     let exporter = TestExporter {
         expected_metric_name,
         expected_instrument_kind,
         expected_number_kind,
         expected_value,
+        controller: controller.clone(),
     };
 
-    let push_controller = opentelemetry::sdk::metrics::controllers::push(
-        Selector::Exact,
-        ExportKindSelector::Stateless,
+    (
+        tracing_subscriber::registry().with(MetricsLayer::new(controller)),
         exporter,
-        tokio::spawn,
-        delayed_interval,
     )
-    .build();
-
-    tracing_subscriber::registry().with(MetricsLayer::new(push_controller))
 }
 
 #[derive(Clone, Debug)]
@@ -185,100 +199,84 @@ struct TestExporter {
     expected_instrument_kind: InstrumentKind,
     expected_number_kind: NumberKind,
     expected_value: Number,
+    controller: BasicController,
 }
 
-#[async_trait]
-impl SpanExporter for TestExporter {
-    async fn export(
-        &mut self,
-        mut _batch: Vec,
-    ) -> opentelemetry::sdk::export::trace::ExportResult {
-        Ok(())
-    }
-}
+impl TestExporter {
+    fn export(&self) -> Result<(), MetricsError> {
+        self.controller.collect(&Context::current())?;
+        self.controller.try_for_each(&mut |library, reader| {
+            reader.try_for_each(self, &mut |record| {
+                assert_eq!(self.expected_metric_name, record.descriptor().name());
+                assert_eq!(
+                    self.expected_instrument_kind,
+                    *record.descriptor().instrument_kind()
+                );
+                assert_eq!(
+                    self.expected_number_kind,
+                    *record.descriptor().number_kind()
+                );
+                match self.expected_instrument_kind {
+                    InstrumentKind::Counter | InstrumentKind::UpDownCounter => {
+                        let number = record
+                            .aggregator()
+                            .unwrap()
+                            .as_any()
+                            .downcast_ref::()
+                            .unwrap()
+                            .sum()
+                            .unwrap();
+
+                        assert_eq!(
+                            Ordering::Equal,
+                            number
+                                .partial_cmp(&NumberKind::U64, &self.expected_value)
+                                .unwrap()
+                        );
+                    }
+                    InstrumentKind::Histogram => {
+                        let histogram = record
+                            .aggregator()
+                            .unwrap()
+                            .as_any()
+                            .downcast_ref::()
+                            .unwrap()
+                            .histogram()
+                            .unwrap();
+
+                        let counts = histogram.counts();
+                        if dbg!(self.expected_value.to_i64(&self.expected_number_kind)) > 100 {
+                            assert_eq!(counts, &[0.0, 0.0, 1.0]);
+                        } else if self.expected_value.to_i64(&self.expected_number_kind) > 0 {
+                            assert_eq!(counts, &[0.0, 1.0, 0.0]);
+                        } else {
+                            assert_eq!(counts, &[1.0, 0.0, 0.0]);
+                        }
+                    }
+                    _ => panic!(
+                        "InstrumentKind {:?} not currently supported!",
+                        self.expected_instrument_kind
+                    ),
+                };
 
-impl MetricsExporter for TestExporter {
-    fn export(&self, checkpoint_set: &mut dyn CheckpointSet) -> opentelemetry::metrics::Result<()> {
-        checkpoint_set.try_for_each(self, &mut |record| {
-            assert_eq!(self.expected_metric_name, record.descriptor().name());
-            assert_eq!(
-                self.expected_instrument_kind,
-                *record.descriptor().instrument_kind()
-            );
-            assert_eq!(
-                self.expected_number_kind,
-                *record.descriptor().number_kind()
-            );
-            let number = match self.expected_instrument_kind {
-                InstrumentKind::Counter | InstrumentKind::UpDownCounter => record
-                    .aggregator()
-                    .unwrap()
-                    .as_any()
-                    .downcast_ref::()
-                    .unwrap()
-                    .sum()
-                    .unwrap(),
-                InstrumentKind::ValueRecorder => record
-                    .aggregator()
-                    .unwrap()
-                    .as_any()
-                    .downcast_ref::()
-                    .unwrap()
-                    .points()
-                    .unwrap()[0]
-                    .clone(),
-                _ => panic!(
-                    "InstrumentKind {:?} not currently supported!",
-                    self.expected_instrument_kind
-                ),
-            };
-            assert_eq!(
-                Ordering::Equal,
-                number
-                    .partial_cmp(&NumberKind::U64, &self.expected_value)
-                    .unwrap()
-            );
-
-            // The following are the same regardless of the individual metric.
-            assert_eq!(
-                INSTRUMENTATION_LIBRARY_NAME,
-                record.descriptor().instrumentation_library().name
-            );
-            assert_eq!(
-                CARGO_PKG_VERSION,
-                record.descriptor().instrumentation_version().unwrap()
-            );
-            assert_eq!(
-                Value::String("unknown_service".into()),
-                record
-                    .resource()
-                    .get(Key::new("service.name".to_string()))
-                    .unwrap()
-            );
-
-            opentelemetry::metrics::Result::Ok(())
+                // The following are the same regardless of the individual metric.
+                assert_eq!(INSTRUMENTATION_LIBRARY_NAME, library.name);
+                assert_eq!(CARGO_PKG_VERSION, library.version.as_ref().unwrap());
+
+                Ok(())
+            })
         })
     }
 }
 
-impl ExportKindFor for TestExporter {
-    fn export_kind_for(&self, _descriptor: &Descriptor) -> ExportKind {
+impl TemporalitySelector for TestExporter {
+    fn temporality_for(
+        &self,
+        _descriptor: &Descriptor,
+        _kind: &aggregation::AggregationKind,
+    ) -> aggregation::Temporality {
         // I don't think the value here makes a difference since
         // we are just testing a single metric.
-        ExportKind::Cumulative
+        aggregation::Temporality::Cumulative
     }
 }
-
-// From opentelemetry::sdk::util::
-// For some reason I can't pull it in from the other crate, it gives
-//   could not find `util` in `sdk`
-/// Helper which wraps `tokio::time::interval` and makes it return a stream
-fn tokio_interval_stream(period: std::time::Duration) -> tokio_stream::wrappers::IntervalStream {
-    tokio_stream::wrappers::IntervalStream::new(tokio::time::interval(period))
-}
-
-// https://github.com/open-telemetry/opentelemetry-rust/blob/2585d109bf90d53d57c91e19c758dca8c36f5512/examples/basic-otlp/src/main.rs#L34-L37
-// Skip first immediate tick from tokio, not needed for async_std.
-fn delayed_interval(duration: Duration) -> impl Stream {
-    tokio_interval_stream(duration).skip(0)
-}
diff --git a/tracing-opentelemetry/tests/trace_state_propagation.rs b/tracing-opentelemetry/tests/trace_state_propagation.rs
index 0f92bc47aa..49200b4fc9 100644
--- a/tracing-opentelemetry/tests/trace_state_propagation.rs
+++ b/tracing-opentelemetry/tests/trace_state_propagation.rs
@@ -1,8 +1,8 @@
-use async_trait::async_trait;
+use futures_util::future::BoxFuture;
 use opentelemetry::{
     propagation::TextMapPropagator,
     sdk::{
-        export::trace::{SpanData, SpanExporter},
+        export::trace::{ExportResult, SpanData, SpanExporter},
         propagation::{BaggagePropagator, TextMapCompositePropagator, TraceContextPropagator},
         trace::{Tracer, TracerProvider},
     },
@@ -158,15 +158,14 @@ fn build_sampled_context() -> (Context, impl Subscriber, TestExporter, TracerPro
 #[derive(Clone, Default, Debug)]
 struct TestExporter(Arc>>);
 
-#[async_trait]
 impl SpanExporter for TestExporter {
-    async fn export(
-        &mut self,
-        mut batch: Vec,
-    ) -> opentelemetry::sdk::export::trace::ExportResult {
-        if let Ok(mut inner) = self.0.lock() {
-            inner.append(&mut batch);
-        }
-        Ok(())
+    fn export(&mut self, mut batch: Vec) -> BoxFuture<'static, ExportResult> {
+        let spans = self.0.clone();
+        Box::pin(async move {
+            if let Ok(mut inner) = spans.lock() {
+                inner.append(&mut batch);
+            }
+            Ok(())
+        })
     }
 }

From 1413f042c3ed064edc4cfa96cc04834b6402ca97 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Mon, 15 Aug 2022 14:04:20 -0700
Subject: [PATCH 087/142] chore: fix `clippy::borrow_deref_ref` lints (#2277)

This fixes new Clippy lints introduced in the latest Rust version.

* chore: fix `clippy::borrow_deref_ref` lints
* chore: fix `clippy::partial_eq_without_eq` lint
---
 tracing-core/src/dispatcher.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tracing-core/src/dispatcher.rs b/tracing-core/src/dispatcher.rs
index a3661817c0..c87c1d6c54 100644
--- a/tracing-core/src/dispatcher.rs
+++ b/tracing-core/src/dispatcher.rs
@@ -631,14 +631,14 @@ impl Dispatch {
     /// `T`.
     #[inline]
     pub fn is(&self) -> bool {
-        ::is::(&*self.subscriber)
+        ::is::(&self.subscriber)
     }
 
     /// Returns some reference to the `Subscriber` this `Dispatch` forwards to
     /// if it is of type `T`, or `None` if it isn't.
     #[inline]
     pub fn downcast_ref(&self) -> Option<&T> {
-        ::downcast_ref(&*self.subscriber)
+        ::downcast_ref(&self.subscriber)
     }
 }
 

From 13ac1e417275952dd1d5995e287f18e614189d2a Mon Sep 17 00:00:00 2001
From: Thoren Paulson <3453006+thoren-d@users.noreply.github.com>
Date: Mon, 15 Aug 2022 14:25:58 -0700
Subject: [PATCH 088/142] docs: add tracing-chrome to list of related crates
 (#2275)

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 0fc9bc0797..7c453ea627 100644
--- a/README.md
+++ b/README.md
@@ -403,6 +403,7 @@ are not maintained by the `tokio` project. These include:
   grouping together logs from the same spans during writing.
 - [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki].
 - [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.
+- [`tracing-chrome`] provides a layer that exports trace data that can be viewed in `chrome://tracing`.
 
 (if you're the maintainer of a `tracing` ecosystem crate not in this list,
 please let us know!)
@@ -441,6 +442,7 @@ please let us know!)
 [`tracing-loki`]: https://crates.io/crates/tracing-loki
 [Grafana Loki]: https://grafana.com/oss/loki/
 [`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt
+[`tracing-chrome`]: https://crates.io/crates/tracing-chrome
 
 **Note:** that some of the ecosystem crates are currently unreleased and
 undergoing active development. They may be less stable than `tracing` and

From 39798aef9a82bf47f672ef7cf3649d3b3d74cbd5 Mon Sep 17 00:00:00 2001
From: Kelly Thomas Kline 
Date: Mon, 15 Aug 2022 16:12:00 -0700
Subject: [PATCH 089/142] docs: correct grammar in README (#2260)

Co-authored-by: Bryan Garza <1396101+bryangarza@users.noreply.github.com>
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 7c453ea627..3d8e93b915 100644
--- a/README.md
+++ b/README.md
@@ -241,7 +241,7 @@ my_future
 `Future::instrument` attaches a span to the future, ensuring that the span's lifetime
 is as long as the future's.
 
-Under the hood, the [`#[instrument]`][instrument] macro performs same the explicit span
+Under the hood, the [`#[instrument]`][instrument] macro performs the same explicit span
 attachment that `Future::instrument` does.
 
 [std-future]: https://doc.rust-lang.org/stable/std/future/trait.Future.html

From 760170bdafe58c978a9f8da764cbec558e314348 Mon Sep 17 00:00:00 2001
From: Michael Goulet 
Date: Fri, 19 Aug 2022 16:00:56 -0700
Subject: [PATCH 090/142] attributes: add fake return to improve span on type
 error (#2270)

## Motivation

Return type errors on instrumented async functions are a bit vague,
since the type error originates within the macro itself due to the
indirection of additional `async {}` blocks generated in the proc-macro
(and due to the way that inference propagates around in Rust).

This leads to a pretty difficult to understand error. For example:

```rust
#[instrument]
async fn foo() -> String {
  ""
}
```

results in...

```
error[E0308]: mismatched types
 --> src/main.rs:1:1
  |
1 | #[tracing::instrument]
  | ^^^^^^^^^^^^^^^^^^^^^^- help: try using a conversion method: `.to_string()`
  | |
  | expected struct `String`, found `&str`
```

## Solution

Installs a fake `return` statement as the first thing that happens in
the auto-generated block of an instrumented async function.

This causes the coercion machinery within rustc to infer the right
return type (matching the the outer function) eagerly, as opposed to
after the `async {}` block has been type-checked.

This will cause us to both be able to point out the return type span
correctly, and properly suggest fixes on the expressions that cause the
type mismatch.

After this change, the example code above compiles to:

```
error[E0308]: mismatched types
  --> src/main.rs:3:5
   |
3  |     ""
   |     ^^- help: try using a conversion method: `.to_string()`
   |     |
   |     expected struct `String`, found `&str`
   |
note: return type inferred to be `String` here
  --> src/main.rs:2:20
   |
2  | async fn foo() -> String {
   |                   ^^^^^^
```
---
 tracing-attributes/Cargo.toml                 |  2 +
 tracing-attributes/src/expand.rs              | 89 ++++++++++++++-----
 tracing-attributes/tests/ui.rs                |  7 ++
 .../tests/ui/async_instrument.rs              | 46 ++++++++++
 .../tests/ui/async_instrument.stderr          | 89 +++++++++++++++++++
 5 files changed, 213 insertions(+), 20 deletions(-)
 create mode 100644 tracing-attributes/tests/ui.rs
 create mode 100644 tracing-attributes/tests/ui/async_instrument.rs
 create mode 100644 tracing-attributes/tests/ui/async_instrument.stderr

diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml
index 5d8915331f..abce8a5195 100644
--- a/tracing-attributes/Cargo.toml
+++ b/tracing-attributes/Cargo.toml
@@ -50,6 +50,8 @@ tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", featur
 tokio-test = { version = "0.3.0" }
 tracing-core = { path = "../tracing-core", version = "0.1.28"}
 async-trait = "0.1.56"
+trybuild = "1.0.64"
+rustversion = "1.0.9"
 
 [badges]
 maintenance = { status = "experimental" }
diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs
index 7e385566c4..70204395ea 100644
--- a/tracing-attributes/src/expand.rs
+++ b/tracing-attributes/src/expand.rs
@@ -2,10 +2,11 @@ use std::iter;
 
 use proc_macro2::TokenStream;
 use quote::{quote, quote_spanned, ToTokens};
+use syn::visit_mut::VisitMut;
 use syn::{
     punctuated::Punctuated, spanned::Spanned, Block, Expr, ExprAsync, ExprCall, FieldPat, FnArg,
     Ident, Item, ItemFn, Pat, PatIdent, PatReference, PatStruct, PatTuple, PatTupleStruct, PatType,
-    Path, Signature, Stmt, Token, TypePath,
+    Path, ReturnType, Signature, Stmt, Token, Type, TypePath,
 };
 
 use crate::{
@@ -18,7 +19,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
     input: MaybeItemFnRef<'a, B>,
     args: InstrumentArgs,
     instrumented_function_name: &str,
-    self_type: Option<&syn::TypePath>,
+    self_type: Option<&TypePath>,
 ) -> proc_macro2::TokenStream {
     // these are needed ahead of time, as ItemFn contains the function body _and_
     // isn't representable inside a quote!/quote_spanned! macro
@@ -31,7 +32,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
     } = input;
 
     let Signature {
-        output: return_type,
+        output,
         inputs: params,
         unsafety,
         asyncness,
@@ -49,8 +50,35 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
 
     let warnings = args.warnings();
 
+    let (return_type, return_span) = if let ReturnType::Type(_, return_type) = &output {
+        (erase_impl_trait(return_type), return_type.span())
+    } else {
+        // Point at function name if we don't have an explicit return type
+        (syn::parse_quote! { () }, ident.span())
+    };
+    // Install a fake return statement as the first thing in the function
+    // body, so that we eagerly infer that the return type is what we
+    // declared in the async fn signature.
+    // The `#[allow(..)]` is given because the return statement is
+    // unreachable, but does affect inference, so it needs to be written
+    // exactly that way for it to do its magic.
+    let fake_return_edge = quote_spanned! {return_span=>
+        #[allow(unreachable_code, clippy::diverging_sub_expression, clippy::let_unit_value)]
+        if false {
+            let __tracing_attr_fake_return: #return_type =
+                unreachable!("this is just for type inference, and is unreachable code");
+            return __tracing_attr_fake_return;
+        }
+    };
+    let block = quote! {
+        {
+            #fake_return_edge
+            #block
+        }
+    };
+
     let body = gen_block(
-        block,
+        &block,
         params,
         asyncness.is_some(),
         args,
@@ -60,7 +88,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
 
     quote!(
         #(#attrs) *
-        #vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #return_type
+        #vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #output
         #where_clause
         {
             #warnings
@@ -76,7 +104,7 @@ fn gen_block(
     async_context: bool,
     mut args: InstrumentArgs,
     instrumented_function_name: &str,
-    self_type: Option<&syn::TypePath>,
+    self_type: Option<&TypePath>,
 ) -> proc_macro2::TokenStream {
     // generate the span's name
     let span_name = args
@@ -393,11 +421,11 @@ impl RecordType {
         "Wrapping",
     ];
 
-    /// Parse `RecordType` from [syn::Type] by looking up
+    /// Parse `RecordType` from [Type] by looking up
     /// the [RecordType::TYPES_FOR_VALUE] array.
-    fn parse_from_ty(ty: &syn::Type) -> Self {
+    fn parse_from_ty(ty: &Type) -> Self {
         match ty {
-            syn::Type::Path(syn::TypePath { path, .. })
+            Type::Path(TypePath { path, .. })
                 if path
                     .segments
                     .iter()
@@ -410,9 +438,7 @@ impl RecordType {
             {
                 RecordType::Value
             }
-            syn::Type::Reference(syn::TypeReference { elem, .. }) => {
-                RecordType::parse_from_ty(elem)
-            }
+            Type::Reference(syn::TypeReference { elem, .. }) => RecordType::parse_from_ty(elem),
             _ => RecordType::Debug,
         }
     }
@@ -471,7 +497,7 @@ pub(crate) struct AsyncInfo<'block> {
     // statement that must be patched
     source_stmt: &'block Stmt,
     kind: AsyncKind<'block>,
-    self_type: Option,
+    self_type: Option,
     input: &'block ItemFn,
 }
 
@@ -606,11 +632,11 @@ impl<'block> AsyncInfo<'block> {
                     if ident == "_self" {
                         let mut ty = *ty.ty.clone();
                         // extract the inner type if the argument is "&self" or "&mut self"
-                        if let syn::Type::Reference(syn::TypeReference { elem, .. }) = ty {
+                        if let Type::Reference(syn::TypeReference { elem, .. }) = ty {
                             ty = *elem;
                         }
 
-                        if let syn::Type::Path(tp) = ty {
+                        if let Type::Path(tp) = ty {
                             self_type = Some(tp);
                             break;
                         }
@@ -722,7 +748,7 @@ struct IdentAndTypesRenamer<'a> {
     idents: Vec<(Ident, Ident)>,
 }
 
-impl<'a> syn::visit_mut::VisitMut for IdentAndTypesRenamer<'a> {
+impl<'a> VisitMut for IdentAndTypesRenamer<'a> {
     // we deliberately compare strings because we want to ignore the spans
     // If we apply clippy's lint, the behavior changes
     #[allow(clippy::cmp_owned)]
@@ -734,11 +760,11 @@ impl<'a> syn::visit_mut::VisitMut for IdentAndTypesRenamer<'a> {
         }
     }
 
-    fn visit_type_mut(&mut self, ty: &mut syn::Type) {
+    fn visit_type_mut(&mut self, ty: &mut Type) {
         for (type_name, new_type) in &self.types {
-            if let syn::Type::Path(TypePath { path, .. }) = ty {
+            if let Type::Path(TypePath { path, .. }) = ty {
                 if path_to_string(path) == *type_name {
-                    *ty = syn::Type::Path(new_type.clone());
+                    *ty = Type::Path(new_type.clone());
                 }
             }
         }
@@ -751,10 +777,33 @@ struct AsyncTraitBlockReplacer<'a> {
     patched_block: Block,
 }
 
-impl<'a> syn::visit_mut::VisitMut for AsyncTraitBlockReplacer<'a> {
+impl<'a> VisitMut for AsyncTraitBlockReplacer<'a> {
     fn visit_block_mut(&mut self, i: &mut Block) {
         if i == self.block {
             *i = self.patched_block.clone();
         }
     }
 }
+
+// Replaces any `impl Trait` with `_` so it can be used as the type in
+// a `let` statement's LHS.
+struct ImplTraitEraser;
+
+impl VisitMut for ImplTraitEraser {
+    fn visit_type_mut(&mut self, t: &mut Type) {
+        if let Type::ImplTrait(..) = t {
+            *t = syn::TypeInfer {
+                underscore_token: Token![_](t.span()),
+            }
+            .into();
+        } else {
+            syn::visit_mut::visit_type_mut(self, t);
+        }
+    }
+}
+
+fn erase_impl_trait(ty: &Type) -> Type {
+    let mut ty = ty.clone();
+    ImplTraitEraser.visit_type_mut(&mut ty);
+    ty
+}
diff --git a/tracing-attributes/tests/ui.rs b/tracing-attributes/tests/ui.rs
new file mode 100644
index 0000000000..f11cc019eb
--- /dev/null
+++ b/tracing-attributes/tests/ui.rs
@@ -0,0 +1,7 @@
+// Only test on nightly, since UI tests are bound to change over time
+#[rustversion::stable]
+#[test]
+fn async_instrument() {
+    let t = trybuild::TestCases::new();
+    t.compile_fail("tests/ui/async_instrument.rs");
+}
diff --git a/tracing-attributes/tests/ui/async_instrument.rs b/tracing-attributes/tests/ui/async_instrument.rs
new file mode 100644
index 0000000000..5b245746a6
--- /dev/null
+++ b/tracing-attributes/tests/ui/async_instrument.rs
@@ -0,0 +1,46 @@
+#![allow(unreachable_code)]
+
+#[tracing::instrument]
+async fn unit() {
+    ""
+}
+
+#[tracing::instrument]
+async fn simple_mismatch() -> String {
+    ""
+}
+
+// FIXME: this span is still pretty poor
+#[tracing::instrument]
+async fn opaque_unsatisfied() -> impl std::fmt::Display {
+    ("",)
+}
+
+struct Wrapper(T);
+
+#[tracing::instrument]
+async fn mismatch_with_opaque() -> Wrapper {
+    ""
+}
+
+#[tracing::instrument]
+async fn early_return_unit() {
+    if true {
+        return "";
+    }
+}
+
+#[tracing::instrument]
+async fn early_return() -> String {
+    if true {
+        return "";
+    }
+    String::new()
+}
+
+#[tracing::instrument]
+async fn extra_semicolon() -> i32 {
+    1;
+}
+
+fn main() {}
diff --git a/tracing-attributes/tests/ui/async_instrument.stderr b/tracing-attributes/tests/ui/async_instrument.stderr
new file mode 100644
index 0000000000..5214f92a7e
--- /dev/null
+++ b/tracing-attributes/tests/ui/async_instrument.stderr
@@ -0,0 +1,89 @@
+error[E0308]: mismatched types
+ --> tests/ui/async_instrument.rs:5:5
+  |
+5 |     ""
+  |     ^^ expected `()`, found `&str`
+  |
+note: return type inferred to be `()` here
+ --> tests/ui/async_instrument.rs:4:10
+  |
+4 | async fn unit() {
+  |          ^^^^
+
+error[E0308]: mismatched types
+  --> tests/ui/async_instrument.rs:10:5
+   |
+10 |     ""
+   |     ^^- help: try using a conversion method: `.to_string()`
+   |     |
+   |     expected struct `String`, found `&str`
+   |
+note: return type inferred to be `String` here
+  --> tests/ui/async_instrument.rs:9:31
+   |
+9  | async fn simple_mismatch() -> String {
+   |                               ^^^^^^
+
+error[E0277]: `(&str,)` doesn't implement `std::fmt::Display`
+  --> tests/ui/async_instrument.rs:14:1
+   |
+14 | #[tracing::instrument]
+   | ^^^^^^^^^^^^^^^^^^^^^^ `(&str,)` cannot be formatted with the default formatter
+   |
+   = help: the trait `std::fmt::Display` is not implemented for `(&str,)`
+   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
+   = note: this error originates in the attribute macro `tracing::instrument` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0308]: mismatched types
+  --> tests/ui/async_instrument.rs:23:5
+   |
+23 |     ""
+   |     ^^ expected struct `Wrapper`, found `&str`
+   |
+   = note: expected struct `Wrapper<_>`
+           found reference `&'static str`
+note: return type inferred to be `Wrapper<_>` here
+  --> tests/ui/async_instrument.rs:22:36
+   |
+22 | async fn mismatch_with_opaque() -> Wrapper {
+   |                                    ^^^^^^^
+help: try wrapping the expression in `Wrapper`
+   |
+23 |     Wrapper("")
+   |     ++++++++  +
+
+error[E0308]: mismatched types
+  --> tests/ui/async_instrument.rs:29:16
+   |
+29 |         return "";
+   |                ^^ expected `()`, found `&str`
+   |
+note: return type inferred to be `()` here
+  --> tests/ui/async_instrument.rs:27:10
+   |
+27 | async fn early_return_unit() {
+   |          ^^^^^^^^^^^^^^^^^
+
+error[E0308]: mismatched types
+  --> tests/ui/async_instrument.rs:36:16
+   |
+36 |         return "";
+   |                ^^- help: try using a conversion method: `.to_string()`
+   |                |
+   |                expected struct `String`, found `&str`
+   |
+note: return type inferred to be `String` here
+  --> tests/ui/async_instrument.rs:34:28
+   |
+34 | async fn early_return() -> String {
+   |                            ^^^^^^
+
+error[E0308]: mismatched types
+  --> tests/ui/async_instrument.rs:42:35
+   |
+42 |   async fn extra_semicolon() -> i32 {
+   |  ___________________________________^
+43 | |     1;
+   | |      - help: remove this semicolon
+44 | | }
+   | |_^ expected `i32`, found `()`

From 41b0ace0e2db4c3df075a7de796d100376cda1ba Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Mon, 22 Aug 2022 12:31:20 -0400
Subject: [PATCH 091/142] subscriber: say which crate the disabled level
 warning refers to (#2285)

I just saw this warning when trying to debug something in Miri:
```
warning: some trace filter directives would enable traces that are disabled statically
 | `miri::tls=trace` would enable the TRACE level for the `miri::tls` target
 = note: the static max level is `info`
 = help: to enable DEBUG logging, remove the `max_level_info` feature
```

I spent 10min figuring out why the `log` crate is doing this (Miri is
using env-logger for logging), until I realized that this error actually
originates from inside rustc, which I guess uses `tracing`. It would
have helped if the error message would say which crate is even talking
to me here. :)
---
 tracing-subscriber/src/filter/env/builder.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tracing-subscriber/src/filter/env/builder.rs b/tracing-subscriber/src/filter/env/builder.rs
index 7bc65484ea..f776f1f18e 100644
--- a/tracing-subscriber/src/filter/env/builder.rs
+++ b/tracing-subscriber/src/filter/env/builder.rs
@@ -282,7 +282,7 @@ impl Builder {
             };
             let (feature, earlier_level) = help_msg();
             ctx_help(&format!(
-                "to enable {}logging, remove the `{}` feature",
+                "to enable {}logging, remove the `{}` feature from the `tracing` crate",
                 earlier_level, feature
             ));
         }

From 9fccc013c8b34e0745fe8e66f55f364e3147a884 Mon Sep 17 00:00:00 2001
From: Jack Wrenn 
Date: Tue, 23 Aug 2022 09:54:12 -0700
Subject: [PATCH 092/142] core: add `{Subscriber,Layer}::on_register_dispatch`
 (#2269)

The `on_register_dispatch` method is invoked when a `Subscriber` is
registered as a `Dispatch`. This method should be overridden to
perform actions upon the installation of a subscriber/layer;
for instance, to send a copy of the subscriber's `Dispatch` to a
worker thread.
---
 tracing-core/src/callsite.rs                  |  1 +
 tracing-core/src/dispatcher.rs                | 19 +++++++++++++++++--
 tracing-core/src/subscriber.rs                |  9 ++++++---
 .../src/filter/layer_filters/mod.rs           |  6 +++++-
 tracing-subscriber/src/layer/layered.rs       | 12 ++++++------
 tracing-subscriber/src/layer/mod.rs           | 15 ++++++++++++++-
 tracing-subscriber/src/reload.rs              |  6 +++++-
 7 files changed, 54 insertions(+), 14 deletions(-)

diff --git a/tracing-core/src/callsite.rs b/tracing-core/src/callsite.rs
index bc7405ffa0..f887132364 100644
--- a/tracing-core/src/callsite.rs
+++ b/tracing-core/src/callsite.rs
@@ -487,6 +487,7 @@ impl Callsites {
 
 pub(crate) fn register_dispatch(dispatch: &Dispatch) {
     let dispatchers = DISPATCHERS.register_dispatch(dispatch);
+    dispatch.subscriber().on_register_dispatch(dispatch);
     CALLSITES.rebuild_interest(dispatchers);
 }
 
diff --git a/tracing-core/src/dispatcher.rs b/tracing-core/src/dispatcher.rs
index c87c1d6c54..be6b5c6224 100644
--- a/tracing-core/src/dispatcher.rs
+++ b/tracing-core/src/dispatcher.rs
@@ -430,8 +430,23 @@ impl Dispatch {
         Registrar(Arc::downgrade(&self.subscriber))
     }
 
-    /// Registers a new callsite with this subscriber, returning whether or not
-    /// the subscriber is interested in being notified about the callsite.
+    #[inline(always)]
+    #[cfg(feature = "alloc")]
+    pub(crate) fn subscriber(&self) -> &(dyn Subscriber + Send + Sync) {
+        match self.subscriber {
+            Kind::Scoped(ref s) => Arc::deref(s),
+            Kind::Global(s) => s,
+        }
+    }
+
+    #[inline(always)]
+    #[cfg(not(feature = "alloc"))]
+    pub(crate) fn subscriber(&self) -> &(dyn Subscriber + Send + Sync) {
+        &self.subscriber
+    }
+
+    /// Registers a new callsite with this collector, returning whether or not
+    /// the collector is interested in being notified about the callsite.
     ///
     /// This calls the [`register_callsite`] function on the [`Subscriber`]
     /// that this `Dispatch` forwards to.
diff --git a/tracing-core/src/subscriber.rs b/tracing-core/src/subscriber.rs
index a6f0834e2c..d628975824 100644
--- a/tracing-core/src/subscriber.rs
+++ b/tracing-core/src/subscriber.rs
@@ -1,5 +1,5 @@
-//! Subscribers collect and record trace data.
-use crate::{span, Event, LevelFilter, Metadata};
+//! Collectors collect and record trace data.
+use crate::{span, Dispatch, Event, LevelFilter, Metadata};
 
 use crate::stdlib::{
     any::{Any, TypeId},
@@ -81,7 +81,10 @@ use crate::stdlib::{
 /// [`event`]: Subscriber::event
 /// [`event_enabled`]: Subscriber::event_enabled
 pub trait Subscriber: 'static {
-    // === Span registry methods ==============================================
+    /// Invoked when this subscriber becomes a [`Dispatch`].
+    fn on_register_dispatch(&self, subscriber: &Dispatch) {
+        let _ = subscriber;
+    }
 
     /// Registers a new [callsite] with this subscriber, returning whether or not
     /// the subscriber is interested in being notified about the callsite.
diff --git a/tracing-subscriber/src/filter/layer_filters/mod.rs b/tracing-subscriber/src/filter/layer_filters/mod.rs
index 7c2ca4f640..e50ee6f007 100644
--- a/tracing-subscriber/src/filter/layer_filters/mod.rs
+++ b/tracing-subscriber/src/filter/layer_filters/mod.rs
@@ -44,7 +44,7 @@ use std::{
 use tracing_core::{
     span,
     subscriber::{Interest, Subscriber},
-    Event, Metadata,
+    Dispatch, Event, Metadata,
 };
 pub mod combinator;
 
@@ -607,6 +607,10 @@ where
     F: layer::Filter + 'static,
     L: Layer,
 {
+    fn on_register_dispatch(&self, collector: &Dispatch) {
+        self.layer.on_register_dispatch(collector);
+    }
+
     fn on_layer(&mut self, subscriber: &mut S) {
         self.id = MagicPlfDowncastMarker(subscriber.register_filter());
         self.layer.on_layer(subscriber);
diff --git a/tracing-subscriber/src/layer/layered.rs b/tracing-subscriber/src/layer/layered.rs
index 805ec13dc8..91e475f631 100644
--- a/tracing-subscriber/src/layer/layered.rs
+++ b/tracing-subscriber/src/layer/layered.rs
@@ -1,9 +1,4 @@
-use tracing_core::{
-    metadata::Metadata,
-    span,
-    subscriber::{Interest, Subscriber},
-    Event, LevelFilter,
-};
+use tracing_core::{metadata::Metadata, span, Dispatch, Event, Interest, LevelFilter, Subscriber};
 
 use crate::{
     filter,
@@ -245,6 +240,11 @@ where
     B: Layer,
     S: Subscriber,
 {
+    fn on_register_dispatch(&self, subscriber: &Dispatch) {
+        self.layer.on_register_dispatch(subscriber);
+        self.inner.on_register_dispatch(subscriber);
+    }
+
     fn on_layer(&mut self, subscriber: &mut S) {
         self.layer.on_layer(subscriber);
         self.inner.on_layer(subscriber);
diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs
index 753b6cb249..3283a5ef5f 100644
--- a/tracing-subscriber/src/layer/mod.rs
+++ b/tracing-subscriber/src/layer/mod.rs
@@ -678,7 +678,7 @@ use tracing_core::{
     metadata::Metadata,
     span,
     subscriber::{Interest, Subscriber},
-    Event, LevelFilter,
+    Dispatch, Event, LevelFilter,
 };
 
 use core::any::TypeId;
@@ -711,6 +711,14 @@ where
     S: Subscriber,
     Self: 'static,
 {
+    /// Performs late initialization when installing this layer as a
+    /// [subscriber].
+    ///
+    /// [subscriber]: tracing_core::Subscriber
+    fn on_register_dispatch(&self, subscriber: &Dispatch) {
+        let _ = subscriber;
+    }
+
     /// Performs late initialization when attaching a `Layer` to a
     /// [`Subscriber`].
     ///
@@ -1591,6 +1599,11 @@ feature! {
 
     macro_rules! layer_impl_body {
         () => {
+            #[inline]
+            fn on_register_dispatch(&self, subscriber: &Dispatch) {
+                self.deref().on_register_dispatch(subscriber);
+            }
+
             #[inline]
             fn on_layer(&mut self, subscriber: &mut S) {
                 self.deref_mut().on_layer(subscriber);
diff --git a/tracing-subscriber/src/reload.rs b/tracing-subscriber/src/reload.rs
index 0c6c1c45c4..c72e824b7d 100644
--- a/tracing-subscriber/src/reload.rs
+++ b/tracing-subscriber/src/reload.rs
@@ -75,7 +75,7 @@ use std::{
 use tracing_core::{
     callsite, span,
     subscriber::{Interest, Subscriber},
-    Event, LevelFilter, Metadata,
+    Dispatch, Event, LevelFilter, Metadata,
 };
 
 /// Wraps a `Layer` or `Filter`, allowing it to be reloaded dynamically at runtime.
@@ -115,6 +115,10 @@ where
     L: crate::Layer + 'static,
     S: Subscriber,
 {
+    fn on_register_dispatch(&self, subscriber: &Dispatch) {
+        try_lock!(self.inner.read()).on_register_dispatch(subscriber);
+    }
+
     fn on_layer(&mut self, subscriber: &mut S) {
         try_lock!(self.inner.write(), else return).on_layer(subscriber);
     }

From e4d3e366059eb5c3eb742ca79d0c63962b22ff0c Mon Sep 17 00:00:00 2001
From: WorldSEnder 
Date: Thu, 25 Aug 2022 19:17:16 +0200
Subject: [PATCH 093/142] tracing: add `tracing-web` to the list of related
 crates (#2283)

Fixes #2280

## Motivation

I've implemented an alternative to [`tracing-wasm`] to fix some issues
that I had when using it (most are open as longer time bug reports, no
need to recount them here). It should be more up to date with the
current tracing framework and, being layer based, is easier to compose
with other subscribers, e.g. supports formatting and timing on event
logs out of the box.

## Solution

Add the [`tracing-web`] crate to the list of related crates.

[`tracing-wasm`]: (https://github.com/storyai/tracing-wasm)
[`tracing-web`]: https://crates.io/crates/tracing-web/
---
 README.md          | 3 +++
 tracing/src/lib.rs | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/README.md b/README.md
index 3d8e93b915..23b37cc9f2 100644
--- a/README.md
+++ b/README.md
@@ -390,6 +390,8 @@ are not maintained by the `tokio` project. These include:
   _inside_ of functions.
 - [`tracing-wasm`] provides a `Subscriber`/`Layer` implementation that reports
   events and spans via browser `console.log` and [User Timing API (`window.performance`)].
+- [`tracing-web`] provides a layer implementation of level-aware logging of events
+  to web browsers' `console.*` and span events to the [User Timing API (`window.performance`)].
 - [`test-log`] takes care of initializing `tracing` for tests, based on
   environment variables with an `env_logger` compatible syntax.
 - [`tracing-unwrap`] provides convenience methods to report failed unwraps on `Result` or `Option` types to a `Subscriber`.
@@ -425,6 +427,7 @@ please let us know!)
 [`color-eyre`]: https://docs.rs/color-eyre
 [`spandoc`]: https://docs.rs/spandoc
 [`tracing-wasm`]: https://docs.rs/tracing-wasm
+[`tracing-web`]: https://crates.io/crates/tracing-web
 [`test-log`]: https://crates.io/crates/test-log
 [User Timing API (`window.performance`)]: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API
 [`tracing-unwrap`]: https://docs.rs/tracing-unwrap
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index 7d60c7721d..bc215e6032 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -727,6 +727,8 @@
 //!    in [bunyan] format, enriched with timing information.
 //!  - [`tracing-wasm`] provides a `Subscriber`/`Layer` implementation that reports
 //!    events and spans via browser `console.log` and [User Timing API (`window.performance`)].
+//!  - [`tracing-web`] provides a layer implementation of level-aware logging of events
+//!    to web browsers' `console.*` and span events to the [User Timing API (`window.performance`)].
 //!  - [`tide-tracing`] provides a [tide] middleware to trace all incoming requests and responses.
 //!  - [`test-log`] takes care of initializing `tracing` for tests, based on
 //!    environment variables with an `env_logger` compatible syntax.
@@ -761,6 +763,7 @@
 //! [`tracing-bunyan-formatter`]: https://crates.io/crates/tracing-bunyan-formatter
 //! [bunyan]: https://github.com/trentm/node-bunyan
 //! [`tracing-wasm`]: https://docs.rs/tracing-wasm
+//! [`tracing-web`]: https://docs.rs/tracing-web
 //! [User Timing API (`window.performance`)]: https://developer.mozilla.org/en-US/docs/Web/API/User_Timing_API
 //! [`tide-tracing`]: https://crates.io/crates/tide-tracing
 //! [tide]: https://crates.io/crates/tide

From e42e322351cc9075754ce7bd4d484c59d6316506 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wojciech=20Przytu=C5=82a?=
 <59482568+wprzytula@users.noreply.github.com>
Date: Thu, 8 Sep 2022 00:55:18 +0200
Subject: [PATCH 094/142] opentelemetry: fix `on_event` not respecting events'
 explicit parents (#2296)

One can want to have an event added from outside context of a span,
especially in an async code (e.g. between polls of the future, or
between polling multiple futures instrumented with that span). Then it
is expected that the event will be attached indeed to the span specified
as the parent and not the contextual one.

Fixes: #2295

See #2295
---
 tracing-opentelemetry/src/layer.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs
index 94304bbce8..7624b613e3 100644
--- a/tracing-opentelemetry/src/layer.rs
+++ b/tracing-opentelemetry/src/layer.rs
@@ -805,8 +805,8 @@ where
     /// [`ERROR`]: tracing::Level::ERROR
     /// [`Error`]: opentelemetry::trace::StatusCode::Error
     fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) {
-        // Ignore events that are not in the context of a span
-        if let Some(span) = ctx.lookup_current() {
+        // Ignore events that have no explicit parent set *and* are not in the context of a span
+        if let Some(span) = ctx.event_span(event) {
             // Performing read operations before getting a write lock to avoid a deadlock
             // See https://github.com/tokio-rs/tracing/issues/763
             #[cfg(feature = "tracing-log")]

From ffad660134d786ee2410b5387e4b7ad9915aaf95 Mon Sep 17 00:00:00 2001
From: Xiami <1927254+Xiami2012@users.noreply.github.com>
Date: Sat, 17 Sep 2022 04:39:07 +0800
Subject: [PATCH 095/142] tracing: add `dyn` keyword for trait objects in
 `valueset!` expansion (#2308)

## Motivation

`bare_trait_objects` lint will become a hard error in Rust 2021.

While tracing is still using Rust 2018, `bare_trait_objects` is a
warning in the MSRV and `dyn` keyword is recommended.

## Solution

Replace all `&Value` with `&dyn Value` in macro expansions.
---
 tracing/src/macros.rs | 38 +++++++++++++++++++-------------------
 1 file changed, 19 insertions(+), 19 deletions(-)

diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index b134af6e81..f3968e5c11 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -2193,79 +2193,79 @@ macro_rules! valueset {
     // };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&debug(&$val) as &Value)) },
+            @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&display(&$val) as &Value)) },
+            @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&$val as &Value)) },
+            @ { $($out),*, (&$next, Some(&$val as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&$($k).+ as &Value)) },
+            @ { $($out),*, (&$next, Some(&$($k).+ as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&debug(&$($k).+) as &Value)) },
+            @ { $($out),*, (&$next, Some(&debug(&$($k).+) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&display(&$($k).+) as &Value)) },
+            @ { $($out),*, (&$next, Some(&display(&$($k).+) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&debug(&$val) as &Value)) },
+            @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&display(&$val) as &Value)) },
+            @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&$val as &Value)) },
+            @ { $($out),*, (&$next, Some(&$val as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&$($k).+ as &Value)) },
+            @ { $($out),*, (&$next, Some(&$($k).+ as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&debug(&$($k).+) as &Value)) },
+            @ { $($out),*, (&$next, Some(&debug(&$($k).+) as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&display(&$($k).+) as &Value)) },
+            @ { $($out),*, (&$next, Some(&display(&$($k).+) as &dyn Value)) },
             $next,
         )
     };
@@ -2273,47 +2273,47 @@ macro_rules! valueset {
     // Handle literal names
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&debug(&$val) as &Value)) },
+            @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&display(&$val) as &Value)) },
+            @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&$val as &Value)) },
+            @ { $($out),*, (&$next, Some(&$val as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&debug(&$val) as &Value)) },
+            @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&display(&$val) as &Value)) },
+            @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, Some(&$val as &Value)) },
+            @ { $($out),*, (&$next, Some(&$val as &dyn Value)) },
             $next,
         )
     };
 
     // Remainder is unparseable, but exists --- must be format args!
     (@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => {
-        $crate::valueset!(@ { (&$next, Some(&format_args!($($rest)+) as &Value)), $($out),* }, $next, )
+        $crate::valueset!(@ { (&$next, Some(&format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
     };
 
     // === entry ===

From a4f2ee2a12f3269dd19e9461d48fe2e6d32beba6 Mon Sep 17 00:00:00 2001
From: Julian Tescher 
Date: Mon, 19 Sep 2022 11:51:33 -0700
Subject: [PATCH 096/142] opentelemetry: prepare for v0.18.0 release (#2312)

### Breaking Changes

- Upgrade to `v0.18.0` of `opentelemetry` (#2303)
  For list of breaking changes in OpenTelemetry, see the
  [v0.18.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0180).

### Fixed

- `on_event` respects event's explicit parent (#2296)
---
 tracing-opentelemetry/CHANGELOG.md | 17 +++++++++++++++++
 tracing-opentelemetry/Cargo.toml   |  2 +-
 tracing-opentelemetry/README.md    |  4 ++--
 tracing-opentelemetry/src/lib.rs   |  2 +-
 4 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/tracing-opentelemetry/CHANGELOG.md b/tracing-opentelemetry/CHANGELOG.md
index 6fb43d55db..2f06fe9b60 100644
--- a/tracing-opentelemetry/CHANGELOG.md
+++ b/tracing-opentelemetry/CHANGELOG.md
@@ -1,3 +1,20 @@
+# 0.18.0 (September 18, 2022)
+
+### Breaking Changes
+
+- Upgrade to `v0.18.0` of `opentelemetry` ([#2303])
+  For list of breaking changes in OpenTelemetry, see the
+  [v0.18.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0180).
+
+### Fixed
+
+- `on_event` respects event's explicit parent ([#2296])
+
+Thanks to @wprzytula for contributing to this release!
+
+[#2303]: https://github.com/tokio-rs/tracing/pull/2303
+[#2296]: https://github.com/tokio-rs/tracing/pull/2296
+
 # 0.17.4 (July 1, 2022)
 
 This release adds optional support for recording `std::error::Error`s using
diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml
index b9142f1e4e..1ba06c8cd0 100644
--- a/tracing-opentelemetry/Cargo.toml
+++ b/tracing-opentelemetry/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "tracing-opentelemetry"
-version = "0.17.4"
+version = "0.18.0"
 authors = [
     "Julian Tescher ",
     "Tokio Contributors "
diff --git a/tracing-opentelemetry/README.md b/tracing-opentelemetry/README.md
index 9260d80636..4ccbb4a2aa 100644
--- a/tracing-opentelemetry/README.md
+++ b/tracing-opentelemetry/README.md
@@ -17,9 +17,9 @@ Utilities for adding [OpenTelemetry] interoperability to [`tracing`].
 [Documentation][docs-url] | [Chat][discord-url]
 
 [crates-badge]: https://img.shields.io/crates/v/tracing-opentelemetry.svg
-[crates-url]: https://crates.io/crates/tracing-opentelemetry/0.17.4
+[crates-url]: https://crates.io/crates/tracing-opentelemetry/0.18.0
 [docs-badge]: https://docs.rs/tracing-opentelemetry/badge.svg
-[docs-url]: https://docs.rs/tracing-opentelemetry/0.17.4/tracing_opentelemetry
+[docs-url]: https://docs.rs/tracing-opentelemetry/0.18.0/tracing_opentelemetry
 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue
 [docs-master-url]: https://tracing-rs.netlify.com/tracing_opentelemetry
 [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
diff --git a/tracing-opentelemetry/src/lib.rs b/tracing-opentelemetry/src/lib.rs
index d3bc5bbce3..9f5dd1cc83 100644
--- a/tracing-opentelemetry/src/lib.rs
+++ b/tracing-opentelemetry/src/lib.rs
@@ -100,7 +100,7 @@
 //! [subscriber]: tracing_subscriber::subscribe
 #![deny(unreachable_pub)]
 #![cfg_attr(test, deny(warnings))]
-#![doc(html_root_url = "https://docs.rs/tracing-opentelemetry/0.17.4")]
+#![doc(html_root_url = "https://docs.rs/tracing-opentelemetry/0.18.0")]
 #![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/"

From 640cd3f39053d6c3826567d7d12986ad213e79a1 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Fri, 23 Sep 2022 14:40:04 -0700
Subject: [PATCH 097/142] attributes: update trybuild output for Rust 1.64.0
 (#2322)

The error messages for these tests appear to have changed a bit with
Rust 1.64. This commit includes changes to the `.stderr` file after
running the tests with `TRYBUILD=overwrite`, in order to update the
expected output for the latest Rust.
---
 tracing-attributes/tests/ui/async_instrument.stderr | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/tracing-attributes/tests/ui/async_instrument.stderr b/tracing-attributes/tests/ui/async_instrument.stderr
index 5214f92a7e..db6f6b4343 100644
--- a/tracing-attributes/tests/ui/async_instrument.stderr
+++ b/tracing-attributes/tests/ui/async_instrument.stderr
@@ -34,6 +34,15 @@ error[E0277]: `(&str,)` doesn't implement `std::fmt::Display`
    = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
    = note: this error originates in the attribute macro `tracing::instrument` (in Nightly builds, run with -Z macro-backtrace for more info)
 
+error[E0277]: `(&str,)` doesn't implement `std::fmt::Display`
+  --> tests/ui/async_instrument.rs:15:34
+   |
+15 | async fn opaque_unsatisfied() -> impl std::fmt::Display {
+   |                                  ^^^^^^^^^^^^^^^^^^^^^^ `(&str,)` cannot be formatted with the default formatter
+   |
+   = help: the trait `std::fmt::Display` is not implemented for `(&str,)`
+   = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
+
 error[E0308]: mismatched types
   --> tests/ui/async_instrument.rs:23:5
    |

From b740179fc63a0638f5f4ea82c839000f101d1718 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Fri, 23 Sep 2022 14:42:01 -0700
Subject: [PATCH 098/142] attributes: suppress dead code warnings for compile
 tests (#2322)

The `async_fn` test file in `tracing-attributes` contains several
functions that exist just to test whether they _compile_, rather than
make assertions about their behavior. Because these functions are never
called, they (naturally) emit dead code warnings.

This commit adds `#[allow(dead_code)]` to the compilation tests, plus a
comment explaining why we do this.
---
 tracing-attributes/tests/async_fn.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs
index 7e27fb5ce2..9d0d61c8ff 100644
--- a/tracing-attributes/tests/async_fn.rs
+++ b/tracing-attributes/tests/async_fn.rs
@@ -14,6 +14,7 @@ async fn test_async_fn(polls: usize) -> Result<(), ()> {
 
 // Reproduces a compile error when returning an `impl Trait` from an
 // instrumented async fn (see https://github.com/tokio-rs/tracing/issues/1615)
+#[allow(dead_code)] // this is just here to test whether it compiles.
 #[instrument]
 async fn test_ret_impl_trait(n: i32) -> Result, ()> {
     let n = n;
@@ -22,6 +23,7 @@ async fn test_ret_impl_trait(n: i32) -> Result, ()> {
 
 // Reproduces a compile error when returning an `impl Trait` from an
 // instrumented async fn (see https://github.com/tokio-rs/tracing/issues/1615)
+#[allow(dead_code)] // this is just here to test whether it compiles.
 #[instrument(err)]
 async fn test_ret_impl_trait_err(n: i32) -> Result, &'static str> {
     Ok((0..10).filter(move |x| *x < n))
@@ -53,6 +55,7 @@ async fn repro_1613_2() {
 }
 
 // Reproduces https://github.com/tokio-rs/tracing/issues/1831
+#[allow(dead_code)] // this is just here to test whether it compiles.
 #[instrument]
 #[deny(unused_braces)]
 fn repro_1831() -> Pin>> {
@@ -61,6 +64,7 @@ fn repro_1831() -> Pin>> {
 
 // This replicates the pattern used to implement async trait methods on nightly using the
 // `type_alias_impl_trait` feature
+#[allow(dead_code)] // this is just here to test whether it compiles.
 #[instrument(ret, err)]
 #[deny(unused_braces)]
 #[allow(clippy::manual_async_fn)]

From 37c2434f214dc7cf6e526c306ded3263fe8a345b Mon Sep 17 00:00:00 2001
From: Jack Wrenn 
Date: Sat, 24 Sep 2022 16:18:03 -0400
Subject: [PATCH 099/142] core: add `Dispatch::downgrade()` and `WeakDispatch`
 (#2293)

Allows `Subscriber`s and `Layer`s to stash their own `Dispatch` without
causing a memory leak.

## Motivation

Resolves a shortcoming of #2269: that it's impossible for `Subscriber`s
or `Layer`s to stash a copy of their own `Dispatch` without creating a
reference cycle that would prevent them from being dropped.

## Solution

Introduces `WeakDispatch` (analogous to `std::sync::Weak`) that holds a
weak pointer to a `Subscriber`. `WeakDispatch` can be created via
`Dispatch::downgrade`, and can be transformed into a `Dispatch` via
`WeakDispatch::upgrade`.

Co-authored-by: Eliza Weisman 
---
 tracing-core/src/dispatcher.rs      | 102 +++++++++++++++++++++++++---
 tracing-core/src/subscriber.rs      |  18 +++++
 tracing-subscriber/src/layer/mod.rs |  25 +++++--
 tracing/src/dispatcher.rs           |   2 +-
 4 files changed, 133 insertions(+), 14 deletions(-)

diff --git a/tracing-core/src/dispatcher.rs b/tracing-core/src/dispatcher.rs
index be6b5c6224..36b3cfd85f 100644
--- a/tracing-core/src/dispatcher.rs
+++ b/tracing-core/src/dispatcher.rs
@@ -134,7 +134,7 @@ use crate::stdlib::{
     fmt,
     sync::{
         atomic::{AtomicBool, AtomicUsize, Ordering},
-        Arc,
+        Arc, Weak,
     },
 };
 
@@ -142,16 +142,50 @@ use crate::stdlib::{
 use crate::stdlib::{
     cell::{Cell, RefCell, RefMut},
     error,
-    sync::Weak,
 };
 
+#[cfg(feature = "alloc")]
+use alloc::sync::{Arc, Weak};
+
+#[cfg(feature = "alloc")]
+use core::ops::Deref;
+
 /// `Dispatch` trace data to a [`Subscriber`].
-///
 #[derive(Clone)]
 pub struct Dispatch {
     subscriber: Arc,
 }
 
+/// `WeakDispatch` is a version of [`Dispatch`] that holds a non-owning reference
+/// to a [`Subscriber`].
+///
+/// The Subscriber` may be accessed by calling [`WeakDispatch::upgrade`],
+/// which returns an `Option`. If all [`Dispatch`] clones that point
+/// at the `Subscriber` have been dropped, [`WeakDispatch::upgrade`] will return
+/// `None`. Otherwise, it will return `Some(Dispatch)`.
+///
+/// A `WeakDispatch` may be created from a [`Dispatch`] by calling the
+/// [`Dispatch::downgrade`] method. The primary use for creating a
+/// [`WeakDispatch`] is to allow a Subscriber` to hold a cyclical reference to
+/// itself without creating a memory leak. See [here] for details.
+///
+/// This type is analogous to the [`std::sync::Weak`] type, but for a
+/// [`Dispatch`] rather than an [`Arc`].
+///
+/// [`Arc`]: std::sync::Arc
+/// [here]: Subscriber#avoiding-memory-leaks
+#[derive(Clone)]
+pub struct WeakDispatch {
+    subscriber: Weak,
+}
+
+#[cfg(feature = "alloc")]
+#[derive(Clone)]
+enum Kind {
+    Global(&'static (dyn Collect + Send + Sync)),
+    Scoped(T),
+}
+
 #[cfg(feature = "std")]
 thread_local! {
     static CURRENT_STATE: State = State {
@@ -430,12 +464,23 @@ impl Dispatch {
         Registrar(Arc::downgrade(&self.subscriber))
     }
 
-    #[inline(always)]
-    #[cfg(feature = "alloc")]
-    pub(crate) fn subscriber(&self) -> &(dyn Subscriber + Send + Sync) {
-        match self.subscriber {
-            Kind::Scoped(ref s) => Arc::deref(s),
-            Kind::Global(s) => s,
+    /// Creates a [`WeakDispatch`] from this `Dispatch`.
+    ///
+    /// A [`WeakDispatch`] is similar to a [`Dispatch`], but it does not prevent
+    /// the underlying [`Subscriber`] from being dropped. Instead, it only permits
+    /// access while other references to the `Subscriber` exist. This is equivalent
+    /// to the standard library's [`Arc::downgrade`] method, but for `Dispatch`
+    /// rather than `Arc`.
+    ///
+    /// The primary use for creating a [`WeakDispatch`] is to allow a `Subscriber`
+    /// to hold a cyclical reference to itself without creating a memory leak.
+    /// See [here] for details.
+    ///
+    /// [`Arc::downgrade`]: std::sync::Arc::downgrade
+    /// [here]: Subscriber#avoiding-memory-leaks
+    pub fn downgrade(&self) -> WeakDispatch {
+        WeakDispatch {
+            subscriber: Arc::downgrade(&self.subscriber),
         }
     }
 
@@ -682,6 +727,45 @@ where
     }
 }
 
+// === impl WeakDispatch ===
+
+impl WeakDispatch {
+    /// Attempts to upgrade this `WeakDispatch` to a [`Dispatch`].
+    ///
+    /// Returns `None` if the referenced `Dispatch` has already been dropped.
+    ///
+    /// ## Examples
+    ///
+    /// ```
+    /// # use tracing_core::subscriber::NoSubscriber;
+    /// # use tracing_core::dispatcher::Dispatch;
+    /// let strong = Dispatch::new(NoSubscriber::default());
+    /// let weak = strong.downgrade();
+    ///
+    /// // The strong here keeps it alive, so we can still access the object.
+    /// assert!(weak.upgrade().is_some());
+    ///
+    /// drop(strong); // But not any more.
+    /// assert!(weak.upgrade().is_none());
+    /// ```
+    pub fn upgrade(&self) -> Option {
+        self.subscriber
+            .upgrade()
+            .map(|subscriber| Dispatch { subscriber })
+    }
+}
+
+impl fmt::Debug for WeakDispatch {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut tuple = f.debug_tuple("WeakDispatch");
+        match self.subscriber.upgrade() {
+            Some(subscriber) => tuple.field(&format_args!("Some({:p})", subscriber)),
+            None => tuple.field(&format_args!("None")),
+        };
+        tuple.finish()
+    }
+}
+
 #[cfg(feature = "std")]
 impl Registrar {
     pub(crate) fn upgrade(&self) -> Option {
diff --git a/tracing-core/src/subscriber.rs b/tracing-core/src/subscriber.rs
index d628975824..e8f4441196 100644
--- a/tracing-core/src/subscriber.rs
+++ b/tracing-core/src/subscriber.rs
@@ -82,6 +82,24 @@ use crate::stdlib::{
 /// [`event_enabled`]: Subscriber::event_enabled
 pub trait Subscriber: 'static {
     /// Invoked when this subscriber becomes a [`Dispatch`].
+    ///
+    /// ## Avoiding Memory Leaks
+    ///
+    /// `Subscriber`s should not store their own [`Dispatch`]. Because the
+    /// `Dispatch` owns the `Subscriber`, storing the `Dispatch` within the
+    /// `Subscriber` will create a reference count cycle, preventing the `Dispatch`
+    /// from ever being dropped.
+    ///
+    /// Instead, when it is necessary to store a cyclical reference to the
+    /// `Dispatch` within a `Subscriber`, use [`Dispatch::downgrade`] to convert a
+    /// `Dispatch` into a [`WeakDispatch`]. This type is analogous to
+    /// [`std::sync::Weak`], and does not create a reference count cycle. A
+    /// [`WeakDispatch`] can be stored within a `Subscriber` without causing a
+    /// memory leak, and can be [upgraded] into a `Dispatch` temporarily when
+    /// the `Dispatch` must be accessed by the `Subscriber`.
+    ///
+    /// [`WeakDispatch`]: crate::dispatcher::WeakDispatch
+    /// [upgraded]: crate::dispatcher::WeakDispatch::upgrade
     fn on_register_dispatch(&self, subscriber: &Dispatch) {
         let _ = subscriber;
     }
diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs
index 3283a5ef5f..0b4992ff27 100644
--- a/tracing-subscriber/src/layer/mod.rs
+++ b/tracing-subscriber/src/layer/mod.rs
@@ -712,11 +712,28 @@ where
     Self: 'static,
 {
     /// Performs late initialization when installing this layer as a
-    /// [subscriber].
+    /// [`Subscriber`].
     ///
-    /// [subscriber]: tracing_core::Subscriber
-    fn on_register_dispatch(&self, subscriber: &Dispatch) {
-        let _ = subscriber;
+    /// ## Avoiding Memory Leaks
+    ///
+    /// `Layer`s should not store the [`Dispatch`] pointing to the [`Subscriber`]
+    /// that they are a part of. Because the `Dispatch` owns the `Subscriber`,
+    /// storing the `Dispatch` within the `Subscriber` will create a reference
+    /// count cycle, preventing the `Dispatch` from ever being dropped.
+    ///
+    /// Instead, when it is necessary to store a cyclical reference to the
+    /// `Dispatch` within a `Layer`, use [`Dispatch::downgrade`] to convert a
+    /// `Dispatch` into a [`WeakDispatch`]. This type is analogous to
+    /// [`std::sync::Weak`], and does not create a reference count cycle. A
+    /// [`WeakDispatch`] can be stored within a subscriber without causing a
+    /// memory leak, and can be [upgraded] into a `Dispatch` temporarily when
+    /// the `Dispatch` must be accessed by the subscriber.
+    ///
+    /// [`WeakDispatch`]: tracing_core::dispatcher::WeakDispatch
+    /// [upgraded]: tracing_core::dispatcher::WeakDispatch::upgrade
+    /// [`Subscriber`]: tracing_core::Subscriber
+    fn on_register_dispatch(&self, collector: &Dispatch) {
+        let _ = collector;
     }
 
     /// Performs late initialization when attaching a `Layer` to a
diff --git a/tracing/src/dispatcher.rs b/tracing/src/dispatcher.rs
index 8817ac033f..a84b99f4eb 100644
--- a/tracing/src/dispatcher.rs
+++ b/tracing/src/dispatcher.rs
@@ -133,7 +133,7 @@ pub use tracing_core::dispatcher::with_default;
 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
 pub use tracing_core::dispatcher::DefaultGuard;
 pub use tracing_core::dispatcher::{
-    get_default, set_global_default, Dispatch, SetGlobalDefaultError,
+    get_default, set_global_default, Dispatch, SetGlobalDefaultError, WeakDispatch,
 };
 
 /// Private API for internal use by tracing's macros.

From c542120055d247950579a99963a91faba1af78b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=96zg=C3=BCn=20=C3=96zerk?= 
Date: Sat, 24 Sep 2022 23:57:01 +0300
Subject: [PATCH 100/142] appender: add option to automatically delete old log
 files (#2323)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

## Motivation

`tracing-appender` does not have `Rotation` based on size yet. Also, it
doesn't have the feature of keeping the most recent `N` log files

I believe the second feature is more easy to implement, and also will
partially solve the `Rotation` based on size problem. Because people may
choose `hourly` or `daily` rotation based on their needs, and put an
extra boundary of `keep the last 5 files` for example. Of course it
won't handle all the edge cases for `Rotation` based on size. But it
will cover most of the scenarios. And also, it is a good feature to have
on its own :)

## Solution

Introduce another field called `max_files: Option` to the `Inner`
of `RollingFileAppender` struct. I managed to did not touch any of the
existing functions, so it **WON'T BE A BREAKING CHANGE**. Yay :)

The solution is, whenever the rotation should happen, the
`refresh_writer()` is called. So I embed the following logic into that
function:

1- check the log folder and detect the log files 2- if there are more
log files than the `max_files` amount 3- store the filenames in a
vector, and sort them by their dates (dates are already present in the
filename) 4- keep deleting the oldest ones, till we have desired amount
of log files in the log folder

P.S. this PR was opened before, but got closed since it would be easier
for the maintainers to target `master` branch instead of `v0.1.x` Also,
@CBenoit contributed to this PR, it would be great to give credit to him
:)

Co-authored-by: Benoît Cortier 
Co-authored-by: Eliza Weisman 
---
 tracing-appender/Cargo.toml             |   4 +-
 tracing-appender/src/rolling.rs         | 389 +++++++++++++++++++-----
 tracing-appender/src/rolling/builder.rs |  52 ++++
 3 files changed, 368 insertions(+), 77 deletions(-)

diff --git a/tracing-appender/Cargo.toml b/tracing-appender/Cargo.toml
index c7716a2564..97d1e7d0ca 100644
--- a/tracing-appender/Cargo.toml
+++ b/tracing-appender/Cargo.toml
@@ -22,7 +22,7 @@ rust-version = "1.53.0"
 
 [dependencies]
 crossbeam-channel = "0.5.6"
-time = { version = "0.3.2", default-features = false, features = ["formatting"] }
+time = { version = "0.3.2", default-features = false, features = ["formatting", "parsing"] }
 parking_lot = { optional = true, version = "0.12.1" }
 thiserror = "1"
 
@@ -33,10 +33,8 @@ default-features = false
 features = ["fmt", "std"]
 
 [dev-dependencies]
-
 criterion = { version = "0.3.6", default-features = false }
 tracing = { path = "../tracing", version = "0.1.35" }
-time = { version = "0.3.2", default-features = false, features = ["formatting", "parsing"] }
 tempfile = "3"
 
 [[bench]]
diff --git a/tracing-appender/src/rolling.rs b/tracing-appender/src/rolling.rs
index 8e9597c29a..8e630cc9e1 100644
--- a/tracing-appender/src/rolling.rs
+++ b/tracing-appender/src/rolling.rs
@@ -34,7 +34,7 @@ use std::{
     path::{Path, PathBuf},
     sync::atomic::{AtomicUsize, Ordering},
 };
-use time::{format_description, Duration, OffsetDateTime, Time};
+use time::{format_description, Date, Duration, OffsetDateTime, Time};
 
 mod builder;
 pub use builder::{Builder, InitError};
@@ -104,8 +104,10 @@ struct Inner {
     log_directory: PathBuf,
     log_filename_prefix: Option,
     log_filename_suffix: Option,
+    date_format: Vec>,
     rotation: Rotation,
     next_date: AtomicUsize,
+    max_files: Option,
 }
 
 // === impl RollingFileAppender ===
@@ -187,6 +189,7 @@ impl RollingFileAppender {
             ref rotation,
             ref prefix,
             ref suffix,
+            ref max_files,
         } = builder;
         let directory = directory.as_ref().to_path_buf();
         let now = OffsetDateTime::now_utc();
@@ -196,6 +199,7 @@ impl RollingFileAppender {
             directory,
             prefix.clone(),
             suffix.clone(),
+            *max_files,
         )?;
         Ok(Self {
             state,
@@ -242,7 +246,7 @@ impl<'a> tracing_subscriber::fmt::writer::MakeWriter<'a> for RollingFileAppender
             // Did we get the right to lock the file? If not, another thread
             // did it and we can just make a writer.
             if self.state.advance_date(now, current_time) {
-                self.state.refresh_writer(now, &mut *self.writer.write());
+                self.state.refresh_writer(now, &mut self.writer.write());
             }
         }
         RollingWriter(self.writer.read())
@@ -488,32 +492,14 @@ impl Rotation {
         }
     }
 
-    pub(crate) fn join_date(
-        &self,
-        filename: Option<&str>,
-        date: &OffsetDateTime,
-        suffix: Option<&str>,
-    ) -> String {
-        let format = match *self {
+    fn date_format(&self) -> Vec> {
+        match *self {
             Rotation::MINUTELY => format_description::parse("[year]-[month]-[day]-[hour]-[minute]"),
             Rotation::HOURLY => format_description::parse("[year]-[month]-[day]-[hour]"),
             Rotation::DAILY => format_description::parse("[year]-[month]-[day]"),
             Rotation::NEVER => format_description::parse("[year]-[month]-[day]"),
         }
-        .expect("Unable to create a formatter; this is a bug in tracing-appender");
-        let date = date
-            .format(&format)
-            .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender");
-
-        match (self, filename, suffix) {
-            (&Rotation::NEVER, Some(filename), None) => filename.to_string(),
-            (&Rotation::NEVER, Some(filename), Some(suffix)) => format!("{}.{}", filename, suffix),
-            (&Rotation::NEVER, None, Some(suffix)) => suffix.to_string(),
-            (_, Some(filename), Some(suffix)) => format!("{}.{}.{}", filename, date, suffix),
-            (_, Some(filename), None) => format!("{}.{}", filename, date),
-            (_, None, Some(suffix)) => format!("{}.{}", date, suffix),
-            (_, None, None) => date,
-        }
+        .expect("Unable to create a formatter; this is a bug in tracing-appender")
     }
 }
 
@@ -538,36 +524,122 @@ impl Inner {
         directory: impl AsRef,
         log_filename_prefix: Option,
         log_filename_suffix: Option,
+        max_files: Option,
     ) -> Result<(Self, RwLock), builder::InitError> {
         let log_directory = directory.as_ref().to_path_buf();
-        let filename = rotation.join_date(
-            log_filename_prefix.as_deref(),
-            &now,
-            log_filename_suffix.as_deref(),
-        );
+        let date_format = rotation.date_format();
         let next_date = rotation.next_date(&now);
-        let writer = RwLock::new(create_writer(log_directory.as_ref(), &filename)?);
 
         let inner = Inner {
             log_directory,
             log_filename_prefix,
             log_filename_suffix,
+            date_format,
             next_date: AtomicUsize::new(
                 next_date
                     .map(|date| date.unix_timestamp() as usize)
                     .unwrap_or(0),
             ),
             rotation,
+            max_files,
         };
+        let filename = inner.join_date(&now);
+        let writer = RwLock::new(create_writer(inner.log_directory.as_ref(), &filename)?);
         Ok((inner, writer))
     }
 
+    pub(crate) fn join_date(&self, date: &OffsetDateTime) -> String {
+        let date = date
+            .format(&self.date_format)
+            .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender");
+
+        match (
+            &self.rotation,
+            &self.log_filename_prefix,
+            &self.log_filename_suffix,
+        ) {
+            (&Rotation::NEVER, Some(filename), None) => filename.to_string(),
+            (&Rotation::NEVER, Some(filename), Some(suffix)) => format!("{}.{}", filename, suffix),
+            (&Rotation::NEVER, None, Some(suffix)) => suffix.to_string(),
+            (_, Some(filename), Some(suffix)) => format!("{}.{}.{}", filename, date, suffix),
+            (_, Some(filename), None) => format!("{}.{}", filename, date),
+            (_, None, Some(suffix)) => format!("{}.{}", date, suffix),
+            (_, None, None) => date,
+        }
+    }
+
+    fn prune_old_logs(&self, max_files: usize) {
+        let files = fs::read_dir(&self.log_directory).map(|dir| {
+            dir.filter_map(|entry| {
+                let entry = entry.ok()?;
+                let metadata = entry.metadata().ok()?;
+
+                // the appender only creates files, not directories or symlinks,
+                // so we should never delete a dir or symlink.
+                if !metadata.is_file() {
+                    return None;
+                }
+
+                let filename = entry.file_name();
+                // if the filename is not a UTF-8 string, skip it.
+                let filename = filename.to_str()?;
+                if let Some(prefix) = &self.log_filename_prefix {
+                    if !filename.starts_with(prefix) {
+                        return None;
+                    }
+                }
+
+                if let Some(suffix) = &self.log_filename_suffix {
+                    if !filename.ends_with(suffix) {
+                        return None;
+                    }
+                }
+
+                if self.log_filename_prefix.is_none()
+                    && self.log_filename_suffix.is_none()
+                    && Date::parse(filename, &self.date_format).is_err()
+                {
+                    return None;
+                }
+
+                let created = metadata.created().ok()?;
+                Some((entry, created))
+            })
+            .collect::>()
+        });
+
+        let mut files = match files {
+            Ok(files) => files,
+            Err(error) => {
+                eprintln!("Error reading the log directory/files: {}", error);
+                return;
+            }
+        };
+        if files.len() < max_files {
+            return;
+        }
+
+        // sort the files by their creation timestamps.
+        files.sort_by_key(|(_, created_at)| *created_at);
+
+        // delete files, so that (n-1) files remain, because we will create another log file
+        for (file, _) in files.iter().take(files.len() - (max_files - 1)) {
+            if let Err(error) = fs::remove_file(file.path()) {
+                eprintln!(
+                    "Failed to remove old log file {}: {}",
+                    file.path().display(),
+                    error
+                );
+            }
+        }
+    }
+
     fn refresh_writer(&self, now: OffsetDateTime, file: &mut File) {
-        let filename = self.rotation.join_date(
-            self.log_filename_prefix.as_deref(),
-            &now,
-            self.log_filename_suffix.as_deref(),
-        );
+        let filename = self.join_date(&now);
+
+        if let Some(max_files) = self.max_files {
+            self.prune_old_logs(max_files);
+        }
 
         match create_writer(&self.log_directory, &filename) {
             Ok(new_file) => {
@@ -727,66 +799,212 @@ mod test {
     }
 
     #[test]
-    fn test_path_concatination() {
+    fn test_path_concatenation() {
         let format = format_description::parse(
             "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \
          sign:mandatory]:[offset_minute]:[offset_second]",
         )
         .unwrap();
+        let directory = tempfile::tempdir().expect("failed to create tempdir");
 
         let now = OffsetDateTime::parse("2020-02-01 10:01:00 +00:00:00", &format).unwrap();
 
-        // per-minute
-        let path = Rotation::MINUTELY.join_date(Some("app.log"), &now, None);
-        assert_eq!("app.log.2020-02-01-10-01", path);
+        struct TestCase {
+            expected: &'static str,
+            rotation: Rotation,
+            prefix: Option<&'static str>,
+            suffix: Option<&'static str>,
+        }
 
-        // per-hour
-        let path = Rotation::HOURLY.join_date(Some("app.log"), &now, None);
-        assert_eq!("app.log.2020-02-01-10", path);
+        let test = |TestCase {
+                        expected,
+                        rotation,
+                        prefix,
+                        suffix,
+                    }| {
+            let (inner, _) = Inner::new(
+                now,
+                rotation.clone(),
+                directory.path(),
+                prefix.map(ToString::to_string),
+                suffix.map(ToString::to_string),
+                None,
+            )
+            .unwrap();
+            let path = inner.join_date(&now);
+            assert_eq!(
+                expected, path,
+                "rotation = {:?}, prefix = {:?}, suffix = {:?}",
+                rotation, prefix, suffix
+            );
+        };
 
-        // per-day
-        let path = Rotation::DAILY.join_date(Some("app.log"), &now, None);
-        assert_eq!("app.log.2020-02-01", path);
+        let test_cases = vec![
+            // prefix only
+            TestCase {
+                expected: "app.log.2020-02-01-10-01",
+                rotation: Rotation::MINUTELY,
+                prefix: Some("app.log"),
+                suffix: None,
+            },
+            TestCase {
+                expected: "app.log.2020-02-01-10",
+                rotation: Rotation::HOURLY,
+                prefix: Some("app.log"),
+                suffix: None,
+            },
+            TestCase {
+                expected: "app.log.2020-02-01",
+                rotation: Rotation::DAILY,
+                prefix: Some("app.log"),
+                suffix: None,
+            },
+            TestCase {
+                expected: "app.log",
+                rotation: Rotation::NEVER,
+                prefix: Some("app.log"),
+                suffix: None,
+            },
+            // prefix and suffix
+            TestCase {
+                expected: "app.2020-02-01-10-01.log",
+                rotation: Rotation::MINUTELY,
+                prefix: Some("app"),
+                suffix: Some("log"),
+            },
+            TestCase {
+                expected: "app.2020-02-01-10.log",
+                rotation: Rotation::HOURLY,
+                prefix: Some("app"),
+                suffix: Some("log"),
+            },
+            TestCase {
+                expected: "app.2020-02-01.log",
+                rotation: Rotation::DAILY,
+                prefix: Some("app"),
+                suffix: Some("log"),
+            },
+            TestCase {
+                expected: "app.log",
+                rotation: Rotation::NEVER,
+                prefix: Some("app"),
+                suffix: Some("log"),
+            },
+            // suffix only
+            TestCase {
+                expected: "2020-02-01-10-01.log",
+                rotation: Rotation::MINUTELY,
+                prefix: None,
+                suffix: Some("log"),
+            },
+            TestCase {
+                expected: "2020-02-01-10.log",
+                rotation: Rotation::HOURLY,
+                prefix: None,
+                suffix: Some("log"),
+            },
+            TestCase {
+                expected: "2020-02-01.log",
+                rotation: Rotation::DAILY,
+                prefix: None,
+                suffix: Some("log"),
+            },
+            TestCase {
+                expected: "log",
+                rotation: Rotation::NEVER,
+                prefix: None,
+                suffix: Some("log"),
+            },
+        ];
+        for test_case in test_cases {
+            test(test_case)
+        }
+    }
 
-        // never
-        let path = Rotation::NEVER.join_date(Some("app.log"), &now, None);
-        assert_eq!("app.log", path);
+    #[test]
+    fn test_make_writer() {
+        use std::sync::{Arc, Mutex};
+        use tracing_subscriber::prelude::*;
 
-        // per-minute with suffix
-        let path = Rotation::MINUTELY.join_date(Some("app"), &now, Some("log"));
-        assert_eq!("app.2020-02-01-10-01.log", path);
+        let format = format_description::parse(
+            "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \
+         sign:mandatory]:[offset_minute]:[offset_second]",
+        )
+        .unwrap();
 
-        // per-hour with suffix
-        let path = Rotation::HOURLY.join_date(Some("app"), &now, Some("log"));
-        assert_eq!("app.2020-02-01-10.log", path);
+        let now = OffsetDateTime::parse("2020-02-01 10:01:00 +00:00:00", &format).unwrap();
+        let directory = tempfile::tempdir().expect("failed to create tempdir");
+        let (state, writer) = Inner::new(
+            now,
+            Rotation::HOURLY,
+            directory.path(),
+            Some("test_make_writer".to_string()),
+            None,
+            None,
+        )
+        .unwrap();
+
+        let clock = Arc::new(Mutex::new(now));
+        let now = {
+            let clock = clock.clone();
+            Box::new(move || *clock.lock().unwrap())
+        };
+        let appender = RollingFileAppender { state, writer, now };
+        let default = tracing_subscriber::fmt()
+            .without_time()
+            .with_level(false)
+            .with_target(false)
+            .with_max_level(tracing_subscriber::filter::LevelFilter::TRACE)
+            .with_writer(appender)
+            .finish()
+            .set_default();
+
+        tracing::info!("file 1");
 
-        // per-day with suffix
-        let path = Rotation::DAILY.join_date(Some("app"), &now, Some("log"));
-        assert_eq!("app.2020-02-01.log", path);
+        // advance time by one second
+        (*clock.lock().unwrap()) += Duration::seconds(1);
 
-        // never with suffix
-        let path = Rotation::NEVER.join_date(Some("app"), &now, Some("log"));
-        assert_eq!("app.log", path);
+        tracing::info!("file 1");
 
-        // per-minute without prefix
-        let path = Rotation::MINUTELY.join_date(None, &now, Some("app.log"));
-        assert_eq!("2020-02-01-10-01.app.log", path);
+        // advance time by one hour
+        (*clock.lock().unwrap()) += Duration::hours(1);
 
-        // per-hour without prefix
-        let path = Rotation::HOURLY.join_date(None, &now, Some("app.log"));
-        assert_eq!("2020-02-01-10.app.log", path);
+        tracing::info!("file 2");
 
-        // per-day without prefix
-        let path = Rotation::DAILY.join_date(None, &now, Some("app.log"));
-        assert_eq!("2020-02-01.app.log", path);
+        // advance time by one second
+        (*clock.lock().unwrap()) += Duration::seconds(1);
+
+        tracing::info!("file 2");
+
+        drop(default);
+
+        let dir_contents = fs::read_dir(directory.path()).expect("Failed to read directory");
+        println!("dir={:?}", dir_contents);
+        for entry in dir_contents {
+            println!("entry={:?}", entry);
+            let path = entry.expect("Expected dir entry").path();
+            let file = fs::read_to_string(&path).expect("Failed to read file");
+            println!("path={}\nfile={:?}", path.display(), file);
 
-        // never without prefix
-        let path = Rotation::NEVER.join_date(None, &now, Some("app.log"));
-        assert_eq!("app.log", path);
+            match path
+                .extension()
+                .expect("found a file without a date!")
+                .to_str()
+                .expect("extension should be UTF8")
+            {
+                "2020-02-01-10" => {
+                    assert_eq!("file 1\nfile 1\n", file);
+                }
+                "2020-02-01-11" => {
+                    assert_eq!("file 2\nfile 2\n", file);
+                }
+                x => panic!("unexpected date {}", x),
+            }
+        }
     }
 
     #[test]
-    fn test_make_writer() {
+    fn test_max_log_files() {
         use std::sync::{Arc, Mutex};
         use tracing_subscriber::prelude::*;
 
@@ -802,8 +1020,9 @@ mod test {
             now,
             Rotation::HOURLY,
             directory.path(),
-            Some("test_make_writer".to_string()),
+            Some("test_max_log_files".to_string()),
             None,
+            Some(2),
         )
         .unwrap();
 
@@ -832,6 +1051,11 @@ mod test {
         // advance time by one hour
         (*clock.lock().unwrap()) += Duration::hours(1);
 
+        // depending on the filesystem, the creation timestamp's resolution may
+        // be as coarse as one second, so we need to wait a bit here to ensure
+        // that the next file actually is newer than the old one.
+        std::thread::sleep(std::time::Duration::from_secs(1));
+
         tracing::info!("file 2");
 
         // advance time by one second
@@ -839,10 +1063,24 @@ mod test {
 
         tracing::info!("file 2");
 
+        // advance time by one hour
+        (*clock.lock().unwrap()) += Duration::hours(1);
+
+        // again, sleep to ensure that the creation timestamps actually differ.
+        std::thread::sleep(std::time::Duration::from_secs(1));
+
+        tracing::info!("file 3");
+
+        // advance time by one second
+        (*clock.lock().unwrap()) += Duration::seconds(1);
+
+        tracing::info!("file 3");
+
         drop(default);
 
         let dir_contents = fs::read_dir(directory.path()).expect("Failed to read directory");
         println!("dir={:?}", dir_contents);
+
         for entry in dir_contents {
             println!("entry={:?}", entry);
             let path = entry.expect("Expected dir entry").path();
@@ -856,11 +1094,14 @@ mod test {
                 .expect("extension should be UTF8")
             {
                 "2020-02-01-10" => {
-                    assert_eq!("file 1\nfile 1\n", file);
+                    panic!("this file should have been pruned already!");
                 }
                 "2020-02-01-11" => {
                     assert_eq!("file 2\nfile 2\n", file);
                 }
+                "2020-02-01-12" => {
+                    assert_eq!("file 3\nfile 3\n", file);
+                }
                 x => panic!("unexpected date {}", x),
             }
         }
diff --git a/tracing-appender/src/rolling/builder.rs b/tracing-appender/src/rolling/builder.rs
index ea5d39cd5f..8c92ca1238 100644
--- a/tracing-appender/src/rolling/builder.rs
+++ b/tracing-appender/src/rolling/builder.rs
@@ -10,6 +10,7 @@ pub struct Builder {
     pub(super) rotation: Rotation,
     pub(super) prefix: Option,
     pub(super) suffix: Option,
+    pub(super) max_files: Option,
 }
 
 /// Errors returned by [`Builder::build`].
@@ -39,15 +40,20 @@ impl Builder {
     /// | :-------- | :------------ | :---- |
     /// | [`rotation`] | [`Rotation::NEVER`] | By default, log files will never be rotated. |
     /// | [`filename_prefix`] | `""` | By default, log file names will not have a prefix. |
+    /// | [`filename_suffix`] | `""` | By default, log file names will not have a suffix. |
+    /// | [`max_log_files`] | `None` | By default, there is no limit for maximum log file count. |
     ///
     /// [`rotation`]: Self::rotation
     /// [`filename_prefix`]: Self::filename_prefix
+    /// [`filename_suffix`]: Self::filename_suffix
+    /// [`max_log_files`]: Self::max_log_files
     #[must_use]
     pub const fn new() -> Self {
         Self {
             rotation: Rotation::NEVER,
             prefix: None,
             suffix: None,
+            max_files: None,
         }
     }
 
@@ -181,6 +187,52 @@ impl Builder {
         Self { suffix, ..self }
     }
 
+    /// Keeps the last `n` log files on disk.
+    ///
+    /// When a new log file is created, if there are `n` or more
+    /// existing log files in the directory, the oldest will be deleted.
+    /// If no value is supplied, the `RollingAppender` will not remove any files.
+    ///
+    /// Files are considered candidates for deletion based on the following
+    /// criteria:
+    ///
+    /// * The file must not be a directory or symbolic link.
+    /// * If the appender is configured with a [`filename_prefix`], the file
+    ///   name must start with that prefix.
+    /// * If the appender is configured with a [`filename_suffix`], the file
+    ///   name must end with that suffix.
+    /// * If the appender has neither a filename prefix nor a suffix, then the
+    ///   file name must parse as a valid date based on the appender's date
+    ///   format.
+    ///
+    /// Files matching these criteria may be deleted if the maximum number of
+    /// log files in the directory has been reached.
+    ///
+    /// [`filename_prefix`]: Self::filename_prefix
+    /// [`filename_suffix`]: Self::filename_suffix
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use tracing_appender::rolling::RollingFileAppender;
+    ///
+    /// # fn docs() {
+    /// let appender = RollingFileAppender::builder()
+    ///     .max_log_files(5) // only the most recent 5 log files will be kept
+    ///     // ...
+    ///     .build("/var/log")
+    ///     .expect("failed to initialize rolling file appender");
+    /// # drop(appender)
+    /// # }
+    /// ```
+    #[must_use]
+    pub fn max_log_files(self, n: usize) -> Self {
+        Self {
+            max_files: Some(n),
+            ..self
+        }
+    }
+
     /// Builds a new [`RollingFileAppender`] with the configured parameters,
     /// emitting log files to the provided directory.
     ///

From 130c499b7c7db95da05b00b034224392ac6bdce5 Mon Sep 17 00:00:00 2001
From: Gus Wynn 
Date: Tue, 27 Sep 2022 10:52:26 -0700
Subject: [PATCH 101/142] subscriber: fix `None` layers setting the max level
 to `OFF` (#2321)

## Motivation

Currently, when using the `Layer` impl for `Option>`, the
`Layer::max_level_hint` returns  `Some(LevelFilter::OFF)`. This was
intended to allow totally disabling output in the case where a
`Subscriber` is composed entirely of `None` `Layer`s. However, when
other `Layer`s *do* exist but return `None` from their `max_level_hint`
implementations to indicate that they don't care what the max level is,
the presence of a single `None` layer will globally disable everything,
which is not the wanted behavior.

Fixes #2265

## Solution

This branch introduces a special downcast marker that can be used to
detect when a `Layer` in a `Layered` is `None`. This allows the
`pick_level_hint` method to short-circuit when a `Layer` implementation
which is `None` returns `Some(LevelFilter::OFF)` from its
`max_level_hint` if the other half of the `Layered` is not `None`. The
tests I added should be pretty thorough!

Additionally, the downcast marker is special-cased in the `reload`
`Layer`. Normally, this `Layer` doesn't support downcasting, but it can
in the case of the special marker value.

Co-authored-by: Eliza Weisman 
---
 tracing-subscriber/src/layer/layered.rs |  38 +++-
 tracing-subscriber/src/layer/mod.rs     |  43 +++++
 tracing-subscriber/src/reload.rs        |  20 +++
 tracing-subscriber/tests/option.rs      | 227 +++++++++++++++++++++++-
 4 files changed, 323 insertions(+), 5 deletions(-)

diff --git a/tracing-subscriber/src/layer/layered.rs b/tracing-subscriber/src/layer/layered.rs
index 91e475f631..f09c58c97c 100644
--- a/tracing-subscriber/src/layer/layered.rs
+++ b/tracing-subscriber/src/layer/layered.rs
@@ -115,7 +115,11 @@ where
     }
 
     fn max_level_hint(&self) -> Option {
-        self.pick_level_hint(self.layer.max_level_hint(), self.inner.max_level_hint())
+        self.pick_level_hint(
+            self.layer.max_level_hint(),
+            self.inner.max_level_hint(),
+            super::subscriber_is_none(&self.inner),
+        )
     }
 
     fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
@@ -267,7 +271,11 @@ where
     }
 
     fn max_level_hint(&self) -> Option {
-        self.pick_level_hint(self.layer.max_level_hint(), self.inner.max_level_hint())
+        self.pick_level_hint(
+            self.layer.max_level_hint(),
+            self.inner.max_level_hint(),
+            super::layer_is_none(&self.inner),
+        )
     }
 
     #[inline]
@@ -470,6 +478,7 @@ where
         &self,
         outer_hint: Option,
         inner_hint: Option,
+        inner_is_none: bool,
     ) -> Option {
         if self.inner_is_registry {
             return outer_hint;
@@ -487,6 +496,31 @@ where
             return None;
         }
 
+        // If the layer is `Option::None`, then we
+        // want to short-circuit the layer underneath, if it
+        // returns `None`, to override the `None` layer returning
+        // `Some(OFF)`, which should ONLY apply when there are
+        // no other layers that return `None`. Note this
+        // `None` does not == `Some(TRACE)`, it means
+        // something more like: "whatever all the other
+        // layers agree on, default to `TRACE` if none
+        // have an opinion". We also choose do this AFTER
+        // we check for per-layer filters, which
+        // have their own logic.
+        //
+        // Also note that this does come at some perf cost, but
+        // this function is only called on initialization and
+        // subscriber reloading.
+        if super::layer_is_none(&self.layer) {
+            return cmp::max(outer_hint, Some(inner_hint?));
+        }
+
+        // Similarly, if the layer on the inside is `None` and it returned an
+        // `Off` hint, we want to override that with the outer hint.
+        if inner_is_none && inner_hint == Some(LevelFilter::OFF) {
+            return outer_hint;
+        }
+
         cmp::max(outer_hint, inner_hint)
     }
 }
diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs
index 0b4992ff27..b4533771c5 100644
--- a/tracing-subscriber/src/layer/mod.rs
+++ b/tracing-subscriber/src/layer/mod.rs
@@ -1497,6 +1497,47 @@ pub struct Identity {
 
 // === impl Layer ===
 
+#[derive(Clone, Copy)]
+pub(crate) struct NoneLayerMarker(());
+static NONE_LAYER_MARKER: NoneLayerMarker = NoneLayerMarker(());
+
+/// Is a type implementing `Layer` `Option::<_>::None`?
+pub(crate) fn layer_is_none(layer: &L) -> bool
+where
+    L: Layer,
+    S: Subscriber,
+{
+    unsafe {
+        // Safety: we're not actually *doing* anything with this pointer ---
+        // this only care about the `Option`, which is essentially being used
+        // as a bool. We can rely on the pointer being valid, because it is
+        // a crate-private type, and is only returned by the `Layer` impl
+        // for `Option`s. However, even if the layer *does* decide to be
+        // evil and give us an invalid pointer here, that's fine, because we'll
+        // never actually dereference it.
+        layer.downcast_raw(TypeId::of::())
+    }
+    .is_some()
+}
+
+/// Is a type implementing `Subscriber` `Option::<_>::None`?
+pub(crate) fn subscriber_is_none(subscriber: &S) -> bool
+where
+    S: Subscriber,
+{
+    unsafe {
+        // Safety: we're not actually *doing* anything with this pointer ---
+        // this only care about the `Option`, which is essentially being used
+        // as a bool. We can rely on the pointer being valid, because it is
+        // a crate-private type, and is only returned by the `Layer` impl
+        // for `Option`s. However, even if the subscriber *does* decide to be
+        // evil and give us an invalid pointer here, that's fine, because we'll
+        // never actually dereference it.
+        subscriber.downcast_raw(TypeId::of::())
+    }
+    .is_some()
+}
+
 impl Layer for Option
 where
     L: Layer,
@@ -1605,6 +1646,8 @@ where
     unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
         if id == TypeId::of::() {
             Some(self as *const _ as *const ())
+        } else if id == TypeId::of::() && self.is_none() {
+            Some(&NONE_LAYER_MARKER as *const _ as *const ())
         } else {
             self.as_ref().and_then(|inner| inner.downcast_raw(id))
         }
diff --git a/tracing-subscriber/src/reload.rs b/tracing-subscriber/src/reload.rs
index c72e824b7d..096f83d38a 100644
--- a/tracing-subscriber/src/reload.rs
+++ b/tracing-subscriber/src/reload.rs
@@ -67,6 +67,7 @@
 use crate::layer;
 use crate::sync::RwLock;
 
+use core::any::TypeId;
 use std::{
     error, fmt,
     marker::PhantomData,
@@ -182,6 +183,25 @@ where
     fn max_level_hint(&self) -> Option {
         try_lock!(self.inner.read(), else return None).max_level_hint()
     }
+
+    #[doc(hidden)]
+    unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
+        // Safety: it is generally unsafe to downcast through a reload, because
+        // the pointer can be invalidated after the lock is dropped.
+        // `NoneLayerMarker` is a special case because it
+        // is never dereferenced.
+        //
+        // Additionally, even if the marker type *is* dereferenced (which it
+        // never will be), the pointer should be valid even if the subscriber
+        // is reloaded, because all `NoneLayerMarker` pointers that we return
+        // actually point to the global static singleton `NoneLayerMarker`,
+        // rather than to a field inside the lock.
+        if id == TypeId::of::() {
+            return try_lock!(self.inner.read(), else return None).downcast_raw(id);
+        }
+
+        None
+    }
 }
 
 // ===== impl Filter =====
diff --git a/tracing-subscriber/tests/option.rs b/tracing-subscriber/tests/option.rs
index 738cc0a6c5..c87519c308 100644
--- a/tracing-subscriber/tests/option.rs
+++ b/tracing-subscriber/tests/option.rs
@@ -1,7 +1,23 @@
 #![cfg(feature = "registry")]
-use tracing::level_filters::LevelFilter;
-use tracing::Subscriber;
-use tracing_subscriber::prelude::*;
+use tracing_core::{subscriber::Interest, LevelFilter, Metadata, Subscriber};
+use tracing_subscriber::{layer, prelude::*};
+
+// A basic layer that returns its inner for `max_level_hint`
+#[derive(Debug)]
+struct BasicLayer(Option);
+impl tracing_subscriber::Layer for BasicLayer {
+    fn register_callsite(&self, _m: &Metadata<'_>) -> Interest {
+        Interest::sometimes()
+    }
+
+    fn enabled(&self, _m: &Metadata<'_>, _: layer::Context<'_, S>) -> bool {
+        true
+    }
+
+    fn max_level_hint(&self) -> Option {
+        self.0
+    }
+}
 
 // This test is just used to compare to the tests below
 #[test]
@@ -34,8 +50,213 @@ fn just_option_some_layer() {
     assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::OFF));
 }
 
+/// Tests that the logic tested in `doesnt_override_none` works through the reload subscriber
 #[test]
 fn just_option_none_layer() {
     let subscriber = tracing_subscriber::registry().with(Some(LevelFilter::ERROR));
     assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::ERROR));
 }
+
+// Test that the `None` max level hint only applies if its the only layer
+#[test]
+fn none_outside_doesnt_override_max_level() {
+    // None means the other layer takes control
+    let subscriber = tracing_subscriber::registry()
+        .with(BasicLayer(None))
+        .with(None::);
+    assert_eq!(
+        subscriber.max_level_hint(),
+        None,
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // The `None`-returning layer still wins
+    let subscriber = tracing_subscriber::registry()
+        .with(BasicLayer(None))
+        .with(Some(LevelFilter::ERROR));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::ERROR),
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // Check that we aren't doing anything truly wrong
+    let subscriber = tracing_subscriber::registry()
+        .with(BasicLayer(Some(LevelFilter::DEBUG)))
+        .with(None::);
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::DEBUG),
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // Test that per-subscriber filters aren't affected
+
+    // One layer is None so it "wins"
+    let subscriber = tracing_subscriber::registry()
+        .with(BasicLayer(None))
+        .with(None::.with_filter(LevelFilter::DEBUG));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        None,
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // The max-levels wins
+    let subscriber = tracing_subscriber::registry()
+        .with(BasicLayer(Some(LevelFilter::INFO)))
+        .with(None::.with_filter(LevelFilter::DEBUG));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::DEBUG),
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // Test filter on the other layer
+    let subscriber = tracing_subscriber::registry()
+        .with(BasicLayer(Some(LevelFilter::INFO)).with_filter(LevelFilter::DEBUG))
+        .with(None::);
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::DEBUG),
+        "\n stack: {:#?}",
+        subscriber
+    );
+    let subscriber = tracing_subscriber::registry()
+        .with(BasicLayer(None).with_filter(LevelFilter::DEBUG))
+        .with(None::);
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::DEBUG),
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // The `OFF` from `None` over overridden.
+    let subscriber = tracing_subscriber::registry()
+        .with(BasicLayer(Some(LevelFilter::INFO)))
+        .with(None::);
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::INFO),
+        "\n stack: {:#?}",
+        subscriber
+    );
+}
+
+// Test that the `None` max level hint only applies if its the only layer
+#[test]
+fn none_inside_doesnt_override_max_level() {
+    // None means the other layer takes control
+    let subscriber = tracing_subscriber::registry()
+        .with(None::)
+        .with(BasicLayer(None));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        None,
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // The `None`-returning layer still wins
+    let subscriber = tracing_subscriber::registry()
+        .with(Some(LevelFilter::ERROR))
+        .with(BasicLayer(None));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::ERROR),
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // Check that we aren't doing anything truly wrong
+    let subscriber = tracing_subscriber::registry()
+        .with(None::)
+        .with(BasicLayer(Some(LevelFilter::DEBUG)));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::DEBUG),
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // Test that per-subscriber filters aren't affected
+
+    // One layer is None so it "wins"
+    let subscriber = tracing_subscriber::registry()
+        .with(None::.with_filter(LevelFilter::DEBUG))
+        .with(BasicLayer(None));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        None,
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // The max-levels wins
+    let subscriber = tracing_subscriber::registry()
+        .with(None::.with_filter(LevelFilter::DEBUG))
+        .with(BasicLayer(Some(LevelFilter::INFO)));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::DEBUG),
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // Test filter on the other layer
+    let subscriber = tracing_subscriber::registry()
+        .with(None::)
+        .with(BasicLayer(Some(LevelFilter::INFO)).with_filter(LevelFilter::DEBUG));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::DEBUG),
+        "\n stack: {:#?}",
+        subscriber
+    );
+    let subscriber = tracing_subscriber::registry()
+        .with(None::)
+        .with(BasicLayer(None).with_filter(LevelFilter::DEBUG));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::DEBUG),
+        "\n stack: {:#?}",
+        subscriber
+    );
+
+    // The `OFF` from `None` over overridden.
+    let subscriber = tracing_subscriber::registry()
+        .with(None::)
+        .with(BasicLayer(Some(LevelFilter::INFO)));
+    assert_eq!(
+        subscriber.max_level_hint(),
+        Some(LevelFilter::INFO),
+        "\n stack: {:#?}",
+        subscriber
+    );
+}
+
+/// Tests that the logic tested in `doesnt_override_none` works through the reload layer
+#[test]
+fn reload_works_with_none() {
+    let (layer1, handle1) = tracing_subscriber::reload::Layer::new(None::);
+    let (layer2, _handle2) = tracing_subscriber::reload::Layer::new(None::);
+
+    let subscriber = tracing_subscriber::registry().with(layer1).with(layer2);
+    assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::OFF));
+
+    // reloading one should pass through correctly.
+    handle1.reload(Some(BasicLayer(None))).unwrap();
+    assert_eq!(subscriber.max_level_hint(), None);
+
+    // Check pass-through of an actual level as well
+    handle1
+        .reload(Some(BasicLayer(Some(LevelFilter::DEBUG))))
+        .unwrap();
+    assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::DEBUG));
+}

From 257f86f0920c52a58c25da60e6f0b7e8324da487 Mon Sep 17 00:00:00 2001
From: Nicolas Moutschen 
Date: Fri, 30 Sep 2022 20:34:01 +0200
Subject: [PATCH 102/142] opentelemetry: fix metrics docs using `value.`
 instead of `histogram.` (#2326)

## Motivation

This fixes a discrepancy in the `tracing-opentelemetry` docs for the new
MetricsLayer. The docs refer to `value.` as a way to collect discrete
data point, but this doesn't match any of the prefix constant mentioned
in the same file.

```rust
const METRIC_PREFIX_MONOTONIC_COUNTER: &str = "monotonic_counter.";
const METRIC_PREFIX_COUNTER: &str = "counter.";
const METRIC_PREFIX_HISTOGRAM: &str = "histogram.";
```

## Solution

This fixes the documentation and test by referring to `histogram.`
instead of `value.`.
---
 tracing-opentelemetry/src/metrics.rs              | 8 ++++----
 tracing-opentelemetry/tests/metrics_publishing.rs | 6 +++---
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/tracing-opentelemetry/src/metrics.rs b/tracing-opentelemetry/src/metrics.rs
index 91b9e63a8f..42c90baecb 100644
--- a/tracing-opentelemetry/src/metrics.rs
+++ b/tracing-opentelemetry/src/metrics.rs
@@ -263,7 +263,7 @@ impl<'a> Visit for MetricVisitor<'a> {
 /// - `monotonic_counter.` (non-negative numbers): Used when the counter should
 ///   only ever increase
 /// - `counter.`: Used when the counter can go up or down
-/// - `value.`: Used for discrete data points (i.e., summing them does not make
+/// - `histogram.`: Used for discrete data points (i.e., summing them does not make
 ///   semantic sense)
 ///
 /// Examples:
@@ -276,9 +276,9 @@ impl<'a> Visit for MetricVisitor<'a> {
 /// info!(counter.baz = -1);
 /// info!(counter.xyz = 1.1);
 ///
-/// info!(value.qux = 1);
-/// info!(value.abc = -1);
-/// info!(value.def = 1.1);
+/// info!(histogram.qux = 1);
+/// info!(histogram.abc = -1);
+/// info!(histogram.def = 1.1);
 /// ```
 ///
 /// # Mixing data types
diff --git a/tracing-opentelemetry/tests/metrics_publishing.rs b/tracing-opentelemetry/tests/metrics_publishing.rs
index 284005e538..2f9c20ef7f 100644
--- a/tracing-opentelemetry/tests/metrics_publishing.rs
+++ b/tracing-opentelemetry/tests/metrics_publishing.rs
@@ -129,7 +129,7 @@ async fn u64_histogram_is_exported() {
     );
 
     tracing::subscriber::with_default(subscriber, || {
-        tracing::info!(value.abcdefg = 9_u64);
+        tracing::info!(histogram.abcdefg = 9_u64);
     });
 
     exporter.export().unwrap();
@@ -145,7 +145,7 @@ async fn i64_histogram_is_exported() {
     );
 
     tracing::subscriber::with_default(subscriber, || {
-        tracing::info!(value.abcdefg_auenatsou = -19_i64);
+        tracing::info!(histogram.abcdefg_auenatsou = -19_i64);
     });
 
     exporter.export().unwrap();
@@ -161,7 +161,7 @@ async fn f64_histogram_is_exported() {
     );
 
     tracing::subscriber::with_default(subscriber, || {
-        tracing::info!(value.abcdefg_racecar = 777.0012_f64);
+        tracing::info!(histogram.abcdefg_racecar = 777.0012_f64);
     });
 
     exporter.export().unwrap();

From d7372d76b1f09057ab7401517eeaae744e65b4c7 Mon Sep 17 00:00:00 2001
From: tl-rodrigo-gryzinski
 <73602926+tl-rodrigo-gryzinski@users.noreply.github.com>
Date: Mon, 3 Oct 2022 18:01:11 +0100
Subject: [PATCH 103/142] tracing: add `reqwest-tracing` to related crates in
 README (#2331)

---
 README.md          | 3 +++
 tracing/src/lib.rs | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/README.md b/README.md
index 23b37cc9f2..41e9cf40a9 100644
--- a/README.md
+++ b/README.md
@@ -406,6 +406,7 @@ are not maintained by the `tokio` project. These include:
 - [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki].
 - [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.
 - [`tracing-chrome`] provides a layer that exports trace data that can be viewed in `chrome://tracing`.
+- [`reqwest-tracing`] provides a middleware to trace [`reqwest`] HTTP requests.
 
 (if you're the maintainer of a `tracing` ecosystem crate not in this list,
 please let us know!)
@@ -446,6 +447,8 @@ please let us know!)
 [Grafana Loki]: https://grafana.com/oss/loki/
 [`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt
 [`tracing-chrome`]: https://crates.io/crates/tracing-chrome
+[`reqwest-tracing`]: https://crates.io/crates/reqwest-tracing
+[`reqwest`]: https://crates.io/crates/reqwest
 
 **Note:** that some of the ecosystem crates are currently unreleased and
 undergoing active development. They may be less stable than `tracing` and
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index bc215e6032..de30c15e5d 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -746,6 +746,7 @@
 //!    grouping together logs from the same spans during writing.
 //!  - [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki].
 //!  - [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.
+//!  - [`reqwest-tracing`] provides a middleware to trace [`reqwest`] HTTP requests.
 //!
 //! If you're the maintainer of a `tracing` ecosystem crate not listed above,
 //! please let us know! We'd love to add your project to the list!
@@ -784,6 +785,8 @@
 //! [`tracing-loki`]: https://crates.io/crates/tracing-loki
 //! [Grafana Loki]: https://grafana.com/oss/loki/
 //! [`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt
+//! [`reqwest-tracing`]: https://crates.io/crates/reqwest-tracing
+//! [`reqwest`]: https://crates.io/crates/reqwest
 //!
 //! 
 //!     Note: Some of these ecosystem crates are currently

From 8b01ea9b9c0dfc06ab101940c94e23934c4d4cc8 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Thu, 6 Oct 2022 10:52:01 -0700
Subject: [PATCH 104/142] core: prepare to release v0.1.30 (#2339)

# 0.1.30 (October 6, 2022)

This release of `tracing-core` adds a new `on_register_dispatch` method
to the `Subscriber` trait to allow the `Subscriber` to perform
initialization after being registered as a `Dispatch`, and a
`WeakDispatch` type to allow a `Subscriber` to store its own `Dispatch`
without creating reference count cycles.

### Added

- `Subscriber::on_register_dispatch` method ([#2269])
- `WeakDispatch` type and `Dispatch::downgrade()` function ([#2293])

Thanks to @jswrenn for contributing to this release!

[#2269]: https://github.com/tokio-rs/tracing/pull/2269
[#2293]: https://github.com/tokio-rs/tracing/pull/2293
---
 tracing-core/CHANGELOG.md | 18 ++++++++++++++++++
 tracing-core/Cargo.toml   |  2 +-
 tracing-core/README.md    | 26 +++++++++++++-------------
 3 files changed, 32 insertions(+), 14 deletions(-)

diff --git a/tracing-core/CHANGELOG.md b/tracing-core/CHANGELOG.md
index 2db8553c98..cf0d8e3b95 100644
--- a/tracing-core/CHANGELOG.md
+++ b/tracing-core/CHANGELOG.md
@@ -1,3 +1,21 @@
+# 0.1.30 (October 6, 2022)
+
+This release of `tracing-core` adds a new `on_register_dispatch` method to the
+`Subscriber` trait to allow the `Subscriber` to perform initialization after
+being registered as a `Dispatch`, and a `WeakDispatch` type to allow a
+`Subscriber` to store its own `Dispatch` without creating reference count
+cycles.
+
+### Added
+
+- `Subscriber::on_register_dispatch` method ([#2269])
+- `WeakDispatch` type and `Dispatch::downgrade()` function ([#2293])
+
+Thanks to @jswrenn for contributing to this release!
+
+[#2269]: https://github.com/tokio-rs/tracing/pull/2269
+[#2293]: https://github.com/tokio-rs/tracing/pull/2293
+
 # 0.1.29 (July 29, 2022)
 
 This release of `tracing-core` adds `PartialEq` and `Eq` implementations for
diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml
index 8cb3bd9400..aacd334ae5 100644
--- a/tracing-core/Cargo.toml
+++ b/tracing-core/Cargo.toml
@@ -8,7 +8,7 @@ name = "tracing-core"
 #   - README.md
 # - Update CHANGELOG.md.
 # - Create "v0.1.x" git tag.
-version = "0.1.29"
+version = "0.1.30"
 authors = ["Tokio Contributors "]
 license = "MIT"
 readme = "README.md"
diff --git a/tracing-core/README.md b/tracing-core/README.md
index c6f7b03337..f06c760593 100644
--- a/tracing-core/README.md
+++ b/tracing-core/README.md
@@ -16,9 +16,9 @@ Core primitives for application-level tracing.
 [Documentation][docs-url] | [Chat][discord-url]
 
 [crates-badge]: https://img.shields.io/crates/v/tracing-core.svg
-[crates-url]: https://crates.io/crates/tracing-core/0.1.29
+[crates-url]: https://crates.io/crates/tracing-core/0.1.30
 [docs-badge]: https://docs.rs/tracing-core/badge.svg
-[docs-url]: https://docs.rs/tracing-core/0.1.29
+[docs-url]: https://docs.rs/tracing-core/0.1.30
 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue
 [docs-master-url]: https://tracing-rs.netlify.com/tracing_core
 [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
@@ -79,22 +79,22 @@ The following crate feature flags are available:
 
   ```toml
   [dependencies]
-  tracing-core = { version = "0.1.29", default-features = false }
+  tracing-core = { version = "0.1.30", default-features = false }
   ```
 
   **Note**:`tracing-core`'s `no_std` support requires `liballoc`.
 
 [`tracing`]: ../tracing
-[`span::Id`]: https://docs.rs/tracing-core/0.1.29/tracing_core/span/struct.Id.html
-[`Event`]: https://docs.rs/tracing-core/0.1.29/tracing_core/event/struct.Event.html
-[`Subscriber`]: https://docs.rs/tracing-core/0.1.29/tracing_core/subscriber/trait.Subscriber.html
-[`Metadata`]: https://docs.rs/tracing-core/0.1.29/tracing_core/metadata/struct.Metadata.html
-[`Callsite`]: https://docs.rs/tracing-core/0.1.29/tracing_core/callsite/trait.Callsite.html
-[`Field`]: https://docs.rs/tracing-core/0.1.29/tracing_core/field/struct.Field.html
-[`FieldSet`]: https://docs.rs/tracing-core/0.1.29/tracing_core/field/struct.FieldSet.html
-[`Value`]: https://docs.rs/tracing-core/0.1.29/tracing_core/field/trait.Value.html
-[`ValueSet`]: https://docs.rs/tracing-core/0.1.29/tracing_core/field/struct.ValueSet.html
-[`Dispatch`]: https://docs.rs/tracing-core/0.1.29/tracing_core/dispatcher/struct.Dispatch.html
+[`span::Id`]: https://docs.rs/tracing-core/0.1.30/tracing_core/span/struct.Id.html
+[`Event`]: https://docs.rs/tracing-core/0.1.30/tracing_core/event/struct.Event.html
+[`Subscriber`]: https://docs.rs/tracing-core/0.1.30/tracing_core/subscriber/trait.Subscriber.html
+[`Metadata`]: https://docs.rs/tracing-core/0.1.30/tracing_core/metadata/struct.Metadata.html
+[`Callsite`]: https://docs.rs/tracing-core/0.1.30/tracing_core/callsite/trait.Callsite.html
+[`Field`]: https://docs.rs/tracing-core/0.1.30/tracing_core/field/struct.Field.html
+[`FieldSet`]: https://docs.rs/tracing-core/0.1.30/tracing_core/field/struct.FieldSet.html
+[`Value`]: https://docs.rs/tracing-core/0.1.30/tracing_core/field/trait.Value.html
+[`ValueSet`]: https://docs.rs/tracing-core/0.1.30/tracing_core/field/struct.ValueSet.html
+[`Dispatch`]: https://docs.rs/tracing-core/0.1.30/tracing_core/dispatcher/struct.Dispatch.html
 
 ## Supported Rust Versions
 

From 92cb2f030029951032c4601b6396f42f633e3118 Mon Sep 17 00:00:00 2001
From: Eashwar Ranganathan 
Date: Thu, 6 Oct 2022 11:01:43 -0700
Subject: [PATCH 105/142] attributes: fix handling of inner attributes (#2307)

## Motivation

When the `instrument` attribute is used on a function with inner
attributes, the proc macro generates code above the attributes within
the function block that causes compilation errors. These should be
parsed out separately and handled.

Fixes #2294

## Solution

I updated `MaybeItemFn` and `MaybeItemFnRef` to so they hold both the
outer and inner attributes for the instrumented function and updated the
codegen to inlcude them in the appropriate locations.

I couldn't preserve the existing implementation of
`From<&'_ ItemFn> for MaybeItemFnRef<'_, Box>`, because it is
now necessary to separate the inner and outer attributes of the
`ItemFn` into two separate `Vec`s. That implementation was replaced
with a `From for MaybeItemFn`, which uses `Iterator::partition`
to separate out the inner and outer attributes.

Co-authored-by: Eliza Weisman 
---
 tracing-attributes/src/expand.rs       | 29 +++++++------
 tracing-attributes/src/lib.rs          | 56 +++++++++++++++++---------
 tracing-attributes/tests/async_fn.rs   |  9 +++++
 tracing-attributes/tests/instrument.rs |  9 +++++
 4 files changed, 72 insertions(+), 31 deletions(-)

diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs
index 70204395ea..7005b4423e 100644
--- a/tracing-attributes/src/expand.rs
+++ b/tracing-attributes/src/expand.rs
@@ -11,7 +11,7 @@ use syn::{
 
 use crate::{
     attr::{Field, Fields, FormatMode, InstrumentArgs},
-    MaybeItemFnRef,
+    MaybeItemFn, MaybeItemFnRef,
 };
 
 /// Given an existing function, generate an instrumented version of that function
@@ -25,7 +25,8 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
     // isn't representable inside a quote!/quote_spanned! macro
     // (Syn's ToTokens isn't implemented for ItemFn)
     let MaybeItemFnRef {
-        attrs,
+        outer_attrs,
+        inner_attrs,
         vis,
         sig,
         block,
@@ -87,10 +88,11 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
     );
 
     quote!(
-        #(#attrs) *
+        #(#outer_attrs) *
         #vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #output
         #where_clause
         {
+            #(#inner_attrs) *
             #warnings
             #body
         }
@@ -657,7 +659,7 @@ impl<'block> AsyncInfo<'block> {
         self,
         args: InstrumentArgs,
         instrumented_function_name: &str,
-    ) -> proc_macro::TokenStream {
+    ) -> Result {
         // let's rewrite some statements!
         let mut out_stmts: Vec = self
             .input
@@ -678,12 +680,15 @@ impl<'block> AsyncInfo<'block> {
             // instrument the future by rewriting the corresponding statement
             out_stmts[iter] = match self.kind {
                 // `Box::pin(immediately_invoked_async_fn())`
-                AsyncKind::Function(fun) => gen_function(
-                    fun.into(),
-                    args,
-                    instrumented_function_name,
-                    self.self_type.as_ref(),
-                ),
+                AsyncKind::Function(fun) => {
+                    let fun = MaybeItemFn::from(fun.clone());
+                    gen_function(
+                        fun.as_ref(),
+                        args,
+                        instrumented_function_name,
+                        self.self_type.as_ref(),
+                    )
+                }
                 // `async move { ... }`, optionally pinned
                 AsyncKind::Async {
                     async_expr,
@@ -714,13 +719,13 @@ impl<'block> AsyncInfo<'block> {
         let vis = &self.input.vis;
         let sig = &self.input.sig;
         let attrs = &self.input.attrs;
-        quote!(
+        Ok(quote!(
             #(#attrs) *
             #vis #sig {
                 #(#out_stmts) *
             }
         )
-        .into()
+        .into())
     }
 }
 
diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs
index 49b3c794a0..084bdbcc5e 100644
--- a/tracing-attributes/src/lib.rs
+++ b/tracing-attributes/src/lib.rs
@@ -86,7 +86,7 @@ extern crate proc_macro;
 use proc_macro2::TokenStream;
 use quote::ToTokens;
 use syn::parse::{Parse, ParseStream};
-use syn::{Attribute, Block, ItemFn, Signature, Visibility};
+use syn::{Attribute, ItemFn, Signature, Visibility};
 
 mod attr;
 mod expand;
@@ -587,11 +587,13 @@ fn instrument_precise(
     // check for async_trait-like patterns in the block, and instrument
     // the future instead of the wrapper
     if let Some(async_like) = expand::AsyncInfo::from_fn(&input) {
-        return Ok(async_like.gen_async(args, instrumented_function_name.as_str()));
+        return async_like.gen_async(args, instrumented_function_name.as_str());
     }
 
+    let input = MaybeItemFn::from(input);
+
     Ok(expand::gen_function(
-        (&input).into(),
+        input.as_ref(),
         args,
         instrumented_function_name.as_str(),
         None,
@@ -603,7 +605,8 @@ fn instrument_precise(
 /// which's block is just a `TokenStream` (it may contain invalid code).
 #[derive(Debug, Clone)]
 struct MaybeItemFn {
-    attrs: Vec,
+    outer_attrs: Vec,
+    inner_attrs: Vec,
     vis: Visibility,
     sig: Signature,
     block: TokenStream,
@@ -612,7 +615,8 @@ struct MaybeItemFn {
 impl MaybeItemFn {
     fn as_ref(&self) -> MaybeItemFnRef<'_, TokenStream> {
         MaybeItemFnRef {
-            attrs: &self.attrs,
+            outer_attrs: &self.outer_attrs,
+            inner_attrs: &self.inner_attrs,
             vis: &self.vis,
             sig: &self.sig,
             block: &self.block,
@@ -624,12 +628,14 @@ impl MaybeItemFn {
 /// (just like `ItemFn`, but skips parsing the body).
 impl Parse for MaybeItemFn {
     fn parse(input: ParseStream<'_>) -> syn::Result {
-        let attrs = input.call(syn::Attribute::parse_outer)?;
+        let outer_attrs = input.call(Attribute::parse_outer)?;
         let vis: Visibility = input.parse()?;
         let sig: Signature = input.parse()?;
+        let inner_attrs = input.call(Attribute::parse_inner)?;
         let block: TokenStream = input.parse()?;
         Ok(Self {
-            attrs,
+            outer_attrs,
+            inner_attrs,
             vis,
             sig,
             block,
@@ -637,23 +643,35 @@ impl Parse for MaybeItemFn {
     }
 }
 
+impl From for MaybeItemFn {
+    fn from(
+        ItemFn {
+            attrs,
+            vis,
+            sig,
+            block,
+        }: ItemFn,
+    ) -> Self {
+        let (outer_attrs, inner_attrs) = attrs
+            .into_iter()
+            .partition(|attr| attr.style == syn::AttrStyle::Outer);
+        Self {
+            outer_attrs,
+            inner_attrs,
+            vis,
+            sig,
+            block: block.to_token_stream(),
+        }
+    }
+}
+
 /// A generic reference type for `MaybeItemFn`,
 /// that takes a generic block type `B` that implements `ToTokens` (eg. `TokenStream`, `Block`).
 #[derive(Debug, Clone)]
 struct MaybeItemFnRef<'a, B: ToTokens> {
-    attrs: &'a Vec,
+    outer_attrs: &'a Vec,
+    inner_attrs: &'a Vec,
     vis: &'a Visibility,
     sig: &'a Signature,
     block: &'a B,
 }
-
-impl<'a> From<&'a ItemFn> for MaybeItemFnRef<'a, Box> {
-    fn from(val: &'a ItemFn) -> Self {
-        MaybeItemFnRef {
-            attrs: &val.attrs,
-            vis: &val.vis,
-            sig: &val.sig,
-            block: &val.block,
-        }
-    }
-}
diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs
index 9d0d61c8ff..c89963672c 100644
--- a/tracing-attributes/tests/async_fn.rs
+++ b/tracing-attributes/tests/async_fn.rs
@@ -32,6 +32,15 @@ async fn test_ret_impl_trait_err(n: i32) -> Result, &'
 #[instrument]
 async fn test_async_fn_empty() {}
 
+// Reproduces a compile error when an instrumented function body contains inner
+// attributes (https://github.com/tokio-rs/tracing/issues/2294).
+#[deny(unused_variables)]
+#[instrument]
+async fn repro_async_2294() {
+    #![allow(unused_variables)]
+    let i = 42;
+}
+
 // Reproduces https://github.com/tokio-rs/tracing/issues/1613
 #[instrument]
 // LOAD-BEARING `#[rustfmt::skip]`! This is necessary to reproduce the bug;
diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs
index 2b2fee71e7..7686927488 100644
--- a/tracing-attributes/tests/instrument.rs
+++ b/tracing-attributes/tests/instrument.rs
@@ -3,6 +3,15 @@ use tracing::Level;
 use tracing_attributes::instrument;
 use tracing_mock::*;
 
+// Reproduces a compile error when an instrumented function body contains inner
+// attributes (https://github.com/tokio-rs/tracing/issues/2294).
+#[deny(unused_variables)]
+#[instrument]
+fn repro_2294() {
+    #![allow(unused_variables)]
+    let i = 42;
+}
+
 #[test]
 fn override_everything() {
     #[instrument(target = "my_target", level = "debug")]

From 64b221dc6485faec054ae740815130aa22708aba Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Thu, 6 Oct 2022 12:00:48 -0700
Subject: [PATCH 106/142] attributes: prepare to release v0.1.23 (#2340)

# 0.1.23 (October 6, 2022)

This release of `tracing-attributes` fixes a bug where compiler
diagnostic spans for type errors in `#[instrument]`ed `async fn`s have
the location of the `#[instrument]` attribute rather than the location
of the actual error, and a bug where inner attributes in
`#[instrument]`ed functions would cause a compiler error.

### Fixed

- Fix incorrect handling of inner attributes in `#[instrument]`ed
  functions ([#2307])
- Add fake return to improve spans generated for type errors in `async
  fn`s ([#2270])
- Updated `syn` dependency to fix compilation with `-Z minimal-versions`
  ([#2246])

Thanks to new contributors @compiler-errors and @e-nomem, as well as
@CAD97, for contributing to this release!

[#2307]: https://github.com/tokio-rs/tracing/pull/2307
[#2270]: https://github.com/tokio-rs/tracing/pull/2270
[#2246]: https://github.com/tokio-rs/tracing/pull/2246
---
 tracing-attributes/CHANGELOG.md | 21 +++++++++++++++++++++
 tracing-attributes/Cargo.toml   |  2 +-
 tracing-attributes/README.md    |  4 ++--
 tracing-attributes/src/lib.rs   |  4 ++--
 4 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/tracing-attributes/CHANGELOG.md b/tracing-attributes/CHANGELOG.md
index 4fcc81f294..42cb09b6a1 100644
--- a/tracing-attributes/CHANGELOG.md
+++ b/tracing-attributes/CHANGELOG.md
@@ -1,3 +1,24 @@
+# 0.1.23 (October 6, 2022)
+
+This release of `tracing-attributes` fixes a bug where compiler diagnostic spans
+for type errors in `#[instrument]`ed `async fn`s have the location of the
+`#[instrument]` attribute rather than the location of the actual error, and a
+bug where inner attributes in `#[instrument]`ed functions would cause a compiler
+error.
+### Fixed
+
+- Fix incorrect handling of inner attributes in `#[instrument]`ed functions ([#2307])
+- Add fake return to improve spans generated for type errors in `async fn`s ([#2270])
+- Updated `syn` dependency to fix compilation with `-Z minimal-versions`
+  ([#2246])
+
+Thanks to new contributors @compiler-errors and @e-nomem, as well as @CAD97, for
+contributing to this release!
+
+[#2307]: https://github.com/tokio-rs/tracing/pull/2307
+[#2270]: https://github.com/tokio-rs/tracing/pull/2270
+[#2246]: https://github.com/tokio-rs/tracing/pull/2246
+
 # 0.1.22 (July 1, 2022)
 
 This release fixes an issue where using the `err` or `ret` arguments to
diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml
index abce8a5195..d6e3adb807 100644
--- a/tracing-attributes/Cargo.toml
+++ b/tracing-attributes/Cargo.toml
@@ -8,7 +8,7 @@ name = "tracing-attributes"
 #   - README.md
 # - Update CHANGELOG.md.
 # - Create "v0.1.x" git tag.
-version = "0.1.22"
+version = "0.1.23"
 authors = [
     "Tokio Contributors ",
     "Eliza Weisman ",
diff --git a/tracing-attributes/README.md b/tracing-attributes/README.md
index 6361794680..356b511f2a 100644
--- a/tracing-attributes/README.md
+++ b/tracing-attributes/README.md
@@ -18,7 +18,7 @@ Macro attributes for application-level tracing.
 [crates-badge]: https://img.shields.io/crates/v/tracing-attributes.svg
 [crates-url]: https://crates.io/crates/tracing-attributes
 [docs-badge]: https://docs.rs/tracing-attributes/badge.svg
-[docs-url]: https://docs.rs/tracing-attributes/0.1.22
+[docs-url]: https://docs.rs/tracing-attributes/0.1.23
 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue
 [docs-master-url]: https://tracing-rs.netlify.com/tracing_attributes
 [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
@@ -47,7 +47,7 @@ First, add this to your `Cargo.toml`:
 
 ```toml
 [dependencies]
-tracing-attributes = "0.1.22"
+tracing-attributes = "0.1.23"
 ```
 
 
diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs
index 084bdbcc5e..f5974e4e52 100644
--- a/tracing-attributes/src/lib.rs
+++ b/tracing-attributes/src/lib.rs
@@ -16,7 +16,7 @@
 //!
 //! ```toml
 //! [dependencies]
-//! tracing-attributes = "0.1.22"
+//! tracing-attributes = "0.1.23"
 //! ```
 //!
 //! The [`#[instrument]`][instrument] attribute can now be added to a function
@@ -52,7 +52,7 @@
 //! supported compiler version is not considered a semver breaking change as
 //! long as doing so complies with this policy.
 //!
-#![doc(html_root_url = "https://docs.rs/tracing-attributes/0.1.22")]
+#![doc(html_root_url = "https://docs.rs/tracing-attributes/0.1.23")]
 #![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/"

From 8e35927d7ef5bad3b20fd53592ddb951ae7bec91 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Thu, 6 Oct 2022 12:38:39 -0700
Subject: [PATCH 107/142] tracing: prepare to release v0.1.37 (#2341)

# 0.1.37 (October 6, 2022)

This release of `tracing` incorporates changes from `tracing-core`
[v0.1.30][core-0.1.30] and `tracing-attributes` [v0.1.23][attrs-0.1.23],
including the new `Subscriber::on_register_dispatch` method for
performing late initialization after a `Subscriber` is registered as a
`Dispatch`, and bugfixes for the `#[instrument]` attribute.
Additionally, it fixes instances of the `bare_trait_objects` lint, which
is now a warning on `tracing`'s MSRV and will become an error in the
next edition.

### Fixed

- **attributes**: Incorrect handling of inner attributes in
  `#[instrument]`ed functions (https://github.com/tokio-rs/tracing/pull/2307)
- **attributes**: Incorrect location of compiler diagnostic spans
  generated for type errors in `#[instrument]`ed `async fn`s (https://github.com/tokio-rs/tracing/pull/2270)
- **attributes**: Updated `syn` dependency to fix compilation with `-Z
  minimal-versions` (https://github.com/tokio-rs/tracing/pull/2246)
- `bare_trait_objects` warning in `valueset!` macro expansion (https://github.com/tokio-rs/tracing/pull/2308)

### Added

- **core**: `Subscriber::on_register_dispatch` method (https://github.com/tokio-rs/tracing/pull/2269)
- **core**: `WeakDispatch` type and `Dispatch::downgrade()` function
  (https://github.com/tokio-rs/tracing/pull/2293)

### Changed

- `tracing-core`: updated to [0.1.30][core-0.1.30]
- `tracing-attributes`: updated to [0.1.23][attrs-0.1.23]

### Documented

- Added [`tracing-web`] and [`reqwest-tracing`] to related crates
  (#2283, #2331)

Thanks to new contributors @compiler-errors, @e-nomem, @WorldSEnder,
@Xiami2012, and @tl-rodrigo-gryzinski, as well as @jswrenn and @CAD97,
for contributing to this release!

[core-0.1.30]:
    https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.30
[attrs-0.1.23]:
    https://github.com/tokio-rs/tracing/releases/tag/tracing-attributes-0.1.23
[`tracing-web`]: https://crates.io/crates/tracing-web/
[`reqwest-tracing`]: https://crates.io/crates/reqwest-tracing/
---
 tracing/CHANGELOG.md | 52 ++++++++++++++++++++++++++++++++++++++++++++
 tracing/Cargo.toml   |  6 ++---
 tracing/README.md    |  4 ++--
 tracing/src/lib.rs   |  4 ++--
 4 files changed, 59 insertions(+), 7 deletions(-)

diff --git a/tracing/CHANGELOG.md b/tracing/CHANGELOG.md
index 8b7d58808b..978e0ca554 100644
--- a/tracing/CHANGELOG.md
+++ b/tracing/CHANGELOG.md
@@ -1,3 +1,55 @@
+# 0.1.37 (October 6, 2022)
+
+This release of `tracing` incorporates changes from `tracing-core`
+[v0.1.30][core-0.1.30] and `tracing-attributes` [v0.1.23][attrs-0.1.23],
+including the new `Subscriber::on_register_dispatch` method for performing late
+initialization after a `Subscriber` is registered as a `Dispatch`, and bugfixes
+for the `#[instrument]` attribute. Additionally, it fixes instances of the
+`bare_trait_objects` lint, which is now a warning on `tracing`'s MSRV and will
+become an error in the next edition.
+
+### Fixed
+
+- **attributes**: Incorrect handling of inner attributes in `#[instrument]`ed
+  functions ([#2307])
+- **attributes**: Incorrect location of compiler diagnostic spans generated for
+  type errors in `#[instrument]`ed `async fn`s ([#2270])
+- **attributes**: Updated `syn` dependency to fix compilation with `-Z
+  minimal-versions` ([#2246])
+- `bare_trait_objects` warning in `valueset!` macro expansion ([#2308])
+
+### Added
+
+- **core**: `Subscriber::on_register_dispatch` method ([#2269])
+- **core**: `WeakDispatch` type and `Dispatch::downgrade()` function ([#2293])
+
+### Changed
+
+- `tracing-core`: updated to [0.1.30][core-0.1.30]
+- `tracing-attributes`: updated to [0.1.23][attrs-0.1.23]
+
+### Documented
+
+- Added [`tracing-web`] and [`reqwest-tracing`] to related crates ([#2283],
+  [#2331])
+
+Thanks to new contributors @compiler-errors, @e-nomem, @WorldSEnder, @Xiami2012,
+and @tl-rodrigo-gryzinski, as well as @jswrenn and @CAD97, for contributing to
+this release!
+
+[core-0.1.30]: https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.30
+[attrs-0.1.23]: https://github.com/tokio-rs/tracing/releases/tag/tracing-attributes-0.1.23
+[`tracing-web`]: https://crates.io/crates/tracing-web/
+[`reqwest-tracing`]: https://crates.io/crates/reqwest-tracing/
+[#2246]: https://github.com/tokio-rs/tracing/pull/2246
+[#2269]: https://github.com/tokio-rs/tracing/pull/2269
+[#2283]: https://github.com/tokio-rs/tracing/pull/2283
+[#2270]: https://github.com/tokio-rs/tracing/pull/2270
+[#2293]: https://github.com/tokio-rs/tracing/pull/2293
+[#2307]: https://github.com/tokio-rs/tracing/pull/2307
+[#2308]: https://github.com/tokio-rs/tracing/pull/2308
+[#2331]: https://github.com/tokio-rs/tracing/pull/2331
+
 # 0.1.36 (July 29, 2022)
 
 This release adds support for owned values and fat pointers as arguments to the
diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml
index a738d24f15..313c3370e6 100644
--- a/tracing/Cargo.toml
+++ b/tracing/Cargo.toml
@@ -8,7 +8,7 @@ name = "tracing"
 #   - README.md
 # - Update CHANGELOG.md.
 # - Create "v0.1.x" git tag
-version = "0.1.36"
+version = "0.1.37"
 authors = ["Eliza Weisman ", "Tokio Contributors "]
 license = "MIT"
 readme = "README.md"
@@ -28,9 +28,9 @@ edition = "2018"
 rust-version = "1.49.0"
 
 [dependencies]
-tracing-core = { path = "../tracing-core", version = "0.1.29", default-features = false }
+tracing-core = { path = "../tracing-core", version = "0.1.30", default-features = false }
 log = { version = "0.4.17", optional = true }
-tracing-attributes = { path = "../tracing-attributes", version = "0.1.22", optional = true }
+tracing-attributes = { path = "../tracing-attributes", version = "0.1.23", optional = true }
 cfg-if = "1.0.0"
 pin-project-lite = "0.2.9"
 
diff --git a/tracing/README.md b/tracing/README.md
index 16e671e05c..6947345771 100644
--- a/tracing/README.md
+++ b/tracing/README.md
@@ -250,7 +250,7 @@ my_future
 is as long as the future's.
 
 The second, and preferred, option is through the
-[`#[instrument]`](https://docs.rs/tracing/0.1.36/tracing/attr.instrument.html)
+[`#[instrument]`](https://docs.rs/tracing/0.1.37/tracing/attr.instrument.html)
 attribute:
 
 ```rust
@@ -297,7 +297,7 @@ span.in_scope(|| {
 // Dropping the span will close it, indicating that it has ended.
 ```
 
-The [`#[instrument]`](https://docs.rs/tracing/0.1.36/tracing/attr.instrument.html) attribute macro
+The [`#[instrument]`](https://docs.rs/tracing/0.1.37/tracing/attr.instrument.html) attribute macro
 can reduce some of this boilerplate:
 
 ```rust
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index de30c15e5d..342e04a825 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -817,7 +817,7 @@
 //!
 //!   ```toml
 //!   [dependencies]
-//!   tracing = { version = "0.1.36", default-features = false }
+//!   tracing = { version = "0.1.37", default-features = false }
 //!   ```
 //!
 //! 
@@ -900,7 +900,7 @@
 //! [flags]: #crate-feature-flags
 #![cfg_attr(not(feature = "std"), no_std)]
 #![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))]
-#![doc(html_root_url = "https://docs.rs/tracing/0.1.36")]
+#![doc(html_root_url = "https://docs.rs/tracing/0.1.37")]
 #![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/"

From a4fc92ccf84a7988562ce7604283c7eae78294d2 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Thu, 6 Oct 2022 14:02:12 -0700
Subject: [PATCH 108/142] subscriber: revert "impl `LookupSpan` for `Box`
 and `Arc` (#2247)"

This reverts commit a0824d398aa2511de28371d30dda9203360a6cf5 (PR #2247).
As discussed in [this comment][1], the implementation for `Arc`s may
cause subtly incorrect behavior if actually used, due to the `&mut self`
receiver of the `LookupSpan::register_filter` method, since the `Arc`
cannot be mutably borrowed if any clones of it exist.

The APIs added in PRs #2269 and #2293 offer an alternative solution to
the same problems this change was intended to solve, and --- since this
change hasn't been published yet --- it can safely be reverted.

[1]:
    https://giethub.com/tokio-rs/tracing/pull/2247#issuecomment-1199924876
---
 tracing-subscriber/src/layer/mod.rs    |  4 +-
 tracing-subscriber/src/registry/mod.rs | 65 --------------------------
 2 files changed, 3 insertions(+), 66 deletions(-)

diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs
index b4533771c5..bdc154301a 100644
--- a/tracing-subscriber/src/layer/mod.rs
+++ b/tracing-subscriber/src/layer/mod.rs
@@ -685,7 +685,7 @@ use core::any::TypeId;
 
 feature! {
     #![feature = "alloc"]
-    use alloc::{vec::Vec, boxed::Box};
+    use alloc::boxed::Box;
     use core::ops::{Deref, DerefMut};
 }
 
@@ -1656,6 +1656,8 @@ where
 
 feature! {
     #![any(feature = "std", feature = "alloc")]
+    #[cfg(not(feature = "std"))]
+    use alloc::vec::Vec;
 
     macro_rules! layer_impl_body {
         () => {
diff --git a/tracing-subscriber/src/registry/mod.rs b/tracing-subscriber/src/registry/mod.rs
index 0f9fe76a1a..38af53e8ad 100644
--- a/tracing-subscriber/src/registry/mod.rs
+++ b/tracing-subscriber/src/registry/mod.rs
@@ -230,11 +230,6 @@ pub struct Scope<'a, R> {
 feature! {
     #![any(feature = "alloc", feature = "std")]
 
-    use alloc::{
-        boxed::Box,
-        sync::Arc
-    };
-
     #[cfg(not(feature = "smallvec"))]
     use alloc::vec::{self, Vec};
 
@@ -256,66 +251,6 @@ feature! {
     #[cfg(feature = "smallvec")]
     type SpanRefVecArray<'span, L> = [SpanRef<'span, L>; 16];
 
-    impl<'a, S> LookupSpan<'a> for Arc
-    where
-        S: LookupSpan<'a>,
-    {
-        type Data = >::Data;
-
-        fn span_data(&'a self, id: &Id) -> Option {
-            self.as_ref().span_data(id)
-        }
-
-        fn span(&'a self, id: &Id) -> Option>
-        where
-            Self: Sized,
-        {
-            self.as_ref().span(id).map(
-                |SpanRef {
-                    registry: _,
-                    data,
-                    #[cfg(feature = "registry")]
-                    filter,
-                }| SpanRef {
-                    registry: self,
-                    data,
-                    #[cfg(feature = "registry")]
-                    filter,
-                },
-            )
-        }
-    }
-
-    impl<'a, S> LookupSpan<'a> for Box
-    where
-        S: LookupSpan<'a>,
-    {
-        type Data = >::Data;
-
-        fn span_data(&'a self, id: &Id) -> Option {
-            self.as_ref().span_data(id)
-        }
-
-        fn span(&'a self, id: &Id) -> Option>
-        where
-            Self: Sized,
-        {
-            self.as_ref().span(id).map(
-                |SpanRef {
-                    registry: _,
-                    data,
-                    #[cfg(feature = "registry")]
-                    filter,
-                }| SpanRef {
-                    registry: self,
-                    data,
-                    #[cfg(feature = "registry")]
-                    filter,
-                },
-            )
-        }
-    }
-
     impl<'a, R> Scope<'a, R>
     where
         R: LookupSpan<'a>,

From a42e96380181b1ab91bf602586786e83cb3919a6 Mon Sep 17 00:00:00 2001
From: Poliorcetics 
Date: Mon, 29 Aug 2022 23:11:12 +0200
Subject: [PATCH 109/142] subscriber: replace unmaintained `ansi_term` with
 `nu-ansi-term` (#2287)

This increases the MSRV of `tracing-subscriber` to 1.50+.

## Motivation

[ansi_term] last time update is over 2 years ago, use
nushell team forked [nu-ansi-term] instead

## Solution

Use [nu-ansi-term].

Closes #2040

[nu-ansi-term]: https://github.com/nushell/nu-ansi-term
[ansi_term]: https://github.com/ogham/rust-ansi-term
---
 .github/workflows/CI.yml                      | 76 ++++++++-----------
 examples/Cargo.toml                           |  2 +-
 .../examples/sloggish/sloggish_subscriber.rs  |  4 +-
 tracing-subscriber/Cargo.toml                 |  6 +-
 tracing-subscriber/README.md                  |  4 +-
 tracing-subscriber/src/filter/env/builder.rs  | 16 ++--
 tracing-subscriber/src/fmt/format/mod.rs      | 16 ++--
 tracing-subscriber/src/fmt/format/pretty.rs   | 12 +--
 tracing-subscriber/src/lib.rs                 |  4 +-
 9 files changed, 64 insertions(+), 76 deletions(-)

diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index b7532f0639..9d4020ac6c 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -179,50 +179,38 @@ jobs:
     name: "cargo check (MSRV on ubuntu-latest)"
     needs: check
     runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v3
-    - name: "install Rust ${{ env.MSRV }}"
-      uses: actions-rs/toolchain@v1
-      with:
-        toolchain: ${{ env.MSRV }}
-        profile: minimal
-    - name: "install Rust nightly"
-      uses: actions-rs/toolchain@v1
-      with:
-        toolchain: nightly
-        profile: minimal
-    - name: Select minimal versions
-      uses: actions-rs/cargo@v1
-      with:
-        command: update
-        args: -Z minimal-versions
-        toolchain: nightly
-    - name: Check
-      uses: actions-rs/cargo@v1
-      with:
-        command: check
-        # skip the following crates:
-        # - tracing-appender, as it has its own MSRV.
-        #   TODO(eliza): remove this when appender is on the same MSRV as
-        #   everything else
-        # - the examples, as they are not published & we don't care about
-        #   MSRV support for them.
-        # - tracing-futures, as it depends on ancient tokio versions.
-        #   TODO(eliza): remove this when the ancient tokio deps are dropped
-        args: >-
-          --workspace --all-features --locked
-          --exclude=tracing-appender
-          --exclude=tracing-examples
-          --exclude=tracing-futures
-          --exclude=tracing-opentelemetry
-        toolchain: ${{ env.MSRV }}
-
-  # TODO: remove this once tracing's MSRV is bumped.
-  check-msrv-appender:
-    # Run `cargo check` on our minimum supported Rust version (1.53.0).
-    name: "cargo check (tracing-appender MSRV)"
-    needs: check
-    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        # cargo hack --feature-powerset will have a significant permutation
+        # number, we can't just use --all as it increases the runtime
+        # further than what we would like to
+        subcrate:
+        - tracing-appender
+        - tracing-attributes
+        - tracing-core
+        - tracing-futures
+        - tracing-log
+        - tracing-macros
+        - tracing-serde
+        - tracing-subscriber
+        - tracing-tower
+        - tracing-opentelemetry
+        - tracing
+        toolchain:
+        - 1.49.0
+        - stable
+        # TODO(eliza): remove this when appender is on the same MSRV.
+        #              same for tracing subscriber
+        exclude:
+        - subcrate: tracing-appender
+          toolchain: 1.49.0
+        - subcrate: tracing-subscriber
+          toolchain: 1.49.0
+        include:
+        - subcrate: tracing-appender
+          toolchain: 1.53.0
+        - subcrate: tracing-subscriber
+          toolchain: 1.50.0
     steps:
     - uses: actions/checkout@v3
     - name: "install Rust ${{ env.APPENDER_MSRV }}"
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index c08515bc79..76d663a894 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -43,7 +43,7 @@ bytes = "1"
 argh = "0.1.8"
 
 # sloggish example
-ansi_term = "0.12.1"
+nu-ansi-term = "0.46.0"
 humantime = "2.1.0"
 log = "0.4.17"
 
diff --git a/examples/examples/sloggish/sloggish_subscriber.rs b/examples/examples/sloggish/sloggish_subscriber.rs
index f438ed6526..971e607fda 100644
--- a/examples/examples/sloggish/sloggish_subscriber.rs
+++ b/examples/examples/sloggish/sloggish_subscriber.rs
@@ -10,7 +10,7 @@
 //!
 //! [`slog-term`]: https://docs.rs/slog-term/2.4.0/slog_term/
 //! [`slog` README]: https://github.com/slog-rs/slog#terminal-output-example
-use ansi_term::{Color, Style};
+use nu_ansi_term::{Color, Style};
 use tracing::{
     field::{Field, Visit},
     Id, Level, Subscriber,
@@ -129,7 +129,7 @@ impl<'a> Visit for Event<'a> {
             write!(
                 &mut self.stderr,
                 "{}",
-                // Have to alloc here due to `ansi_term`'s API...
+                // Have to alloc here due to `nu_ansi_term`'s API...
                 Style::new().bold().paint(format!("{:?}", value))
             )
             .unwrap();
diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml
index 3ed2e0db5e..b580b5fccf 100644
--- a/tracing-subscriber/Cargo.toml
+++ b/tracing-subscriber/Cargo.toml
@@ -20,7 +20,7 @@ categories = [
     "asynchronous",
 ]
 keywords = ["logging", "tracing", "metrics", "subscriber"]
-rust-version = "1.49.0"
+rust-version = "1.50.0"
 
 [features]
 
@@ -29,7 +29,7 @@ alloc = []
 std = ["alloc", "tracing-core/std"]
 env-filter = ["matchers", "regex", "once_cell", "tracing", "std", "thread_local"]
 fmt = ["registry", "std"]
-ansi = ["fmt", "ansi_term"]
+ansi = ["fmt", "nu-ansi-term"]
 registry = ["sharded-slab", "thread_local", "std"]
 json = ["tracing-serde", "serde", "serde_json"]
 valuable = ["tracing-core/valuable", "valuable_crate", "valuable-serde", "tracing-serde/valuable"]
@@ -49,7 +49,7 @@ once_cell = { optional = true, version = "1.13.0" }
 
 # fmt
 tracing-log = { path = "../tracing-log", version = "0.1.3", optional = true, default-features = false, features = ["log-tracer", "std"] }
-ansi_term = { version = "0.12.1", optional = true }
+nu-ansi-term = { version = "0.46.0", optional = true }
 time = { version = "0.3.2", features = ["formatting"], optional = true }
 
 # only required by the json feature
diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md
index ba2b4e1c33..124fb956d0 100644
--- a/tracing-subscriber/README.md
+++ b/tracing-subscriber/README.md
@@ -32,14 +32,14 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers.
 [discord-url]: https://discord.gg/EeF3cQw
 [maint-badge]: https://img.shields.io/badge/maintenance-experimental-blue.svg
 
-*Compiler support: [requires `rustc` 1.49+][msrv]*
+*Compiler support: [requires `rustc` 1.50+][msrv]*
 
 [msrv]: #supported-rust-versions
 
 ## Supported Rust Versions
 
 Tracing is built against the latest stable release. The minimum supported
-version is 1.49. The current Tracing version is not guaranteed to build on Rust
+version is 1.50. 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
diff --git a/tracing-subscriber/src/filter/env/builder.rs b/tracing-subscriber/src/filter/env/builder.rs
index f776f1f18e..c814707e6c 100644
--- a/tracing-subscriber/src/filter/env/builder.rs
+++ b/tracing-subscriber/src/filter/env/builder.rs
@@ -209,15 +209,15 @@ impl Builder {
         }
 
         if !disabled.is_empty() {
-            #[cfg(feature = "ansi_term")]
-            use ansi_term::{Color, Style};
+            #[cfg(feature = "nu_ansi_term")]
+            use nu_ansi_term::{Color, Style};
             // NOTE: We can't use a configured `MakeWriter` because the EnvFilter
             // has no knowledge of any underlying subscriber or collector, which
             // may or may not use a `MakeWriter`.
             let warn = |msg: &str| {
-                #[cfg(not(feature = "ansi_term"))]
+                #[cfg(not(feature = "nu_ansi_term"))]
                 let msg = format!("warning: {}", msg);
-                #[cfg(feature = "ansi_term")]
+                #[cfg(feature = "nu_ansi_term")]
                 let msg = {
                     let bold = Style::new().bold();
                     let mut warning = Color::Yellow.paint("warning");
@@ -227,9 +227,9 @@ impl Builder {
                 eprintln!("{}", msg);
             };
             let ctx_prefixed = |prefix: &str, msg: &str| {
-                #[cfg(not(feature = "ansi_term"))]
+                #[cfg(not(feature = "nu_ansi_term"))]
                 let msg = format!("{} {}", prefix, msg);
-                #[cfg(feature = "ansi_term")]
+                #[cfg(feature = "nu_ansi_term")]
                 let msg = {
                     let mut equal = Color::Fixed(21).paint("="); // dark blue
                     equal.style_ref_mut().is_bold = true;
@@ -240,9 +240,9 @@ impl Builder {
             let ctx_help = |msg| ctx_prefixed("help:", msg);
             let ctx_note = |msg| ctx_prefixed("note:", msg);
             let ctx = |msg: &str| {
-                #[cfg(not(feature = "ansi_term"))]
+                #[cfg(not(feature = "nu_ansi_term"))]
                 let msg = format!("note: {}", msg);
-                #[cfg(feature = "ansi_term")]
+                #[cfg(feature = "nu_ansi_term")]
                 let msg = {
                     let mut pipe = Color::Fixed(21).paint("|");
                     pipe.style_ref_mut().is_bold = true;
diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs
index ec79ac1402..b8a482e55f 100644
--- a/tracing-subscriber/src/fmt/format/mod.rs
+++ b/tracing-subscriber/src/fmt/format/mod.rs
@@ -46,7 +46,7 @@ use tracing_core::{
 use tracing_log::NormalizeEvent;
 
 #[cfg(feature = "ansi")]
-use ansi_term::{Colour, Style};
+use nu_ansi_term::{Color, Style};
 
 #[cfg(feature = "json")]
 mod json;
@@ -101,7 +101,7 @@ pub use pretty::*;
 ///   does not support ANSI escape codes (such as a log file), and they should
 ///   not be emitted.
 ///
-///   Crates like [`ansi_term`] and [`owo-colors`] can be used to add ANSI
+///   Crates like [`nu_ansi_term`] and [`owo-colors`] can be used to add ANSI
 ///   escape codes to formatted output.
 ///
 /// * The actual [`Event`] to be formatted.
@@ -189,7 +189,7 @@ pub use pretty::*;
 /// [implements `FormatFields`]: super::FmtContext#impl-FormatFields<'writer>
 /// [ANSI terminal escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code
 /// [`Writer::has_ansi_escapes`]: Writer::has_ansi_escapes
-/// [`ansi_term`]: https://crates.io/crates/ansi_term
+/// [`nu_ansi_term`]: https://crates.io/crates/nu_ansi_term
 /// [`owo-colors`]: https://crates.io/crates/owo-colors
 /// [default formatter]: Full
 pub trait FormatEvent
@@ -1484,11 +1484,11 @@ impl<'a> fmt::Display for FmtLevel<'a> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if self.ansi {
             match *self.level {
-                Level::TRACE => write!(f, "{}", Colour::Purple.paint(TRACE_STR)),
-                Level::DEBUG => write!(f, "{}", Colour::Blue.paint(DEBUG_STR)),
-                Level::INFO => write!(f, "{}", Colour::Green.paint(INFO_STR)),
-                Level::WARN => write!(f, "{}", Colour::Yellow.paint(WARN_STR)),
-                Level::ERROR => write!(f, "{}", Colour::Red.paint(ERROR_STR)),
+                Level::TRACE => write!(f, "{}", Color::Purple.paint(TRACE_STR)),
+                Level::DEBUG => write!(f, "{}", Color::Blue.paint(DEBUG_STR)),
+                Level::INFO => write!(f, "{}", Color::Green.paint(INFO_STR)),
+                Level::WARN => write!(f, "{}", Color::Yellow.paint(WARN_STR)),
+                Level::ERROR => write!(f, "{}", Color::Red.paint(ERROR_STR)),
             }
         } else {
             match *self.level {
diff --git a/tracing-subscriber/src/fmt/format/pretty.rs b/tracing-subscriber/src/fmt/format/pretty.rs
index a50d08ba75..12071de922 100644
--- a/tracing-subscriber/src/fmt/format/pretty.rs
+++ b/tracing-subscriber/src/fmt/format/pretty.rs
@@ -14,7 +14,7 @@ use tracing_core::{
 #[cfg(feature = "tracing-log")]
 use tracing_log::NormalizeEvent;
 
-use ansi_term::{Colour, Style};
+use nu_ansi_term::{Color, Style};
 
 /// An excessively pretty, human-readable event formatter.
 ///
@@ -143,11 +143,11 @@ impl Default for Pretty {
 impl Pretty {
     fn style_for(level: &Level) -> Style {
         match *level {
-            Level::TRACE => Style::new().fg(Colour::Purple),
-            Level::DEBUG => Style::new().fg(Colour::Blue),
-            Level::INFO => Style::new().fg(Colour::Green),
-            Level::WARN => Style::new().fg(Colour::Yellow),
-            Level::ERROR => Style::new().fg(Colour::Red),
+            Level::TRACE => Style::new().fg(Color::Purple),
+            Level::DEBUG => Style::new().fg(Color::Blue),
+            Level::INFO => Style::new().fg(Color::Green),
+            Level::WARN => Style::new().fg(Color::Yellow),
+            Level::ERROR => Style::new().fg(Color::Red),
         }
     }
 
diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs
index 9de70a90d1..8089230071 100644
--- a/tracing-subscriber/src/lib.rs
+++ b/tracing-subscriber/src/lib.rs
@@ -10,7 +10,7 @@
 //! `tracing-subscriber` is intended for use by both `Subscriber` authors and
 //! application authors using `tracing` to instrument their applications.
 //!
-//! *Compiler support: [requires `rustc` 1.49+][msrv]*
+//! *Compiler support: [requires `rustc` 1.50+][msrv]*
 //!
 //! [msrv]: #supported-rust-versions
 //!
@@ -138,7 +138,7 @@
 //! ## Supported Rust Versions
 //!
 //! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.49. The current Tracing version is not guaranteed to build on
+//! version is 1.50. 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

From ecd7e06fdda72e2b33657785c25a0aaba641805a Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Thu, 6 Oct 2022 15:00:59 -0700
Subject: [PATCH 110/142] chore(ci): remove requirement for removed job

This should fix the build, oopsie.
---
 .github/workflows/CI.yml | 1 -
 1 file changed, 1 deletion(-)

diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 9d4020ac6c..6dfd3e3f71 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -385,7 +385,6 @@ jobs:
       - minimal-versions
       - cargo-hack
       - check-msrv
-      - check-msrv-appender
       - test-build-wasm
       - test-wasm
       - test-features-stable

From b28c9351dd4f34ed3c7d5df88bb5c2e694d9c951 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Thu, 6 Oct 2022 15:25:39 -0700
Subject: [PATCH 111/142] subscriber: prepare to release v0.3.16 (#2342)

# 0.3.16 (October 6, 2022)

This release of `tracing-subscriber` fixes a regression introduced in
[v0.3.15][subscriber-0.3.15] where `Option::None`'s `Layer`
implementation would set the max level hint to `OFF`. In addition, it
adds several new APIs, including the `Filter::event_enabled` method for
filtering events based on fields values, and the ability to log internal
errors that occur when writing a log line.

This release also replaces the dependency on the unmaintained
[`ansi-term`] crate with the [`nu-ansi-term`] crate, resolving an
*informational* security advisory ([RUSTSEC-2021-0139] for
[`ansi-term`]'s maintainance status. This increases the minimum
supported Rust version (MSRV) to Rust 1.50+, although the crate should
still compile for the previous MSRV of Rust 1.49+ when the `ansi`
feature is not enabled.

### Fixed

- **layer**: `Option::None`'s `Layer` impl always setting the
  `max_level_hint` to `LevelFilter::OFF` (#2321)
- Compilation with `-Z minimal versions` (#2246)
- **env-filter**: Clarify that disabled level warnings are emitted by
  `tracing-subscriber` (#2285)

### Added

- **fmt**: Log internal errors to `stderr` if writing a log line fails
  (#2102)
- **fmt**: `FmtLayer::log_internal_errors` and
  `FmtSubscriber::log_internal_errors` methods for configuring whether
  internal writer errors are printed to `stderr` (#2102)
- **fmt**: `#[must_use]` attributes on builders to warn if a
  `Subscriber` is configured but not set as the default subscriber
  (#2239)
- **filter**: `Filter::event_enabled` method for filtering an event
  based on its fields (#2245, #2251)
- **filter**: `Targets::default_level` accessor (#2242)

### Changed

- **ansi**: Replaced dependency on unmaintained `ansi-term` crate with
  `nu-ansi-term` ((#2287, fixes informational advisory
  [RUSTSEC-2021-0139])
- `tracing-core`: updated to [0.1.30][core-0.1.30]
- Minimum Supported Rust Version (MSRV) increased to Rust 1.50+ (when
  the `ansi`) feature flag is enabled (#2287)

### Documented

- **fmt**: Correct inaccuracies in `fmt::init` documentation (#2224)
- **filter**: Fix incorrect doc link in `filter::Not` combinator
  (#2249)

Thanks to new contributors @cgbur, @DesmondWillowbrook, @RalfJung, and
@poliorcetics, as well as returning contributors @CAD97, @connec,
@jswrenn, @guswynn, and @bryangarza, for contributing to this release!

[nu-ansi-term]: https://github.com/nushell/nu-ansi-term
[ansi_term]: https://github.com/ogham/rust-ansi-term
[RUSTSEC-2021-0139]: https://rustsec.org/advisories/RUSTSEC-2021-0139.html
[core-0.1.30]: https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.30
[subscriber-0.3.15]: https://github.com/tokio-rs/tracing/releases/tag/tracing-subscriber-0.3.15
---
 tracing-subscriber/CHANGELOG.md | 69 +++++++++++++++++++++++++++++++++
 tracing-subscriber/Cargo.toml   |  4 +-
 2 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/tracing-subscriber/CHANGELOG.md b/tracing-subscriber/CHANGELOG.md
index e2ac4e7c31..f283dc61e2 100644
--- a/tracing-subscriber/CHANGELOG.md
+++ b/tracing-subscriber/CHANGELOG.md
@@ -1,3 +1,72 @@
+# 0.3.16 (October 6, 2022)
+
+This release of `tracing-subscriber` fixes a regression introduced in
+[v0.3.15][subscriber-0.3.15] where `Option::None`'s `Layer` implementation would
+set the max level hint to `OFF`. In addition, it adds several new APIs,
+including the `Filter::event_enabled` method for filtering events based on
+fields values, and the ability to log internal errors that occur when writing a
+log line.
+
+This release also replaces the dependency on the unmaintained [`ansi-term`]
+crate with the [`nu-ansi-term`] crate, resolving an *informational* security
+advisory ([RUSTSEC-2021-0139]) for [`ansi-term`]'s maintainance status. This
+increases the minimum supported Rust version (MSRV) to Rust 1.50+, although the
+crate should still compile for the previous MSRV of Rust 1.49+ when the `ansi`
+feature is not enabled.
+
+### Fixed
+
+- **layer**: `Option::None`'s `Layer` impl always setting the `max_level_hint`
+  to `LevelFilter::OFF` ([#2321])
+- Compilation with `-Z minimal versions` ([#2246])
+- **env-filter**: Clarify that disabled level warnings are emitted by
+  `tracing-subscriber` ([#2285])
+
+### Added
+
+- **fmt**: Log internal errors to `stderr` if writing a log line fails ([#2102])
+- **fmt**: `FmtLayer::log_internal_errors` and
+  `FmtSubscriber::log_internal_errors` methods for configuring whether internal
+  writer errors are printed to `stderr` ([#2102])
+- **fmt**: `#[must_use]` attributes on builders to warn if a `Subscriber` is
+  configured but not set as the default subscriber ([#2239])
+- **filter**: `Filter::event_enabled` method for filtering an event based on its
+  fields ([#2245], [#2251])
+- **filter**: `Targets::default_level` accessor ([#2242])
+
+### Changed
+
+- **ansi**: Replaced dependency on unmaintained `ansi-term` crate with
+  `nu-ansi-term` (([#2287], fixes informational advisory [RUSTSEC-2021-0139])
+- `tracing-core`: updated to [0.1.30][core-0.1.30]
+- Minimum Supported Rust Version (MSRV) increased to Rust 1.50+ (when the
+  `ansi`) feature flag is enabled ([#2287])
+
+### Documented
+
+- **fmt**: Correct inaccuracies in `fmt::init` documentation ([#2224])
+- **filter**: Fix incorrect doc link in `filter::Not` combinator ([#2249])
+
+Thanks to new contributors @cgbur, @DesmondWillowbrook, @RalfJung, and
+@poliorcetics, as well as returning contributors @CAD97, @connec, @jswrenn,
+@guswynn, and @bryangarza, for contributing to this release!
+
+[nu-ansi-term]: https://github.com/nushell/nu-ansi-term
+[ansi_term]: https://github.com/ogham/rust-ansi-term
+[RUSTSEC-2021-0139]: https://rustsec.org/advisories/RUSTSEC-2021-0139.html
+[core-0.1.30]: https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.30
+[subscriber-0.3.15]: https://github.com/tokio-rs/tracing/releases/tag/tracing-subscriber-0.3.15
+[#2321]: https://github.com/tokio-rs/tracing/pull/2321
+[#2246]: https://github.com/tokio-rs/tracing/pull/2246
+[#2285]: https://github.com/tokio-rs/tracing/pull/2285
+[#2102]: https://github.com/tokio-rs/tracing/pull/2102
+[#2239]: https://github.com/tokio-rs/tracing/pull/2239
+[#2245]: https://github.com/tokio-rs/tracing/pull/2245
+[#2251]: https://github.com/tokio-rs/tracing/pull/2251
+[#2287]: https://github.com/tokio-rs/tracing/pull/2287
+[#2224]: https://github.com/tokio-rs/tracing/pull/2224
+[#2249]: https://github.com/tokio-rs/tracing/pull/2249
+
 # 0.3.15 (Jul 20, 2022)
 
 This release fixes a bug where the `reload` layer would fail to pass through
diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml
index b580b5fccf..22373ae044 100644
--- a/tracing-subscriber/Cargo.toml
+++ b/tracing-subscriber/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "tracing-subscriber"
-version = "0.3.15"
+version = "0.3.16"
 authors = [
     "Eliza Weisman ",
     "David Barsky ",
@@ -38,7 +38,7 @@ valuable = ["tracing-core/valuable", "valuable_crate", "valuable-serde", "tracin
 local-time = ["time/local-offset"]
 
 [dependencies]
-tracing-core = { path = "../tracing-core", version = "0.1.28", default-features = false }
+tracing-core = { path = "../tracing-core", version = "0.1.30", default-features = false }
 
 # only required by the filter feature
 tracing = { optional = true, path = "../tracing", version = "0.1.35", default-features = false }

From d2f47f1e3fe77ac9e446a4ce6bb98f22189702e9 Mon Sep 17 00:00:00 2001
From: keepsimple1 
Date: Wed, 12 Apr 2023 14:22:44 -0700
Subject: [PATCH 112/142] chore: fix clippy warnings for v0.1.x (#2552)

## Motivation

Clippy check fails in recent CI runs in v0.1.x branch PRs, for example
this run:
https://github.com/tokio-rs/tracing/actions/runs/4641107803/jobs/8215263838


Relevant error logs:
```
error: lint `const_err` has been removed: converted into hard error, see issue #71800  for more information
   --> tracing-core/src/lib.rs:132:5
    |
132 |     const_err,
    |     ^^^^^^^^^
    |
    = note: `-D renamed-and-removed-lints` implied by `-D warnings`


error: deref which would be done by auto-deref
   --> tracing-core/src/dispatcher.rs:371:26
    |
371 |                 return f(&*entered.current());
    |                          ^^^^^^^^^^^^^^^^^^^ help: try this: `&entered.current()`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
    = note: `-D clippy::explicit-auto-deref` implied by `-D warnings`


error: deref which would be done by auto-deref
   --> tracing-core/src/dispatcher.rs:393:20
    |
393 |             Some(f(&*entered.current()))
    |                    ^^^^^^^^^^^^^^^^^^^ help: try this: `&entered.current()`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
```

## Solution

Fix the warnings based on the suggestions for Clippy.
---
 tracing-appender/src/lib.rs                         | 1 -
 tracing-attributes/src/expand.rs                    | 2 +-
 tracing-attributes/src/lib.rs                       | 1 -
 tracing-attributes/tests/ui/async_instrument.stderr | 2 +-
 tracing-core/src/dispatcher.rs                      | 4 ++--
 tracing-core/src/lib.rs                             | 1 -
 tracing-error/src/lib.rs                            | 1 -
 tracing-flame/src/lib.rs                            | 1 -
 tracing-futures/src/lib.rs                          | 1 -
 tracing-log/src/lib.rs                              | 1 -
 tracing-serde/src/lib.rs                            | 1 -
 tracing-subscriber/src/filter/targets.rs            | 2 +-
 tracing-subscriber/src/lib.rs                       | 1 -
 tracing-subscriber/src/registry/sharded.rs          | 2 +-
 tracing-tower/src/lib.rs                            | 1 -
 tracing/src/lib.rs                                  | 1 -
 16 files changed, 6 insertions(+), 17 deletions(-)

diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs
index 42346b8491..b93d18fec1 100644
--- a/tracing-appender/src/lib.rs
+++ b/tracing-appender/src/lib.rs
@@ -133,7 +133,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,
diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs
index 7005b4423e..cb12ad87e4 100644
--- a/tracing-attributes/src/expand.rs
+++ b/tracing-attributes/src/expand.rs
@@ -134,7 +134,7 @@ fn gen_block(
             .into_iter()
             .flat_map(|param| match param {
                 FnArg::Typed(PatType { pat, ty, .. }) => {
-                    param_names(*pat, RecordType::parse_from_ty(&*ty))
+                    param_names(*pat, RecordType::parse_from_ty(&ty))
                 }
                 FnArg::Receiver(_) => Box::new(iter::once((
                     Ident::new("self", param.span()),
diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs
index f5974e4e52..cc0b8e5198 100644
--- a/tracing-attributes/src/lib.rs
+++ b/tracing-attributes/src/lib.rs
@@ -64,7 +64,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,
diff --git a/tracing-attributes/tests/ui/async_instrument.stderr b/tracing-attributes/tests/ui/async_instrument.stderr
index db6f6b4343..519b71e045 100644
--- a/tracing-attributes/tests/ui/async_instrument.stderr
+++ b/tracing-attributes/tests/ui/async_instrument.stderr
@@ -93,6 +93,6 @@ error[E0308]: mismatched types
 42 |   async fn extra_semicolon() -> i32 {
    |  ___________________________________^
 43 | |     1;
-   | |      - help: remove this semicolon
+   | |      - help: remove this semicolon to return this value
 44 | | }
    | |_^ expected `i32`, found `()`
diff --git a/tracing-core/src/dispatcher.rs b/tracing-core/src/dispatcher.rs
index 36b3cfd85f..7f2f4060aa 100644
--- a/tracing-core/src/dispatcher.rs
+++ b/tracing-core/src/dispatcher.rs
@@ -368,7 +368,7 @@ where
     CURRENT_STATE
         .try_with(|state| {
             if let Some(entered) = state.enter() {
-                return f(&*entered.current());
+                return f(&entered.current());
             }
 
             f(&Dispatch::none())
@@ -390,7 +390,7 @@ pub fn get_current(f: impl FnOnce(&Dispatch) -> T) -> Option {
     CURRENT_STATE
         .try_with(|state| {
             let entered = state.enter()?;
-            Some(f(&*entered.current()))
+            Some(f(&entered.current()))
         })
         .ok()?
 }
diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs
index c1f87b22f0..694f0f5a03 100644
--- a/tracing-core/src/lib.rs
+++ b/tracing-core/src/lib.rs
@@ -129,7 +129,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,
diff --git a/tracing-error/src/lib.rs b/tracing-error/src/lib.rs
index b94205ceeb..e3ba0ac743 100644
--- a/tracing-error/src/lib.rs
+++ b/tracing-error/src/lib.rs
@@ -190,7 +190,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,
diff --git a/tracing-flame/src/lib.rs b/tracing-flame/src/lib.rs
index f7d670aa61..51dd436d34 100644
--- a/tracing-flame/src/lib.rs
+++ b/tracing-flame/src/lib.rs
@@ -117,7 +117,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,
diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs
index 2712c160ac..100f16ce18 100644
--- a/tracing-futures/src/lib.rs
+++ b/tracing-futures/src/lib.rs
@@ -81,7 +81,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,
diff --git a/tracing-log/src/lib.rs b/tracing-log/src/lib.rs
index 44b3f1d32d..e2eff92014 100644
--- a/tracing-log/src/lib.rs
+++ b/tracing-log/src/lib.rs
@@ -112,7 +112,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,
diff --git a/tracing-serde/src/lib.rs b/tracing-serde/src/lib.rs
index f099853ec0..6530e09478 100644
--- a/tracing-serde/src/lib.rs
+++ b/tracing-serde/src/lib.rs
@@ -168,7 +168,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,
diff --git a/tracing-subscriber/src/filter/targets.rs b/tracing-subscriber/src/filter/targets.rs
index e1407114b5..58ccfe4d21 100644
--- a/tracing-subscriber/src/filter/targets.rs
+++ b/tracing-subscriber/src/filter/targets.rs
@@ -324,7 +324,7 @@ impl Targets {
     /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF));
     /// ```
     pub fn default_level(&self) -> Option {
-        self.0.directives().into_iter().find_map(|d| {
+        self.0.directives().find_map(|d| {
             if d.target.is_none() {
                 Some(d.level)
             } else {
diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs
index 8089230071..63cd3d3154 100644
--- a/tracing-subscriber/src/lib.rs
+++ b/tracing-subscriber/src/lib.rs
@@ -180,7 +180,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,
diff --git a/tracing-subscriber/src/registry/sharded.rs b/tracing-subscriber/src/registry/sharded.rs
index 7978997678..07c94fccb5 100644
--- a/tracing-subscriber/src/registry/sharded.rs
+++ b/tracing-subscriber/src/registry/sharded.rs
@@ -422,7 +422,7 @@ impl<'a> SpanData<'a> for Data<'a> {
     }
 
     fn metadata(&self) -> &'static Metadata<'static> {
-        (*self).inner.metadata
+        self.inner.metadata
     }
 
     fn parent(&self) -> Option<&Id> {
diff --git a/tracing-tower/src/lib.rs b/tracing-tower/src/lib.rs
index 633dbb599b..3510071b40 100644
--- a/tracing-tower/src/lib.rs
+++ b/tracing-tower/src/lib.rs
@@ -9,7 +9,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index 342e04a825..3d70d7189a 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -911,7 +911,6 @@
     rust_2018_idioms,
     unreachable_pub,
     bad_style,
-    const_err,
     dead_code,
     improper_ctypes,
     non_shorthand_field_patterns,

From eb6c6a39ae88e11f2380b895a8f34a2e4fa9572c Mon Sep 17 00:00:00 2001
From: Andrew Halle 
Date: Thu, 13 Apr 2023 15:01:11 -0700
Subject: [PATCH 113/142] subscriber: fix doc typo (#2548)

**This commit**
- tyoe -> type.

Co-authored-by: Eliza Weisman 
---
 tracing-subscriber/src/fmt/writer.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tracing-subscriber/src/fmt/writer.rs b/tracing-subscriber/src/fmt/writer.rs
index 3fe9455664..2b9f9e973e 100644
--- a/tracing-subscriber/src/fmt/writer.rs
+++ b/tracing-subscriber/src/fmt/writer.rs
@@ -16,7 +16,7 @@ use tracing_core::Metadata;
 /// This trait is already implemented for function pointers and
 /// immutably-borrowing closures that return an instance of [`io::Write`], such
 /// as [`io::stdout`] and [`io::stderr`]. Additionally, it is implemented for
-/// [`std::sync::Mutex`][mutex] when the tyoe inside the mutex implements
+/// [`std::sync::Mutex`][mutex] when the type inside the mutex implements
 /// [`io::Write`].
 ///
 /// # Examples

From 981b6bf9195fb444e80ff47827469bfa0aa2d5ee Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Mon, 10 Oct 2022 11:58:21 -0700
Subject: [PATCH 114/142] chore: allow renamed and removed lints on docs build
 (#2344)

Currently, the Netlify docs are failing because of a warning that one of
the lints we explicitly enable is no longer a warning on nightly but
becoming a hard error. This is because the docs are built on nightly
with `-D warnings`, which denies all warnings...which seems not ideal.

For now, let's not remove that lint from the `deny` list, as it's still
a valid lint on stable. Instead, this PR explicitly allows that lint on
the docs build.
---
 netlify.toml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/netlify.toml b/netlify.toml
index 271c5d4b3a..6e3beff1a6 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -8,6 +8,7 @@
 [build.environment]
   RUSTDOCFLAGS="""
     -D warnings \
+    --force-warn renamed-and-removed-lints \
     --cfg docsrs \
     --cfg tracing_unstable
     """

From 399e707a402386d64ebeb29ba12a5941a13eb3f9 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Mon, 10 Oct 2022 12:10:58 -0700
Subject: [PATCH 115/142] subscriber: impl `fmt::Display` for `filter::Targets`
 (#2343)

## Motivation

There's currently a `fmt::Display` impl for `EnvFilter` that emits an
equiovalent filter string that can be parsed back into an `EnvFilter`,
but the `Targets` filter does not have a `fmt::Display` impl. We ought
to have one, especially to make using `Targets` with `clap` v4.0 easier.

## Solution

This branch adds a `fmt::Display` impl for `filter::Targets`. The
implementation is pretty straightforward.

I also added tests that a `Targets`' `fmt::Display` output can be parsed
back into a filter that's equivalent to the original.
---
 tracing-subscriber/src/filter/targets.rs | 53 ++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/tracing-subscriber/src/filter/targets.rs b/tracing-subscriber/src/filter/targets.rs
index 58ccfe4d21..19f2d99086 100644
--- a/tracing-subscriber/src/filter/targets.rs
+++ b/tracing-subscriber/src/filter/targets.rs
@@ -16,6 +16,7 @@ use crate::{
 #[cfg(not(feature = "std"))]
 use alloc::string::String;
 use core::{
+    fmt,
     iter::{Extend, FilterMap, FromIterator},
     slice,
     str::FromStr,
@@ -488,6 +489,20 @@ impl<'a> IntoIterator for &'a Targets {
     }
 }
 
+impl fmt::Display for Targets {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut directives = self.0.directives();
+        if let Some(directive) = directives.next() {
+            write!(f, "{}", directive)?;
+            for directive in directives {
+                write!(f, ",{}", directive)?;
+            }
+        }
+
+        Ok(())
+    }
+}
+
 /// An owning iterator over the [target]-[level] pairs of a `Targets` filter.
 ///
 /// This struct is created by the `IntoIterator` trait implementation of [`Targets`].
@@ -778,4 +793,42 @@ mod tests {
             crate2=debug,crate3=trace,crate3::mod2::mod1=off",
         );
     }
+
+    /// Test that the `fmt::Display` implementation for `Targets` emits a string
+    /// that can itself be parsed as a `Targets`, and that the parsed `Targets`
+    /// is equivalent to the original one.
+    #[test]
+    fn display_roundtrips() {
+        fn test_roundtrip(s: &str) {
+            let filter = expect_parse(s);
+            // we don't assert that the display output is equivalent to the
+            // original parsed filter string, because the `Display` impl always
+            // uses lowercase level names and doesn't use the
+            // target-without-level shorthand syntax. while they may not be
+            // textually equivalent, though, they should still *parse* to the
+            // same filter.
+            let formatted = filter.to_string();
+            let filter2 = match dbg!(&formatted).parse::() {
+                Ok(filter) => filter,
+                Err(e) => panic!(
+                    "failed to parse formatted filter string {:?}: {}",
+                    formatted, e
+                ),
+            };
+            assert_eq!(filter, filter2);
+        }
+
+        test_roundtrip("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
+        test_roundtrip(
+            "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\
+        crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF",
+        );
+        test_roundtrip(
+            "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
+             crate2=debug,crate3=trace,crate3::mod2::mod1=off",
+        );
+        test_roundtrip("crate1::mod1,crate1::mod2,info");
+        test_roundtrip("crate1");
+        test_roundtrip("info");
+    }
 }

From 89d0ca845d121e33234c8dee5a33af40ae45b9e9 Mon Sep 17 00:00:00 2001
From: nitnelave 
Date: Mon, 10 Oct 2022 23:51:59 +0200
Subject: [PATCH 116/142] attributes: support custom levels for `ret` and `err`
 (#2335)

This branch adds the ability to override the level of the events
generated by the `ret` and `err` arguments to `#[instrument]`. An
overridden level can be specified with:

```rust

```
```rust

```
and so on.

This syntax is fully backwards compatible with existing uses of the
attribute.

In addition, some refactoring was done to how levels are parsed and how
the tokens for a specified level is generated.

Fixes #2330
; Conflicts:
;	tracing-attributes/src/lib.rs
---
 tracing-attributes/src/attr.rs   | 184 +++++++++++++++++++------------
 tracing-attributes/src/expand.rs |  39 ++++---
 tracing-attributes/src/lib.rs    |  20 ++++
 tracing-attributes/tests/err.rs  |  69 ++++++++++++
 tracing-attributes/tests/ret.rs  |  50 +++++++++
 5 files changed, 279 insertions(+), 83 deletions(-)

diff --git a/tracing-attributes/src/attr.rs b/tracing-attributes/src/attr.rs
index ff875e1797..0ad68ef756 100644
--- a/tracing-attributes/src/attr.rs
+++ b/tracing-attributes/src/attr.rs
@@ -6,6 +6,14 @@ use quote::{quote, quote_spanned, ToTokens};
 use syn::ext::IdentExt as _;
 use syn::parse::{Parse, ParseStream};
 
+/// Arguments to `#[instrument(err(...))]` and `#[instrument(ret(...))]` which describe how the
+/// return value event should be emitted.
+#[derive(Clone, Default, Debug)]
+pub(crate) struct EventArgs {
+    level: Option,
+    pub(crate) mode: FormatMode,
+}
+
 #[derive(Clone, Default, Debug)]
 pub(crate) struct InstrumentArgs {
     level: Option,
@@ -16,51 +24,15 @@ pub(crate) struct InstrumentArgs {
     pub(crate) skips: HashSet,
     pub(crate) skip_all: bool,
     pub(crate) fields: Option,
-    pub(crate) err_mode: Option,
-    pub(crate) ret_mode: Option,
+    pub(crate) err_args: Option,
+    pub(crate) ret_args: Option,
     /// Errors describing any unrecognized parse inputs that we skipped.
     parse_warnings: Vec,
 }
 
 impl InstrumentArgs {
-    pub(crate) fn level(&self) -> impl ToTokens {
-        fn is_level(lit: &LitInt, expected: u64) -> bool {
-            match lit.base10_parse::() {
-                Ok(value) => value == expected,
-                Err(_) => false,
-            }
-        }
-
-        match &self.level {
-            Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("trace") => {
-                quote!(tracing::Level::TRACE)
-            }
-            Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("debug") => {
-                quote!(tracing::Level::DEBUG)
-            }
-            Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("info") => {
-                quote!(tracing::Level::INFO)
-            }
-            Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("warn") => {
-                quote!(tracing::Level::WARN)
-            }
-            Some(Level::Str(ref lit)) if lit.value().eq_ignore_ascii_case("error") => {
-                quote!(tracing::Level::ERROR)
-            }
-            Some(Level::Int(ref lit)) if is_level(lit, 1) => quote!(tracing::Level::TRACE),
-            Some(Level::Int(ref lit)) if is_level(lit, 2) => quote!(tracing::Level::DEBUG),
-            Some(Level::Int(ref lit)) if is_level(lit, 3) => quote!(tracing::Level::INFO),
-            Some(Level::Int(ref lit)) if is_level(lit, 4) => quote!(tracing::Level::WARN),
-            Some(Level::Int(ref lit)) if is_level(lit, 5) => quote!(tracing::Level::ERROR),
-            Some(Level::Path(ref pat)) => quote!(#pat),
-            Some(_) => quote! {
-                compile_error!(
-                    "unknown verbosity level, expected one of \"trace\", \
-                     \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5"
-                )
-            },
-            None => quote!(tracing::Level::INFO),
-        }
+    pub(crate) fn level(&self) -> Level {
+        self.level.clone().unwrap_or(Level::Info)
     }
 
     pub(crate) fn target(&self) -> impl ToTokens {
@@ -167,12 +139,12 @@ impl Parse for InstrumentArgs {
                 args.fields = Some(input.parse()?);
             } else if lookahead.peek(kw::err) {
                 let _ = input.parse::();
-                let mode = FormatMode::parse(input)?;
-                args.err_mode = Some(mode);
+                let err_args = EventArgs::parse(input)?;
+                args.err_args = Some(err_args);
             } else if lookahead.peek(kw::ret) {
                 let _ = input.parse::()?;
-                let mode = FormatMode::parse(input)?;
-                args.ret_mode = Some(mode);
+                let ret_args = EventArgs::parse(input)?;
+                args.ret_args = Some(ret_args);
             } else if lookahead.peek(Token![,]) {
                 let _ = input.parse::()?;
             } else {
@@ -190,6 +162,55 @@ impl Parse for InstrumentArgs {
     }
 }
 
+impl EventArgs {
+    pub(crate) fn level(&self, default: Level) -> Level {
+        self.level.clone().unwrap_or(default)
+    }
+}
+
+impl Parse for EventArgs {
+    fn parse(input: ParseStream<'_>) -> syn::Result {
+        if !input.peek(syn::token::Paren) {
+            return Ok(Self::default());
+        }
+        let content;
+        let _ = syn::parenthesized!(content in input);
+        let mut result = Self::default();
+        let mut parse_one_arg =
+            || {
+                let lookahead = content.lookahead1();
+                if lookahead.peek(kw::level) {
+                    if result.level.is_some() {
+                        return Err(content.error("expected only a single `level` argument"));
+                    }
+                    result.level = Some(content.parse()?);
+                } else if result.mode != FormatMode::default() {
+                    return Err(content.error("expected only a single format argument"));
+                } else if let Some(ident) = content.parse::>()? {
+                    match ident.to_string().as_str() {
+                        "Debug" => result.mode = FormatMode::Debug,
+                        "Display" => result.mode = FormatMode::Display,
+                        _ => return Err(syn::Error::new(
+                            ident.span(),
+                            "unknown event formatting mode, expected either `Debug` or `Display`",
+                        )),
+                    }
+                }
+                Ok(())
+            };
+        parse_one_arg()?;
+        if !content.is_empty() {
+            if content.lookahead1().peek(Token![,]) {
+                let _ = content.parse::()?;
+                parse_one_arg()?;
+            } else {
+                return Err(content.error("expected `,` or `)`"));
+            }
+        }
+        Ok(result)
+    }
+}
+
 struct StrArg {
     value: LitStr,
     _p: std::marker::PhantomData,
@@ -260,27 +281,6 @@ impl Default for FormatMode {
     }
 }
 
-impl Parse for FormatMode {
-    fn parse(input: ParseStream<'_>) -> syn::Result {
-        if !input.peek(syn::token::Paren) {
-            return Ok(FormatMode::default());
-        }
-        let content;
-        let _ = syn::parenthesized!(content in input);
-        let maybe_mode: Option = content.parse()?;
-        maybe_mode.map_or(Ok(FormatMode::default()), |ident| {
-            match ident.to_string().as_str() {
-                "Debug" => Ok(FormatMode::Debug),
-                "Display" => Ok(FormatMode::Display),
-                _ => Err(syn::Error::new(
-                    ident.span(),
-                    "unknown error mode, must be Debug or Display",
-                )),
-            }
-        })
-    }
-}
-
 #[derive(Clone, Debug)]
 pub(crate) struct Fields(pub(crate) Punctuated);
 
@@ -376,9 +376,12 @@ impl ToTokens for FieldKind {
 }
 
 #[derive(Clone, Debug)]
-enum Level {
-    Str(LitStr),
-    Int(LitInt),
+pub(crate) enum Level {
+    Trace,
+    Debug,
+    Info,
+    Warn,
+    Error,
     Path(Path),
 }
 
@@ -388,9 +391,37 @@ impl Parse for Level {
         let _ = input.parse::()?;
         let lookahead = input.lookahead1();
         if lookahead.peek(LitStr) {
-            Ok(Self::Str(input.parse()?))
+            let str: LitStr = input.parse()?;
+            match str.value() {
+                s if s.eq_ignore_ascii_case("trace") => Ok(Level::Trace),
+                s if s.eq_ignore_ascii_case("debug") => Ok(Level::Debug),
+                s if s.eq_ignore_ascii_case("info") => Ok(Level::Info),
+                s if s.eq_ignore_ascii_case("warn") => Ok(Level::Warn),
+                s if s.eq_ignore_ascii_case("error") => Ok(Level::Error),
+                _ => Err(input.error(
+                    "unknown verbosity level, expected one of \"trace\", \
+                     \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5",
+                )),
+            }
         } else if lookahead.peek(LitInt) {
-            Ok(Self::Int(input.parse()?))
+            fn is_level(lit: &LitInt, expected: u64) -> bool {
+                match lit.base10_parse::() {
+                    Ok(value) => value == expected,
+                    Err(_) => false,
+                }
+            }
+            let int: LitInt = input.parse()?;
+            match &int {
+                i if is_level(i, 1) => Ok(Level::Trace),
+                i if is_level(i, 2) => Ok(Level::Debug),
+                i if is_level(i, 3) => Ok(Level::Info),
+                i if is_level(i, 4) => Ok(Level::Warn),
+                i if is_level(i, 5) => Ok(Level::Error),
+                _ => Err(input.error(
+                    "unknown verbosity level, expected one of \"trace\", \
+                     \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5",
+                )),
+            }
         } else if lookahead.peek(Ident) {
             Ok(Self::Path(input.parse()?))
         } else {
@@ -399,6 +430,19 @@ impl Parse for Level {
     }
 }
 
+impl ToTokens for Level {
+    fn to_tokens(&self, tokens: &mut TokenStream) {
+        match self {
+            Level::Trace => tokens.extend(quote!(tracing::Level::TRACE)),
+            Level::Debug => tokens.extend(quote!(tracing::Level::DEBUG)),
+            Level::Info => tokens.extend(quote!(tracing::Level::INFO)),
+            Level::Warn => tokens.extend(quote!(tracing::Level::WARN)),
+            Level::Error => tokens.extend(quote!(tracing::Level::ERROR)),
+            Level::Path(ref pat) => tokens.extend(quote!(#pat)),
+        }
+    }
+}
+
 mod kw {
     syn::custom_keyword!(fields);
     syn::custom_keyword!(skip);
diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs
index cb12ad87e4..0339dcb1f3 100644
--- a/tracing-attributes/src/expand.rs
+++ b/tracing-attributes/src/expand.rs
@@ -10,7 +10,7 @@ use syn::{
 };
 
 use crate::{
-    attr::{Field, Fields, FormatMode, InstrumentArgs},
+    attr::{Field, Fields, FormatMode, InstrumentArgs, Level},
     MaybeItemFn, MaybeItemFnRef,
 };
 
@@ -116,7 +116,8 @@ fn gen_block(
         .map(|name| quote!(#name))
         .unwrap_or_else(|| quote!(#instrumented_function_name));
 
-    let level = args.level();
+    let args_level = args.level();
+    let level = args_level.clone();
 
     let follows_from = args.follows_from.iter();
     let follows_from = quote! {
@@ -232,21 +233,33 @@ fn gen_block(
 
     let target = args.target();
 
-    let err_event = match args.err_mode {
-        Some(FormatMode::Default) | Some(FormatMode::Display) => {
-            Some(quote!(tracing::error!(target: #target, error = %e)))
+    let err_event = match args.err_args {
+        Some(event_args) => {
+            let level_tokens = event_args.level(Level::Error);
+            match event_args.mode {
+                FormatMode::Default | FormatMode::Display => Some(quote!(
+                    tracing::event!(target: #target, #level_tokens, error = %e)
+                )),
+                FormatMode::Debug => Some(quote!(
+                    tracing::event!(target: #target, #level_tokens, error = ?e)
+                )),
+            }
         }
-        Some(FormatMode::Debug) => Some(quote!(tracing::error!(target: #target, error = ?e))),
         _ => None,
     };
 
-    let ret_event = match args.ret_mode {
-        Some(FormatMode::Display) => Some(quote!(
-            tracing::event!(target: #target, #level, return = %x)
-        )),
-        Some(FormatMode::Default) | Some(FormatMode::Debug) => Some(quote!(
-            tracing::event!(target: #target, #level, return = ?x)
-        )),
+    let ret_event = match args.ret_args {
+        Some(event_args) => {
+            let level_tokens = event_args.level(args_level);
+            match event_args.mode {
+                FormatMode::Display => Some(quote!(
+                    tracing::event!(target: #target, #level_tokens, return = %x)
+                )),
+                FormatMode::Default | FormatMode::Debug => Some(quote!(
+                    tracing::event!(target: #target, #level_tokens, return = ?x)
+                )),
+            }
+        }
         _ => None,
     };
 
diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs
index cc0b8e5198..00e24d0163 100644
--- a/tracing-attributes/src/lib.rs
+++ b/tracing-attributes/src/lib.rs
@@ -422,6 +422,16 @@ mod expand;
 /// By default, this will be [`INFO`], but if the level is overridden, the event will be at the same
 /// level.
 ///
+/// It's also possible to override the level for the `ret` event independently:
+///
+/// ```
+/// # use tracing_attributes::instrument;
+/// #[instrument(ret(level = "warn"))]
+/// fn my_function() -> i32 {
+///     42
+/// }
+/// ```
+///
 /// **Note**:  if the function returns a `Result`, `ret` will record returned values if and
 /// only if the function returns [`Result::Ok`].
 ///
@@ -448,6 +458,16 @@ mod expand;
 /// }
 /// ```
 ///
+/// Similarly, you can override the level of the `err` event:
+///
+/// ```
+/// # use tracing_attributes::instrument;
+/// #[instrument(err(level = "info"))]
+/// fn my_function(arg: usize) -> Result<(), std::io::Error> {
+///     Ok(())
+/// }
+/// ```
+///
 /// By default, error values will be recorded using their `std::fmt::Display` implementations.
 /// If an error implements `std::fmt::Debug`, it can be recorded using its `Debug` implementation
 /// instead, by writing `err(Debug)`:
diff --git a/tracing-attributes/tests/err.rs b/tracing-attributes/tests/err.rs
index 9e6d6b78c3..a030138407 100644
--- a/tracing-attributes/tests/err.rs
+++ b/tracing-attributes/tests/err.rs
@@ -231,3 +231,72 @@ fn test_err_custom_target() {
     });
     handle.assert_finished();
 }
+
+#[instrument(err(level = "info"))]
+fn err_info() -> Result {
+    u8::try_from(1234)
+}
+
+#[test]
+fn test_err_info() {
+    let span = span::mock().named("err_info");
+    let (collector, handle) = collector::mock()
+        .new_span(span.clone())
+        .enter(span.clone())
+        .event(event::mock().at_level(Level::INFO))
+        .exit(span.clone())
+        .drop_span(span)
+        .done()
+        .run_with_handle();
+    with_default(collector, || err_info().ok());
+    handle.assert_finished();
+}
+
+#[instrument(err(Debug, level = "info"))]
+fn err_dbg_info() -> Result {
+    u8::try_from(1234)
+}
+
+#[test]
+fn test_err_dbg_info() {
+    let span = span::mock().named("err_dbg_info");
+    let (collector, handle) = collector::mock()
+        .new_span(span.clone())
+        .enter(span.clone())
+        .event(
+            event::mock().at_level(Level::INFO).with_fields(
+                field::mock("error")
+                    // use the actual error value that will be emitted, so
+                    // that this test doesn't break if the standard library
+                    // changes the `fmt::Debug` output from the error type
+                    // in the future.
+                    .with_value(&tracing::field::debug(u8::try_from(1234).unwrap_err())),
+            ),
+        )
+        .exit(span.clone())
+        .drop_span(span)
+        .done()
+        .run_with_handle();
+    with_default(collector, || err_dbg_info().ok());
+    handle.assert_finished();
+}
+
+#[instrument(level = "warn", err(level = "info"))]
+fn err_warn_info() -> Result {
+    u8::try_from(1234)
+}
+
+#[test]
+fn test_err_warn_info() {
+    let span = span::mock().named("err_warn_info").at_level(Level::WARN);
+    let (collector, handle) = collector::mock()
+        .new_span(span.clone())
+        .enter(span.clone())
+        .event(event::mock().at_level(Level::INFO))
+        .exit(span.clone())
+        .drop_span(span)
+        .done()
+        .run_with_handle();
+    with_default(collector, || err_warn_info().ok());
+    handle.assert_finished();
+}
diff --git a/tracing-attributes/tests/ret.rs b/tracing-attributes/tests/ret.rs
index cfd2de10d3..c29c87ea8a 100644
--- a/tracing-attributes/tests/ret.rs
+++ b/tracing-attributes/tests/ret.rs
@@ -253,3 +253,53 @@ fn test_ret_and_ok() {
     with_default(subscriber, || ret_and_ok().ok());
     handle.assert_finished();
 }
+
+#[instrument(level = "warn", ret(level = "info"))]
+fn ret_warn_info() -> i32 {
+    42
+}
+
+#[test]
+fn test_warn_info() {
+    let span = span::mock().named("ret_warn_info").at_level(Level::WARN);
+    let (collector, handle) = collector::mock()
+        .new_span(span.clone())
+        .enter(span.clone())
+        .event(
+            event::mock()
+                .with_fields(field::mock("return").with_value(&tracing::field::debug(42)))
+                .at_level(Level::INFO),
+        )
+        .exit(span.clone())
+        .drop_span(span)
+        .done()
+        .run_with_handle();
+
+    with_default(collector, ret_warn_info);
+    handle.assert_finished();
+}
+
+#[instrument(ret(level = "warn", Debug))]
+fn ret_dbg_warn() -> i32 {
+    42
+}
+
+#[test]
+fn test_dbg_warn() {
+    let span = span::mock().named("ret_dbg_warn").at_level(Level::INFO);
+    let (collector, handle) = collector::mock()
+        .new_span(span.clone())
+        .enter(span.clone())
+        .event(
+            event::mock()
+                .with_fields(field::mock("return").with_value(&tracing::field::debug(42)))
+                .at_level(Level::WARN),
+        )
+        .exit(span.clone())
+        .drop_span(span)
+        .done()
+        .run_with_handle();
+
+    with_default(collector, ret_dbg_warn);
+    handle.assert_finished();
+}

From b38122deffbdaa67bb11977f30df8e6fa1c0ea32 Mon Sep 17 00:00:00 2001
From: Jeremy Fitzhardinge 
Date: Thu, 27 Oct 2022 12:20:11 -0700
Subject: [PATCH 117/142] attributes: improve docs; tests for using Levels in
 `#[instrument]` (#2350)

This branch adds documentation and tests noting that the `#[instrument]`
macro accepts `tracing::Level` directly. Using `tracing::Level` directly
allows for IDE autocomplete and earlier detection of typos.

The documentation for tracing-attributes was also rewritten to remove
the usage of the second-person perspective, making it more consistent
with the rest of tracing's documentation.

Co-authored-by: David Barsky 
; Conflicts:
;	tracing-attributes/Cargo.toml
;	tracing-attributes/src/lib.rs
---
 tracing-attributes/Cargo.toml          |  2 +-
 tracing-attributes/src/lib.rs          | 65 ++++++++++----------------
 tracing-attributes/tests/err.rs        | 12 ++---
 tracing-attributes/tests/instrument.rs |  2 +-
 tracing-attributes/tests/levels.rs     | 46 ++++++++++++++++++
 tracing-attributes/tests/ret.rs        |  8 ++--
 tracing-core/src/field.rs              |  1 +
 tracing-flame/src/lib.rs               |  3 +-
 tracing-futures/src/executor/mod.rs    |  2 -
 tracing/src/field.rs                   |  1 +
 10 files changed, 85 insertions(+), 57 deletions(-)

diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml
index d6e3adb807..8e41eb31d0 100644
--- a/tracing-attributes/Cargo.toml
+++ b/tracing-attributes/Cargo.toml
@@ -47,7 +47,7 @@ quote = "1"
 tracing = { path = "../tracing", version = "0.1.35" }
 tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] }
 tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", features = ["env-filter"] }
-tokio-test = { version = "0.3.0" }
+tokio-test = "0.4.2"
 tracing-core = { path = "../tracing-core", version = "0.1.28"}
 async-trait = "0.1.56"
 trybuild = "1.0.64"
diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs
index 00e24d0163..462b53dca5 100644
--- a/tracing-attributes/src/lib.rs
+++ b/tracing-attributes/src/lib.rs
@@ -12,7 +12,7 @@
 //!
 //! ## Usage
 //!
-//! First, add this to your `Cargo.toml`:
+//! In the `Cargo.toml`:
 //!
 //! ```toml
 //! [dependencies]
@@ -24,7 +24,7 @@
 //! called. For example:
 //!
 //! ```
-//! use tracing_attributes::instrument;
+//! use tracing::instrument;
 //!
 //! #[instrument]
 //! pub fn my_function(my_arg: usize) {
@@ -204,16 +204,17 @@ mod expand;
 ///
 /// # Adding Fields
 ///
-/// Additional fields (key-value pairs with arbitrary data) may be added to the
-/// generated span using the `fields` argument on the `#[instrument]` macro. Any
+/// Additional fields (key-value pairs with arbitrary data) can be passed to
+/// to the generated span through the `fields` argument on the
+/// `#[instrument]` macro. Strings, integers or boolean literals are accepted values
+/// for each field. The name of the field must be a single valid Rust
+/// identifier, nested (dotted) field names are not supported. Any
 /// Rust expression can be used as a field value in this manner. These
 /// expressions will be evaluated at the beginning of the function's body, so
 /// arguments to the function may be used in these expressions. Field names may
 /// also be specified *without* values. Doing so will result in an [empty field]
 /// whose value may be recorded later within the function body.
 ///
-/// This supports the same [field syntax] as the `span!` and `event!` macros.
-///
 /// Note that overlap between the names of fields and (non-skipped) arguments
 /// will result in a compile error.
 ///
@@ -323,11 +324,15 @@ mod expand;
 /// Setting the level for the generated span:
 /// ```
 /// # use tracing_attributes::instrument;
-/// #[instrument(level = "debug")]
+/// # use tracing::Level;
+/// #[instrument(level = Level::DEBUG)]
 /// pub fn my_function() {
 ///     // ...
 /// }
 /// ```
+/// Levels can be specified either with [`Level`] constants, literal strings
+/// (e.g., `"debug"`, `"info"`) or numerically (1—5, corresponding to [`Level::TRACE`]—[`Level::ERROR`]).
+///
 /// Overriding the generated span's name:
 /// ```
 /// # use tracing_attributes::instrument;
@@ -398,7 +403,7 @@ mod expand;
 /// }
 /// ```
 ///
-/// To add an additional context to the span, pass key-value pairs to `fields`:
+/// To add additional context to the span, pass key-value pairs to `fields`:
 ///
 /// ```
 /// # use tracing_attributes::instrument;
@@ -426,7 +431,8 @@ mod expand;
 ///
 /// ```
 /// # use tracing_attributes::instrument;
-/// #[instrument(ret(level = "warn"))]
+/// # use tracing::Level;
+/// #[instrument(ret(level = Level::WARN))]
 /// fn my_function() -> i32 {
 ///     42
 /// }
@@ -447,8 +453,8 @@ mod expand;
 /// }
 /// ```
 ///
-/// If the function returns a `Result` and `E` implements `std::fmt::Display`, you can add
-/// `err` or `err(Display)` to emit error events when the function returns `Err`:
+/// If the function returns a `Result` and `E` implements `std::fmt::Display`, adding
+/// `err` or `err(Display)` will emit error events when the function returns `Err`:
 ///
 /// ```
 /// # use tracing_attributes::instrument;
@@ -458,11 +464,12 @@ mod expand;
 /// }
 /// ```
 ///
-/// Similarly, you can override the level of the `err` event:
+/// Similarly, overriding the level of the `err` event :
 ///
 /// ```
 /// # use tracing_attributes::instrument;
-/// #[instrument(err(level = "info"))]
+/// # use tracing::Level;
+/// #[instrument(err(level = Level::INFO))]
 /// fn my_function(arg: usize) -> Result<(), std::io::Error> {
 ///     Ok(())
 /// }
@@ -470,7 +477,7 @@ mod expand;
 ///
 /// By default, error values will be recorded using their `std::fmt::Display` implementations.
 /// If an error implements `std::fmt::Debug`, it can be recorded using its `Debug` implementation
-/// instead, by writing `err(Debug)`:
+/// instead by writing `err(Debug)`:
 ///
 /// ```
 /// # use tracing_attributes::instrument;
@@ -529,33 +536,6 @@ mod expand;
 /// }
 /// ```
 ///
-/// Note than on `async-trait` <= 0.1.43, references to the `Self`
-/// type inside the `fields` argument were only allowed when the instrumented
-/// function is a method (i.e., the function receives `self` as an argument).
-/// For example, this *used to not work* because the instrument function
-/// didn't receive `self`:
-/// ```
-/// # use tracing::instrument;
-/// use async_trait::async_trait;
-///
-/// #[async_trait]
-/// pub trait Bar {
-///     async fn bar();
-/// }
-///
-/// #[derive(Debug)]
-/// struct BarImpl(usize);
-///
-/// #[async_trait]
-/// impl Bar for BarImpl {
-///     #[instrument(fields(tmp = std::any::type_name::()))]
-///     async fn bar() {}
-/// }
-/// ```
-/// Instead, you should manually rewrite any `Self` types as the type for
-/// which you implement the trait: `#[instrument(fields(tmp = std::any::type_name::()))]`
-/// (or maybe you can just bump `async-trait`).
-///
 /// [span]: https://docs.rs/tracing/latest/tracing/span/index.html
 /// [name]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.name
 /// [target]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.target
@@ -567,6 +547,9 @@ mod expand;
 /// [`follows_from`]: https://docs.rs/tracing/latest/tracing/struct.Span.html#method.follows_from
 /// [`tracing`]: https://github.com/tokio-rs/tracing
 /// [`fmt::Debug`]: std::fmt::Debug
+/// [`Level`]: https://docs.rs/tracing/latest/tracing/struct.Level.html
+/// [`Level::TRACE`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.TRACE
+/// [`Level::ERROR`]: https://docs.rs/tracing/latest/tracing/struct.Level.html#associatedconstant.ERROR
 #[proc_macro_attribute]
 pub fn instrument(
     args: proc_macro::TokenStream,
diff --git a/tracing-attributes/tests/err.rs b/tracing-attributes/tests/err.rs
index a030138407..deb2f1d59d 100644
--- a/tracing-attributes/tests/err.rs
+++ b/tracing-attributes/tests/err.rs
@@ -240,7 +240,7 @@ fn err_info() -> Result {
 #[test]
 fn test_err_info() {
     let span = span::mock().named("err_info");
-    let (collector, handle) = collector::mock()
+    let (subscriber, handle) = subscriber::mock()
         .new_span(span.clone())
         .enter(span.clone())
         .event(event::mock().at_level(Level::INFO))
@@ -248,7 +248,7 @@ fn test_err_info() {
         .drop_span(span)
         .done()
         .run_with_handle();
-    with_default(collector, || err_info().ok());
+    with_default(subscriber, || err_info().ok());
     handle.assert_finished();
 }
 
@@ -260,7 +260,7 @@ fn err_dbg_info() -> Result {
 #[test]
 fn test_err_dbg_info() {
     let span = span::mock().named("err_dbg_info");
-    let (collector, handle) = collector::mock()
+    let (subscriber, handle) = subscriber::mock()
         .new_span(span.clone())
         .enter(span.clone())
         .event(
@@ -277,7 +277,7 @@ fn test_err_dbg_info() {
         .drop_span(span)
         .done()
         .run_with_handle();
-    with_default(collector, || err_dbg_info().ok());
+    with_default(subscriber, || err_dbg_info().ok());
     handle.assert_finished();
 }
 
@@ -289,7 +289,7 @@ fn err_warn_info() -> Result {
 #[test]
 fn test_err_warn_info() {
     let span = span::mock().named("err_warn_info").at_level(Level::WARN);
-    let (collector, handle) = collector::mock()
+    let (subscriber, handle) = subscriber::mock()
         .new_span(span.clone())
         .enter(span.clone())
         .event(event::mock().at_level(Level::INFO))
@@ -297,6 +297,6 @@ fn test_err_warn_info() {
         .drop_span(span)
         .done()
         .run_with_handle();
-    with_default(collector, || err_warn_info().ok());
+    with_default(subscriber, || err_warn_info().ok());
     handle.assert_finished();
 }
diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs
index 7686927488..b215b8455d 100644
--- a/tracing-attributes/tests/instrument.rs
+++ b/tracing-attributes/tests/instrument.rs
@@ -17,7 +17,7 @@ fn override_everything() {
     #[instrument(target = "my_target", level = "debug")]
     fn my_fn() {}
 
-    #[instrument(level = "debug", target = "my_target")]
+    #[instrument(level = Level::DEBUG, target = "my_target")]
     fn my_other_fn() {}
 
     let span = span::mock()
diff --git a/tracing-attributes/tests/levels.rs b/tracing-attributes/tests/levels.rs
index b074ea4f28..94fc7e85a2 100644
--- a/tracing-attributes/tests/levels.rs
+++ b/tracing-attributes/tests/levels.rs
@@ -94,3 +94,49 @@ fn numeric_levels() {
 
     handle.assert_finished();
 }
+
+#[test]
+fn enum_levels() {
+    #[instrument(level = Level::TRACE)]
+    fn trace() {}
+
+    #[instrument(level = Level::DEBUG)]
+    fn debug() {}
+
+    #[instrument(level = tracing::Level::INFO)]
+    fn info() {}
+
+    #[instrument(level = Level::WARN)]
+    fn warn() {}
+
+    #[instrument(level = Level::ERROR)]
+    fn error() {}
+    let (subscriber, handle) = subscriber::mock()
+        .new_span(span::mock().named("trace").at_level(Level::TRACE))
+        .enter(span::mock().named("trace").at_level(Level::TRACE))
+        .exit(span::mock().named("trace").at_level(Level::TRACE))
+        .new_span(span::mock().named("debug").at_level(Level::DEBUG))
+        .enter(span::mock().named("debug").at_level(Level::DEBUG))
+        .exit(span::mock().named("debug").at_level(Level::DEBUG))
+        .new_span(span::mock().named("info").at_level(Level::INFO))
+        .enter(span::mock().named("info").at_level(Level::INFO))
+        .exit(span::mock().named("info").at_level(Level::INFO))
+        .new_span(span::mock().named("warn").at_level(Level::WARN))
+        .enter(span::mock().named("warn").at_level(Level::WARN))
+        .exit(span::mock().named("warn").at_level(Level::WARN))
+        .new_span(span::mock().named("error").at_level(Level::ERROR))
+        .enter(span::mock().named("error").at_level(Level::ERROR))
+        .exit(span::mock().named("error").at_level(Level::ERROR))
+        .done()
+        .run_with_handle();
+
+    with_default(subscriber, || {
+        trace();
+        debug();
+        info();
+        warn();
+        error();
+    });
+
+    handle.assert_finished();
+}
diff --git a/tracing-attributes/tests/ret.rs b/tracing-attributes/tests/ret.rs
index c29c87ea8a..f20b66797c 100644
--- a/tracing-attributes/tests/ret.rs
+++ b/tracing-attributes/tests/ret.rs
@@ -262,7 +262,7 @@ fn ret_warn_info() -> i32 {
 #[test]
 fn test_warn_info() {
     let span = span::mock().named("ret_warn_info").at_level(Level::WARN);
-    let (collector, handle) = collector::mock()
+    let (subscriber, handle) = subscriber::mock()
         .new_span(span.clone())
         .enter(span.clone())
         .event(
@@ -275,7 +275,7 @@ fn test_warn_info() {
         .done()
         .run_with_handle();
 
-    with_default(collector, ret_warn_info);
+    with_default(subscriber, ret_warn_info);
     handle.assert_finished();
 }
 
@@ -287,7 +287,7 @@ fn ret_dbg_warn() -> i32 {
 #[test]
 fn test_dbg_warn() {
     let span = span::mock().named("ret_dbg_warn").at_level(Level::INFO);
-    let (collector, handle) = collector::mock()
+    let (subscriber, handle) = subscriber::mock()
         .new_span(span.clone())
         .enter(span.clone())
         .event(
@@ -300,6 +300,6 @@ fn test_dbg_warn() {
         .done()
         .run_with_handle();
 
-    with_default(collector, ret_dbg_warn);
+    with_default(subscriber, ret_dbg_warn);
     handle.assert_finished();
 }
diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index e103c75a9d..7e2c26b1d7 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -315,6 +315,7 @@ pub trait Visit {
     /// Note: This is only enabled when the Rust standard library is
     /// present.
     /// 
+ /// #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { diff --git a/tracing-flame/src/lib.rs b/tracing-flame/src/lib.rs index 51dd436d34..145d2be90f 100644 --- a/tracing-flame/src/lib.rs +++ b/tracing-flame/src/lib.rs @@ -133,8 +133,7 @@ while_true )] -pub use error::Error; - +use error::Error; use error::Kind; use once_cell::sync::Lazy; use std::cell::Cell; diff --git a/tracing-futures/src/executor/mod.rs b/tracing-futures/src/executor/mod.rs index 442b523b84..ced3b5a460 100644 --- a/tracing-futures/src/executor/mod.rs +++ b/tracing-futures/src/executor/mod.rs @@ -1,7 +1,5 @@ #[cfg(feature = "futures-01")] mod futures_01; -#[cfg(feature = "futures-01")] -pub use self::futures_01::*; #[cfg(feature = "futures_preview")] mod futures_preview; diff --git a/tracing/src/field.rs b/tracing/src/field.rs index b3f9fbdfca..886c53a916 100644 --- a/tracing/src/field.rs +++ b/tracing/src/field.rs @@ -126,6 +126,7 @@ use crate::Metadata; /// string comparisons. Thus, if possible, once the key for a field is known, it /// should be used whenever possible. ///
+/// pub trait AsField: crate::sealed::Sealed { /// Attempts to convert `&self` into a `Field` with the specified `metadata`. /// From 77257c12069a352847b65d17ee551f83fdc3ba78 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 3 Nov 2022 19:13:18 -0700 Subject: [PATCH 118/142] chore: track Rust 1.65 clippy lints (#2366) --- clippy.toml | 2 +- tracing-subscriber/benches/support/mod.rs | 2 +- tracing-subscriber/src/fmt/format/json.rs | 2 +- tracing-subscriber/src/registry/sharded.rs | 2 +- tracing-subscriber/tests/field_filter.rs | 2 +- tracing/src/span.rs | 4 ++-- tracing/tests/span.rs | 6 +++--- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/clippy.toml b/clippy.toml index bc44be8361..fcd0eeda24 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,4 +1,4 @@ -blacklisted-names = [] +disallowed-names = [] cognitive-complexity-threshold = 100 too-many-arguments-threshold = 8 type-complexity-threshold = 375 diff --git a/tracing-subscriber/benches/support/mod.rs b/tracing-subscriber/benches/support/mod.rs index 25e9e7e229..3abaa807aa 100644 --- a/tracing-subscriber/benches/support/mod.rs +++ b/tracing-subscriber/benches/support/mod.rs @@ -33,7 +33,7 @@ impl MultithreadedBench { thread::spawn(move || { let dispatch = this.dispatch.clone(); tracing::dispatcher::with_default(&dispatch, move || { - f(&*this.start); + f(&this.start); this.end.wait(); }) }); diff --git a/tracing-subscriber/src/fmt/format/json.rs b/tracing-subscriber/src/fmt/format/json.rs index c2f4d37553..bf32f7c9a8 100644 --- a/tracing-subscriber/src/fmt/format/json.rs +++ b/tracing-subscriber/src/fmt/format/json.rs @@ -720,7 +720,7 @@ mod test { ); let span = tracing::info_span!("the span", na = tracing::field::Empty); - span.record("na", &"value"); + span.record("na", "value"); let _enter = span.enter(); tracing::info!("an event inside the root span"); diff --git a/tracing-subscriber/src/registry/sharded.rs b/tracing-subscriber/src/registry/sharded.rs index 07c94fccb5..88520a2a66 100644 --- a/tracing-subscriber/src/registry/sharded.rs +++ b/tracing-subscriber/src/registry/sharded.rs @@ -902,7 +902,7 @@ mod tests { drop(span3); - state.assert_closed_in_order(&["child", "parent", "grandparent"]); + state.assert_closed_in_order(["child", "parent", "grandparent"]); }); } } diff --git a/tracing-subscriber/tests/field_filter.rs b/tracing-subscriber/tests/field_filter.rs index f14a0626d3..385d024f65 100644 --- a/tracing-subscriber/tests/field_filter.rs +++ b/tracing-subscriber/tests/field_filter.rs @@ -103,7 +103,7 @@ fn record_after_created() { tracing::debug!("i'm disabled!"); }); - span.record("enabled", &true); + span.record("enabled", true); span.in_scope(|| { tracing::debug!("i'm enabled!"); }); diff --git a/tracing/src/span.rs b/tracing/src/span.rs index 58822f4d9b..7be56abdf5 100644 --- a/tracing/src/span.rs +++ b/tracing/src/span.rs @@ -1617,7 +1617,7 @@ mod test { #[test] fn test_record_backwards_compat() { - Span::current().record("some-key", &"some text"); - Span::current().record("some-key", &false); + Span::current().record("some-key", "some text"); + Span::current().record("some-key", false); } } diff --git a/tracing/tests/span.rs b/tracing/tests/span.rs index 4ed6500235..b148f7317b 100644 --- a/tracing/tests/span.rs +++ b/tracing/tests/span.rs @@ -563,7 +563,7 @@ fn record_new_value_for_field() { with_default(subscriber, || { let span = tracing::span!(Level::TRACE, "foo", bar = 5, baz = false); - span.record("baz", &true); + span.record("baz", true); span.in_scope(|| {}) }); @@ -598,8 +598,8 @@ fn record_new_values_for_fields() { with_default(subscriber, || { let span = tracing::span!(Level::TRACE, "foo", bar = 4, baz = false); - span.record("bar", &5); - span.record("baz", &true); + span.record("bar", 5); + span.record("baz", true); span.in_scope(|| {}) }); From d89ce00e26a0ade896f769efbabbc18647d17b82 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 4 Nov 2022 11:37:41 -0700 Subject: [PATCH 119/142] subscriber: fix wrong `doc_cfg` attribute (#2368) This `doc_cfg` attribute's `cfg` part has multiple predicates without an `all`, which iis what's breaking the netlify build. I'm...kind of surprised this ever succeeded, since the cfg is malformed... --- tracing-subscriber/src/fmt/time/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-subscriber/src/fmt/time/mod.rs b/tracing-subscriber/src/fmt/time/mod.rs index e5b7c83b04..1d1bba2406 100644 --- a/tracing-subscriber/src/fmt/time/mod.rs +++ b/tracing-subscriber/src/fmt/time/mod.rs @@ -12,7 +12,7 @@ mod time_crate; pub use time_crate::UtcTime; #[cfg(feature = "local-time")] -#[cfg_attr(docsrs, doc(cfg(unsound_local_offset, feature = "local-time")))] +#[cfg_attr(docsrs, doc(cfg(all(unsound_local_offset, feature = "local-time"))))] pub use time_crate::LocalTime; #[cfg(feature = "time")] From 5ff8a410e13b9f9f21aec904399c449bb04a0a41 Mon Sep 17 00:00:00 2001 From: Nicolas Stinus Date: Fri, 4 Nov 2022 14:54:21 -0400 Subject: [PATCH 120/142] appender: allow worker thread name to be configured (#2365) ## Motivation The worker thread name in non blocking mode is always "tracing-appender". It can be convenient to quickly identify the appender threads for audit reasons or affinity pinning. ## Solution This patch adds a new setter to the builder and propagates the info to the thread initialization. Closes #2364 --- tracing-appender/src/non_blocking.rs | 25 ++++++++++++++++++++++--- tracing-appender/src/worker.rs | 4 ++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/tracing-appender/src/non_blocking.rs b/tracing-appender/src/non_blocking.rs index ac9067808a..1ac2ba54a1 100644 --- a/tracing-appender/src/non_blocking.rs +++ b/tracing-appender/src/non_blocking.rs @@ -154,14 +154,18 @@ impl NonBlocking { writer: T, buffered_lines_limit: usize, is_lossy: bool, + thread_name: String, ) -> (NonBlocking, WorkerGuard) { let (sender, receiver) = bounded(buffered_lines_limit); let (shutdown_sender, shutdown_receiver) = bounded(0); let worker = Worker::new(receiver, writer, shutdown_receiver); - let worker_guard = - WorkerGuard::new(worker.worker_thread(), sender.clone(), shutdown_sender); + let worker_guard = WorkerGuard::new( + worker.worker_thread(thread_name), + sender.clone(), + shutdown_sender, + ); ( Self { @@ -187,6 +191,7 @@ impl NonBlocking { pub struct NonBlockingBuilder { buffered_lines_limit: usize, is_lossy: bool, + thread_name: String, } impl NonBlockingBuilder { @@ -207,9 +212,22 @@ impl NonBlockingBuilder { self } + /// Override the worker thread's name. + /// + /// The default worker thread name is "tracing-appender". + pub fn thread_name(mut self, name: &str) -> NonBlockingBuilder { + self.thread_name = name.to_string(); + self + } + /// Completes the builder, returning the configured `NonBlocking`. pub fn finish(self, writer: T) -> (NonBlocking, WorkerGuard) { - NonBlocking::create(writer, self.buffered_lines_limit, self.is_lossy) + NonBlocking::create( + writer, + self.buffered_lines_limit, + self.is_lossy, + self.thread_name, + ) } } @@ -218,6 +236,7 @@ impl Default for NonBlockingBuilder { NonBlockingBuilder { buffered_lines_limit: DEFAULT_BUFFERED_LINES_LIMIT, is_lossy: true, + thread_name: "tracing-appender".to_string(), } } } diff --git a/tracing-appender/src/worker.rs b/tracing-appender/src/worker.rs index 622cb2c61a..e913183edd 100644 --- a/tracing-appender/src/worker.rs +++ b/tracing-appender/src/worker.rs @@ -67,9 +67,9 @@ impl Worker { } /// Creates a worker thread that processes a channel until it's disconnected - pub(crate) fn worker_thread(mut self) -> std::thread::JoinHandle<()> { + pub(crate) fn worker_thread(mut self, name: String) -> std::thread::JoinHandle<()> { thread::Builder::new() - .name("tracing-appender".to_string()) + .name(name) .spawn(move || { loop { match self.work() { From 6bd3d40d2bda81206ff8de5f215ca385b166e077 Mon Sep 17 00:00:00 2001 From: Abhishek <68317979+Abhicodes-crypto@users.noreply.github.com> Date: Sat, 5 Nov 2022 01:08:28 +0530 Subject: [PATCH 121/142] attributes: allow `clippy::unreachable` warning (#2356) ## Motivation PR #2270 added an unreachable branch with an explicit return value to `#[instrument]` in `async fn`s in order to fix type inference issues. That PR added the appropriate `#[allow]` attribute for the Rust compiler's unreachable code linting, but not Clippy's, so a Clippy warning is still emitted. See: https://github.com/tokio-rs/tracing/pull/2270#issuecomment-1282881008 ## Solution Adding the clippy lint warning as discussed here: https://github.com/tokio-rs/tracing/pull/2270#issuecomment-1282887973 --- tracing-attributes/src/expand.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index 0339dcb1f3..9e1a0a7ed0 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -64,7 +64,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>( // unreachable, but does affect inference, so it needs to be written // exactly that way for it to do its magic. let fake_return_edge = quote_spanned! {return_span=> - #[allow(unreachable_code, clippy::diverging_sub_expression, clippy::let_unit_value)] + #[allow(unreachable_code, clippy::diverging_sub_expression, clippy::let_unit_value, clippy::unreachable)] if false { let __tracing_attr_fake_return: #return_type = unreachable!("this is just for type inference, and is unreachable code"); From e48d2471fa818754d76b9b1ae1f6cdd7228d76ce Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Thu, 10 Nov 2022 22:31:08 +0200 Subject: [PATCH 122/142] appender: Fix typo in RollingFileAppender docs (#2375) The `MakeWriter` trait comes from the `tracing-subscriber` crate, not `tracing-appender`. --- tracing-appender/src/rolling.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-appender/src/rolling.rs b/tracing-appender/src/rolling.rs index 8e630cc9e1..15ae35ba06 100644 --- a/tracing-appender/src/rolling.rs +++ b/tracing-appender/src/rolling.rs @@ -46,7 +46,7 @@ pub use builder::{Builder, InitError}; /// writes without blocking the current thread. /// /// Additionally, `RollingFileAppender` also implements the [`MakeWriter`] -/// trait from `tracing-appender`, so it may also be used +/// trait from `tracing-subscriber`, so it may also be used /// directly, without [`NonBlocking`]. /// /// [write]: std::io::Write From 6465c65d7ca534426d7258e701b23b8ec73060d1 Mon Sep 17 00:00:00 2001 From: LeoniePhiline <22329650+LeoniePhiline@users.noreply.github.com> Date: Fri, 11 Nov 2022 21:00:14 +0100 Subject: [PATCH 123/142] fix(docs): Remove accidentally duplicated words (#2378) ## Motivation There is a small wording mistake in the tracing-subscriber docs that can be fixed quickly and easily. ## Solution Two duplicate words were removed. --- tracing-subscriber/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 63cd3d3154..7bc61a6c19 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -46,7 +46,7 @@ //! //! ## Feature Flags //! -//! - `std`: Enables APIs that depend on the on the Rust standard library +//! - `std`: Enables APIs that depend on the Rust standard library //! (enabled by default). //! - `alloc`: Depend on [`liballoc`] (enabled by "std"). //! - `env-filter`: Enables the [`EnvFilter`] type, which implements filtering From 6c55b6691578d64683e5f771eb28ca4b66d0bb29 Mon Sep 17 00:00:00 2001 From: Luca Palmieri <20745048+LukeMathWalker@users.noreply.github.com> Date: Wed, 16 Nov 2022 18:09:24 +0000 Subject: [PATCH 124/142] chore: Remove `doc(html_root_url)` attribute from all crates. (#2384) Fixes #2383. ; Conflicts: ; tracing-appender/src/lib.rs ; tracing-attributes/src/lib.rs ; tracing-core/src/lib.rs ; tracing-error/src/lib.rs ; tracing-futures/src/lib.rs ; tracing-log/src/lib.rs ; tracing-opentelemetry/src/lib.rs ; tracing-serde/src/lib.rs ; tracing-subscriber/src/lib.rs ; tracing/src/lib.rs --- tracing-appender/src/lib.rs | 1 - tracing-attributes/src/lib.rs | 1 - tracing-core/src/lib.rs | 1 - tracing-error/src/lib.rs | 1 - tracing-futures/src/lib.rs | 1 - tracing-log/src/lib.rs | 1 - tracing-opentelemetry/src/lib.rs | 1 - tracing-serde/src/lib.rs | 1 - tracing-subscriber/src/lib.rs | 5 ++--- tracing/src/lib.rs | 1 - 10 files changed, 2 insertions(+), 12 deletions(-) diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs index b93d18fec1..afd5160dbb 100644 --- a/tracing-appender/src/lib.rs +++ b/tracing-appender/src/lib.rs @@ -121,7 +121,6 @@ //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! -#![doc(html_root_url = "https://docs.rs/tracing-appender/0.2.2")] #![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/" diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 462b53dca5..dd692512ff 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -52,7 +52,6 @@ //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! -#![doc(html_root_url = "https://docs.rs/tracing-attributes/0.1.23")] #![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/" diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs index 694f0f5a03..e4767babc1 100644 --- a/tracing-core/src/lib.rs +++ b/tracing-core/src/lib.rs @@ -116,7 +116,6 @@ //! [`Dispatch`]: dispatcher::Dispatch //! [`tokio-rs/tracing`]: https://github.com/tokio-rs/tracing //! [`tracing`]: https://crates.io/crates/tracing -#![doc(html_root_url = "https://docs.rs/tracing-core/0.1.22")] #![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/" diff --git a/tracing-error/src/lib.rs b/tracing-error/src/lib.rs index e3ba0ac743..39bf5e1eee 100644 --- a/tracing-error/src/lib.rs +++ b/tracing-error/src/lib.rs @@ -179,7 +179,6 @@ //! long as doing so complies with this policy. //! #![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))] -#![doc(html_root_url = "https://docs.rs/tracing-error/0.2.0")] #![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/" diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs index 100f16ce18..f38d2f9ca5 100644 --- a/tracing-futures/src/lib.rs +++ b/tracing-futures/src/lib.rs @@ -70,7 +70,6 @@ //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! -#![doc(html_root_url = "https://docs.rs/tracing-futures/0.2.5")] #![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/" diff --git a/tracing-log/src/lib.rs b/tracing-log/src/lib.rs index e2eff92014..75a5397d7e 100644 --- a/tracing-log/src/lib.rs +++ b/tracing-log/src/lib.rs @@ -100,7 +100,6 @@ //! [`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_root_url = "https://docs.rs/tracing-log/0.1.3")] #![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/" diff --git a/tracing-opentelemetry/src/lib.rs b/tracing-opentelemetry/src/lib.rs index 9f5dd1cc83..56044a4408 100644 --- a/tracing-opentelemetry/src/lib.rs +++ b/tracing-opentelemetry/src/lib.rs @@ -100,7 +100,6 @@ //! [subscriber]: tracing_subscriber::subscribe #![deny(unreachable_pub)] #![cfg_attr(test, deny(warnings))] -#![doc(html_root_url = "https://docs.rs/tracing-opentelemetry/0.18.0")] #![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/" diff --git a/tracing-serde/src/lib.rs b/tracing-serde/src/lib.rs index 6530e09478..705ccce505 100644 --- a/tracing-serde/src/lib.rs +++ b/tracing-serde/src/lib.rs @@ -155,7 +155,6 @@ //! //! [`tracing`]: https://crates.io/crates/tracing //! [`serde`]: https://crates.io/crates/serde -#![doc(html_root_url = "https://docs.rs/tracing-serde/0.1.3")] #![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/" diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 7bc61a6c19..b1ef7480e9 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -158,9 +158,8 @@ //! [`env_logger` crate]: https://crates.io/crates/env_logger //! [`parking_lot`]: https://crates.io/crates/parking_lot //! [`time` crate]: https://crates.io/crates/time -//! [`libstd`]: std -//! [`liballoc`]: alloc -#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.3.15")] +//! [`liballoc`]: https://doc.rust-lang.org/alloc/index.html +//! [`libstd`]: https://doc.rust-lang.org/std/index.html #![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/" diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 3d70d7189a..dfb76e9971 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -900,7 +900,6 @@ //! [flags]: #crate-feature-flags #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg), deny(rustdoc::broken_intra_doc_links))] -#![doc(html_root_url = "https://docs.rs/tracing/0.1.37")] #![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/" From 10c32f23b3c574a490c9c9d9e740c72ce80b3a3c Mon Sep 17 00:00:00 2001 From: John Howard Date: Thu, 8 Dec 2022 14:25:27 -0800 Subject: [PATCH 125/142] subscriber: dim `Compact` targets, matching the default formatter (#2409) ## Motivation Fixes https://github.com/tokio-rs/tracing/issues/2408 ## Solution Just switch to use `dim` Before: ![2022-12-08_13-58-40](https://user-images.githubusercontent.com/623453/206576169-63ee4e20-b56f-4c63-a9b3-80ba2e97eec9.png) After: ![2022-12-08_13-55-36](https://user-images.githubusercontent.com/623453/206576055-878d360f-7b95-4e18-bc31-4fb6f1b71a3a.png) Full mode for comparison: ![2022-12-08_13-55-48](https://user-images.githubusercontent.com/623453/206576054-6e38852c-cb3a-4b84-98e5-50463cdb5073.png) --- tracing-subscriber/src/fmt/format/mod.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs index b8a482e55f..fa22c78ec8 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -1082,12 +1082,11 @@ where }; write!(writer, "{}", fmt_ctx)?; - let bold = writer.bold(); let dimmed = writer.dimmed(); let mut needs_space = false; if self.display_target { - write!(writer, "{}{}", bold.paint(meta.target()), dimmed.paint(":"))?; + write!(writer, "{}{}", dimmed.paint(meta.target()), dimmed.paint(":"))?; needs_space = true; } @@ -1096,7 +1095,7 @@ where if self.display_target { writer.write_char(' ')?; } - write!(writer, "{}{}", bold.paint(filename), dimmed.paint(":"))?; + write!(writer, "{}{}", dimmed.paint(filename), dimmed.paint(":"))?; needs_space = true; } } @@ -1106,9 +1105,9 @@ where write!( writer, "{}{}{}{}", - bold.prefix(), + dimmed.prefix(), line_number, - bold.suffix(), + dimmed.suffix(), dimmed.paint(":") )?; needs_space = true; @@ -2039,7 +2038,7 @@ pub(super) mod test { #[cfg(feature = "ansi")] #[test] fn with_ansi_true() { - let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[0m \u{1b}[1mtracing_subscriber::fmt::format::test\u{1b}[0m\u{1b}[2m:\u{1b}[0m hello\n"; + let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[0m \u{1b}[2mtracing_subscriber::fmt::format::test\u{1b}[0m\u{1b}[2m:\u{1b}[0m hello\n"; test_ansi(true, expected, crate::fmt::Subscriber::builder().compact()) } From 7cceb79b1173d7ad140b1fde2d4725213245c27b Mon Sep 17 00:00:00 2001 From: Andrew Pollack Date: Sat, 31 Dec 2022 11:59:44 -0800 Subject: [PATCH 126/142] attributes: add compile error on const fn instrumentation (#2418) ## Motivation The `#[instrument]` macro cannot be used on `const fn`s, because the generated code will perform runtime tracing behavior. However, when adding the attribute to a `const fn`, the compiler errors generated currently are somewhat unclear (see #2414). It would be better if we generated a less verbose error that simply states that `#[instrument]` is not supported on `const fn`s. ## Solution This branch changes the `#[instrument]` macro to detect when the annotated function is a `const fn`, and emit a simpler, more descritpive error message. The new error simply states that the `#[instrument]` attribute cannot be used on `const fn`s, and should be much less confusing to the user. Fixes #2414 --- tracing-attributes/src/lib.rs | 17 ++++++++++++++++- tracing-attributes/tests/ui.rs | 7 +++++++ tracing-attributes/tests/ui/const_instrument.rs | 8 ++++++++ .../tests/ui/const_instrument.stderr | 15 +++++++++++++++ 4 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tracing-attributes/tests/ui/const_instrument.rs create mode 100644 tracing-attributes/tests/ui/const_instrument.stderr diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index dd692512ff..b07415ef62 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -82,7 +82,7 @@ extern crate proc_macro; use proc_macro2::TokenStream; -use quote::ToTokens; +use quote::{quote, ToTokens}; use syn::parse::{Parse, ParseStream}; use syn::{Attribute, ItemFn, Signature, Visibility}; @@ -535,6 +535,14 @@ mod expand; /// } /// ``` /// +/// `const fn` cannot be instrumented, and will result in a compilation failure: +/// +/// ```compile_fail +/// # use tracing_attributes::instrument; +/// #[instrument] +/// const fn my_const_function() {} +/// ``` +/// /// [span]: https://docs.rs/tracing/latest/tracing/span/index.html /// [name]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.name /// [target]: https://docs.rs/tracing/latest/tracing/struct.Metadata.html#method.target @@ -585,6 +593,13 @@ fn instrument_precise( let input = syn::parse::(item)?; let instrumented_function_name = input.sig.ident.to_string(); + if input.sig.constness.is_some() { + return Ok(quote! { + compile_error!("the `#[instrument]` attribute may not be used with `const fn`s") + } + .into()); + } + // check for async_trait-like patterns in the block, and instrument // the future instead of the wrapper if let Some(async_like) = expand::AsyncInfo::from_fn(&input) { diff --git a/tracing-attributes/tests/ui.rs b/tracing-attributes/tests/ui.rs index f11cc019eb..73d7fdcef8 100644 --- a/tracing-attributes/tests/ui.rs +++ b/tracing-attributes/tests/ui.rs @@ -5,3 +5,10 @@ fn async_instrument() { let t = trybuild::TestCases::new(); t.compile_fail("tests/ui/async_instrument.rs"); } + +#[rustversion::stable] +#[test] +fn const_instrument() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/const_instrument.rs"); +} diff --git a/tracing-attributes/tests/ui/const_instrument.rs b/tracing-attributes/tests/ui/const_instrument.rs new file mode 100644 index 0000000000..a251e8f66b --- /dev/null +++ b/tracing-attributes/tests/ui/const_instrument.rs @@ -0,0 +1,8 @@ +#![allow(unreachable_code)] + +#[tracing::instrument] +const fn unit() { + "" +} + +fn main() {} diff --git a/tracing-attributes/tests/ui/const_instrument.stderr b/tracing-attributes/tests/ui/const_instrument.stderr new file mode 100644 index 0000000000..e76d4acad9 --- /dev/null +++ b/tracing-attributes/tests/ui/const_instrument.stderr @@ -0,0 +1,15 @@ +error: macros that expand to items must be delimited with braces or followed by a semicolon + --> tests/ui/const_instrument.rs:3:1 + | +3 | #[tracing::instrument] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `tracing::instrument` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: the `#[instrument]` attribute may not be used with `const fn`s + --> tests/ui/const_instrument.rs:3:1 + | +3 | #[tracing::instrument] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `tracing::instrument` (in Nightly builds, run with -Z macro-backtrace for more info) From 2ea8f8cc509300f193811a63f7270cfcaa81bc22 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Sat, 28 Jan 2023 01:44:10 +0100 Subject: [PATCH 127/142] chore: fix new clippy lints from 1.67.0 (#2451) There are new warnings as errors reported by clippy in Rust 1.67.0. This are causing builds to fail, e.g.: https://github.com/tokio-rs/tracing/actions/runs/4027112923/jobs/6922513360 In both cases they are reports of lifetimes that can be elided. This change removes the unnecessary lifetime annotations to make clippy happy. --- tracing-error/src/layer.rs | 4 ++-- tracing-opentelemetry/src/layer.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tracing-error/src/layer.rs b/tracing-error/src/layer.rs index e804c5e8f3..59f5e3e16a 100644 --- a/tracing-error/src/layer.rs +++ b/tracing-error/src/layer.rs @@ -103,9 +103,9 @@ where } impl WithContext { - pub(crate) fn with_context<'a>( + pub(crate) fn with_context( &self, - dispatch: &'a Dispatch, + dispatch: &Dispatch, id: &span::Id, mut f: impl FnMut(&'static Metadata<'static>, &str) -> bool, ) { diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs index 7624b613e3..dc8a1c608d 100644 --- a/tracing-opentelemetry/src/layer.rs +++ b/tracing-opentelemetry/src/layer.rs @@ -83,9 +83,9 @@ pub(crate) struct WithContext( impl WithContext { // This function allows a function to be called in the context of the // "remembered" subscriber. - pub(crate) fn with_context<'a>( + pub(crate) fn with_context( &self, - dispatch: &'a tracing::Dispatch, + dispatch: &tracing::Dispatch, id: &span::Id, mut f: impl FnMut(&mut OtelData, &dyn PreSampledTracer), ) { From c2cde4f1fc459a3f9e7f784272c6e0e4d3b7683a Mon Sep 17 00:00:00 2001 From: Scott Robinson Date: Thu, 9 Feb 2023 05:26:13 +1100 Subject: [PATCH 128/142] attributes: document default level for `err` (#2433) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation The current description for the default level of the `err` return value event _strongly implies_ it's the same as the span. However, the implementation actually defaults to `ERROR`. ## Solution This PR documents that, so future generations don't have to chase down the truth, like I did. 😉 --- tracing-attributes/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index b07415ef62..b97b4fd754 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -463,6 +463,8 @@ mod expand; /// } /// ``` /// +/// The level of the error value event defaults to `ERROR`. +/// /// Similarly, overriding the level of the `err` event : /// /// ``` From b4a0cae4d750a475551f8afbb3fe3966f1eed791 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Tue, 4 Apr 2023 19:18:53 -0400 Subject: [PATCH 129/142] chore: move tracing-opentelemetry to its own repo (#2523) This PR removes tracing-opentelemetry to a dedicated repo located at https://github.com/tokio-rs/tracing-opentelemetry. (Note that at time of writing this PR, the new repo has not be made public). We're moving tracing-opentelemetry to a dedicated repository for the following reasons: 1. opentelemetry's MSRV is higher than that of `tracing`'s. 2. more importantly, the main `tracing` repo is getting a bit unweildy and it feels unreasonable to maintain backports for crates that integrate with the larger tracing ecosystem. (https://github.com/tokio-rs/tracing-opentelemetry does not have the examples present in this repo; this will occur in a PR that will be linked from _this_ PR.) --- .github/CODEOWNERS | 3 - .github/workflows/CI.yml | 10 +- Cargo.toml | 1 - README.md | 6 - examples/Cargo.toml | 7 +- examples/README.md | 6 - .../examples/opentelemetry-remote-context.rs | 46 - examples/examples/opentelemetry.rs | 49 - tracing-opentelemetry/CHANGELOG.md | 310 ---- tracing-opentelemetry/Cargo.toml | 56 - tracing-opentelemetry/LICENSE | 25 - tracing-opentelemetry/README.md | 132 -- tracing-opentelemetry/benches/trace.rs | 126 -- tracing-opentelemetry/src/layer.rs | 1405 ----------------- tracing-opentelemetry/src/lib.rs | 145 -- tracing-opentelemetry/src/metrics.rs | 367 ----- tracing-opentelemetry/src/span_ext.rs | 170 -- tracing-opentelemetry/src/tracer.rs | 234 --- .../tests/metrics_publishing.rs | 282 ---- .../tests/trace_state_propagation.rs | 171 -- tracing-opentelemetry/trace.png | Bin 87211 -> 0 bytes 21 files changed, 2 insertions(+), 3549 deletions(-) delete mode 100644 examples/examples/opentelemetry-remote-context.rs delete mode 100644 examples/examples/opentelemetry.rs delete mode 100644 tracing-opentelemetry/CHANGELOG.md delete mode 100644 tracing-opentelemetry/Cargo.toml delete mode 100644 tracing-opentelemetry/LICENSE delete mode 100644 tracing-opentelemetry/README.md delete mode 100644 tracing-opentelemetry/benches/trace.rs delete mode 100644 tracing-opentelemetry/src/layer.rs delete mode 100644 tracing-opentelemetry/src/lib.rs delete mode 100644 tracing-opentelemetry/src/metrics.rs delete mode 100644 tracing-opentelemetry/src/span_ext.rs delete mode 100644 tracing-opentelemetry/src/tracer.rs delete mode 100644 tracing-opentelemetry/tests/metrics_publishing.rs delete mode 100644 tracing-opentelemetry/tests/trace_state_propagation.rs delete mode 100644 tracing-opentelemetry/trace.png diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 1c37fa40ff..fade89af20 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -8,8 +8,5 @@ # David contributed the Registry implementation. /tracing-subscriber/registry @davidbarsky @hawkw @tokio-rs/tracing -# Julian contributed the OpenTelemetry implementation. -/tracing-opentelemetry/ @jtescher @tokio-rs/tracing - # Zeki contributed the TracingAppender implementation /tracing-appender/ @zekisherif @tokio-rs/tracing diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 6dfd3e3f71..bced9c4933 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -133,17 +133,11 @@ jobs: - tracing-macros - tracing-serde - tracing-tower - - tracing-opentelemetry - tracing - tracing-subscriber steps: - uses: actions/checkout@v3 - - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - profile: minimal - override: true - + - uses: dtolnay/rust-toolchain@stable - name: install cargo-hack uses: taiki-e/install-action@cargo-hack - name: cargo hack check @@ -194,7 +188,6 @@ jobs: - tracing-serde - tracing-subscriber - tracing-tower - - tracing-opentelemetry - tracing toolchain: - 1.49.0 @@ -295,7 +288,6 @@ jobs: - tracing-journald - tracing-log - tracing-macros - - tracing-opentelemetry - tracing-serde - tracing-subscriber - tracing-tower diff --git a/Cargo.toml b/Cargo.toml index 35d6147776..d272eef50a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ members = [ "tracing-log", "tracing-macros", "tracing-mock", - "tracing-opentelemetry", "tracing-subscriber", "tracing-serde", "tracing-appender", diff --git a/README.md b/README.md index 41e9cf40a9..2bc566ed6e 100644 --- a/README.md +++ b/README.md @@ -305,11 +305,6 @@ The crates included as part of Tracing are: * [`tracing-log`]: Compatibility with the `log` crate (unstable). -* [`tracing-opentelemetry`]: Provides a layer that connects spans from multiple - systems into a trace and emits them to [OpenTelemetry]-compatible distributed - tracing systems for processing and visualization. - ([crates.io][otel-crates]|[docs][otel-docs]) - * [`tracing-serde`]: A compatibility layer for serializing trace data with `serde` (unstable). @@ -337,7 +332,6 @@ The crates included as part of Tracing are: [`tracing-macros`]: tracing-macros [`tracing-attributes`]: tracing-attributes [`tracing-log`]: tracing-log -[`tracing-opentelemetry`]: tracing-opentelemetry [`tracing-serde`]: tracing-serde [`tracing-subscriber`]: tracing-subscriber [`tracing-tower`]: tracing-tower diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 76d663a894..510a0680b8 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -21,9 +21,8 @@ tracing-futures = { version = "0.2.1", path = "../tracing-futures", features = [ tracing-attributes = { path = "../tracing-attributes", version = "0.1.22" } tracing-log = { path = "../tracing-log", version = "0.1.3", features = ["env_logger"] } tracing-serde = { path = "../tracing-serde" } -tracing-opentelemetry = { path = "../tracing-opentelemetry" } -tracing-journald = { path = "../tracing-journald" } tracing-appender = { path = "../tracing-appender", version = "0.2.0" } +tracing-journald = { path = "../tracing-journald" } # serde example serde_json = "1.0.82" @@ -51,10 +50,6 @@ log = "0.4.17" inferno = "0.11.6" tempfile = "3" -# opentelemetry example -opentelemetry = { version = "0.18.0", default-features = false, features = ["trace"] } -opentelemetry-jaeger = "0.17.0" - # fmt examples snafu = "0.6.10" thiserror = "1.0.31" diff --git a/examples/README.md b/examples/README.md index f9998f2684..496fd5f75c 100644 --- a/examples/README.md +++ b/examples/README.md @@ -72,12 +72,6 @@ This directory contains a collection of examples that demonstrate the use of the unstructured logs from dependencies as `tracing` events, by instrumenting [this example][echo] from `hyper`, and using `tracing-log` to record logs emitted by `hyper`. -- **tracing-opentelemetry**: - + `opentelemetry`: Demonstrates how `tracing-opentelemetry` can be used to - export and visualize `tracing` span data. - + `opentelemetry-remote-context`: Demonstrates how `tracing-opentelemetry` - can be used to extract and inject remote context when traces span multiple - systems. [tasks]: (https://docs.rs/tokio/0.2.21/tokio/task/index.html) [tokio-proxy]: https://github.com/tokio-rs/tokio/blob/v0.1.x/tokio/examples/proxy.rs diff --git a/examples/examples/opentelemetry-remote-context.rs b/examples/examples/opentelemetry-remote-context.rs deleted file mode 100644 index 0213631ea2..0000000000 --- a/examples/examples/opentelemetry-remote-context.rs +++ /dev/null @@ -1,46 +0,0 @@ -use opentelemetry::sdk::propagation::TraceContextPropagator; -use opentelemetry::{global, Context}; -use std::collections::HashMap; -use tracing::span; -use tracing_opentelemetry::OpenTelemetrySpanExt; -use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::Registry; - -fn make_request(_cx: Context) { - // perform external request after injecting context - // e.g. if there are request headers that impl `opentelemetry::propagation::Injector` - // then `propagator.inject_context(cx, request.headers_mut())` -} - -fn build_example_carrier() -> HashMap { - let mut carrier = HashMap::new(); - carrier.insert( - "traceparent".to_string(), - "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01".to_string(), - ); - - carrier -} - -fn main() { - // Set a format for propagating context. This MUST be provided, as the default is a no-op. - global::set_text_map_propagator(TraceContextPropagator::new()); - let subscriber = Registry::default().with(tracing_opentelemetry::layer()); - - tracing::subscriber::with_default(subscriber, || { - // Extract context from request headers - let parent_context = global::get_text_map_propagator(|propagator| { - propagator.extract(&build_example_carrier()) - }); - - // Generate tracing span as usual - let app_root = span!(tracing::Level::INFO, "app_start"); - - // Assign parent trace from external context - app_root.set_parent(parent_context); - - // To include tracing context in client requests from _this_ app, - // use `context` to extract the current OpenTelemetry context. - make_request(app_root.context()); - }); -} diff --git a/examples/examples/opentelemetry.rs b/examples/examples/opentelemetry.rs deleted file mode 100644 index e00cd3a500..0000000000 --- a/examples/examples/opentelemetry.rs +++ /dev/null @@ -1,49 +0,0 @@ -use opentelemetry::global; -use std::{error::Error, thread, time::Duration}; -use tracing::{span, trace, warn}; -use tracing_attributes::instrument; -use tracing_subscriber::prelude::*; - -#[instrument] -#[inline] -fn expensive_work() -> &'static str { - span!(tracing::Level::INFO, "expensive_step_1") - .in_scope(|| thread::sleep(Duration::from_millis(25))); - span!(tracing::Level::INFO, "expensive_step_2") - .in_scope(|| thread::sleep(Duration::from_millis(25))); - - "success" -} - -fn main() -> Result<(), Box> { - // Install an otel pipeline with a simple span processor that exports data one at a time when - // spans end. See the `install_batch` option on each exporter's pipeline builder to see how to - // export in batches. - let tracer = opentelemetry_jaeger::new_agent_pipeline() - .with_service_name("report_example") - .install_simple()?; - let opentelemetry = tracing_opentelemetry::layer().with_tracer(tracer); - tracing_subscriber::registry() - .with(opentelemetry) - .try_init()?; - - { - let root = span!(tracing::Level::INFO, "app_start", work_units = 2); - let _enter = root.enter(); - - let work_result = expensive_work(); - - span!(tracing::Level::INFO, "faster_work") - .in_scope(|| thread::sleep(Duration::from_millis(10))); - - warn!("About to exit!"); - trace!("status: {}", work_result); - } // Once this scope is closed, all spans inside are closed as well - - // Shut down the current tracer provider. This will invoke the shutdown - // method on all span processors. span processors should export remaining - // spans before return. - global::shutdown_tracer_provider(); - - Ok(()) -} diff --git a/tracing-opentelemetry/CHANGELOG.md b/tracing-opentelemetry/CHANGELOG.md deleted file mode 100644 index 2f06fe9b60..0000000000 --- a/tracing-opentelemetry/CHANGELOG.md +++ /dev/null @@ -1,310 +0,0 @@ -# 0.18.0 (September 18, 2022) - -### Breaking Changes - -- Upgrade to `v0.18.0` of `opentelemetry` ([#2303]) - For list of breaking changes in OpenTelemetry, see the - [v0.18.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0180). - -### Fixed - -- `on_event` respects event's explicit parent ([#2296]) - -Thanks to @wprzytula for contributing to this release! - -[#2303]: https://github.com/tokio-rs/tracing/pull/2303 -[#2296]: https://github.com/tokio-rs/tracing/pull/2296 - -# 0.17.4 (July 1, 2022) - -This release adds optional support for recording `std::error::Error`s using -[OpenTelemetry's semantic conventions for exceptions][exn-semconv]. - -### Added - -- `Layer::with_exception_fields` to enable emitting `exception.message` and - `exception.backtrace` semantic-convention fields when an `Error` is recorded - as a span or event field ([#2135]) -- `Layer::with_exception_field_propagation` to enable setting `exception.message` and - `exception.backtrace` semantic-convention fields on the current span when an - event with an `Error` field is recorded ([#2135]) - -Thanks to @lilymara-onesignal for contributing to this release! - -[thread-semconv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ -[#2135]: https://github.com/tokio-rs/tracing/pull/2135 - -# 0.17.3 (June 7, 2022) - -This release adds support for emitting thread names and IDs to OpenTelemetry, as -well as recording `std::error::Error` values in a structured manner with their -source chain included. Additionally, this release fixes issues related to event -and span source code locations. - -### Added - -- `Layer::with_threads` to enable recording thread names/IDs according to - [OpenTelemetry semantic conventions][thread-semconv] ([#2134]) -- `Error::source` chain when recording `std::error::Error` values ([#2122]) -- `Layer::with_location` method (replaces `Layer::with_event_location`) - ([#2124]) - -### Changed - -- `std::error::Error` values are now recorded using `fmt::Display` rather than - `fmt::Debug` ([#2122]) - -### Fixed - -- Fixed event source code locations overwriting the parent span's source - location ([#2099]) -- Fixed `Layer::with_event_location` not controlling whether locations are - emitted for spans as well as events ([#2124]) - -### Deprecated - -- `Layer::with_event_location`: renamed to `Layer::with_location`, as it now - controls both span and event locations ([#2124]) - -Thanks to new contributors @lilymara-onesignal, @hubertbudzynski, and @DevinCarr -for contributing to this release! - -[thread-semconv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes -[#2134]: https://github.com/tokio-rs/tracing/pull/2134 -[#2122]: https://github.com/tokio-rs/tracing/pull/2122 -[#2124]: https://github.com/tokio-rs/tracing/pull/2124 -[#2099]: https://github.com/tokio-rs/tracing/pull/2099 - -# 0.17.2 (February 21, 2022) - -This release fixes [an issue][#1944] introduced in v0.17.1 where -`tracing-opentelemetry` could not be compiled with `default-features = false`. - -### Fixed - -- Compilation failure with `tracing-log` feature disabled ([#1949]) - -[#1949]: https://github.com/tokio-rs/tracing/pull/1917 -[#1944]: https://github.com/tokio-rs/tracing/issues/1944 - -# 0.17.1 (February 11, 2022) (YANKED) - -### Added - -- `OpenTelemetryLayer` can now add detailed location information to - forwarded events (defaults to on) ([#1911]) -- `OpenTelemetryLayer::with_event_location` to control whether source locations - are recorded ([#1911]) -### Changed - -- Avoid unnecessary allocations to improve performance when recording events - ([#1917]) - -Thanks to @djc for contributing to this release! - -[#1917]: https://github.com/tokio-rs/tracing/pull/1917 -[#1911]: https://github.com/tokio-rs/tracing/pull/1911 - -# 0.17.0 (February 3, 2022) - -### Breaking Changes - -- Upgrade to `v0.17.0` of `opentelemetry` (#1853) - For list of breaking changes in OpenTelemetry, see the - [v0.17.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0170). - -# 0.16.1 (October 23, 2021) - -### Breaking Changes - -- Upgrade to `v0.3.0` of `tracing-subscriber` ([#1677]) - For list of breaking changes in `tracing-subscriber`, see the - [v0.3.0 changelog]. - -### Added - -- `OpenTelemetrySpanExt::add_link` method for adding a link between a `tracing` - span and a provided OpenTelemetry `Context` ([#1516]) - -Thanks to @LehMaxence for contributing to this release! - -[v0.3.0 changelog]: https://github.com/tokio-rs/tracing/releases/tag/tracing-subscriber-0.3.0 -[#1516]: https://github.com/tokio-rs/tracing/pull/1516 -[#1677]: https://github.com/tokio-rs/tracing/pull/1677 - -# 0.15.0 (August 7, 2021) - -### Breaking Changes - -- Upgrade to `v0.17.1` of `opentelemetry` (#1497) - For list of breaking changes in OpenTelemetry, see the - [v0.17.1 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0160). - -# 0.14.0 (July 9, 2021) - -### Breaking Changes - -- Upgrade to `v0.15.0` of `opentelemetry` ([#1441]) - For list of breaking changes in OpenTelemetry, see the - [v0.14.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0140). - -### Added - -- Spans now include Opentelemetry `code.namespace`, `code.filepath`, and - `code.lineno` attributes ([#1411]) - -### Changed - -- Improve performance by pre-allocating attribute `Vec`s ([#1327]) - -Thanks to @Drevoed, @lilymara-onesignal, and @Folyd for contributing -to this release! - -[#1441]: https://github.com/tokio-rs/tracing/pull/1441 -[#1411]: https://github.com/tokio-rs/tracing/pull/1411 -[#1327]: https://github.com/tokio-rs/tracing/pull/1327 - -# 0.13.0 (May 15, 2021) - -### Breaking Changes - -- Upgrade to `v0.14.0` of `opentelemetry` (#1394) - For list of breaking changes in OpenTelemetry, see the - [v0.14.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0140). - -# 0.12.0 (March 31, 2021) - -### Breaking Changes - -- Upgrade to `v0.13.0` of `opentelemetry` (#1322) - For list of breaking changes in OpenTelemetry, see the - [v0.13.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0130). - -### Changed - -- Improve performance when tracked inactivity is disabled (#1315) - -# 0.11.0 (January 25, 2021) - -### Breaking Changes - -- Upgrade to `v0.12.0` of `opentelemetry` (#1200) - For list of breaking changes in OpenTelemetry, see the - [v0.12.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0120). - -# 0.10.0 (December 30, 2020) - -### Breaking Changes - -- Upgrade to `v0.11.0` of `opentelemetry` (#1161) - For list of breaking changes in OpenTelemetry, see the - [v0.11.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/opentelemetry/CHANGELOG.md#v0110). -- Update `OpenTelemetrySpanExt::set_parent` to take a context by value as it is - now stored and propagated. (#1161) -- Rename `PreSampledTracer::sampled_span_context` to - `PreSampledTracer::sampled_context` as it now returns a full otel context. (#1161) - -# 0.9.0 (November 13, 2020) - -### Added - -- Track busy/idle timings as attributes via `with_tracked_inactivity` (#1096) - -### Breaking Changes - -- Upgrade to `v0.10.0` of `opentelemetry` (#1049) - For list of breaking changes in OpenTelemetry, see the - [v0.10.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/opentelemetry/CHANGELOG.md#v0100). - -# 0.8.0 (October 13, 2020) - -### Added - -- Implement additional record types (bool, i64, u64) (#1007) - -### Breaking changes - -- Add `PreSampledTracer` interface, removes need to specify sampler (#962) - -### Fixed - -- Connect external traces (#956) -- Assign default ids if missing (#1027) - -# 0.7.0 (August 14, 2020) - -### Breaking Changes - -- Upgrade to `v0.8.0` of `opentelemetry` (#932) - For list of breaking changes in OpenTelemetry, see the - [v0.8.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/CHANGELOG.md#v080). - -# 0.6.0 (August 4, 2020) - -### Breaking Changes - -- Upgrade to `v0.7.0` of `opentelemetry` (#867) - For list of breaking changes in OpenTelemetry, see the - [v0.7.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/CHANGELOG.md#v070). - -# 0.5.0 (June 2, 2020) - -### Added - -- Support `tracing-log` special values (#735) -- Support `Span::follows_from` creating otel span links (#723) -- Dynamic otel span names via `otel.name` field (#732) - -### Breaking Changes - -- Upgrade to `v0.6.0` of `opentelemetry` (#745) - -### Fixed - -- Filter out invalid parent contexts when building span contexts (#743) - -# 0.4.0 (May 12, 2020) - -### Added - -- `tracing_opentelemetry::layer()` method to construct a default layer. -- `OpenTelemetryLayer::with_sampler` method to configure the opentelemetry - sampling behavior. -- `OpenTelemetryLayer::new` method to configure both the tracer and sampler. - -### Breaking Changes - -- `OpenTelemetrySpanExt::set_parent` now accepts a reference to an extracted - parent `Context` instead of a `SpanContext` to match propagators. -- `OpenTelemetrySpanExt::context` now returns a `Context` instead of a - `SpanContext` to match propagators. -- `OpenTelemetryLayer::with_tracer` now takes `&self` as a parameter -- Upgrade to `v0.5.0` of `opentelemetry`. - -### Fixed - -- Fixes bug where child spans were always marked as sampled - -# 0.3.1 (April 19, 2020) - -### Added - -- Change span status code to unknown on error event - -# 0.3.0 (April 5, 2020) - -### Added - -- Span extension for injecting and extracting `opentelemetry` span contexts - into `tracing` spans - -### Removed - -- Disabled the `metrics` feature of the opentelemetry as it is unused. - -# 0.2.0 (February 7, 2020) - -### Changed - -- Update `tracing-subscriber` to 0.2.0 stable -- Update to `opentelemetry` 0.2.0 diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml deleted file mode 100644 index 1ba06c8cd0..0000000000 --- a/tracing-opentelemetry/Cargo.toml +++ /dev/null @@ -1,56 +0,0 @@ -[package] -name = "tracing-opentelemetry" -version = "0.18.0" -authors = [ - "Julian Tescher ", - "Tokio Contributors " -] -description = "OpenTelemetry integration for tracing" -homepage = "https://github.com/tokio-rs/tracing/tree/master/tracing-opentelemetry" -repository = "https://github.com/tokio-rs/tracing" -readme = "README.md" -categories = [ - "development-tools::debugging", - "development-tools::profiling", - "asynchronous", -] -keywords = ["tracing", "opentelemetry", "jaeger", "zipkin", "async"] -license = "MIT" -edition = "2018" -rust-version = "1.56.0" - -[features] -default = ["tracing-log", "metrics"] -# Enables support for exporting OpenTelemetry metrics -metrics = ["opentelemetry/metrics"] - -[dependencies] -opentelemetry = { version = "0.18.0", default-features = false, features = ["trace"] } -tracing = { path = "../tracing", version = "0.1.35", default-features = false, features = ["std"] } -tracing-core = { path = "../tracing-core", version = "0.1.28" } -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", default-features = false, features = ["registry", "std"] } -tracing-log = { path = "../tracing-log", version = "0.1.3", default-features = false, optional = true } -once_cell = "1.13.0" - -# Fix minimal-versions -async-trait = { version = "0.1.56", optional = true } -thiserror = { version = "1.0.31", optional = true } - -[dev-dependencies] -async-trait = "0.1.56" -criterion = { version = "0.3.6", default-features = false } -opentelemetry-jaeger = "0.17.0" -futures-util = { version = "0.3", default-features = false } -tokio = { version = "1", features = ["full"] } -tokio-stream = "0.1" - -[lib] -bench = false - -[[bench]] -name = "trace" -harness = false - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/tracing-opentelemetry/LICENSE b/tracing-opentelemetry/LICENSE deleted file mode 100644 index cdb28b4b56..0000000000 --- a/tracing-opentelemetry/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2019 Tokio Contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/tracing-opentelemetry/README.md b/tracing-opentelemetry/README.md deleted file mode 100644 index 4ccbb4a2aa..0000000000 --- a/tracing-opentelemetry/README.md +++ /dev/null @@ -1,132 +0,0 @@ -![Tracing — Structured, application-level diagnostics][splash] - -[splash]: https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/splash.svg - -# Tracing OpenTelemetry - -Utilities for adding [OpenTelemetry] interoperability to [`tracing`]. - -[![Crates.io][crates-badge]][crates-url] -[![Documentation][docs-badge]][docs-url] -[![Documentation (master)][docs-master-badge]][docs-master-url] -[![MIT licensed][mit-badge]][mit-url] -[![Build Status][actions-badge]][actions-url] -[![Discord chat][discord-badge]][discord-url] -![maintenance status][maint-badge] - -[Documentation][docs-url] | [Chat][discord-url] - -[crates-badge]: https://img.shields.io/crates/v/tracing-opentelemetry.svg -[crates-url]: https://crates.io/crates/tracing-opentelemetry/0.18.0 -[docs-badge]: https://docs.rs/tracing-opentelemetry/badge.svg -[docs-url]: https://docs.rs/tracing-opentelemetry/0.18.0/tracing_opentelemetry -[docs-master-badge]: https://img.shields.io/badge/docs-master-blue -[docs-master-url]: https://tracing-rs.netlify.com/tracing_opentelemetry -[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg -[mit-url]: LICENSE -[actions-badge]: https://github.com/tokio-rs/tracing/workflows/CI/badge.svg -[actions-url]:https://github.com/tokio-rs/tracing/actions?query=workflow%3ACI -[discord-badge]: https://img.shields.io/discord/500028886025895936?logo=discord&label=discord&logoColor=white -[discord-url]: https://discord.gg/EeF3cQw -[maint-badge]: https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg - -## Overview - -[`tracing`] is a framework for instrumenting Rust programs to collect -structured, event-based diagnostic information. This crate provides a -subscriber that connects spans from multiple systems into a trace and -emits them to [OpenTelemetry]-compatible distributed tracing systems -for processing and visualization. - -The crate provides the following types: - -* [`OpenTelemetryLayer`] adds OpenTelemetry context to all `tracing` [span]s. -* [`OpenTelemetrySpanExt`] allows OpenTelemetry parent trace information to be - injected and extracted from a `tracing` [span]. - -[`OpenTelemetryLayer`]: https://docs.rs/tracing-opentelemetry/latest/tracing_opentelemetry/struct.OpenTelemetryLayer.html -[`OpenTelemetrySpanExt`]: https://docs.rs/tracing-opentelemetry/latest/tracing_opentelemetry/trait.OpenTelemetrySpanExt.html -[span]: https://docs.rs/tracing/latest/tracing/span/index.html -[`tracing`]: https://crates.io/crates/tracing -[OpenTelemetry]: https://opentelemetry.io/ - -*Compiler support: [requires `rustc` 1.56+][msrv]* - -[msrv]: #supported-rust-versions - -## Examples - -### Basic Usage - -```rust -use opentelemetry::sdk::export::trace::stdout; -use tracing::{error, span}; -use tracing_subscriber::layer::SubscriberExt; -use tracing_subscriber::Registry; - -fn main() { - // Install a new OpenTelemetry trace pipeline - let tracer = stdout::new_pipeline().install_simple(); - - // Create a tracing layer with the configured tracer - let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); - - // Use the tracing subscriber `Registry`, or any other subscriber - // that impls `LookupSpan` - let subscriber = Registry::default().with(telemetry); - - // Trace executed code - tracing::subscriber::with_default(subscriber, || { - // Spans will be sent to the configured OpenTelemetry exporter - let root = span!(tracing::Level::TRACE, "app_start", work_units = 2); - let _enter = root.enter(); - - error!("This event will be logged in the root span."); - }); -} -``` - -### Visualization example - -```console -# Run a supported collector like jaeger in the background -$ docker run -d -p6831:6831/udp -p6832:6832/udp -p16686:16686 jaegertracing/all-in-one:latest - -# Run example to produce spans (from parent examples directory) -$ cargo run --example opentelemetry - -# View spans (see the image below) -$ firefox http://localhost:16686/ -``` - -![Jaeger UI](trace.png) - -## Feature Flags - - - `metrics`: Enables the [`MetricsSubscriber`] type, a [subscriber] that - exports OpenTelemetry metrics from specifically-named events. This enables - the `metrics` feature flag on the `opentelemetry` crate. - -## Supported Rust Versions - -Tracing Opentelemetry 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.45, the minimum supported version will not be increased -past 1.42, 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. - -## License - -This project is licensed under the [MIT license](LICENSE). - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in Tracing by you, shall be licensed as MIT, without any additional -terms or conditions. diff --git a/tracing-opentelemetry/benches/trace.rs b/tracing-opentelemetry/benches/trace.rs deleted file mode 100644 index 8dbc96eaa5..0000000000 --- a/tracing-opentelemetry/benches/trace.rs +++ /dev/null @@ -1,126 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use opentelemetry::{ - sdk::trace::{Tracer, TracerProvider}, - trace::{SpanBuilder, Tracer as _, TracerProvider as _}, - Context, -}; -use std::time::SystemTime; -use tracing::trace_span; -use tracing_subscriber::prelude::*; - -fn many_children(c: &mut Criterion) { - let mut group = c.benchmark_group("otel_many_children"); - - group.bench_function("spec_baseline", |b| { - let provider = TracerProvider::default(); - let tracer = provider.tracer("bench"); - b.iter(|| { - fn dummy(tracer: &Tracer, cx: &Context) { - for _ in 0..99 { - tracer.start_with_context("child", cx); - } - } - - tracer.in_span("parent", |cx| dummy(&tracer, &cx)); - }); - }); - - { - let _subscriber = tracing_subscriber::registry() - .with(RegistryAccessLayer) - .set_default(); - group.bench_function("no_data_baseline", |b| b.iter(tracing_harness)); - } - - { - let _subscriber = tracing_subscriber::registry() - .with(OtelDataLayer) - .set_default(); - group.bench_function("data_only_baseline", |b| b.iter(tracing_harness)); - } - - { - let provider = TracerProvider::default(); - let tracer = provider.tracer("bench"); - let otel_layer = tracing_opentelemetry::layer() - .with_tracer(tracer) - .with_tracked_inactivity(false); - let _subscriber = tracing_subscriber::registry() - .with(otel_layer) - .set_default(); - - group.bench_function("full", |b| b.iter(tracing_harness)); - } -} - -struct NoDataSpan; -struct RegistryAccessLayer; - -impl tracing_subscriber::Layer for RegistryAccessLayer -where - S: tracing_core::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>, -{ - fn on_new_span( - &self, - _attrs: &tracing_core::span::Attributes<'_>, - id: &tracing::span::Id, - ctx: tracing_subscriber::layer::Context<'_, S>, - ) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - extensions.insert(NoDataSpan); - } - - fn on_close(&self, id: tracing::span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) { - let span = ctx.span(&id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - extensions.remove::(); - } -} - -struct OtelDataLayer; - -impl tracing_subscriber::Layer for OtelDataLayer -where - S: tracing_core::Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>, -{ - fn on_new_span( - &self, - attrs: &tracing_core::span::Attributes<'_>, - id: &tracing::span::Id, - ctx: tracing_subscriber::layer::Context<'_, S>, - ) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - extensions.insert( - SpanBuilder::from_name(attrs.metadata().name()).with_start_time(SystemTime::now()), - ); - } - - fn on_close(&self, id: tracing::span::Id, ctx: tracing_subscriber::layer::Context<'_, S>) { - let span = ctx.span(&id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - if let Some(builder) = extensions.remove::() { - builder.with_end_time(SystemTime::now()); - } - } -} - -fn tracing_harness() { - fn dummy() { - for _ in 0..99 { - let child = trace_span!("child"); - let _enter = child.enter(); - } - } - - let parent = trace_span!("parent"); - let _enter = parent.enter(); - - dummy(); -} - -criterion_group!(benches, many_children); -criterion_main!(benches); diff --git a/tracing-opentelemetry/src/layer.rs b/tracing-opentelemetry/src/layer.rs deleted file mode 100644 index dc8a1c608d..0000000000 --- a/tracing-opentelemetry/src/layer.rs +++ /dev/null @@ -1,1405 +0,0 @@ -use crate::{OtelData, PreSampledTracer}; -use once_cell::unsync; -use opentelemetry::{ - trace::{self as otel, noop, OrderMap, TraceContextExt}, - Context as OtelContext, Key, KeyValue, StringValue, Value, -}; -use std::any::TypeId; -use std::fmt; -use std::marker; -use std::thread; -use std::time::{Instant, SystemTime}; -use tracing_core::span::{self, Attributes, Id, Record}; -use tracing_core::{field, Event, Subscriber}; -#[cfg(feature = "tracing-log")] -use tracing_log::NormalizeEvent; -use tracing_subscriber::layer::Context; -use tracing_subscriber::registry::LookupSpan; -use tracing_subscriber::Layer; - -const SPAN_NAME_FIELD: &str = "otel.name"; -const SPAN_KIND_FIELD: &str = "otel.kind"; -const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code"; -const SPAN_STATUS_MESSAGE_FIELD: &str = "otel.status_message"; - -const FIELD_EXCEPTION_MESSAGE: &str = "exception.message"; -const FIELD_EXCEPTION_STACKTRACE: &str = "exception.stacktrace"; - -/// An [OpenTelemetry] propagation layer for use in a project that uses -/// [tracing]. -/// -/// [OpenTelemetry]: https://opentelemetry.io -/// [tracing]: https://github.com/tokio-rs/tracing -pub struct OpenTelemetryLayer { - tracer: T, - location: bool, - tracked_inactivity: bool, - with_threads: bool, - exception_config: ExceptionFieldConfig, - get_context: WithContext, - _registry: marker::PhantomData, -} - -impl Default for OpenTelemetryLayer -where - S: Subscriber + for<'span> LookupSpan<'span>, -{ - fn default() -> Self { - OpenTelemetryLayer::new(noop::NoopTracer::new()) - } -} - -/// Construct a layer to track spans via [OpenTelemetry]. -/// -/// [OpenTelemetry]: https://opentelemetry.io -/// -/// # Examples -/// -/// ```rust,no_run -/// use tracing_subscriber::layer::SubscriberExt; -/// use tracing_subscriber::Registry; -/// -/// // Use the tracing subscriber `Registry`, or any other subscriber -/// // that impls `LookupSpan` -/// let subscriber = Registry::default().with(tracing_opentelemetry::layer()); -/// # drop(subscriber); -/// ``` -pub fn layer() -> OpenTelemetryLayer -where - S: Subscriber + for<'span> LookupSpan<'span>, -{ - OpenTelemetryLayer::default() -} - -// this function "remembers" the types of the subscriber so that we -// can downcast to something aware of them without knowing those -// types at the callsite. -// -// See https://github.com/tokio-rs/tracing/blob/4dad420ee1d4607bad79270c1520673fa6266a3d/tracing-error/src/layer.rs -pub(crate) struct WithContext( - fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer)), -); - -impl WithContext { - // This function allows a function to be called in the context of the - // "remembered" subscriber. - pub(crate) fn with_context( - &self, - dispatch: &tracing::Dispatch, - id: &span::Id, - mut f: impl FnMut(&mut OtelData, &dyn PreSampledTracer), - ) { - (self.0)(dispatch, id, &mut f) - } -} - -fn str_to_span_kind(s: &str) -> Option { - match s { - s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server), - s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client), - s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer), - s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer), - s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal), - _ => None, - } -} - -fn str_to_status(s: &str) -> otel::Status { - match s { - s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok, - s if s.eq_ignore_ascii_case("error") => otel::Status::error(""), - _ => otel::Status::Unset, - } -} - -struct SpanEventVisitor<'a, 'b> { - event_builder: &'a mut otel::Event, - span_builder: Option<&'b mut otel::SpanBuilder>, - exception_config: ExceptionFieldConfig, -} - -impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { - /// Record events on the underlying OpenTelemetry [`Span`] from `bool` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_bool(&mut self, field: &field::Field, value: bool) { - match field.name() { - "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), - name => { - self.event_builder - .attributes - .push(KeyValue::new(name, value)); - } - } - } - - /// Record events on the underlying OpenTelemetry [`Span`] from `f64` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_f64(&mut self, field: &field::Field, value: f64) { - match field.name() { - "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), - name => { - self.event_builder - .attributes - .push(KeyValue::new(name, value)); - } - } - } - - /// Record events on the underlying OpenTelemetry [`Span`] from `i64` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_i64(&mut self, field: &field::Field, value: i64) { - match field.name() { - "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), - name => { - self.event_builder - .attributes - .push(KeyValue::new(name, value)); - } - } - } - - /// Record events on the underlying OpenTelemetry [`Span`] from `&str` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_str(&mut self, field: &field::Field, value: &str) { - match field.name() { - "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), - name => { - self.event_builder - .attributes - .push(KeyValue::new(name, value.to_string())); - } - } - } - - /// Record events on the underlying OpenTelemetry [`Span`] from values that - /// implement Debug. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { - match field.name() { - "message" => self.event_builder.name = format!("{:?}", value).into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), - name => { - self.event_builder - .attributes - .push(KeyValue::new(name, format!("{:?}", value))); - } - } - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s - /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_error( - &mut self, - field: &tracing_core::Field, - value: &(dyn std::error::Error + 'static), - ) { - let mut chain = Vec::new(); - let mut next_err = value.source(); - - while let Some(err) = next_err { - chain.push(StringValue::from(err.to_string())); - next_err = err.source(); - } - - let error_msg = value.to_string(); - - if self.exception_config.record { - self.event_builder - .attributes - .push(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone())); - - // NOTE: This is actually not the stacktrace of the exception. This is - // the "source chain". It represents the heirarchy of errors from the - // app level to the lowest level such as IO. It does not represent all - // of the callsites in the code that led to the error happening. - // `std::error::Error::backtrace` is a nightly-only API and cannot be - // used here until the feature is stabilized. - self.event_builder - .attributes - .push(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone())); - } - - if self.exception_config.propagate { - if let Some(span) = &mut self.span_builder { - if let Some(attrs) = span.attributes.as_mut() { - attrs.insert(Key::new(FIELD_EXCEPTION_MESSAGE), error_msg.clone().into()); - - // NOTE: This is actually not the stacktrace of the exception. This is - // the "source chain". It represents the heirarchy of errors from the - // app level to the lowest level such as IO. It does not represent all - // of the callsites in the code that led to the error happening. - // `std::error::Error::backtrace` is a nightly-only API and cannot be - // used here until the feature is stabilized. - attrs.insert( - Key::new(FIELD_EXCEPTION_STACKTRACE), - Value::Array(chain.clone().into()), - ); - } - } - } - - self.event_builder - .attributes - .push(Key::new(field.name()).string(error_msg)); - self.event_builder - .attributes - .push(Key::new(format!("{}.chain", field.name())).array(chain)); - } -} - -/// Control over opentelemetry conventional exception fields -#[derive(Clone, Copy)] -struct ExceptionFieldConfig { - /// If an error value is recorded on an event/span, should the otel fields - /// be added - record: bool, - - /// If an error value is recorded on an event, should the otel fields be - /// added to the corresponding span - propagate: bool, -} - -struct SpanAttributeVisitor<'a> { - span_builder: &'a mut otel::SpanBuilder, - exception_config: ExceptionFieldConfig, -} - -impl<'a> SpanAttributeVisitor<'a> { - fn record(&mut self, attribute: KeyValue) { - debug_assert!(self.span_builder.attributes.is_some()); - if let Some(v) = self.span_builder.attributes.as_mut() { - v.insert(attribute.key, attribute.value); - } - } -} - -impl<'a> field::Visit for SpanAttributeVisitor<'a> { - /// Set attributes on the underlying OpenTelemetry [`Span`] from `bool` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_bool(&mut self, field: &field::Field, value: bool) { - self.record(KeyValue::new(field.name(), value)); - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] from `f64` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_f64(&mut self, field: &field::Field, value: f64) { - self.record(KeyValue::new(field.name(), value)); - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] from `i64` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_i64(&mut self, field: &field::Field, value: i64) { - self.record(KeyValue::new(field.name(), value)); - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] from `&str` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_str(&mut self, field: &field::Field, value: &str) { - match field.name() { - SPAN_NAME_FIELD => self.span_builder.name = value.to_string().into(), - SPAN_KIND_FIELD => self.span_builder.span_kind = str_to_span_kind(value), - SPAN_STATUS_CODE_FIELD => self.span_builder.status = str_to_status(value), - SPAN_STATUS_MESSAGE_FIELD => { - self.span_builder.status = otel::Status::error(value.to_string()) - } - _ => self.record(KeyValue::new(field.name(), value.to_string())), - } - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] from values that - /// implement Debug. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { - match field.name() { - SPAN_NAME_FIELD => self.span_builder.name = format!("{:?}", value).into(), - SPAN_KIND_FIELD => { - self.span_builder.span_kind = str_to_span_kind(&format!("{:?}", value)) - } - SPAN_STATUS_CODE_FIELD => { - self.span_builder.status = str_to_status(&format!("{:?}", value)) - } - SPAN_STATUS_MESSAGE_FIELD => { - self.span_builder.status = otel::Status::error(format!("{:?}", value)) - } - _ => self.record(Key::new(field.name()).string(format!("{:?}", value))), - } - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s - /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_error( - &mut self, - field: &tracing_core::Field, - value: &(dyn std::error::Error + 'static), - ) { - let mut chain = Vec::new(); - let mut next_err = value.source(); - - while let Some(err) = next_err { - chain.push(StringValue::from(err.to_string())); - next_err = err.source(); - } - - let error_msg = value.to_string(); - - if self.exception_config.record { - self.record(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone())); - - // NOTE: This is actually not the stacktrace of the exception. This is - // the "source chain". It represents the heirarchy of errors from the - // app level to the lowest level such as IO. It does not represent all - // of the callsites in the code that led to the error happening. - // `std::error::Error::backtrace` is a nightly-only API and cannot be - // used here until the feature is stabilized. - self.record(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone())); - } - - self.record(Key::new(field.name()).string(error_msg)); - self.record(Key::new(format!("{}.chain", field.name())).array(chain)); - } -} - -impl OpenTelemetryLayer -where - S: Subscriber + for<'span> LookupSpan<'span>, - T: otel::Tracer + PreSampledTracer + 'static, -{ - /// Set the [`Tracer`] that this layer will use to produce and track - /// OpenTelemetry [`Span`]s. - /// - /// [`Tracer`]: opentelemetry::trace::Tracer - /// [`Span`]: opentelemetry::trace::Span - /// - /// # Examples - /// - /// ```no_run - /// use tracing_opentelemetry::OpenTelemetryLayer; - /// use tracing_subscriber::layer::SubscriberExt; - /// use tracing_subscriber::Registry; - /// - /// // Create a jaeger exporter pipeline for a `trace_demo` service. - /// let tracer = opentelemetry_jaeger::new_agent_pipeline() - /// .with_service_name("trace_demo") - /// .install_simple() - /// .expect("Error initializing Jaeger exporter"); - /// - /// // Create a layer with the configured tracer - /// let otel_layer = OpenTelemetryLayer::new(tracer); - /// - /// // Use the tracing subscriber `Registry`, or any other subscriber - /// // that impls `LookupSpan` - /// let subscriber = Registry::default().with(otel_layer); - /// # drop(subscriber); - /// ``` - pub fn new(tracer: T) -> Self { - OpenTelemetryLayer { - tracer, - location: true, - tracked_inactivity: true, - with_threads: true, - exception_config: ExceptionFieldConfig { - record: false, - propagate: false, - }, - get_context: WithContext(Self::get_context), - _registry: marker::PhantomData, - } - } - - /// Set the [`Tracer`] that this layer will use to produce and track - /// OpenTelemetry [`Span`]s. - /// - /// [`Tracer`]: opentelemetry::trace::Tracer - /// [`Span`]: opentelemetry::trace::Span - /// - /// # Examples - /// - /// ```no_run - /// use tracing_subscriber::layer::SubscriberExt; - /// use tracing_subscriber::Registry; - /// - /// // Create a jaeger exporter pipeline for a `trace_demo` service. - /// let tracer = opentelemetry_jaeger::new_agent_pipeline() - /// .with_service_name("trace_demo") - /// .install_simple() - /// .expect("Error initializing Jaeger exporter"); - /// - /// // Create a layer with the configured tracer - /// let otel_layer = tracing_opentelemetry::layer().with_tracer(tracer); - /// - /// // Use the tracing subscriber `Registry`, or any other subscriber - /// // that impls `LookupSpan` - /// let subscriber = Registry::default().with(otel_layer); - /// # drop(subscriber); - /// ``` - pub fn with_tracer(self, tracer: Tracer) -> OpenTelemetryLayer - where - Tracer: otel::Tracer + PreSampledTracer + 'static, - { - OpenTelemetryLayer { - tracer, - location: self.location, - tracked_inactivity: self.tracked_inactivity, - with_threads: self.with_threads, - exception_config: self.exception_config, - get_context: WithContext(OpenTelemetryLayer::::get_context), - _registry: self._registry, - } - } - - /// Sets whether or not span and event metadata should include OpenTelemetry - /// exception fields such as `exception.message` and `exception.backtrace` - /// when an `Error` value is recorded. If multiple error values are recorded - /// on the same span/event, only the most recently recorded error value will - /// show up under these fields. - /// - /// These attributes follow the [OpenTelemetry semantic conventions for - /// exceptions][conv]. - /// - /// By default, these attributes are not recorded. - /// - /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ - pub fn with_exception_fields(self, exception_fields: bool) -> Self { - Self { - exception_config: ExceptionFieldConfig { - record: exception_fields, - ..self.exception_config - }, - ..self - } - } - - /// Sets whether or not reporting an `Error` value on an event will - /// propagate the OpenTelemetry exception fields such as `exception.message` - /// and `exception.backtrace` to the corresponding span. You do not need to - /// enable `with_exception_fields` in order to enable this. If multiple - /// error values are recorded on the same span/event, only the most recently - /// recorded error value will show up under these fields. - /// - /// These attributes follow the [OpenTelemetry semantic conventions for - /// exceptions][conv]. - /// - /// By default, these attributes are not propagated to the span. - /// - /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ - pub fn with_exception_field_propagation(self, exception_field_propagation: bool) -> Self { - Self { - exception_config: ExceptionFieldConfig { - propagate: exception_field_propagation, - ..self.exception_config - }, - ..self - } - } - - /// Sets whether or not span and event metadata should include OpenTelemetry - /// attributes with location information, such as the file, module and line number. - /// - /// These attributes follow the [OpenTelemetry semantic conventions for - /// source locations][conv]. - /// - /// By default, locations are enabled. - /// - /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes - pub fn with_location(self, location: bool) -> Self { - Self { location, ..self } - } - - /// Sets whether or not span and event metadata should include OpenTelemetry - /// attributes with location information, such as the file, module and line number. - /// - /// These attributes follow the [OpenTelemetry semantic conventions for - /// source locations][conv]. - /// - /// By default, locations are enabled. - /// - /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes - #[deprecated( - since = "0.17.3", - note = "renamed to `OpenTelemetrySubscriber::with_location`" - )] - pub fn with_event_location(self, event_location: bool) -> Self { - Self { - location: event_location, - ..self - } - } - - /// Sets whether or not spans metadata should include the _busy time_ - /// (total time for which it was entered), and _idle time_ (total time - /// the span existed but was not entered). - pub fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self { - Self { - tracked_inactivity, - ..self - } - } - - /// Sets whether or not spans record additional attributes for the thread - /// name and thread ID of the thread they were created on, following the - /// [OpenTelemetry semantic conventions for threads][conv]. - /// - /// By default, thread attributes are enabled. - /// - /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#general-thread-attributes - pub fn with_threads(self, threads: bool) -> Self { - Self { - with_threads: threads, - ..self - } - } - - /// Retrieve the parent OpenTelemetry [`Context`] from the current tracing - /// [`span`] through the [`Registry`]. This [`Context`] links spans to their - /// parent for proper hierarchical visualization. - /// - /// [`Context`]: opentelemetry::Context - /// [`span`]: tracing::Span - /// [`Registry`]: tracing_subscriber::Registry - fn parent_context(&self, attrs: &Attributes<'_>, ctx: &Context<'_, S>) -> OtelContext { - // If a span is specified, it _should_ exist in the underlying `Registry`. - if let Some(parent) = attrs.parent() { - let span = ctx.span(parent).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - extensions - .get_mut::() - .map(|builder| self.tracer.sampled_context(builder)) - .unwrap_or_default() - // Else if the span is inferred from context, look up any available current span. - } else if attrs.is_contextual() { - ctx.lookup_current() - .and_then(|span| { - let mut extensions = span.extensions_mut(); - extensions - .get_mut::() - .map(|builder| self.tracer.sampled_context(builder)) - }) - .unwrap_or_else(OtelContext::current) - // Explicit root spans should have no parent context. - } else { - OtelContext::new() - } - } - - fn get_context( - dispatch: &tracing::Dispatch, - id: &span::Id, - f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer), - ) { - let subscriber = dispatch - .downcast_ref::() - .expect("subscriber should downcast to expected type; this is a bug!"); - let span = subscriber - .span(id) - .expect("registry should have a span for the current ID"); - let layer = dispatch - .downcast_ref::>() - .expect("layer should downcast to expected type; this is a bug!"); - - let mut extensions = span.extensions_mut(); - if let Some(builder) = extensions.get_mut::() { - f(builder, &layer.tracer); - } - } - - fn extra_span_attrs(&self) -> usize { - let mut extra_attrs = 0; - if self.location { - extra_attrs += 3; - } - if self.with_threads { - extra_attrs += 2; - } - extra_attrs - } -} - -thread_local! { - static THREAD_ID: unsync::Lazy = unsync::Lazy::new(|| { - // OpenTelemetry's semantic conventions require the thread ID to be - // recorded as an integer, but `std::thread::ThreadId` does not expose - // the integer value on stable, so we have to convert it to a `usize` by - // parsing it. Since this requires allocating a `String`, store it in a - // thread local so we only have to do this once. - // TODO(eliza): once `std::thread::ThreadId::as_u64` is stabilized - // (https://github.com/rust-lang/rust/issues/67939), just use that. - thread_id_integer(thread::current().id()) - }); -} - -impl Layer for OpenTelemetryLayer -where - S: Subscriber + for<'span> LookupSpan<'span>, - T: otel::Tracer + PreSampledTracer + 'static, -{ - /// Creates an [OpenTelemetry `Span`] for the corresponding [tracing `Span`]. - /// - /// [OpenTelemetry `Span`]: opentelemetry::trace::Span - /// [tracing `Span`]: tracing::Span - fn on_new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - if self.tracked_inactivity && extensions.get_mut::().is_none() { - extensions.insert(Timings::new()); - } - - let parent_cx = self.parent_context(attrs, &ctx); - let mut builder = self - .tracer - .span_builder(attrs.metadata().name()) - .with_start_time(SystemTime::now()) - // Eagerly assign span id so children have stable parent id - .with_span_id(self.tracer.new_span_id()); - - // Record new trace id if there is no active parent span - if !parent_cx.has_active_span() { - builder.trace_id = Some(self.tracer.new_trace_id()); - } - - let builder_attrs = builder.attributes.get_or_insert(OrderMap::with_capacity( - attrs.fields().len() + self.extra_span_attrs(), - )); - - if self.location { - let meta = attrs.metadata(); - - if let Some(filename) = meta.file() { - builder_attrs.insert("code.filepath".into(), filename.into()); - } - - if let Some(module) = meta.module_path() { - builder_attrs.insert("code.namespace".into(), module.into()); - } - - if let Some(line) = meta.line() { - builder_attrs.insert("code.lineno".into(), (line as i64).into()); - } - } - - if self.with_threads { - THREAD_ID.with(|id| builder_attrs.insert("thread.id".into(), (**id as i64).into())); - if let Some(name) = std::thread::current().name() { - // TODO(eliza): it's a bummer that we have to allocate here, but - // we can't easily get the string as a `static`. it would be - // nice if `opentelemetry` could also take `Arc`s as - // `String` values... - builder_attrs.insert("thread.name".into(), name.to_owned().into()); - } - } - - attrs.record(&mut SpanAttributeVisitor { - span_builder: &mut builder, - exception_config: self.exception_config, - }); - extensions.insert(OtelData { builder, parent_cx }); - } - - fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { - if !self.tracked_inactivity { - return; - } - - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - if let Some(timings) = extensions.get_mut::() { - let now = Instant::now(); - timings.idle += (now - timings.last).as_nanos() as i64; - timings.last = now; - } - } - - fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { - if !self.tracked_inactivity { - return; - } - - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - if let Some(timings) = extensions.get_mut::() { - let now = Instant::now(); - timings.busy += (now - timings.last).as_nanos() as i64; - timings.last = now; - } - } - - /// Record OpenTelemetry [`attributes`] for the given values. - /// - /// [`attributes`]: opentelemetry::trace::SpanBuilder::attributes - fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - if let Some(data) = extensions.get_mut::() { - values.record(&mut SpanAttributeVisitor { - span_builder: &mut data.builder, - exception_config: self.exception_config, - }); - } - } - - fn on_follows_from(&self, id: &Id, follows: &Id, ctx: Context) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - let data = extensions - .get_mut::() - .expect("Missing otel data span extensions"); - - let follows_span = ctx - .span(follows) - .expect("Span to follow not found, this is a bug"); - let mut follows_extensions = follows_span.extensions_mut(); - let follows_data = follows_extensions - .get_mut::() - .expect("Missing otel data span extensions"); - - let follows_context = self - .tracer - .sampled_context(follows_data) - .span() - .span_context() - .clone(); - let follows_link = otel::Link::new(follows_context, Vec::new()); - if let Some(ref mut links) = data.builder.links { - links.push(follows_link); - } else { - data.builder.links = Some(vec![follows_link]); - } - } - - /// Records OpenTelemetry [`Event`] data on event. - /// - /// Note: an [`ERROR`]-level event will also set the OpenTelemetry span status code to - /// [`Error`], signaling that an error has occurred. - /// - /// [`Event`]: opentelemetry::trace::Event - /// [`ERROR`]: tracing::Level::ERROR - /// [`Error`]: opentelemetry::trace::StatusCode::Error - fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { - // Ignore events that have no explicit parent set *and* are not in the context of a span - if let Some(span) = ctx.event_span(event) { - // Performing read operations before getting a write lock to avoid a deadlock - // See https://github.com/tokio-rs/tracing/issues/763 - #[cfg(feature = "tracing-log")] - let normalized_meta = event.normalized_metadata(); - #[cfg(feature = "tracing-log")] - let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); - #[cfg(not(feature = "tracing-log"))] - let meta = event.metadata(); - - let target = Key::new("target"); - - #[cfg(feature = "tracing-log")] - let target = if normalized_meta.is_some() { - target.string(meta.target().to_owned()) - } else { - target.string(event.metadata().target()) - }; - - #[cfg(not(feature = "tracing-log"))] - let target = target.string(meta.target()); - - let mut extensions = span.extensions_mut(); - let span_builder = extensions - .get_mut::() - .map(|data| &mut data.builder); - - let mut otel_event = otel::Event::new( - String::new(), - SystemTime::now(), - vec![Key::new("level").string(meta.level().as_str()), target], - 0, - ); - event.record(&mut SpanEventVisitor { - event_builder: &mut otel_event, - span_builder, - exception_config: self.exception_config, - }); - - if let Some(OtelData { builder, .. }) = extensions.get_mut::() { - if builder.status == otel::Status::Unset - && *meta.level() == tracing_core::Level::ERROR - { - builder.status = otel::Status::error("") - } - - if self.location { - #[cfg(not(feature = "tracing-log"))] - let normalized_meta: Option> = None; - let (file, module) = match &normalized_meta { - Some(meta) => ( - meta.file().map(|s| Value::from(s.to_owned())), - meta.module_path().map(|s| Value::from(s.to_owned())), - ), - None => ( - event.metadata().file().map(Value::from), - event.metadata().module_path().map(Value::from), - ), - }; - - if let Some(file) = file { - otel_event - .attributes - .push(KeyValue::new("code.filepath", file)); - } - if let Some(module) = module { - otel_event - .attributes - .push(KeyValue::new("code.namespace", module)); - } - if let Some(line) = meta.line() { - otel_event - .attributes - .push(KeyValue::new("code.lineno", line as i64)); - } - } - - if let Some(ref mut events) = builder.events { - events.push(otel_event); - } else { - builder.events = Some(vec![otel_event]); - } - } - }; - } - - /// Exports an OpenTelemetry [`Span`] on close. - /// - /// [`Span`]: opentelemetry::trace::Span - fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { - let span = ctx.span(&id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - if let Some(OtelData { - mut builder, - parent_cx, - }) = extensions.remove::() - { - if self.tracked_inactivity { - // Append busy/idle timings when enabled. - if let Some(timings) = extensions.get_mut::() { - let busy_ns = Key::new("busy_ns"); - let idle_ns = Key::new("idle_ns"); - - let attributes = builder - .attributes - .get_or_insert_with(|| OrderMap::with_capacity(2)); - attributes.insert(busy_ns, timings.busy.into()); - attributes.insert(idle_ns, timings.idle.into()); - } - } - - // Assign end time, build and start span, drop span to export - builder - .with_end_time(SystemTime::now()) - .start_with_context(&self.tracer, &parent_cx); - } - } - - // SAFETY: this is safe because the `WithContext` function pointer is valid - // for the lifetime of `&self`. - unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { - match id { - id if id == TypeId::of::() => Some(self as *const _ as *const ()), - id if id == TypeId::of::() => { - Some(&self.get_context as *const _ as *const ()) - } - _ => None, - } - } -} - -struct Timings { - idle: i64, - busy: i64, - last: Instant, -} - -impl Timings { - fn new() -> Self { - Self { - idle: 0, - busy: 0, - last: Instant::now(), - } - } -} - -fn thread_id_integer(id: thread::ThreadId) -> u64 { - let thread_id = format!("{:?}", id); - thread_id - .trim_start_matches("ThreadId(") - .trim_end_matches(')') - .parse::() - .expect("thread ID should parse as an integer") -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::OtelData; - use opentelemetry::{ - trace::{noop, TraceFlags}, - StringValue, - }; - use std::{ - borrow::Cow, - collections::HashMap, - error::Error, - fmt::Display, - sync::{Arc, Mutex}, - thread, - time::SystemTime, - }; - use tracing_subscriber::prelude::*; - - #[derive(Debug, Clone)] - struct TestTracer(Arc>>); - impl otel::Tracer for TestTracer { - type Span = noop::NoopSpan; - fn start_with_context(&self, _name: T, _context: &OtelContext) -> Self::Span - where - T: Into>, - { - noop::NoopSpan::new() - } - fn span_builder(&self, name: T) -> otel::SpanBuilder - where - T: Into>, - { - otel::SpanBuilder::from_name(name) - } - fn build_with_context( - &self, - builder: otel::SpanBuilder, - parent_cx: &OtelContext, - ) -> Self::Span { - *self.0.lock().unwrap() = Some(OtelData { - builder, - parent_cx: parent_cx.clone(), - }); - noop::NoopSpan::new() - } - } - - impl PreSampledTracer for TestTracer { - fn sampled_context(&self, _builder: &mut crate::OtelData) -> OtelContext { - OtelContext::new() - } - fn new_trace_id(&self) -> otel::TraceId { - otel::TraceId::INVALID - } - fn new_span_id(&self) -> otel::SpanId { - otel::SpanId::INVALID - } - } - - impl TestTracer { - fn with_data(&self, f: impl FnOnce(&OtelData) -> T) -> T { - let lock = self.0.lock().unwrap(); - let data = lock.as_ref().expect("no span data has been recorded yet"); - f(data) - } - } - - #[derive(Debug, Clone)] - struct TestSpan(otel::SpanContext); - impl otel::Span for TestSpan { - fn add_event_with_timestamp>>( - &mut self, - _: T, - _: SystemTime, - _: Vec, - ) { - } - fn span_context(&self) -> &otel::SpanContext { - &self.0 - } - fn is_recording(&self) -> bool { - false - } - fn set_attribute(&mut self, _attribute: KeyValue) {} - fn set_status(&mut self, _status: otel::Status) {} - fn update_name>>(&mut self, _new_name: T) {} - fn end_with_timestamp(&mut self, _timestamp: SystemTime) {} - } - - #[derive(Debug)] - struct TestDynError { - msg: &'static str, - source: Option>, - } - impl Display for TestDynError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.msg) - } - } - impl Error for TestDynError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match &self.source { - Some(source) => Some(source), - None => None, - } - } - } - impl TestDynError { - fn new(msg: &'static str) -> Self { - Self { msg, source: None } - } - fn with_parent(self, parent_msg: &'static str) -> Self { - Self { - msg: parent_msg, - source: Some(Box::new(self)), - } - } - } - - #[test] - fn dynamic_span_names() { - let dynamic_name = "GET http://example.com".to_string(); - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("static_name", otel.name = dynamic_name.as_str()); - }); - - let recorded_name = tracer - .0 - .lock() - .unwrap() - .as_ref() - .map(|b| b.builder.name.clone()); - assert_eq!(recorded_name, Some(dynamic_name.into())) - } - - #[test] - fn span_kind() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("request", otel.kind = "server"); - }); - - let recorded_kind = tracer.with_data(|data| data.builder.span_kind.clone()); - assert_eq!(recorded_kind, Some(otel::SpanKind::Server)) - } - - #[test] - fn span_status_code() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok); - }); - let recorded_status = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .status - .clone(); - - assert_eq!(recorded_status, otel::Status::Ok) - } - - #[test] - fn span_status_message() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); - - let message = "message"; - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("request", otel.status_message = message); - }); - - let recorded_status_message = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .status - .clone(); - - assert_eq!(recorded_status_message, otel::Status::error(message)) - } - - #[test] - fn trace_id_from_existing_context() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); - let trace_id = otel::TraceId::from(42u128.to_be_bytes()); - let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new( - trace_id, - otel::SpanId::from(1u64.to_be_bytes()), - TraceFlags::default(), - false, - Default::default(), - ))); - let _g = existing_cx.attach(); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("request", otel.kind = "server"); - }); - - let recorded_trace_id = - tracer.with_data(|data| data.parent_cx.span().span_context().trace_id()); - assert_eq!(recorded_trace_id, trace_id) - } - - #[test] - fn includes_timings() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with( - layer() - .with_tracer(tracer.clone()) - .with_tracked_inactivity(true), - ); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); - let keys = attributes - .iter() - .map(|(key, _)| key.as_str()) - .collect::>(); - assert!(keys.contains(&"idle_ns")); - assert!(keys.contains(&"busy_ns")); - } - - #[test] - fn records_error_fields() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with( - layer() - .with_tracer(tracer.clone()) - .with_exception_fields(true), - ); - - let err = TestDynError::new("base error") - .with_parent("intermediate error") - .with_parent("user error"); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!( - "request", - error = &err as &(dyn std::error::Error + 'static) - ); - }); - - let attributes = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .attributes - .as_ref() - .unwrap() - .clone(); - - let key_values = attributes - .into_iter() - .map(|(key, value)| (key.as_str().to_owned(), value)) - .collect::>(); - - assert_eq!(key_values["error"].as_str(), "user error"); - assert_eq!( - key_values["error.chain"], - Value::Array( - vec![ - StringValue::from("intermediate error"), - StringValue::from("base error") - ] - .into() - ) - ); - - assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); - assert_eq!( - key_values[FIELD_EXCEPTION_STACKTRACE], - Value::Array( - vec![ - StringValue::from("intermediate error"), - StringValue::from("base error") - ] - .into() - ) - ); - } - - #[test] - fn includes_span_location() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry() - .with(layer().with_tracer(tracer.clone()).with_location(true)); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); - let keys = attributes - .iter() - .map(|(key, _)| key.as_str()) - .collect::>(); - assert!(keys.contains(&"code.filepath")); - assert!(keys.contains(&"code.namespace")); - assert!(keys.contains(&"code.lineno")); - } - - #[test] - fn excludes_span_location() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry() - .with(layer().with_tracer(tracer.clone()).with_location(false)); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); - let keys = attributes - .iter() - .map(|(key, _)| key.as_str()) - .collect::>(); - assert!(!keys.contains(&"code.filepath")); - assert!(!keys.contains(&"code.namespace")); - assert!(!keys.contains(&"code.lineno")); - } - - #[test] - fn includes_thread() { - let thread = thread::current(); - let expected_name = thread - .name() - .map(|name| Value::String(name.to_owned().into())); - let expected_id = Value::I64(thread_id_integer(thread.id()) as i64); - - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry() - .with(layer().with_tracer(tracer.clone()).with_threads(true)); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer - .with_data(|data| data.builder.attributes.as_ref().unwrap().clone()) - .drain(..) - .map(|(key, value)| (key.as_str().to_string(), value)) - .collect::>(); - assert_eq!(attributes.get("thread.name"), expected_name.as_ref()); - assert_eq!(attributes.get("thread.id"), Some(&expected_id)); - } - - #[test] - fn excludes_thread() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry() - .with(layer().with_tracer(tracer.clone()).with_threads(false)); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); - let keys = attributes - .iter() - .map(|(key, _)| key.as_str()) - .collect::>(); - assert!(!keys.contains(&"thread.name")); - assert!(!keys.contains(&"thread.id")); - } - - #[test] - fn propagates_error_fields_from_event_to_span() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with( - layer() - .with_tracer(tracer.clone()) - .with_exception_field_propagation(true), - ); - - let err = TestDynError::new("base error") - .with_parent("intermediate error") - .with_parent("user error"); - - tracing::subscriber::with_default(subscriber, || { - let _guard = tracing::debug_span!("request",).entered(); - - tracing::error!( - error = &err as &(dyn std::error::Error + 'static), - "request error!" - ) - }); - - let attributes = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .attributes - .as_ref() - .unwrap() - .clone(); - - let key_values = attributes - .into_iter() - .map(|(key, value)| (key.as_str().to_owned(), value)) - .collect::>(); - - assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); - assert_eq!( - key_values[FIELD_EXCEPTION_STACKTRACE], - Value::Array( - vec![ - StringValue::from("intermediate error"), - StringValue::from("base error") - ] - .into() - ) - ); - } -} diff --git a/tracing-opentelemetry/src/lib.rs b/tracing-opentelemetry/src/lib.rs deleted file mode 100644 index 56044a4408..0000000000 --- a/tracing-opentelemetry/src/lib.rs +++ /dev/null @@ -1,145 +0,0 @@ -//! # Tracing OpenTelemetry -//! -//! [`tracing`] is a framework for instrumenting Rust programs to collect -//! structured, event-based diagnostic information. This crate provides a layer -//! that connects spans from multiple systems into a trace and emits them to -//! [OpenTelemetry]-compatible distributed tracing systems for processing and -//! visualization. -//! -//! [OpenTelemetry]: https://opentelemetry.io -//! [`tracing`]: https://github.com/tokio-rs/tracing -//! -//! *Compiler support: [requires `rustc` 1.56+][msrv]* -//! -//! [msrv]: #supported-rust-versions -//! -//! ### Special Fields -//! -//! Fields with an `otel.` prefix are reserved for this crate and have specific -//! meaning. They are treated as ordinary fields by other layers. The current -//! special fields are: -//! -//! * `otel.name`: Override the span name sent to OpenTelemetry exporters. -//! Setting this field is useful if you want to display non-static information -//! in your span name. -//! * `otel.kind`: Set the span kind to one of the supported OpenTelemetry [span kinds]. -//! * `otel.status_code`: Set the span status code to one of the supported OpenTelemetry [span status codes]. -//! * `otel.status_message`: Set the span status message. -//! -//! [span kinds]: opentelemetry::trace::SpanKind -//! [span status codes]: opentelemetry::trace::StatusCode -//! -//! ### Semantic Conventions -//! -//! OpenTelemetry defines conventional names for attributes of common -//! operations. These names can be assigned directly as fields, e.g. -//! `trace_span!("request", "otel.kind" = %SpanKind::Client, "http.url" = ..)`, and they -//! will be passed through to your configured OpenTelemetry exporter. You can -//! find the full list of the operations and their expected field names in the -//! [semantic conventions] spec. -//! -//! [semantic conventions]: https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/trace/semantic_conventions -//! -//! ### Stability Status -//! -//! The OpenTelemetry specification is currently in beta so some breaking -//! changes may still occur on the path to 1.0. You can follow the changes via -//! the [spec repository] to track progress toward stabilization. -//! -//! [spec repository]: https://github.com/open-telemetry/opentelemetry-specification -//! -//! ## Examples -//! -//! ``` -//! use opentelemetry::sdk::export::trace::stdout; -//! use tracing::{error, span}; -//! use tracing_subscriber::layer::SubscriberExt; -//! use tracing_subscriber::Registry; -//! -//! // Create a new OpenTelemetry pipeline -//! let tracer = stdout::new_pipeline().install_simple(); -//! -//! // Create a tracing layer with the configured tracer -//! let telemetry = tracing_opentelemetry::layer().with_tracer(tracer); -//! -//! // Use the tracing subscriber `Registry`, or any other subscriber -//! // that impls `LookupSpan` -//! let subscriber = Registry::default().with(telemetry); -//! -//! // Trace executed code -//! tracing::subscriber::with_default(subscriber, || { -//! // Spans will be sent to the configured OpenTelemetry exporter -//! let root = span!(tracing::Level::TRACE, "app_start", work_units = 2); -//! let _enter = root.enter(); -//! -//! error!("This event will be logged in the root span."); -//! }); -//! ``` -//! -//! ## Feature Flags -//! -//! - `metrics`: Enables the [`MetricsSubscriber`] type, a [subscriber] that -//! exports OpenTelemetry metrics from specifically-named events. This enables -//! the `metrics` feature flag on the `opentelemetry` crate. *Enabled by -//! default*. -//! -//! ## 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.45, the minimum supported version will not be -//! increased past 1.42, 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. -//! -//! [subscriber]: tracing_subscriber::subscribe -#![deny(unreachable_pub)] -#![cfg_attr(test, deny(warnings))] -#![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, - // Allows displaying cfgs/feature flags in the documentation. - feature(doc_cfg, doc_auto_cfg), - // Allows adding traits to RustDoc's list of "notable traits" - feature(doc_notable_trait), - // Fail the docs build if any intra-docs links are broken - deny(rustdoc::broken_intra_doc_links), -)] - -/// Implementation of the trace::Subscriber trait; publishes OpenTelemetry metrics. -#[cfg(feature = "metrics")] -mod metrics; - -/// Implementation of the trace::Layer as a source of OpenTelemetry data. -mod layer; -/// Span extension which enables OpenTelemetry context management. -mod span_ext; -/// Protocols for OpenTelemetry Tracers that are compatible with Tracing -mod tracer; - -pub use layer::{layer, OpenTelemetryLayer}; - -#[cfg(feature = "metrics")] -pub use metrics::MetricsLayer; -pub use span_ext::OpenTelemetrySpanExt; -pub use tracer::PreSampledTracer; - -/// Per-span OpenTelemetry data tracked by this crate. -/// -/// Useful for implementing [PreSampledTracer] in alternate otel SDKs. -#[derive(Debug, Clone)] -pub struct OtelData { - /// The parent otel `Context` for the current tracing span. - pub parent_cx: opentelemetry::Context, - - /// The otel span data recorded during the current tracing span. - pub builder: opentelemetry::trace::SpanBuilder, -} diff --git a/tracing-opentelemetry/src/metrics.rs b/tracing-opentelemetry/src/metrics.rs deleted file mode 100644 index 42c90baecb..0000000000 --- a/tracing-opentelemetry/src/metrics.rs +++ /dev/null @@ -1,367 +0,0 @@ -use std::{collections::HashMap, fmt, sync::RwLock}; -use tracing::{field::Visit, Subscriber}; -use tracing_core::Field; - -use opentelemetry::{ - metrics::{Counter, Histogram, Meter, MeterProvider, UpDownCounter}, - sdk::metrics::controllers::BasicController, - Context as OtelContext, -}; -use tracing_subscriber::{layer::Context, registry::LookupSpan, Layer}; - -const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); -const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry"; - -const METRIC_PREFIX_MONOTONIC_COUNTER: &str = "monotonic_counter."; -const METRIC_PREFIX_COUNTER: &str = "counter."; -const METRIC_PREFIX_HISTOGRAM: &str = "histogram."; -const I64_MAX: u64 = i64::MAX as u64; - -#[derive(Default)] -pub(crate) struct Instruments { - u64_counter: MetricsMap>, - f64_counter: MetricsMap>, - i64_up_down_counter: MetricsMap>, - f64_up_down_counter: MetricsMap>, - u64_histogram: MetricsMap>, - i64_histogram: MetricsMap>, - f64_histogram: MetricsMap>, -} - -type MetricsMap = RwLock>; - -#[derive(Copy, Clone, Debug)] -pub(crate) enum InstrumentType { - CounterU64(u64), - CounterF64(f64), - UpDownCounterI64(i64), - UpDownCounterF64(f64), - HistogramU64(u64), - HistogramI64(i64), - HistogramF64(f64), -} - -impl Instruments { - pub(crate) fn update_metric( - &self, - cx: &OtelContext, - meter: &Meter, - instrument_type: InstrumentType, - metric_name: &'static str, - ) { - fn update_or_insert( - map: &MetricsMap, - name: &'static str, - insert: impl FnOnce() -> T, - update: impl FnOnce(&T), - ) { - { - let lock = map.read().unwrap(); - if let Some(metric) = lock.get(name) { - update(metric); - return; - } - } - - // that metric did not already exist, so we have to acquire a write lock to - // create it. - let mut lock = map.write().unwrap(); - // handle the case where the entry was created while we were waiting to - // acquire the write lock - let metric = lock.entry(name).or_insert_with(insert); - update(metric) - } - - match instrument_type { - InstrumentType::CounterU64(value) => { - update_or_insert( - &self.u64_counter, - metric_name, - || meter.u64_counter(metric_name).init(), - |ctr| ctr.add(cx, value, &[]), - ); - } - InstrumentType::CounterF64(value) => { - update_or_insert( - &self.f64_counter, - metric_name, - || meter.f64_counter(metric_name).init(), - |ctr| ctr.add(cx, value, &[]), - ); - } - InstrumentType::UpDownCounterI64(value) => { - update_or_insert( - &self.i64_up_down_counter, - metric_name, - || meter.i64_up_down_counter(metric_name).init(), - |ctr| ctr.add(cx, value, &[]), - ); - } - InstrumentType::UpDownCounterF64(value) => { - update_or_insert( - &self.f64_up_down_counter, - metric_name, - || meter.f64_up_down_counter(metric_name).init(), - |ctr| ctr.add(cx, value, &[]), - ); - } - InstrumentType::HistogramU64(value) => { - update_or_insert( - &self.u64_histogram, - metric_name, - || meter.u64_histogram(metric_name).init(), - |rec| rec.record(cx, value, &[]), - ); - } - InstrumentType::HistogramI64(value) => { - update_or_insert( - &self.i64_histogram, - metric_name, - || meter.i64_histogram(metric_name).init(), - |rec| rec.record(cx, value, &[]), - ); - } - InstrumentType::HistogramF64(value) => { - update_or_insert( - &self.f64_histogram, - metric_name, - || meter.f64_histogram(metric_name).init(), - |rec| rec.record(cx, value, &[]), - ); - } - }; - } -} - -pub(crate) struct MetricVisitor<'a> { - pub(crate) instruments: &'a Instruments, - pub(crate) meter: &'a Meter, -} - -impl<'a> Visit for MetricVisitor<'a> { - fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) { - // Do nothing - } - - fn record_u64(&mut self, field: &Field, value: u64) { - let cx = OtelContext::current(); - if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::CounterU64(value), - metric_name, - ); - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) { - if value <= I64_MAX { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::UpDownCounterI64(value as i64), - metric_name, - ); - } else { - eprintln!( - "[tracing-opentelemetry]: Received Counter metric, but \ - provided u64: {} is greater than i64::MAX. Ignoring \ - this metric.", - value - ); - } - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::HistogramU64(value), - metric_name, - ); - } - } - - fn record_f64(&mut self, field: &Field, value: f64) { - let cx = OtelContext::current(); - if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::CounterF64(value), - metric_name, - ); - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::UpDownCounterF64(value), - metric_name, - ); - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::HistogramF64(value), - metric_name, - ); - } - } - - fn record_i64(&mut self, field: &Field, value: i64) { - let cx = OtelContext::current(); - if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::CounterU64(value as u64), - metric_name, - ); - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::UpDownCounterI64(value), - metric_name, - ); - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::HistogramI64(value), - metric_name, - ); - } - } -} - -/// A layer that publishes metrics via the OpenTelemetry SDK. -/// -/// # Usage -/// -/// No configuration is needed for this Layer, as it's only responsible for -/// pushing data out to the `opentelemetry` family of crates. For example, when -/// using `opentelemetry-otlp`, that crate will provide its own set of -/// configuration options for setting up the duration metrics will be collected -/// before exporting to the OpenTelemetry Collector, aggregation of data points, -/// etc. -/// -/// ```no_run -/// use tracing_opentelemetry::MetricsLayer; -/// use tracing_subscriber::layer::SubscriberExt; -/// use tracing_subscriber::Registry; -/// # use opentelemetry::sdk::metrics::controllers::BasicController; -/// -/// // Constructing a BasicController is out-of-scope for the docs here, but there -/// // are examples in the opentelemetry repository. See: -/// // https://github.com/open-telemetry/opentelemetry-rust/blob/d4b9befea04bcc7fc19319a6ebf5b5070131c486/examples/basic-otlp/src/main.rs#L35-L52 -/// # let controller: BasicController = unimplemented!(); -/// -/// let opentelemetry_metrics = MetricsLayer::new(controller); -/// let subscriber = Registry::default().with(opentelemetry_metrics); -/// tracing::subscriber::set_global_default(subscriber).unwrap(); -/// ``` -/// -/// To publish a new metric, add a key-value pair to your `tracing::Event` that -/// contains following prefixes: -/// - `monotonic_counter.` (non-negative numbers): Used when the counter should -/// only ever increase -/// - `counter.`: Used when the counter can go up or down -/// - `histogram.`: Used for discrete data points (i.e., summing them does not make -/// semantic sense) -/// -/// Examples: -/// ``` -/// # use tracing::info; -/// info!(monotonic_counter.foo = 1); -/// info!(monotonic_counter.bar = 1.1); -/// -/// info!(counter.baz = 1); -/// info!(counter.baz = -1); -/// info!(counter.xyz = 1.1); -/// -/// info!(histogram.qux = 1); -/// info!(histogram.abc = -1); -/// info!(histogram.def = 1.1); -/// ``` -/// -/// # Mixing data types -/// -/// ## Floating-point numbers -/// -/// Do not mix floating point and non-floating point numbers for the same -/// metric. If a floating point number will be used for a given metric, be sure -/// to cast any other usages of that metric to a floating point number. -/// -/// Do this: -/// ``` -/// # use tracing::info; -/// info!(monotonic_counter.foo = 1_f64); -/// info!(monotonic_counter.foo = 1.1); -/// ``` -/// -/// This is because all data published for a given metric name must be the same -/// numeric type. -/// -/// ## Integers -/// -/// Positive and negative integers can be mixed freely. The instrumentation -/// provided by `tracing` assumes that all integers are `i64` unless explicitly -/// cast to something else. In the case that an integer *is* cast to `u64`, this -/// subscriber will handle the conversion internally. -/// -/// For example: -/// ``` -/// # use tracing::info; -/// // The subscriber receives an i64 -/// info!(counter.baz = 1); -/// -/// // The subscriber receives an i64 -/// info!(counter.baz = -1); -/// -/// // The subscriber receives a u64, but casts it to i64 internally -/// info!(counter.baz = 1_u64); -/// -/// // The subscriber receives a u64, but cannot cast it to i64 because of -/// // overflow. An error is printed to stderr, and the metric is dropped. -/// info!(counter.baz = (i64::MAX as u64) + 1) -/// ``` -/// -/// # Implementation Details -/// -/// `MetricsLayer` holds a set of maps, with each map corresponding to a -/// type of metric supported by OpenTelemetry. These maps are populated lazily. -/// The first time that a metric is emitted by the instrumentation, a `Metric` -/// instance will be created and added to the corresponding map. This means that -/// any time a metric is emitted by the instrumentation, one map lookup has to -/// be performed. -/// -/// In the future, this can be improved by associating each `Metric` instance to -/// its callsite, eliminating the need for any maps. -/// -#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] -pub struct MetricsLayer { - meter: Meter, - instruments: Instruments, -} - -impl MetricsLayer { - /// Create a new instance of MetricsLayer. - pub fn new(controller: BasicController) -> Self { - let meter = - controller.versioned_meter(INSTRUMENTATION_LIBRARY_NAME, Some(CARGO_PKG_VERSION), None); - MetricsLayer { - meter, - instruments: Default::default(), - } - } -} - -impl Layer for MetricsLayer -where - S: Subscriber + for<'span> LookupSpan<'span>, -{ - fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) { - let mut metric_visitor = MetricVisitor { - instruments: &self.instruments, - meter: &self.meter, - }; - event.record(&mut metric_visitor); - } -} diff --git a/tracing-opentelemetry/src/span_ext.rs b/tracing-opentelemetry/src/span_ext.rs deleted file mode 100644 index ade736815a..0000000000 --- a/tracing-opentelemetry/src/span_ext.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::layer::WithContext; -use opentelemetry::{trace::SpanContext, Context, KeyValue}; - -/// Utility functions to allow tracing [`Span`]s to accept and return -/// [OpenTelemetry] [`Context`]s. -/// -/// [`Span`]: tracing::Span -/// [OpenTelemetry]: https://opentelemetry.io -/// [`Context`]: opentelemetry::Context -pub trait OpenTelemetrySpanExt { - /// Associates `self` with a given OpenTelemetry trace, using the provided - /// parent [`Context`]. - /// - /// [`Context`]: opentelemetry::Context - /// - /// # Examples - /// - /// ```rust - /// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt}; - /// use opentelemetry::sdk::propagation::TraceContextPropagator; - /// use tracing_opentelemetry::OpenTelemetrySpanExt; - /// use std::collections::HashMap; - /// use tracing::Span; - /// - /// // Example carrier, could be a framework header map that impls otel's `Extractor`. - /// let mut carrier = HashMap::new(); - /// - /// // Propagator can be swapped with b3 propagator, jaeger propagator, etc. - /// let propagator = TraceContextPropagator::new(); - /// - /// // Extract otel parent context via the chosen propagator - /// let parent_context = propagator.extract(&carrier); - /// - /// // Generate a tracing span as usual - /// let app_root = tracing::span!(tracing::Level::INFO, "app_start"); - /// - /// // Assign parent trace from external context - /// app_root.set_parent(parent_context.clone()); - /// - /// // Or if the current span has been created elsewhere: - /// Span::current().set_parent(parent_context); - /// ``` - fn set_parent(&self, cx: Context); - - /// Associates `self` with a given OpenTelemetry trace, using the provided - /// followed span [`SpanContext`]. - /// - /// [`SpanContext`]: opentelemetry::trace::SpanContext - /// - /// # Examples - /// - /// ```rust - /// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt}; - /// use opentelemetry::sdk::propagation::TraceContextPropagator; - /// use tracing_opentelemetry::OpenTelemetrySpanExt; - /// use std::collections::HashMap; - /// use tracing::Span; - /// - /// // Example carrier, could be a framework header map that impls otel's `Extractor`. - /// let mut carrier = HashMap::new(); - /// - /// // Propagator can be swapped with b3 propagator, jaeger propagator, etc. - /// let propagator = TraceContextPropagator::new(); - /// - /// // Extract otel context of linked span via the chosen propagator - /// let linked_span_otel_context = propagator.extract(&carrier); - /// - /// // Extract the linked span context from the otel context - /// let linked_span_context = linked_span_otel_context.span().span_context().clone(); - /// - /// // Generate a tracing span as usual - /// let app_root = tracing::span!(tracing::Level::INFO, "app_start"); - /// - /// // Assign linked trace from external context - /// app_root.add_link(linked_span_context); - /// - /// // Or if the current span has been created elsewhere: - /// let linked_span_context = linked_span_otel_context.span().span_context().clone(); - /// Span::current().add_link(linked_span_context); - /// ``` - fn add_link(&self, cx: SpanContext); - - /// Associates `self` with a given OpenTelemetry trace, using the provided - /// followed span [`SpanContext`] and attributes. - /// - /// [`SpanContext`]: opentelemetry::trace::SpanContext - fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec); - - /// Extracts an OpenTelemetry [`Context`] from `self`. - /// - /// [`Context`]: opentelemetry::Context - /// - /// # Examples - /// - /// ```rust - /// use opentelemetry::Context; - /// use tracing_opentelemetry::OpenTelemetrySpanExt; - /// use tracing::Span; - /// - /// fn make_request(cx: Context) { - /// // perform external request after injecting context - /// // e.g. if the request's headers impl `opentelemetry::propagation::Injector` - /// // then `propagator.inject_context(cx, request.headers_mut())` - /// } - /// - /// // Generate a tracing span as usual - /// let app_root = tracing::span!(tracing::Level::INFO, "app_start"); - /// - /// // To include tracing context in client requests from _this_ app, - /// // extract the current OpenTelemetry context. - /// make_request(app_root.context()); - /// - /// // Or if the current span has been created elsewhere: - /// make_request(Span::current().context()) - /// ``` - fn context(&self) -> Context; -} - -impl OpenTelemetrySpanExt for tracing::Span { - fn set_parent(&self, cx: Context) { - let mut cx = Some(cx); - self.with_subscriber(move |(id, subscriber)| { - if let Some(get_context) = subscriber.downcast_ref::() { - get_context.with_context(subscriber, id, move |data, _tracer| { - if let Some(cx) = cx.take() { - data.parent_cx = cx; - } - }); - } - }); - } - - fn add_link(&self, cx: SpanContext) { - self.add_link_with_attributes(cx, Vec::new()) - } - - fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec) { - if cx.is_valid() { - let mut cx = Some(cx); - let mut att = Some(attributes); - self.with_subscriber(move |(id, subscriber)| { - if let Some(get_context) = subscriber.downcast_ref::() { - get_context.with_context(subscriber, id, move |data, _tracer| { - if let Some(cx) = cx.take() { - let attr = att.take().unwrap_or_default(); - let follows_link = opentelemetry::trace::Link::new(cx, attr); - data.builder - .links - .get_or_insert_with(|| Vec::with_capacity(1)) - .push(follows_link); - } - }); - } - }); - } - } - - fn context(&self) -> Context { - let mut cx = None; - self.with_subscriber(|(id, subscriber)| { - if let Some(get_context) = subscriber.downcast_ref::() { - get_context.with_context(subscriber, id, |builder, tracer| { - cx = Some(tracer.sampled_context(builder)); - }) - } - }); - - cx.unwrap_or_default() - } -} diff --git a/tracing-opentelemetry/src/tracer.rs b/tracing-opentelemetry/src/tracer.rs deleted file mode 100644 index 66c52fea21..0000000000 --- a/tracing-opentelemetry/src/tracer.rs +++ /dev/null @@ -1,234 +0,0 @@ -use opentelemetry::sdk::trace::{Tracer, TracerProvider}; -use opentelemetry::trace::OrderMap; -use opentelemetry::{ - trace as otel, - trace::{ - noop, SamplingDecision, SamplingResult, SpanBuilder, SpanContext, SpanId, SpanKind, - TraceContextExt, TraceFlags, TraceId, TraceState, - }, - Context as OtelContext, -}; - -/// An interface for authors of OpenTelemetry SDKs to build pre-sampled tracers. -/// -/// The OpenTelemetry spec does not allow trace ids to be updated after a span -/// has been created. In order to associate extracted parent trace ids with -/// existing `tracing` spans, `tracing-opentelemetry` builds up otel span data -/// using a [`SpanBuilder`] instead, and creates / exports full otel spans only -/// when the associated `tracing` span is closed. However, in order to properly -/// inject otel [`Context`] information to downstream requests, the sampling -/// state must now be known _before_ the otel span has been created. -/// -/// The logic for coming to a sampling decision and creating an injectable span -/// context from a [`SpanBuilder`] is encapsulated in the -/// [`PreSampledTracer::sampled_context`] method and has been implemented -/// for the standard OpenTelemetry SDK, but this trait may be implemented by -/// authors of alternate OpenTelemetry SDK implementations if they wish to have -/// `tracing` compatibility. -/// -/// See the [`OpenTelemetrySpanExt::set_parent`] and -/// [`OpenTelemetrySpanExt::context`] methods for example usage. -/// -/// [`Tracer`]: opentelemetry::trace::Tracer -/// [`SpanBuilder`]: opentelemetry::trace::SpanBuilder -/// [`PreSampledTracer::sampled_span_context`]: crate::PreSampledTracer::sampled_span_context -/// [`OpenTelemetrySpanExt::set_parent`]: crate::OpenTelemetrySpanExt::set_parent -/// [`OpenTelemetrySpanExt::context`]: crate::OpenTelemetrySpanExt::context -/// [`Context`]: opentelemetry::Context -pub trait PreSampledTracer { - /// Produce an otel context containing an active and pre-sampled span for - /// the given span builder data. - /// - /// The sampling decision, span context information, and parent context - /// values must match the values recorded when the tracing span is closed. - fn sampled_context(&self, data: &mut crate::OtelData) -> OtelContext; - - /// Generate a new trace id. - fn new_trace_id(&self) -> otel::TraceId; - - /// Generate a new span id. - fn new_span_id(&self) -> otel::SpanId; -} - -impl PreSampledTracer for noop::NoopTracer { - fn sampled_context(&self, data: &mut crate::OtelData) -> OtelContext { - data.parent_cx.clone() - } - - fn new_trace_id(&self) -> otel::TraceId { - otel::TraceId::INVALID - } - - fn new_span_id(&self) -> otel::SpanId { - otel::SpanId::INVALID - } -} - -impl PreSampledTracer for Tracer { - fn sampled_context(&self, data: &mut crate::OtelData) -> OtelContext { - // Ensure tracing pipeline is still installed. - if self.provider().is_none() { - return OtelContext::new(); - } - let provider = self.provider().unwrap(); - let parent_cx = &data.parent_cx; - let builder = &mut data.builder; - - // Gather trace state - let (trace_id, parent_trace_flags) = current_trace_state(builder, parent_cx, &provider); - - // Sample or defer to existing sampling decisions - let (flags, trace_state) = if let Some(result) = &builder.sampling_result { - process_sampling_result(result, parent_trace_flags) - } else { - builder.sampling_result = Some(provider.config().sampler.should_sample( - Some(parent_cx), - trace_id, - &builder.name, - builder.span_kind.as_ref().unwrap_or(&SpanKind::Internal), - builder.attributes.as_ref().unwrap_or(&OrderMap::default()), - builder.links.as_deref().unwrap_or(&[]), - self.instrumentation_library(), - )); - - process_sampling_result( - builder.sampling_result.as_ref().unwrap(), - parent_trace_flags, - ) - } - .unwrap_or_default(); - - let span_id = builder.span_id.unwrap_or(SpanId::INVALID); - let span_context = SpanContext::new(trace_id, span_id, flags, false, trace_state); - parent_cx.with_remote_span_context(span_context) - } - - fn new_trace_id(&self) -> otel::TraceId { - self.provider() - .map(|provider| provider.config().id_generator.new_trace_id()) - .unwrap_or(otel::TraceId::INVALID) - } - - fn new_span_id(&self) -> otel::SpanId { - self.provider() - .map(|provider| provider.config().id_generator.new_span_id()) - .unwrap_or(otel::SpanId::INVALID) - } -} - -fn current_trace_state( - builder: &SpanBuilder, - parent_cx: &OtelContext, - provider: &TracerProvider, -) -> (TraceId, TraceFlags) { - if parent_cx.has_active_span() { - let span = parent_cx.span(); - let sc = span.span_context(); - (sc.trace_id(), sc.trace_flags()) - } else { - ( - builder - .trace_id - .unwrap_or_else(|| provider.config().id_generator.new_trace_id()), - Default::default(), - ) - } -} - -fn process_sampling_result( - sampling_result: &SamplingResult, - trace_flags: TraceFlags, -) -> Option<(TraceFlags, TraceState)> { - match sampling_result { - SamplingResult { - decision: SamplingDecision::Drop, - .. - } => None, - SamplingResult { - decision: SamplingDecision::RecordOnly, - trace_state, - .. - } => Some((trace_flags & !TraceFlags::SAMPLED, trace_state.clone())), - SamplingResult { - decision: SamplingDecision::RecordAndSample, - trace_state, - .. - } => Some((trace_flags | TraceFlags::SAMPLED, trace_state.clone())), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::OtelData; - use opentelemetry::sdk::trace::{config, Sampler, TracerProvider}; - use opentelemetry::trace::{SpanBuilder, SpanId, TracerProvider as _}; - - #[test] - fn assigns_default_trace_id_if_missing() { - let provider = TracerProvider::default(); - let tracer = provider.tracer("test"); - let mut builder = SpanBuilder::from_name("empty".to_string()); - builder.span_id = Some(SpanId::from(1u64.to_be_bytes())); - builder.trace_id = None; - let parent_cx = OtelContext::new(); - let cx = tracer.sampled_context(&mut OtelData { builder, parent_cx }); - let span = cx.span(); - let span_context = span.span_context(); - - assert!(span_context.is_valid()); - } - - #[rustfmt::skip] - fn sampler_data() -> Vec<(&'static str, Sampler, OtelContext, Option, bool)> { - vec![ - // No parent samples - ("empty_parent_cx_always_on", Sampler::AlwaysOn, OtelContext::new(), None, true), - ("empty_parent_cx_always_off", Sampler::AlwaysOff, OtelContext::new(), None, false), - - // Remote parent samples - ("remote_parent_cx_always_on", Sampler::AlwaysOn, OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, true), - ("remote_parent_cx_always_off", Sampler::AlwaysOff, OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, false), - ("sampled_remote_parent_cx_parent_based", Sampler::ParentBased(Box::new(Sampler::AlwaysOff)), OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, true), - ("unsampled_remote_parent_cx_parent_based", Sampler::ParentBased(Box::new(Sampler::AlwaysOn)), OtelContext::new().with_remote_span_context(span_context(TraceFlags::default(), true)), None, false), - - // Existing sampling result defers - ("previous_drop_result_always_on", Sampler::AlwaysOn, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::Drop, attributes: vec![], trace_state: Default::default() }), false), - ("previous_record_and_sample_result_always_off", Sampler::AlwaysOff, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::RecordAndSample, attributes: vec![], trace_state: Default::default() }), true), - - // Existing local parent, defers - ("previous_drop_result_always_on", Sampler::AlwaysOn, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::Drop, attributes: vec![], trace_state: Default::default() }), false), - ("previous_record_and_sample_result_always_off", Sampler::AlwaysOff, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::RecordAndSample, attributes: vec![], trace_state: Default::default() }), true), - ] - } - - #[test] - fn sampled_context() { - for (name, sampler, parent_cx, previous_sampling_result, is_sampled) in sampler_data() { - let provider = TracerProvider::builder() - .with_config(config().with_sampler(sampler)) - .build(); - let tracer = provider.tracer("test"); - let mut builder = SpanBuilder::from_name("parent".to_string()); - builder.sampling_result = previous_sampling_result; - let sampled = tracer.sampled_context(&mut OtelData { builder, parent_cx }); - - assert_eq!( - sampled.span().span_context().is_sampled(), - is_sampled, - "{}", - name - ) - } - } - - fn span_context(trace_flags: TraceFlags, is_remote: bool) -> SpanContext { - SpanContext::new( - TraceId::from(1u128.to_be_bytes()), - SpanId::from(1u64.to_be_bytes()), - trace_flags, - is_remote, - Default::default(), - ) - } -} diff --git a/tracing-opentelemetry/tests/metrics_publishing.rs b/tracing-opentelemetry/tests/metrics_publishing.rs deleted file mode 100644 index 2f9c20ef7f..0000000000 --- a/tracing-opentelemetry/tests/metrics_publishing.rs +++ /dev/null @@ -1,282 +0,0 @@ -use opentelemetry::{ - metrics::MetricsError, - sdk::{ - export::metrics::{ - aggregation::{self, Histogram, Sum, TemporalitySelector}, - InstrumentationLibraryReader, - }, - metrics::{ - aggregators::{HistogramAggregator, SumAggregator}, - controllers::BasicController, - processors, - sdk_api::{Descriptor, InstrumentKind, Number, NumberKind}, - selectors, - }, - }, - Context, -}; -use std::cmp::Ordering; -use tracing::Subscriber; -use tracing_opentelemetry::MetricsLayer; -use tracing_subscriber::prelude::*; - -const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); -const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry"; - -#[tokio::test] -async fn u64_counter_is_exported() { - let (subscriber, exporter) = init_subscriber( - "hello_world".to_string(), - InstrumentKind::Counter, - NumberKind::U64, - Number::from(1_u64), - ); - - tracing::subscriber::with_default(subscriber, || { - tracing::info!(monotonic_counter.hello_world = 1_u64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn u64_counter_is_exported_i64_at_instrumentation_point() { - let (subscriber, exporter) = init_subscriber( - "hello_world2".to_string(), - InstrumentKind::Counter, - NumberKind::U64, - Number::from(1_u64), - ); - - tracing::subscriber::with_default(subscriber, || { - tracing::info!(monotonic_counter.hello_world2 = 1_i64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn f64_counter_is_exported() { - let (subscriber, exporter) = init_subscriber( - "float_hello_world".to_string(), - InstrumentKind::Counter, - NumberKind::F64, - Number::from(1.000000123_f64), - ); - - tracing::subscriber::with_default(subscriber, || { - tracing::info!(monotonic_counter.float_hello_world = 1.000000123_f64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn i64_up_down_counter_is_exported() { - let (subscriber, exporter) = init_subscriber( - "pebcak".to_string(), - InstrumentKind::UpDownCounter, - NumberKind::I64, - Number::from(-5_i64), - ); - - tracing::subscriber::with_default(subscriber, || { - tracing::info!(counter.pebcak = -5_i64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn i64_up_down_counter_is_exported_u64_at_instrumentation_point() { - let (subscriber, exporter) = init_subscriber( - "pebcak2".to_string(), - InstrumentKind::UpDownCounter, - NumberKind::I64, - Number::from(5_i64), - ); - - tracing::subscriber::with_default(subscriber, || { - tracing::info!(counter.pebcak2 = 5_u64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn f64_up_down_counter_is_exported() { - let (subscriber, exporter) = init_subscriber( - "pebcak_blah".to_string(), - InstrumentKind::UpDownCounter, - NumberKind::F64, - Number::from(99.123_f64), - ); - - tracing::subscriber::with_default(subscriber, || { - tracing::info!(counter.pebcak_blah = 99.123_f64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn u64_histogram_is_exported() { - let (subscriber, exporter) = init_subscriber( - "abcdefg".to_string(), - InstrumentKind::Histogram, - NumberKind::U64, - Number::from(9_u64), - ); - - tracing::subscriber::with_default(subscriber, || { - tracing::info!(histogram.abcdefg = 9_u64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn i64_histogram_is_exported() { - let (subscriber, exporter) = init_subscriber( - "abcdefg_auenatsou".to_string(), - InstrumentKind::Histogram, - NumberKind::I64, - Number::from(-19_i64), - ); - - tracing::subscriber::with_default(subscriber, || { - tracing::info!(histogram.abcdefg_auenatsou = -19_i64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn f64_histogram_is_exported() { - let (subscriber, exporter) = init_subscriber( - "abcdefg_racecar".to_string(), - InstrumentKind::Histogram, - NumberKind::F64, - Number::from(777.0012_f64), - ); - - tracing::subscriber::with_default(subscriber, || { - tracing::info!(histogram.abcdefg_racecar = 777.0012_f64); - }); - - exporter.export().unwrap(); -} - -fn init_subscriber( - expected_metric_name: String, - expected_instrument_kind: InstrumentKind, - expected_number_kind: NumberKind, - expected_value: Number, -) -> (impl Subscriber + 'static, TestExporter) { - let controller = opentelemetry::sdk::metrics::controllers::basic(processors::factory( - selectors::simple::histogram(vec![-10.0, 100.0]), - aggregation::cumulative_temporality_selector(), - )) - .build(); - - let exporter = TestExporter { - expected_metric_name, - expected_instrument_kind, - expected_number_kind, - expected_value, - controller: controller.clone(), - }; - - ( - tracing_subscriber::registry().with(MetricsLayer::new(controller)), - exporter, - ) -} - -#[derive(Clone, Debug)] -struct TestExporter { - expected_metric_name: String, - expected_instrument_kind: InstrumentKind, - expected_number_kind: NumberKind, - expected_value: Number, - controller: BasicController, -} - -impl TestExporter { - fn export(&self) -> Result<(), MetricsError> { - self.controller.collect(&Context::current())?; - self.controller.try_for_each(&mut |library, reader| { - reader.try_for_each(self, &mut |record| { - assert_eq!(self.expected_metric_name, record.descriptor().name()); - assert_eq!( - self.expected_instrument_kind, - *record.descriptor().instrument_kind() - ); - assert_eq!( - self.expected_number_kind, - *record.descriptor().number_kind() - ); - match self.expected_instrument_kind { - InstrumentKind::Counter | InstrumentKind::UpDownCounter => { - let number = record - .aggregator() - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .sum() - .unwrap(); - - assert_eq!( - Ordering::Equal, - number - .partial_cmp(&NumberKind::U64, &self.expected_value) - .unwrap() - ); - } - InstrumentKind::Histogram => { - let histogram = record - .aggregator() - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .histogram() - .unwrap(); - - let counts = histogram.counts(); - if dbg!(self.expected_value.to_i64(&self.expected_number_kind)) > 100 { - assert_eq!(counts, &[0.0, 0.0, 1.0]); - } else if self.expected_value.to_i64(&self.expected_number_kind) > 0 { - assert_eq!(counts, &[0.0, 1.0, 0.0]); - } else { - assert_eq!(counts, &[1.0, 0.0, 0.0]); - } - } - _ => panic!( - "InstrumentKind {:?} not currently supported!", - self.expected_instrument_kind - ), - }; - - // The following are the same regardless of the individual metric. - assert_eq!(INSTRUMENTATION_LIBRARY_NAME, library.name); - assert_eq!(CARGO_PKG_VERSION, library.version.as_ref().unwrap()); - - Ok(()) - }) - }) - } -} - -impl TemporalitySelector for TestExporter { - fn temporality_for( - &self, - _descriptor: &Descriptor, - _kind: &aggregation::AggregationKind, - ) -> aggregation::Temporality { - // I don't think the value here makes a difference since - // we are just testing a single metric. - aggregation::Temporality::Cumulative - } -} diff --git a/tracing-opentelemetry/tests/trace_state_propagation.rs b/tracing-opentelemetry/tests/trace_state_propagation.rs deleted file mode 100644 index 49200b4fc9..0000000000 --- a/tracing-opentelemetry/tests/trace_state_propagation.rs +++ /dev/null @@ -1,171 +0,0 @@ -use futures_util::future::BoxFuture; -use opentelemetry::{ - propagation::TextMapPropagator, - sdk::{ - export::trace::{ExportResult, SpanData, SpanExporter}, - propagation::{BaggagePropagator, TextMapCompositePropagator, TraceContextPropagator}, - trace::{Tracer, TracerProvider}, - }, - trace::{SpanContext, TraceContextExt, Tracer as _, TracerProvider as _}, - Context, -}; -use std::collections::{HashMap, HashSet}; -use std::sync::{Arc, Mutex}; -use tracing::Subscriber; -use tracing_opentelemetry::{layer, OpenTelemetrySpanExt}; -use tracing_subscriber::prelude::*; - -#[test] -fn trace_with_active_otel_context() { - let (cx, subscriber, exporter, provider) = build_sampled_context(); - let attached = cx.attach(); - - tracing::subscriber::with_default(subscriber, || { - tracing::debug_span!("child"); - }); - - drop(attached); // end implicit parent - drop(provider); // flush all spans - - let spans = exporter.0.lock().unwrap(); - assert_eq!(spans.len(), 2); - assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context); -} - -#[test] -fn trace_with_assigned_otel_context() { - let (cx, subscriber, exporter, provider) = build_sampled_context(); - - tracing::subscriber::with_default(subscriber, || { - let child = tracing::debug_span!("child"); - child.set_parent(cx); - }); - - drop(provider); // flush all spans - let spans = exporter.0.lock().unwrap(); - assert_eq!(spans.len(), 2); - assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context); -} - -#[test] -fn trace_root_with_children() { - let (_tracer, provider, exporter, subscriber) = test_tracer(); - - tracing::subscriber::with_default(subscriber, || { - // Propagate trace information through tracing parent -> child - let root = tracing::debug_span!("root"); - root.in_scope(|| tracing::debug_span!("child")); - }); - - drop(provider); // flush all spans - let spans = exporter.0.lock().unwrap(); - assert_eq!(spans.len(), 2); - assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context); -} - -#[test] -fn inject_context_into_outgoing_requests() { - let (_tracer, _provider, _exporter, subscriber) = test_tracer(); - let propagator = test_propagator(); - let carrier = test_carrier(); - let cx = propagator.extract(&carrier); - let mut outgoing_req_carrier = HashMap::new(); - - tracing::subscriber::with_default(subscriber, || { - let root = tracing::debug_span!("root"); - root.set_parent(cx); - let _g = root.enter(); - let child = tracing::debug_span!("child"); - propagator.inject_context(&child.context(), &mut outgoing_req_carrier); - }); - - // Ensure all values that should be passed between services are preserved - assert_carrier_attrs_eq(&carrier, &outgoing_req_carrier); -} - -fn assert_shared_attrs_eq(sc_a: &SpanContext, sc_b: &SpanContext) { - assert_eq!(sc_a.trace_id(), sc_b.trace_id()); - assert_eq!(sc_a.trace_state(), sc_b.trace_state()); -} - -fn assert_carrier_attrs_eq( - carrier_a: &HashMap, - carrier_b: &HashMap, -) { - // Match baggage unordered - assert_eq!( - carrier_a - .get("baggage") - .map(|b| b.split_terminator(',').collect::>()), - carrier_b - .get("baggage") - .map(|b| b.split_terminator(',').collect()) - ); - // match trace parent values, except span id - assert_eq!( - carrier_a.get("traceparent").unwrap()[0..36], - carrier_b.get("traceparent").unwrap()[0..36], - ); - // match tracestate values - assert_eq!(carrier_a.get("tracestate"), carrier_b.get("tracestate")); -} - -fn test_tracer() -> (Tracer, TracerProvider, TestExporter, impl Subscriber) { - let exporter = TestExporter::default(); - let provider = TracerProvider::builder() - .with_simple_exporter(exporter.clone()) - .build(); - let tracer = provider.tracer("test"); - let subscriber = tracing_subscriber::registry().with(layer().with_tracer(tracer.clone())); - - (tracer, provider, exporter, subscriber) -} - -fn test_propagator() -> TextMapCompositePropagator { - let baggage_propagator = BaggagePropagator::new(); - let trace_context_propagator = TraceContextPropagator::new(); - - TextMapCompositePropagator::new(vec![ - Box::new(baggage_propagator), - Box::new(trace_context_propagator), - ]) -} - -fn test_carrier() -> HashMap { - let mut carrier = HashMap::new(); - carrier.insert( - "baggage".to_string(), - "key2=value2,key1=value1;property1;property2,key3=value3;propertyKey=propertyValue" - .to_string(), - ); - carrier.insert("tracestate".to_string(), "test1=test2".to_string()); - carrier.insert( - "traceparent".to_string(), - "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01".to_string(), - ); - - carrier -} - -fn build_sampled_context() -> (Context, impl Subscriber, TestExporter, TracerProvider) { - let (tracer, provider, exporter, subscriber) = test_tracer(); - let span = tracer.start("sampled"); - let cx = Context::current_with_span(span); - - (cx, subscriber, exporter, provider) -} - -#[derive(Clone, Default, Debug)] -struct TestExporter(Arc>>); - -impl SpanExporter for TestExporter { - fn export(&mut self, mut batch: Vec) -> BoxFuture<'static, ExportResult> { - let spans = self.0.clone(); - Box::pin(async move { - if let Ok(mut inner) = spans.lock() { - inner.append(&mut batch); - } - Ok(()) - }) - } -} diff --git a/tracing-opentelemetry/trace.png b/tracing-opentelemetry/trace.png deleted file mode 100644 index 4cb98d135830218144b8e6575a41233d344a00e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87211 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

`s)6FQc|!d{!W7d0?mAWX#Gzq^{{ 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 From 45a2cea8bbaf65b20f8f8ebc734130fe764ec9ab Mon Sep 17 00:00:00 2001 From: David Barsky Date: Wed, 5 Apr 2023 14:19:55 -0400 Subject: [PATCH 130/142] subscriber: dim `Compact` targets, matching the default formatter (#2409) Fixes https://github.com/tokio-rs/tracing/issues/2408 Just switch to use `dim` Before: ![2022-12-08_13-58-40](https://user-images.githubusercontent.com/623453/206576169-63ee4e20-b56f-4c63-a9b3-80ba2e97eec9.png) After: ![2022-12-08_13-55-36](https://user-images.githubusercontent.com/623453/206576055-878d360f-7b95-4e18-bc31-4fb6f1b71a3a.png) Full mode for comparison: ![2022-12-08_13-55-48](https://user-images.githubusercontent.com/623453/206576054-6e38852c-cb3a-4b84-98e5-50463cdb5073.png) ; Conflicts: ; tracing-subscriber/src/fmt/format/mod.rs --- .github/workflows/CI.yml | 19 ++++--------------- README.md | 6 +++--- examples/Cargo.toml | 2 +- tracing-appender/README.md | 4 ++-- tracing-appender/src/lib.rs | 4 ++-- tracing-attributes/Cargo.toml | 2 +- tracing-attributes/README.md | 8 ++++---- tracing-attributes/src/lib.rs | 8 ++++---- tracing-core/Cargo.toml | 2 +- tracing-core/README.md | 8 ++++---- tracing-core/src/lib.rs | 8 ++++---- tracing-error/Cargo.toml | 2 +- tracing-error/README.md | 8 ++++---- tracing-error/src/lib.rs | 8 ++++---- tracing-flame/Cargo.toml | 2 +- tracing-flame/README.md | 8 ++++---- tracing-flame/src/lib.rs | 8 ++++---- tracing-futures/Cargo.toml | 2 +- tracing-futures/README.md | 8 ++++---- tracing-futures/src/lib.rs | 8 ++++---- tracing-journald/Cargo.toml | 2 +- tracing-journald/README.md | 10 +++++----- tracing-journald/src/lib.rs | 8 ++++---- tracing-log/Cargo.toml | 2 +- tracing-log/README.md | 8 ++++---- tracing-log/src/lib.rs | 8 ++++---- tracing-macros/Cargo.toml | 2 +- tracing-mock/Cargo.toml | 2 +- tracing-serde/Cargo.toml | 2 +- tracing-serde/README.md | 8 ++++---- tracing-serde/src/lib.rs | 8 ++++---- tracing-subscriber/Cargo.toml | 2 +- tracing-subscriber/README.md | 8 ++++---- tracing-subscriber/src/fmt/mod.rs | 2 +- tracing-subscriber/src/lib.rs | 8 ++++---- tracing-tower/Cargo.toml | 2 +- tracing/Cargo.toml | 2 +- tracing/README.md | 8 ++++---- tracing/src/lib.rs | 8 ++++---- 39 files changed, 107 insertions(+), 118 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index bced9c4933..9d0ffb29de 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -169,8 +169,9 @@ jobs: shell: bash check-msrv: - # Run `cargo check` on our minimum supported Rust version (1.49.0). - name: "cargo check (MSRV on ubuntu-latest)" + # Run `cargo check` on our minimum supported Rust version (1.56.0). This + # checks with minimal versions; maximal versions are checked above. + name: "cargo check (+MSRV -Zminimal-versions)" needs: check runs-on: ubuntu-latest strategy: @@ -190,20 +191,8 @@ jobs: - tracing-tower - tracing toolchain: - - 1.49.0 + - 1.56.0 - stable - # TODO(eliza): remove this when appender is on the same MSRV. - # same for tracing subscriber - exclude: - - subcrate: tracing-appender - toolchain: 1.49.0 - - subcrate: tracing-subscriber - toolchain: 1.49.0 - include: - - subcrate: tracing-appender - toolchain: 1.53.0 - - subcrate: tracing-subscriber - toolchain: 1.50.0 steps: - uses: actions/checkout@v3 - name: "install Rust ${{ env.APPENDER_MSRV }}" diff --git a/README.md b/README.md index 2bc566ed6e..cfe1be8a19 100644 --- a/README.md +++ b/README.md @@ -252,14 +252,14 @@ attachment that `Future::instrument` does. ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +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. diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 510a0680b8..25e56247bb 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -3,7 +3,7 @@ name = "tracing-examples" version = "0.0.0" publish = false edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = [] diff --git a/tracing-appender/README.md b/tracing-appender/README.md index 533397b31c..4eaa1ab91b 100644 --- a/tracing-appender/README.md +++ b/tracing-appender/README.md @@ -152,8 +152,8 @@ 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.45, the minimum supported version will not be -increased past 1.42, three minor versions prior. Increasing the minimum +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. diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs index afd5160dbb..25279fb0ec 100644 --- a/tracing-appender/src/lib.rs +++ b/tracing-appender/src/lib.rs @@ -116,8 +116,8 @@ //! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 8e41eb31d0..199fdda8c9 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -28,7 +28,7 @@ keywords = ["logging", "tracing", "macro", "instrument", "log"] license = "MIT" readme = "README.md" edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" [lib] proc-macro = true diff --git a/tracing-attributes/README.md b/tracing-attributes/README.md index 356b511f2a..a3ae1f4e34 100644 --- a/tracing-attributes/README.md +++ b/tracing-attributes/README.md @@ -37,7 +37,7 @@ structured, event-based diagnostic information. This crate provides the Note that this macro is also re-exported by the main `tracing` crate. -*Compiler support: [requires `rustc` 1.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -69,14 +69,14 @@ pub fn my_function(my_arg: usize) { ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +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. diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index b97b4fd754..9cb1e08147 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -6,7 +6,7 @@ //! //! Note that this macro is also re-exported by the main `tracing` crate. //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -41,14 +41,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml index aacd334ae5..319203a87c 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -24,7 +24,7 @@ categories = [ ] keywords = ["logging", "tracing", "profiling"] edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = ["std", "valuable/std"] diff --git a/tracing-core/README.md b/tracing-core/README.md index f06c760593..1ef78506fe 100644 --- a/tracing-core/README.md +++ b/tracing-core/README.md @@ -53,7 +53,7 @@ The crate provides: In addition, it defines the global callsite registry and per-thread current dispatcher which other components of the tracing system rely on. -*Compiler support: [requires `rustc` 1.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -99,14 +99,14 @@ The following crate feature flags are available: ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.69, 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. diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs index e4767babc1..f5de4eeba9 100644 --- a/tracing-core/src/lib.rs +++ b/tracing-core/src/lib.rs @@ -23,7 +23,7 @@ //! In addition, it defines the global callsite registry and per-thread current //! dispatcher which other components of the tracing system rely on. //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -92,14 +92,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! diff --git a/tracing-error/Cargo.toml b/tracing-error/Cargo.toml index d3c66713b7..fb09c64eb6 100644 --- a/tracing-error/Cargo.toml +++ b/tracing-error/Cargo.toml @@ -32,7 +32,7 @@ keywords = [ "backtrace" ] edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = ["traced-error"] diff --git a/tracing-error/README.md b/tracing-error/README.md index 9d9198df7a..11427aca6a 100644 --- a/tracing-error/README.md +++ b/tracing-error/README.md @@ -48,7 +48,7 @@ The crate provides the following: **Note**: This crate is currently experimental. -*Compiler support: [requires `rustc` 1.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -186,14 +186,14 @@ fn main() { ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +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. diff --git a/tracing-error/src/lib.rs b/tracing-error/src/lib.rs index 39bf5e1eee..55a04d9026 100644 --- a/tracing-error/src/lib.rs +++ b/tracing-error/src/lib.rs @@ -18,7 +18,7 @@ //! //! **Note**: This crate is currently experimental. //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -167,14 +167,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! diff --git a/tracing-flame/Cargo.toml b/tracing-flame/Cargo.toml index d69ea7afb1..2153a0b787 100644 --- a/tracing-flame/Cargo.toml +++ b/tracing-flame/Cargo.toml @@ -19,7 +19,7 @@ categories = [ "asynchronous", ] keywords = ["tracing", "subscriber", "flamegraph", "profiling"] -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = ["smallvec"] diff --git a/tracing-flame/README.md b/tracing-flame/README.md index 2076b45f08..d1bcd267ed 100644 --- a/tracing-flame/README.md +++ b/tracing-flame/README.md @@ -26,7 +26,7 @@ flamegraph/flamechart. Flamegraphs/flamecharts are useful for identifying perfor bottlenecks in an application. For more details, see Brendan Gregg's [post] on flamegraphs. -*Compiler support: [requires `rustc` 1.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions [post]: http://www.brendangregg.com/flamegraphs.html @@ -106,14 +106,14 @@ _flamechart_, which _does not_ sort or collapse identical stack frames. ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +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. diff --git a/tracing-flame/src/lib.rs b/tracing-flame/src/lib.rs index 145d2be90f..5805850555 100644 --- a/tracing-flame/src/lib.rs +++ b/tracing-flame/src/lib.rs @@ -10,7 +10,7 @@ //! issues bottlenecks in an application. For more details, see Brendan Gregg's [post] //! on flamegraphs. //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! [post]: http://www.brendangregg.com/flamegraphs.html @@ -95,14 +95,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml index ba00ec5c12..a34b459b1e 100644 --- a/tracing-futures/Cargo.toml +++ b/tracing-futures/Cargo.toml @@ -16,7 +16,7 @@ categories = [ ] keywords = ["logging", "profiling", "tracing", "futures", "async"] license = "MIT" -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = ["std-future", "std"] diff --git a/tracing-futures/README.md b/tracing-futures/README.md index e2d6b15156..e2f0da6bd8 100644 --- a/tracing-futures/README.md +++ b/tracing-futures/README.md @@ -51,21 +51,21 @@ The crate provides the following traits: [`Subscriber`]: https://docs.rs/tracing/latest/tracing/subscriber/index.html [`tracing`]: https://crates.io/crates/tracing -*Compiler support: [requires `rustc` 1.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +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. diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs index f38d2f9ca5..addf19f159 100644 --- a/tracing-futures/src/lib.rs +++ b/tracing-futures/src/lib.rs @@ -15,7 +15,7 @@ //! * [`WithSubscriber`] allows a `tracing` [`Subscriber`] to be attached to a //! future, sink, stream, or executor. //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -59,14 +59,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! diff --git a/tracing-journald/Cargo.toml b/tracing-journald/Cargo.toml index 790fec4959..f6944f1ca7 100644 --- a/tracing-journald/Cargo.toml +++ b/tracing-journald/Cargo.toml @@ -13,7 +13,7 @@ categories = [ "development-tools::profiling", ] keywords = ["tracing", "journald"] -rust-version = "1.49.0" +rust-version = "1.56.0" [dependencies] libc = "0.2.126" diff --git a/tracing-journald/README.md b/tracing-journald/README.md index a9cae89196..58211c7d41 100644 --- a/tracing-journald/README.md +++ b/tracing-journald/README.md @@ -27,8 +27,8 @@ scoped, structured, and async-aware diagnostics. `tracing-journald` provides a [`tracing-subscriber::Layer`][layer] implementation for logging `tracing` spans and events to [`systemd-journald`][journald], on Linux distributions that use `systemd`. - -*Compiler support: [requires `rustc` 1.49+][msrv]* + +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions [`tracing`]: https://crates.io/crates/tracing @@ -38,14 +38,14 @@ and events to [`systemd-journald`][journald], on Linux distributions that use ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +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. diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs index c91fbddd19..ac816058ca 100644 --- a/tracing-journald/src/lib.rs +++ b/tracing-journald/src/lib.rs @@ -11,7 +11,7 @@ //! and events to [`systemd-journald`][journald], on Linux distributions that //! use `systemd`. //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! [`tracing`]: https://crates.io/crates/tracing @@ -20,14 +20,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! diff --git a/tracing-log/Cargo.toml b/tracing-log/Cargo.toml index b295da9646..ec10a1d1fb 100644 --- a/tracing-log/Cargo.toml +++ b/tracing-log/Cargo.toml @@ -15,7 +15,7 @@ categories = [ keywords = ["logging", "tracing", "log"] license = "MIT" readme = "README.md" -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = ["log-tracer", "trace-logger", "std"] diff --git a/tracing-log/README.md b/tracing-log/README.md index 1cfee3681d..3da5f6b7e1 100644 --- a/tracing-log/README.md +++ b/tracing-log/README.md @@ -56,21 +56,21 @@ This crate provides: [`tracing::Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html [`tracing::Event`]: https://docs.rs/tracing/latest/tracing/struct.Event.html -*Compiler support: [requires `rustc` 1.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +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. diff --git a/tracing-log/src/lib.rs b/tracing-log/src/lib.rs index 75a5397d7e..768d4f6922 100644 --- a/tracing-log/src/lib.rs +++ b/tracing-log/src/lib.rs @@ -16,7 +16,7 @@ //! - An [`env_logger`] module, with helpers for using the [`env_logger` crate] //! with `tracing` (optional, enabled by the `env-logger` feature). //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -80,14 +80,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! diff --git a/tracing-macros/Cargo.toml b/tracing-macros/Cargo.toml index 8680720b88..9f578ef5a0 100644 --- a/tracing-macros/Cargo.toml +++ b/tracing-macros/Cargo.toml @@ -15,7 +15,7 @@ categories = [ ] keywords = ["logging", "tracing"] license = "MIT" -rust-version = "1.49.0" +rust-version = "1.56.0" [dependencies] tracing = "0.1.35" diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml index 02aabbfeaa..29764832a0 100644 --- a/tracing-mock/Cargo.toml +++ b/tracing-mock/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" repository = "https://github.com/tokio-rs/tracing" homepage = "https://tokio.rs" edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" publish = false [dependencies] diff --git a/tracing-serde/Cargo.toml b/tracing-serde/Cargo.toml index b3b0f940b8..e9670906bb 100644 --- a/tracing-serde/Cargo.toml +++ b/tracing-serde/Cargo.toml @@ -16,7 +16,7 @@ categories = [ "encoding", ] keywords = ["logging", "tracing", "serialization"] -rust-version = "1.49.0" +rust-version = "1.56.0" [features] valuable = ["valuable_crate", "valuable-serde", "tracing-core/valuable"] diff --git a/tracing-serde/README.md b/tracing-serde/README.md index 7a41e5de0a..3f1cc82afa 100644 --- a/tracing-serde/README.md +++ b/tracing-serde/README.md @@ -36,7 +36,7 @@ and tracing data to monitor your services in production. The `tracing` crate provides the APIs necessary for instrumenting libraries and applications to emit trace data. -*Compiler support: [requires `rustc` 1.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -97,14 +97,14 @@ trace data. ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +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. diff --git a/tracing-serde/src/lib.rs b/tracing-serde/src/lib.rs index 705ccce505..211feb80f6 100644 --- a/tracing-serde/src/lib.rs +++ b/tracing-serde/src/lib.rs @@ -32,7 +32,7 @@ //! The `tracing` crate provides the APIs necessary for instrumenting //! libraries and applications to emit trace data. //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -142,14 +142,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 22373ae044..48b703becb 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -20,7 +20,7 @@ categories = [ "asynchronous", ] keywords = ["logging", "tracing", "metrics", "subscriber"] -rust-version = "1.50.0" +rust-version = "1.56.0" [features] diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md index 124fb956d0..7c1212c6a9 100644 --- a/tracing-subscriber/README.md +++ b/tracing-subscriber/README.md @@ -32,21 +32,21 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers. [discord-url]: https://discord.gg/EeF3cQw [maint-badge]: https://img.shields.io/badge/maintenance-experimental-blue.svg -*Compiler support: [requires `rustc` 1.50+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.50. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +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. diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index 025e17504d..cd53cc5e88 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -16,7 +16,7 @@ //! tracing-subscriber = "0.3" //! ``` //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: super#supported-rust-versions //! diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index b1ef7480e9..761e239af3 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -10,7 +10,7 @@ //! `tracing-subscriber` is intended for use by both `Subscriber` authors and //! application authors using `tracing` to instrument their applications. //! -//! *Compiler support: [requires `rustc` 1.50+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -138,14 +138,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.50. The current Tracing version is not guaranteed to build on +//! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! diff --git a/tracing-tower/Cargo.toml b/tracing-tower/Cargo.toml index 1ea0a572ea..80923c82d8 100644 --- a/tracing-tower/Cargo.toml +++ b/tracing-tower/Cargo.toml @@ -15,7 +15,7 @@ categories = [ ] keywords = ["logging", "tracing"] license = "MIT" -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = ["tower-layer", "tower-make"] diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 313c3370e6..50bedb07fa 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -25,7 +25,7 @@ categories = [ ] keywords = ["logging", "tracing", "metrics", "async"] edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" [dependencies] tracing-core = { path = "../tracing-core", version = "0.1.30", default-features = false } diff --git a/tracing/README.md b/tracing/README.md index 6947345771..1de6560a66 100644 --- a/tracing/README.md +++ b/tracing/README.md @@ -47,7 +47,7 @@ data as well as textual messages. The `tracing` crate provides the APIs necessary for instrumenting libraries and applications to emit trace data. -*Compiler support: [requires `rustc` 1.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -443,14 +443,14 @@ undergoing active development. They may be less stable than `tracing` and ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +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.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +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. diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index dfb76e9971..e7f52ba0ea 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -19,7 +19,7 @@ //! The `tracing` crate provides the APIs necessary for instrumenting libraries //! and applications to emit trace data. //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! # Core Concepts @@ -859,14 +859,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! 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.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! 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. //! From 6f7a4b8c622203c2657993541e35afa2b11c4a2d Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 5 Apr 2023 12:19:20 -0700 Subject: [PATCH 131/142] chore: fix `clippy::needless_borrow` lint in journald tests (#2547) --- tracing-journald/tests/journal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-journald/tests/journal.rs b/tracing-journald/tests/journal.rs index c2e07cc9ca..7cbcd24d15 100644 --- a/tracing-journald/tests/journal.rs +++ b/tracing-journald/tests/journal.rs @@ -122,7 +122,7 @@ fn read_from_journal(test_name: &str) -> Vec> { let stdout = String::from_utf8( Command::new("journalctl") // We pass --all to circumvent journalctl's default limit of 4096 bytes for field values - .args(&["--user", "--output=json", "--all"]) + .args(["--user", "--output=json", "--all"]) // Filter by the PID of the current test process .arg(format!("_PID={}", std::process::id())) .arg(format!("TEST_NAME={}", test_name)) From 63ae090515545af377839d4ca57aa9ace4a17eee Mon Sep 17 00:00:00 2001 From: Donough Liu Date: Wed, 12 Apr 2023 06:43:23 +0800 Subject: [PATCH 132/142] tracing: `inline` methods to make macro-generated code smaller (#2555) ## Motivation Make `tracing::event!` codegen smaller ## Solution Add `inline` to several functions called by `tracing::event!`. Simple example: https://github.com/ldm0/tracing_test After inlining, executable size drops from 746kb to 697kb (`cargo build --release + strip`), saves 50 bytes per `event!`. Test environment: ``` toolchain: nightly-aarch64-apple-darwin rustc-version: rustc 1.70.0-nightly (88fb1b922 2023-04-10) ``` There are also performance improvements in the benchmarks: ``` event/scoped [-40.689% -40.475% -40.228%] event/scoped_recording [-14.972% -14.685% -14.410%] event/global [-48.412% -48.217% -48.010%] span_fields/scoped [-25.317% -24.876% -24.494%] span_fields/global [-39.695% -39.488% -39.242%] span_repeated/global [-27.514% -26.633% -25.298%] static/baseline_single_threaded [-32.275% -32.032% -31.808%] static/single_threaded [-29.628% -29.376% -29.156%] static/enabled_one [-29.777% -29.544% -29.305%] static/enabled_many [-30.901% -30.504% -30.140%] dynamic/baseline_single_threaded [-20.157% -19.880% -19.603%] ``` I retried benchmark several times and the improvements seem to be fairly stable. raw log: https://gist.github.com/ldm0/6573935f4979d2645fbcf5bde7361386 --- tracing-core/src/field.rs | 3 +++ tracing-core/src/metadata.rs | 1 + tracing/src/macros.rs | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index 7e2c26b1d7..04b8e1b297 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -820,6 +820,7 @@ impl FieldSet { /// /// [`Identifier`]: super::callsite::Identifier /// [`Callsite`]: super::callsite::Callsite + #[inline] pub(crate) fn callsite(&self) -> callsite::Identifier { callsite::Identifier(self.callsite.0) } @@ -857,6 +858,7 @@ impl FieldSet { } /// Returns an iterator over the `Field`s in this `FieldSet`. + #[inline] pub fn iter(&self) -> Iter { let idxs = 0..self.len(); Iter { @@ -960,6 +962,7 @@ impl PartialEq for FieldSet { impl Iterator for Iter { type Item = Field; + #[inline] fn next(&mut self) -> Option { let i = self.idxs.next()?; Some(Field { diff --git a/tracing-core/src/metadata.rs b/tracing-core/src/metadata.rs index a154419a74..5e475c1294 100644 --- a/tracing-core/src/metadata.rs +++ b/tracing-core/src/metadata.rs @@ -273,6 +273,7 @@ impl<'a> Metadata<'a> { } /// Returns the names of the fields on the described span or event. + #[inline] pub fn fields(&self) -> &field::FieldSet { &self.fields } diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index f3968e5c11..a737348cbc 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -2311,7 +2311,7 @@ macro_rules! valueset { ) }; - // Remainder is unparseable, but exists --- must be format args! + // Remainder is unparsable, but exists --- must be format args! (@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => { $crate::valueset!(@ { (&$next, Some(&format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, ) }; From cbafd7527d196632cb778b451b5f83ae80762c93 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 13 Apr 2023 07:06:59 +1000 Subject: [PATCH 133/142] Remove dep `cfg-if` from tracing (#2553) Same reason as https://github.com/rust-lang/log/pull/536 : `cfg_if` is only used in a single place and `tracing` is used by many other crates, so even removing one dependency will be beneficial. Remove dependency `cfg-if` and replace `cfg_if::cfg_if!` with a `const fn get_max_level_inner() -> LevelFilter` and uses `if cfg!(...)` inside. Using if in const function is stablised in [1.46](https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1460-2020-08-27) so this should work fine in msrv 1.56 Signed-off-by: Jiahao XU ; Conflicts: ; tracing/Cargo.toml ; tracing/src/level_filters.rs --- tracing/Cargo.toml | 1 - tracing/src/level_filters.rs | 56 +++++++++++++++++++----------------- 2 files changed, 30 insertions(+), 27 deletions(-) diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 50bedb07fa..7cf472048b 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -31,7 +31,6 @@ rust-version = "1.56.0" tracing-core = { path = "../tracing-core", version = "0.1.30", default-features = false } log = { version = "0.4.17", optional = true } tracing-attributes = { path = "../tracing-attributes", version = "0.1.23", optional = true } -cfg-if = "1.0.0" pin-project-lite = "0.2.9" [dev-dependencies] diff --git a/tracing/src/level_filters.rs b/tracing/src/level_filters.rs index 44f5e5f57a..4e56ada2c5 100644 --- a/tracing/src/level_filters.rs +++ b/tracing/src/level_filters.rs @@ -62,33 +62,37 @@ pub use tracing_core::{metadata::ParseLevelFilterError, LevelFilter}; /// `Span` constructors should compare the level against this value to /// determine if those spans or events are enabled. /// -/// [module-level documentation]: super#compile-time-filters -pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL; +/// [module-level documentation]: self#compile-time-filters +pub const STATIC_MAX_LEVEL: LevelFilter = get_max_level_inner(); -cfg_if::cfg_if! { - if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::OFF; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::ERROR; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::WARN; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::INFO; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::TRACE; - } else if #[cfg(feature = "max_level_off")] { - const MAX_LEVEL: LevelFilter = LevelFilter::OFF; - } else if #[cfg(feature = "max_level_error")] { - const MAX_LEVEL: LevelFilter = LevelFilter::ERROR; - } else if #[cfg(feature = "max_level_warn")] { - const MAX_LEVEL: LevelFilter = LevelFilter::WARN; - } else if #[cfg(feature = "max_level_info")] { - const MAX_LEVEL: LevelFilter = LevelFilter::INFO; - } else if #[cfg(feature = "max_level_debug")] { - const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG; +const fn get_max_level_inner() -> LevelFilter { + if cfg!(not(debug_assertions)) { + if cfg!(feature = "release_max_level_off") { + LevelFilter::OFF + } else if cfg!(feature = "release_max_level_error") { + LevelFilter::ERROR + } else if cfg!(feature = "release_max_level_warn") { + LevelFilter::WARN + } else if cfg!(feature = "release_max_level_info") { + LevelFilter::INFO + } else if cfg!(feature = "release_max_level_debug") { + LevelFilter::DEBUG + } else { + // Same as branch cfg!(feature = "release_max_level_trace") + LevelFilter::TRACE + } + } else if cfg!(feature = "max_level_off") { + LevelFilter::OFF + } else if cfg!(feature = "max_level_error") { + LevelFilter::ERROR + } else if cfg!(feature = "max_level_warn") { + LevelFilter::WARN + } else if cfg!(feature = "max_level_info") { + LevelFilter::INFO + } else if cfg!(feature = "max_level_debug") { + LevelFilter::DEBUG } else { - const MAX_LEVEL: LevelFilter = LevelFilter::TRACE; + // Same as branch cfg!(feature = "max_level_trace") + LevelFilter::TRACE } } From 27eecd5fd718c140da85c214021937caf25b2a06 Mon Sep 17 00:00:00 2001 From: klensy Date: Fri, 14 Apr 2023 00:12:53 +0300 Subject: [PATCH 134/142] attributes: remove unused syn's feature visit (#2530) Remove unused `syn`s feature `visit` ; Conflicts: ; tracing-attributes/Cargo.toml --- tracing-attributes/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 199fdda8c9..81b578a093 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -39,9 +39,9 @@ proc-macro = true async-await = [] [dependencies] -proc-macro2 = "1" -syn = { version = "1.0.98", default-features = false, features = ["full", "parsing", "printing", "visit", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] } -quote = "1" +proc-macro2 = "1.0.40" +syn = { version = "1.0.98", default-features = false, features = ["full", "parsing", "printing", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] } +quote = "1.0.20" [dev-dependencies] tracing = { path = "../tracing", version = "0.1.35" } From a066c36729688133a5b4f04fa4e874a4b28343e6 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 13 Apr 2023 23:29:33 +0200 Subject: [PATCH 135/142] attributes: update to syn 2.0 (#2516) ## Motivation syn 2.0 is out! ## Solution Update to syn 2.0 :rocket: Co-authored-by: David Barsky Co-authored-by: Eliza Weisman --- tracing-attributes/Cargo.toml | 5 ++--- tracing-attributes/src/attr.rs | 6 +++--- tracing-attributes/src/expand.rs | 7 ++----- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 81b578a093..46a299bf2c 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -40,7 +40,7 @@ async-await = [] [dependencies] proc-macro2 = "1.0.40" -syn = { version = "1.0.98", default-features = false, features = ["full", "parsing", "printing", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] } +syn = { version = "2.0", default-features = false, features = ["full", "parsing", "printing", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] } quote = "1.0.20" [dev-dependencies] @@ -48,8 +48,7 @@ tracing = { path = "../tracing", version = "0.1.35" } tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] } tracing-subscriber = { path = "../tracing-subscriber", version = "0.3.0", features = ["env-filter"] } tokio-test = "0.4.2" -tracing-core = { path = "../tracing-core", version = "0.1.28"} -async-trait = "0.1.56" +async-trait = "0.1.67" trybuild = "1.0.64" rustversion = "1.0.9" diff --git a/tracing-attributes/src/attr.rs b/tracing-attributes/src/attr.rs index 0ad68ef756..f5ad409398 100644 --- a/tracing-attributes/src/attr.rs +++ b/tracing-attributes/src/attr.rs @@ -252,7 +252,7 @@ impl Parse for Skips { let _ = input.parse::(); let content; let _ = syn::parenthesized!(content in input); - let names: Punctuated = content.parse_terminated(Ident::parse_any)?; + let names = content.parse_terminated(Ident::parse_any, Token![,])?; let mut skips = HashSet::new(); for name in names { if skips.contains(&name) { @@ -303,7 +303,7 @@ impl Parse for Fields { let _ = input.parse::(); let content; let _ = syn::parenthesized!(content in input); - let fields: Punctuated<_, Token![,]> = content.parse_terminated(Field::parse)?; + let fields = content.parse_terminated(Field::parse, Token![,])?; Ok(Self(fields)) } } @@ -348,7 +348,7 @@ impl ToTokens for Field { let name = &self.name; let kind = &self.kind; tokens.extend(quote! { - #name = #kind#value + #name = #kind #value }) } else if self.kind == FieldKind::Value { // XXX(eliza): I don't like that fields without values produce diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index 9e1a0a7ed0..3c3cd13ad8 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -477,10 +477,7 @@ fn param_names(pat: Pat, record_type: RecordType) -> Box Box::new( + Pat::TupleStruct(PatTupleStruct { elems, .. }) => Box::new( elems .into_iter() .flat_map(|p| param_names(p, RecordType::Debug)), @@ -564,7 +561,7 @@ impl<'block> AsyncInfo<'block> { // last expression of the block: it determines the return value of the // block, this is quite likely a `Box::pin` statement or an async block let (last_expr_stmt, last_expr) = block.stmts.iter().rev().find_map(|stmt| { - if let Stmt::Expr(expr) = stmt { + if let Stmt::Expr(expr, _semi) = stmt { Some((stmt, expr)) } else { None From 049ad730c1baee8821ebfec45dda6f4a40a4ce4f Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 14 Apr 2023 21:38:48 +0200 Subject: [PATCH 136/142] subscriber: add ability to disable ANSI without crate feature (#2532) ## Motivation Currently it is not possible to disable ANSI in `fmt::Subscriber` without enabling the "ansi" crate feature. This makes it difficult for users to implement interoperable settings that are controllable with crate features without having to pull in the dependencies "ansi" does. I hit this while writing an application with multiple logging options set during compile-time and I wanted to cut down on dependencies if possible. ## Solution This changes `fmt::Subscriber::with_ansi()` to not require the "ansi" feature flag. This way, `with_ansi(false)` can be called even when the "ansi" feature is disabled. Calling `with_ansi(true)` when the "ansi" feature is not enabled will panic in debug mode, or print a warning if debug assertions are disabled. Co-authored-by: Eliza Weisman --- tracing-subscriber/src/fmt/fmt_layer.rs | 28 ++++++++++++++++++++++--- tracing-subscriber/src/fmt/mod.rs | 16 +++++++++++++- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/tracing-subscriber/src/fmt/fmt_layer.rs b/tracing-subscriber/src/fmt/fmt_layer.rs index 6e4e2ac0bb..1e0923a547 100644 --- a/tracing-subscriber/src/fmt/fmt_layer.rs +++ b/tracing-subscriber/src/fmt/fmt_layer.rs @@ -273,10 +273,32 @@ impl Layer { } } - /// Enable ANSI terminal colors for formatted output. - #[cfg(feature = "ansi")] - #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] + /// Sets whether or not the formatter emits ANSI terminal escape codes + /// for colors and other text formatting. + /// + /// Enabling ANSI escapes (calling `with_ansi(true)`) requires the "ansi" + /// crate feature flag. Calling `with_ansi(true)` without the "ansi" + /// feature flag enabled will panic if debug assertions are enabled, or + /// print a warning otherwise. + /// + /// This method itself is still available without the feature flag. This + /// is to allow ANSI escape codes to be explicitly *disabled* without + /// having to opt-in to the dependencies required to emit ANSI formatting. + /// This way, code which constructs a formatter that should never emit + /// ANSI escape codes can ensure that they are not used, regardless of + /// whether or not other crates in the dependency graph enable the "ansi" + /// feature flag. pub fn with_ansi(self, ansi: bool) -> Self { + #[cfg(not(feature = "ansi"))] + if ansi { + const ERROR: &str = + "tracing-subscriber: the `ansi` crate feature is required to enable ANSI terminal colors"; + #[cfg(debug_assertions)] + panic!("{}", ERROR); + #[cfg(not(debug_assertions))] + eprintln!("{}", ERROR); + } + Self { is_ansi: ansi, ..self diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index cd53cc5e88..cfe4704758 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -612,7 +612,21 @@ where } } - /// Enable ANSI encoding for formatted events. + /// Sets whether or not the formatter emits ANSI terminal escape codes + /// for colors and other text formatting. + /// + /// Enabling ANSI escapes (calling `with_ansi(true)`) requires the "ansi" + /// crate feature flag. Calling `with_ansi(true)` without the "ansi" + /// feature flag enabled will panic if debug assertions are enabled, or + /// print a warning otherwise. + /// + /// This method itself is still available without the feature flag. This + /// is to allow ANSI escape codes to be explicitly *disabled* without + /// having to opt-in to the dependencies required to emit ANSI formatting. + /// This way, code which constructs a formatter that should never emit + /// ANSI escape codes can ensure that they are not used, regardless of + /// whether or not other crates in the dependency graph enable the "ansi" + /// feature flag. #[cfg(feature = "ansi")] #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] pub fn with_ansi(self, ansi: bool) -> SubscriberBuilder, F, W> { From 29d85b1adbac6e2b2f8c2ab396f6ed18765d7d66 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 21 Apr 2023 16:11:52 -0700 Subject: [PATCH 137/142] mock: move layer mock from tracing-subscriber tests (#2369) The `tracing-subscriber` module `tests::support` included functionality to mock a layer (via the `Layer` trait). This code depends on some items from `tracing_mock::collector` which should otherwise not be public. This change moves the mocking functionality inside `tracing-mock` behind a feature flag. Allowing the `Expect` enum and `MockHandle::new` from `tracing_mock::collector` to be made `pub(crate)` instead of `pub`. Since it's now used from two different modules, the `Expect` enum has been moved to its own module. This requires a lot of modifications to imports so that we're not doing wildcard imports from another crate (i.e. in `tracing-subscriber` importing wildcards from `tracing-mock`). This PR is based on @hds' PR #2369, but modified to track renamings. I also deleted all the doc comments temporarily because updating them was a lot of work and I need to get a release of `tracing-subscriber` out first. Closes: #2359 --- tracing-mock/Cargo.toml | 1 + tracing-mock/src/expectation.rs | 21 +++ .../support.rs => tracing-mock/src/layer.rs | 131 ++++++++---------- tracing-mock/src/lib.rs | 4 + tracing-mock/src/subscriber.rs | 21 +-- tracing-subscriber/Cargo.toml | 2 +- ...d_layer_filters_dont_break_other_layers.rs | 11 +- tracing-subscriber/tests/env_filter/main.rs | 5 +- .../tests/env_filter/per_layer.rs | 1 + ...d_layer_filters_dont_break_other_layers.rs | 11 +- .../layer_filter_interests_are_cached.rs | 4 +- .../tests/layer_filters/boxed.rs | 3 +- .../tests/layer_filters/filter_scopes.rs | 3 +- .../tests/layer_filters/main.rs | 4 +- .../tests/layer_filters/per_event.rs | 2 +- .../tests/layer_filters/trees.rs | 3 +- tracing-subscriber/tests/layer_filters/vec.rs | 3 +- .../multiple_layer_filter_interests_cached.rs | 4 +- ...d_layer_filters_dont_break_other_layers.rs | 11 +- .../vec_subscriber_filter_interests_cached.rs | 9 +- 20 files changed, 125 insertions(+), 129 deletions(-) create mode 100644 tracing-mock/src/expectation.rs rename tracing-subscriber/tests/support.rs => tracing-mock/src/layer.rs (82%) diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml index 29764832a0..2e060df78c 100644 --- a/tracing-mock/Cargo.toml +++ b/tracing-mock/Cargo.toml @@ -20,6 +20,7 @@ publish = false [dependencies] tracing = { path = "../tracing", version = "0.1.35", default-features = false } tracing-core = { path = "../tracing-core", version = "0.1.28", default-features = false } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, optional = true } tokio-test = { version = "0.4.2", optional = true } # Fix minimal-versions; tokio-test fails with otherwise acceptable 0.1.0 diff --git a/tracing-mock/src/expectation.rs b/tracing-mock/src/expectation.rs new file mode 100644 index 0000000000..0328754fc8 --- /dev/null +++ b/tracing-mock/src/expectation.rs @@ -0,0 +1,21 @@ +use crate::{ + event::MockEvent, + field, + span::{MockSpan, NewSpan}, +}; + +#[derive(Debug, Eq, PartialEq)] +pub(crate) enum Expect { + Event(MockEvent), + FollowsFrom { + consequence: MockSpan, + cause: MockSpan, + }, + Enter(MockSpan), + Exit(MockSpan), + CloneSpan(MockSpan), + DropSpan(MockSpan), + Visit(MockSpan, field::Expect), + NewSpan(NewSpan), + Nothing, +} diff --git a/tracing-subscriber/tests/support.rs b/tracing-mock/src/layer.rs similarity index 82% rename from tracing-subscriber/tests/support.rs rename to tracing-mock/src/layer.rs index 50e0e6669d..0a0a02005d 100644 --- a/tracing-subscriber/tests/support.rs +++ b/tracing-mock/src/layer.rs @@ -1,15 +1,13 @@ -#![allow(missing_docs, dead_code)] -pub use tracing_mock::{event, field, span, subscriber}; - +use crate::{ + event::MockEvent, + expectation::Expect, + span::{MockSpan, NewSpan}, + subscriber::MockHandle, +}; use tracing_core::{ span::{Attributes, Id, Record}, Event, Subscriber, }; -use tracing_mock::{ - event::MockEvent, - span::{MockSpan, NewSpan}, - subscriber::{Expect, MockHandle}, -}; use tracing_subscriber::{ layer::{Context, Layer}, registry::{LookupSpan, SpanRef}, @@ -21,49 +19,34 @@ use std::{ sync::{Arc, Mutex}, }; -pub mod layer { - use super::ExpectLayerBuilder; - - pub fn mock() -> ExpectLayerBuilder { - ExpectLayerBuilder { - expected: Default::default(), - name: std::thread::current() - .name() - .map(String::from) - .unwrap_or_default(), - } +#[must_use] +pub fn mock() -> MockLayerBuilder { + MockLayerBuilder { + expected: Default::default(), + name: std::thread::current() + .name() + .map(String::from) + .unwrap_or_default(), } +} - pub fn named(name: impl std::fmt::Display) -> ExpectLayerBuilder { - mock().named(name) - } +#[must_use] +pub fn named(name: impl std::fmt::Display) -> MockLayerBuilder { + mock().named(name) } -pub struct ExpectLayerBuilder { +pub struct MockLayerBuilder { expected: VecDeque, name: String, } -pub struct ExpectLayer { +pub struct MockLayer { expected: Arc>>, current: Mutex>, name: String, } -impl ExpectLayerBuilder { - /// Overrides the name printed by the mock subscriber's debugging output. - /// - /// The debugging output is displayed if the test panics, or if the test is - /// run with `--nocapture`. - /// - /// By default, the mock subscriber's name is the name of the test - /// (*technically*, the name of the thread where it was created, which is - /// the name of the test unless tests are run with `--test-threads=1`). - /// When a test has only one mock subscriber, this is sufficient. However, - /// some tests may include multiple subscribers, in order to test - /// interactions between multiple subscribers. In that case, it can be - /// helpful to give each subscriber a separate name to distinguish where the - /// debugging output comes from. +impl MockLayerBuilder { pub fn named(mut self, name: impl fmt::Display) -> Self { use std::fmt::Write; if !self.name.is_empty() { @@ -74,63 +57,55 @@ impl ExpectLayerBuilder { self } - pub fn enter(mut self, span: MockSpan) -> Self { - self.expected.push_back(Expect::Enter(span)); - self - } - pub fn event(mut self, event: MockEvent) -> Self { self.expected.push_back(Expect::Event(event)); self } - pub fn exit(mut self, span: MockSpan) -> Self { - self.expected.push_back(Expect::Exit(span)); + pub fn new_span(mut self, new_span: I) -> Self + where + I: Into, + { + self.expected.push_back(Expect::NewSpan(new_span.into())); self } - pub fn done(mut self) -> Self { - self.expected.push_back(Expect::Nothing); + pub fn enter(mut self, span: MockSpan) -> Self { + self.expected.push_back(Expect::Enter(span)); self } - pub fn record(mut self, span: MockSpan, fields: I) -> Self - where - I: Into, - { - self.expected.push_back(Expect::Visit(span, fields.into())); + pub fn exit(mut self, span: MockSpan) -> Self { + self.expected.push_back(Expect::Exit(span)); self } - pub fn new_span(mut self, new_span: I) -> Self - where - I: Into, - { - self.expected.push_back(Expect::NewSpan(new_span.into())); + pub fn done(mut self) -> Self { + self.expected.push_back(Expect::Nothing); self } - pub fn run(self) -> ExpectLayer { - ExpectLayer { + pub fn run(self) -> MockLayer { + MockLayer { expected: Arc::new(Mutex::new(self.expected)), name: self.name, current: Mutex::new(Vec::new()), } } - pub fn run_with_handle(self) -> (ExpectLayer, MockHandle) { + pub fn run_with_handle(self) -> (MockLayer, MockHandle) { let expected = Arc::new(Mutex::new(self.expected)); let handle = MockHandle::new(expected.clone(), self.name.clone()); - let layer = ExpectLayer { + let subscriber = MockLayer { expected, name: self.name, current: Mutex::new(Vec::new()), }; - (layer, handle) + (subscriber, handle) } } -impl ExpectLayer { +impl MockLayer { fn check_span_ref<'spans, S>( &self, expected: &MockSpan, @@ -191,9 +166,9 @@ impl ExpectLayer { } } -impl Layer for ExpectLayer +impl Layer for MockLayer where - S: Subscriber + for<'a> LookupSpan<'a>, + C: Subscriber + for<'a> LookupSpan<'a>, { fn register_callsite( &self, @@ -203,7 +178,7 @@ where tracing_core::Interest::always() } - fn on_record(&self, _: &Id, _: &Record<'_>, _: Context<'_, S>) { + fn on_record(&self, _: &Id, _: &Record<'_>, _: Context<'_, C>) { unimplemented!( "so far, we don't have any tests that need an `on_record` \ implementation.\nif you just wrote one that does, feel free to \ @@ -211,7 +186,7 @@ where ); } - fn on_event(&self, event: &Event<'_>, cx: Context<'_, S>) { + fn on_event(&self, event: &Event<'_>, cx: Context<'_, C>) { let name = event.metadata().name(); println!( "[{}] event: {}; level: {}; target: {}", @@ -262,11 +237,15 @@ where } } - fn on_follows_from(&self, _span: &Id, _follows: &Id, _: Context<'_, S>) { - // TODO: it should be possible to expect spans to follow from other spans + fn on_follows_from(&self, _span: &Id, _follows: &Id, _: Context<'_, C>) { + unimplemented!( + "so far, we don't have any tests that need an `on_follows_from` \ + implementation.\nif you just wrote one that does, feel free to \ + implement it!" + ); } - fn on_new_span(&self, span: &Attributes<'_>, id: &Id, cx: Context<'_, S>) { + fn on_new_span(&self, span: &Attributes<'_>, id: &Id, cx: Context<'_, C>) { let meta = span.metadata(); println!( "[{}] new_span: name={:?}; target={:?}; id={:?};", @@ -290,7 +269,7 @@ where } } - fn on_enter(&self, id: &Id, cx: Context<'_, S>) { + fn on_enter(&self, id: &Id, cx: Context<'_, C>) { let span = cx .span(id) .unwrap_or_else(|| panic!("[{}] no span for ID {:?}", self.name, id)); @@ -305,7 +284,7 @@ where self.current.lock().unwrap().push(id.clone()); } - fn on_exit(&self, id: &Id, cx: Context<'_, S>) { + fn on_exit(&self, id: &Id, cx: Context<'_, C>) { if std::thread::panicking() { // `exit()` can be called in `drop` impls, so we must guard against // double panics. @@ -334,7 +313,7 @@ where }; } - fn on_close(&self, id: Id, cx: Context<'_, S>) { + fn on_close(&self, id: Id, cx: Context<'_, C>) { if std::thread::panicking() { // `try_close` can be called in `drop` impls, so we must guard against // double panics. @@ -380,14 +359,14 @@ where } } - fn on_id_change(&self, _old: &Id, _new: &Id, _ctx: Context<'_, S>) { + fn on_id_change(&self, _old: &Id, _new: &Id, _ctx: Context<'_, C>) { panic!("well-behaved subscribers should never do this to us, lol"); } } -impl fmt::Debug for ExpectLayer { +impl fmt::Debug for MockLayer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut s = f.debug_struct("ExpectLayer"); + let mut s = f.debug_struct("MockLayer"); s.field("name", &self.name); if let Ok(expected) = self.expected.try_lock() { diff --git a/tracing-mock/src/lib.rs b/tracing-mock/src/lib.rs index 8b2617233a..a8ba0865b7 100644 --- a/tracing-mock/src/lib.rs +++ b/tracing-mock/src/lib.rs @@ -4,11 +4,15 @@ use std::{ }; pub mod event; +mod expectation; pub mod field; mod metadata; pub mod span; pub mod subscriber; +#[cfg(feature = "tracing-subscriber")] +pub mod layer; + #[derive(Debug, Eq, PartialEq)] pub enum Parent { ContextualRoot, diff --git a/tracing-mock/src/subscriber.rs b/tracing-mock/src/subscriber.rs index 32f27ada00..9a73da28c2 100644 --- a/tracing-mock/src/subscriber.rs +++ b/tracing-mock/src/subscriber.rs @@ -1,6 +1,7 @@ #![allow(missing_docs)] use super::{ event::MockEvent, + expectation::Expect, field as mock_field, span::{MockSpan, NewSpan}, }; @@ -20,22 +21,6 @@ use tracing::{ Event, Metadata, Subscriber, }; -#[derive(Debug, Eq, PartialEq)] -pub enum Expect { - Event(MockEvent), - FollowsFrom { - consequence: MockSpan, - cause: MockSpan, - }, - Enter(MockSpan), - Exit(MockSpan), - CloneSpan(MockSpan), - DropSpan(MockSpan), - Visit(MockSpan, mock_field::Expect), - NewSpan(NewSpan), - Nothing, -} - struct SpanState { name: &'static str, refs: usize, @@ -471,7 +456,7 @@ where } impl MockHandle { - pub fn new(expected: Arc>>, name: String) -> Self { + pub(crate) fn new(expected: Arc>>, name: String) -> Self { Self(expected, name) } @@ -488,7 +473,7 @@ impl MockHandle { } impl Expect { - pub fn bad(&self, name: impl AsRef, what: fmt::Arguments<'_>) { + pub(crate) fn bad(&self, name: impl AsRef, what: fmt::Arguments<'_>) { let name = name.as_ref(); match self { Expect::Event(e) => panic!( diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 48b703becb..5553fd8737 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -70,7 +70,7 @@ valuable-serde = { version = "0.1.0", optional = true, default-features = false [dev-dependencies] tracing = { path = "../tracing", version = "0.1.35" } -tracing-mock = { path = "../tracing-mock" } +tracing-mock = { path = "../tracing-mock", features = ["tracing-subscriber"] } log = "0.4.17" tracing-log = { path = "../tracing-log", version = "0.1.3" } criterion = { version = "0.3.6", default-features = false } diff --git a/tracing-subscriber/tests/cached_layer_filters_dont_break_other_layers.rs b/tracing-subscriber/tests/cached_layer_filters_dont_break_other_layers.rs index 00e98a994c..5b40b60aa3 100644 --- a/tracing-subscriber/tests/cached_layer_filters_dont_break_other_layers.rs +++ b/tracing-subscriber/tests/cached_layer_filters_dont_break_other_layers.rs @@ -1,7 +1,10 @@ #![cfg(feature = "registry")] -mod support; -use self::support::*; use tracing::Level; +use tracing_mock::{ + event, + layer::{self, MockLayer}, + subscriber, +}; use tracing_subscriber::{filter::LevelFilter, prelude::*}; #[test] @@ -102,7 +105,7 @@ fn filter() -> LevelFilter { LevelFilter::INFO } -fn unfiltered(name: &str) -> (ExpectLayer, subscriber::MockHandle) { +fn unfiltered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(event::mock().at_level(Level::TRACE)) .event(event::mock().at_level(Level::DEBUG)) @@ -113,7 +116,7 @@ fn unfiltered(name: &str) -> (ExpectLayer, subscriber::MockHandle) { .run_with_handle() } -fn filtered(name: &str) -> (ExpectLayer, subscriber::MockHandle) { +fn filtered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(event::mock().at_level(Level::INFO)) .event(event::mock().at_level(Level::WARN)) diff --git a/tracing-subscriber/tests/env_filter/main.rs b/tracing-subscriber/tests/env_filter/main.rs index 3c3d4868be..ef984a68a9 100644 --- a/tracing-subscriber/tests/env_filter/main.rs +++ b/tracing-subscriber/tests/env_filter/main.rs @@ -1,12 +1,9 @@ #![cfg(feature = "env-filter")] -#[path = "../support.rs"] -mod support; -use self::support::*; - mod per_layer; use tracing::{self, subscriber::with_default, Level}; +use tracing_mock::{event, field, layer, span, subscriber}; use tracing_subscriber::{ filter::{EnvFilter, LevelFilter}, prelude::*, diff --git a/tracing-subscriber/tests/env_filter/per_layer.rs b/tracing-subscriber/tests/env_filter/per_layer.rs index 8bf5698a4d..4b143b8bfd 100644 --- a/tracing-subscriber/tests/env_filter/per_layer.rs +++ b/tracing-subscriber/tests/env_filter/per_layer.rs @@ -2,6 +2,7 @@ //! `Layer` filter). #![cfg(feature = "registry")] use super::*; +use tracing_mock::{event, field, layer, span}; #[test] fn level_filter_event() { diff --git a/tracing-subscriber/tests/hinted_layer_filters_dont_break_other_layers.rs b/tracing-subscriber/tests/hinted_layer_filters_dont_break_other_layers.rs index 897dae2822..4e5ee4e050 100644 --- a/tracing-subscriber/tests/hinted_layer_filters_dont_break_other_layers.rs +++ b/tracing-subscriber/tests/hinted_layer_filters_dont_break_other_layers.rs @@ -1,7 +1,10 @@ #![cfg(feature = "registry")] -mod support; -use self::support::*; use tracing::{Level, Metadata, Subscriber}; +use tracing_mock::{ + event, + layer::{self, MockLayer}, + subscriber, +}; use tracing_subscriber::{filter::DynFilterFn, layer::Context, prelude::*}; #[test] @@ -110,7 +113,7 @@ fn filter() -> DynFilterFn { .with_max_level_hint(Level::INFO) } -fn unfiltered(name: &str) -> (ExpectLayer, subscriber::MockHandle) { +fn unfiltered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(event::mock().at_level(Level::TRACE)) .event(event::mock().at_level(Level::DEBUG)) @@ -121,7 +124,7 @@ fn unfiltered(name: &str) -> (ExpectLayer, subscriber::MockHandle) { .run_with_handle() } -fn filtered(name: &str) -> (ExpectLayer, subscriber::MockHandle) { +fn filtered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(event::mock().at_level(Level::INFO)) .event(event::mock().at_level(Level::WARN)) diff --git a/tracing-subscriber/tests/layer_filter_interests_are_cached.rs b/tracing-subscriber/tests/layer_filter_interests_are_cached.rs index d89d3bf174..67e8ba763c 100644 --- a/tracing-subscriber/tests/layer_filter_interests_are_cached.rs +++ b/tracing-subscriber/tests/layer_filter_interests_are_cached.rs @@ -1,12 +1,10 @@ #![cfg(feature = "registry")] -mod support; -use self::support::*; - use std::{ collections::HashMap, sync::{Arc, Mutex}, }; use tracing::{Level, Subscriber}; +use tracing_mock::{event, layer}; use tracing_subscriber::{filter, prelude::*}; #[test] diff --git a/tracing-subscriber/tests/layer_filters/boxed.rs b/tracing-subscriber/tests/layer_filters/boxed.rs index 0fe37188e1..b02331a6c3 100644 --- a/tracing-subscriber/tests/layer_filters/boxed.rs +++ b/tracing-subscriber/tests/layer_filters/boxed.rs @@ -1,7 +1,8 @@ use super::*; +use tracing_mock::layer::MockLayer; use tracing_subscriber::{filter, prelude::*, Layer}; -fn layer() -> (ExpectLayer, subscriber::MockHandle) { +fn layer() -> (MockLayer, subscriber::MockHandle) { layer::mock().done().run_with_handle() } diff --git a/tracing-subscriber/tests/layer_filters/filter_scopes.rs b/tracing-subscriber/tests/layer_filters/filter_scopes.rs index 7fd7d843b4..d5608a8965 100644 --- a/tracing-subscriber/tests/layer_filters/filter_scopes.rs +++ b/tracing-subscriber/tests/layer_filters/filter_scopes.rs @@ -1,4 +1,5 @@ use super::*; +use tracing_mock::layer::MockLayer; #[test] fn filters_span_scopes() { @@ -67,7 +68,7 @@ fn filters_span_scopes() { #[test] fn filters_interleaved_span_scopes() { - fn target_layer(target: &'static str) -> (ExpectLayer, subscriber::MockHandle) { + fn target_layer(target: &'static str) -> (MockLayer, subscriber::MockHandle) { layer::named(format!("target_{}", target)) .enter(span::mock().with_target(target)) .enter(span::mock().with_target(target)) diff --git a/tracing-subscriber/tests/layer_filters/main.rs b/tracing-subscriber/tests/layer_filters/main.rs index 10f06c24c6..fed74038c7 100644 --- a/tracing-subscriber/tests/layer_filters/main.rs +++ b/tracing-subscriber/tests/layer_filters/main.rs @@ -1,7 +1,4 @@ #![cfg(feature = "registry")] -#[path = "../support.rs"] -mod support; -use self::support::*; mod boxed; mod downcast_raw; mod filter_scopes; @@ -11,6 +8,7 @@ mod trees; mod vec; use tracing::{level_filters::LevelFilter, Level}; +use tracing_mock::{event, layer, span, subscriber}; use tracing_subscriber::{filter, prelude::*, Layer}; #[test] diff --git a/tracing-subscriber/tests/layer_filters/per_event.rs b/tracing-subscriber/tests/layer_filters/per_event.rs index 9c785f9a23..3a28d94f08 100644 --- a/tracing-subscriber/tests/layer_filters/per_event.rs +++ b/tracing-subscriber/tests/layer_filters/per_event.rs @@ -1,5 +1,5 @@ -use crate::support::*; use tracing::Level; +use tracing_mock::{event, layer}; use tracing_subscriber::{field::Visit, layer::Filter, prelude::*}; struct FilterEvent; diff --git a/tracing-subscriber/tests/layer_filters/trees.rs b/tracing-subscriber/tests/layer_filters/trees.rs index 18cdd8ccc8..02830122ca 100644 --- a/tracing-subscriber/tests/layer_filters/trees.rs +++ b/tracing-subscriber/tests/layer_filters/trees.rs @@ -1,4 +1,5 @@ use super::*; +use tracing_mock::layer::MockLayer; #[test] fn basic_trees() { @@ -54,7 +55,7 @@ fn basic_trees() { #[test] fn filter_span_scopes() { - fn target_layer(target: &'static str) -> (ExpectLayer, subscriber::MockHandle) { + fn target_layer(target: &'static str) -> (MockLayer, subscriber::MockHandle) { layer::named(format!("target_{}", target)) .enter(span::mock().with_target(target).at_level(Level::INFO)) .event( diff --git a/tracing-subscriber/tests/layer_filters/vec.rs b/tracing-subscriber/tests/layer_filters/vec.rs index 87244e4ab5..dbe3674785 100644 --- a/tracing-subscriber/tests/layer_filters/vec.rs +++ b/tracing-subscriber/tests/layer_filters/vec.rs @@ -1,5 +1,6 @@ use super::*; use tracing::Subscriber; +use tracing_mock::layer::{self, MockLayer}; #[test] fn with_filters_unboxed() { @@ -115,6 +116,6 @@ fn all_filtered_max_level_hint() { #[test] fn empty_vec() { // Just a None means everything is off - let subscriber = tracing_subscriber::registry().with(Vec::::new()); + let subscriber = tracing_subscriber::registry().with(Vec::::new()); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::OFF)); } diff --git a/tracing-subscriber/tests/multiple_layer_filter_interests_cached.rs b/tracing-subscriber/tests/multiple_layer_filter_interests_cached.rs index 5c25e7f03c..13e1a94a3e 100644 --- a/tracing-subscriber/tests/multiple_layer_filter_interests_cached.rs +++ b/tracing-subscriber/tests/multiple_layer_filter_interests_cached.rs @@ -1,12 +1,10 @@ #![cfg(feature = "registry")] -mod support; -use self::support::*; - use std::{ collections::HashMap, sync::{Arc, Mutex}, }; use tracing::{Level, Subscriber}; +use tracing_mock::{event, layer}; use tracing_subscriber::{filter, prelude::*}; #[test] diff --git a/tracing-subscriber/tests/unhinted_layer_filters_dont_break_other_layers.rs b/tracing-subscriber/tests/unhinted_layer_filters_dont_break_other_layers.rs index 9fa5c6bd41..d8b38345f9 100644 --- a/tracing-subscriber/tests/unhinted_layer_filters_dont_break_other_layers.rs +++ b/tracing-subscriber/tests/unhinted_layer_filters_dont_break_other_layers.rs @@ -1,7 +1,10 @@ #![cfg(feature = "registry")] -mod support; -use self::support::*; use tracing::Level; +use tracing_mock::{ + event, + layer::{self, MockLayer}, + subscriber, +}; use tracing_subscriber::{filter::DynFilterFn, prelude::*}; #[test] @@ -102,7 +105,7 @@ fn filter() -> DynFilterFn { DynFilterFn::new(|metadata, _| metadata.level() <= &Level::INFO) } -fn unfiltered(name: &str) -> (ExpectLayer, subscriber::MockHandle) { +fn unfiltered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(event::mock().at_level(Level::TRACE)) .event(event::mock().at_level(Level::DEBUG)) @@ -113,7 +116,7 @@ fn unfiltered(name: &str) -> (ExpectLayer, subscriber::MockHandle) { .run_with_handle() } -fn filtered(name: &str) -> (ExpectLayer, subscriber::MockHandle) { +fn filtered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(event::mock().at_level(Level::INFO)) .event(event::mock().at_level(Level::WARN)) diff --git a/tracing-subscriber/tests/vec_subscriber_filter_interests_cached.rs b/tracing-subscriber/tests/vec_subscriber_filter_interests_cached.rs index 10467cb7d6..1bfb4a0adf 100644 --- a/tracing-subscriber/tests/vec_subscriber_filter_interests_cached.rs +++ b/tracing-subscriber/tests/vec_subscriber_filter_interests_cached.rs @@ -1,17 +1,18 @@ #![cfg(feature = "registry")] -mod support; -use self::support::*; - use std::{ collections::HashMap, sync::{Arc, Mutex}, }; use tracing::{Level, Subscriber}; +use tracing_mock::{ + event, + layer::{self, MockLayer}, +}; use tracing_subscriber::{filter, prelude::*}; #[test] fn vec_layer_filter_interests_are_cached() { - let mk_filtered = |level: Level, subscriber: ExpectLayer| { + let mk_filtered = |level: Level, subscriber: MockLayer| { let seen = Arc::new(Mutex::new(HashMap::new())); let filter = filter::filter_fn({ let seen = seen.clone(); From 3a65354837a0f176178e15787fc700dd6fa11a92 Mon Sep 17 00:00:00 2001 From: Ilya Salauyeu <47687266+ilslv@users.noreply.github.com> Date: Wed, 19 Apr 2023 00:55:46 +0200 Subject: [PATCH 138/142] tracing, tracing-futures: instrument `Future` inside `Drop` (#2562) ## Motivation Currently it is not possible to disable ANSI in `fmt::Subscriber` without enabling the "ansi" crate feature. This makes it difficult for users to implement interoperable settings that are controllable with crate features without having to pull in the dependencies "ansi" does. I hit this while writing an application with multiple logging options set during compile-time and I wanted to cut down on dependencies if possible. ## Solution This changes `fmt::Subscriber::with_ansi()` to not require the "ansi" feature flag. This way, `with_ansi(false)` can be called even when the "ansi" feature is disabled. Calling `with_ansi(true)` when the "ansi" feature is not enabled will panic in debug mode, or print a warning if debug assertions are disabled. Co-authored-by: daxpedda Co-authored-by: Eliza Weisman --- tracing-attributes/tests/async_fn.rs | 26 ++++ tracing-attributes/tests/err.rs | 4 + tracing-attributes/tests/follows_from.rs | 2 + tracing-attributes/tests/ret.rs | 2 + tracing-futures/Cargo.toml | 1 + tracing-futures/src/executor/futures_01.rs | 22 ++- tracing-futures/src/lib.rs | 152 +++++++++++++++------ tracing-futures/tests/std_future.rs | 61 +++++++++ tracing/Cargo.toml | 3 +- tracing/src/instrument.rs | 87 ++++++++++-- tracing/tests/instrument.rs | 65 +++++++++ 11 files changed, 360 insertions(+), 65 deletions(-) create mode 100644 tracing/tests/instrument.rs diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs index c89963672c..d6d874ffd7 100644 --- a/tracing-attributes/tests/async_fn.rs +++ b/tracing-attributes/tests/async_fn.rs @@ -90,6 +90,8 @@ fn async_fn_only_enters_for_polls() { .exit(span::mock().named("test_async_fn")) .enter(span::mock().named("test_async_fn")) .exit(span::mock().named("test_async_fn")) + .enter(span::mock().named("test_async_fn")) + .exit(span::mock().named("test_async_fn")) .drop_span(span::mock().named("test_async_fn")) .done() .run_with_handle(); @@ -120,8 +122,12 @@ fn async_fn_nested() { .enter(span2.clone()) .event(event::mock().with_fields(field::mock("nested").with_value(&true))) .exit(span2.clone()) + .enter(span2.clone()) + .exit(span2.clone()) .drop_span(span2) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .done() .run_with_handle(); @@ -199,13 +205,19 @@ fn async_fn_with_async_trait() { .enter(span3.clone()) .event(event::mock().with_fields(field::mock("val").with_value(&2u64))) .exit(span3.clone()) + .enter(span3.clone()) + .exit(span3.clone()) .drop_span(span3) .new_span(span2.clone().with_field(field::mock("self"))) .enter(span2.clone()) .event(event::mock().with_fields(field::mock("val").with_value(&5u64))) .exit(span2.clone()) + .enter(span2.clone()) + .exit(span2.clone()) .drop_span(span2) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .done() .run_with_handle(); @@ -256,6 +268,8 @@ fn async_fn_with_async_trait_and_fields_expressions() { ) .enter(span.clone()) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .done() .run_with_handle(); @@ -331,8 +345,12 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() { .with_field(field::mock("Self").with_value(&std::any::type_name::())), ) .enter(span4.clone()) + .exit(span4.clone()) + .enter(span4.clone()) .exit(span4) .exit(span2.clone()) + .enter(span2.clone()) + .exit(span2.clone()) .drop_span(span2) .new_span( span3 @@ -341,6 +359,8 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() { ) .enter(span3.clone()) .exit(span3.clone()) + .enter(span3.clone()) + .exit(span3.clone()) .drop_span(span3) .done() .run_with_handle(); @@ -382,6 +402,8 @@ fn out_of_scope_fields() { .new_span(span.clone()) .enter(span.clone()) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .done() .run_with_handle(); @@ -417,6 +439,8 @@ fn manual_impl_future() { .enter(span.clone()) .event(poll_event()) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .done() .run_with_handle(); @@ -448,6 +472,8 @@ fn manual_box_pin() { .enter(span.clone()) .event(poll_event()) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .done() .run_with_handle(); diff --git a/tracing-attributes/tests/err.rs b/tracing-attributes/tests/err.rs index deb2f1d59d..485dd11961 100644 --- a/tracing-attributes/tests/err.rs +++ b/tracing-attributes/tests/err.rs @@ -57,6 +57,8 @@ fn test_async() { .enter(span.clone()) .event(event::mock().at_level(Level::ERROR)) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .done() .run_with_handle(); @@ -111,6 +113,8 @@ fn test_mut_async() { .enter(span.clone()) .event(event::mock().at_level(Level::ERROR)) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .done() .run_with_handle(); diff --git a/tracing-attributes/tests/follows_from.rs b/tracing-attributes/tests/follows_from.rs index da0eec6357..a589409ded 100644 --- a/tracing-attributes/tests/follows_from.rs +++ b/tracing-attributes/tests/follows_from.rs @@ -58,6 +58,8 @@ fn follows_from_async_test() { .follows_from(consequence.clone(), cause_b) .follows_from(consequence.clone(), cause_c) .enter(consequence.clone()) + .exit(consequence.clone()) + .enter(consequence.clone()) .exit(consequence) .done() .run_with_handle(); diff --git a/tracing-attributes/tests/ret.rs b/tracing-attributes/tests/ret.rs index f20b66797c..f56c80baaf 100644 --- a/tracing-attributes/tests/ret.rs +++ b/tracing-attributes/tests/ret.rs @@ -138,6 +138,8 @@ fn test_async() { .at_level(Level::INFO), ) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .done() .run_with_handle(); diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml index a34b459b1e..efc87eb248 100644 --- a/tracing-futures/Cargo.toml +++ b/tracing-futures/Cargo.toml @@ -40,6 +40,7 @@ tokio-threadpool = { version = "0.1.18", optional = true } mio = { version = "0.6.23", optional = true } [dev-dependencies] +futures = "0.3.21" tokio-test = "0.4.2" tracing-core = { path = "../tracing-core", version = "0.1.28" } tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] } diff --git a/tracing-futures/src/executor/futures_01.rs b/tracing-futures/src/executor/futures_01.rs index 56ba6e3c42..7d4b674af8 100644 --- a/tracing-futures/src/executor/futures_01.rs +++ b/tracing-futures/src/executor/futures_01.rs @@ -4,16 +4,6 @@ use futures_01::{ Future, }; -macro_rules! deinstrument_err { - ($e:expr) => { - $e.map_err(|e| { - let kind = e.kind(); - let future = e.into_future().inner; - ExecuteError::new(kind, future) - }) - }; -} - impl Executor for Instrumented where T: Executor>, @@ -21,7 +11,11 @@ where { fn execute(&self, future: F) -> Result<(), ExecuteError> { let future = future.instrument(self.span.clone()); - deinstrument_err!(self.inner.execute(future)) + self.inner.execute(future).map_err(|e| { + let kind = e.kind(); + let future = e.into_future().into_inner(); + ExecuteError::new(kind, future) + }) } } @@ -32,7 +26,11 @@ where { fn execute(&self, future: F) -> Result<(), ExecuteError> { let future = self.with_dispatch(future); - deinstrument_err!(self.inner.execute(future)) + self.inner.execute(future).map_err(|e| { + let kind = e.kind(); + let future = e.into_future().inner; + ExecuteError::new(kind, future) + }) } } diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs index addf19f159..8065175d28 100644 --- a/tracing-futures/src/lib.rs +++ b/tracing-futures/src/lib.rs @@ -103,7 +103,11 @@ use pin_project_lite::pin_project; pub(crate) mod stdlib; #[cfg(feature = "std-future")] -use crate::stdlib::{pin::Pin, task::Context}; +use core::{ + mem::{self, ManuallyDrop}, + pin::Pin, + task::Context, +}; #[cfg(feature = "std")] use tracing::{dispatcher, Dispatch}; @@ -118,13 +122,13 @@ pub mod executor; /// /// [span]: mod@tracing::span pub trait Instrument: Sized { - /// Instruments this type with the provided `Span`, returning an - /// `Instrumented` wrapper. + /// Instruments this type with the provided [`Span`], returning an + /// [`Instrumented`] wrapper. /// - /// If the instrumented type is a future, stream, or sink, the attached `Span` - /// will be [entered] every time it is polled. If the instrumented type - /// is a future executor, every future spawned on that executor will be - /// instrumented by the attached `Span`. + /// If the instrumented type is a future, stream, or sink, the attached + /// [`Span`] will be [entered] every time it is polled or [`Drop`]ped. If + /// the instrumented type is a future executor, every future spawned on that + /// executor will be instrumented by the attached [`Span`]. /// /// # Examples /// @@ -145,18 +149,22 @@ pub trait Instrument: Sized { /// # } /// ``` /// - /// [entered]: tracing::Span::enter + /// [entered]: Span::enter() fn instrument(self, span: Span) -> Instrumented { - Instrumented { inner: self, span } + #[cfg(feature = "std-future")] + let inner = ManuallyDrop::new(self); + #[cfg(not(feature = "std-future"))] + let inner = self; + Instrumented { inner, span } } - /// Instruments this type with the [current] `Span`, returning an - /// `Instrumented` wrapper. + /// Instruments this type with the [current] [`Span`], returning an + /// [`Instrumented`] wrapper. /// - /// If the instrumented type is a future, stream, or sink, the attached `Span` - /// will be [entered] every time it is polled. If the instrumented type - /// is a future executor, every future spawned on that executor will be - /// instrumented by the attached `Span`. + /// If the instrumented type is a future, stream, or sink, the attached + /// [`Span`] will be [entered] every time it is polled or [`Drop`]ped. If + /// the instrumented type is a future executor, every future spawned on that + /// executor will be instrumented by the attached [`Span`]. /// /// This can be used to propagate the current span when spawning a new future. /// @@ -180,8 +188,8 @@ pub trait Instrument: Sized { /// # } /// ``` /// - /// [current]: tracing::Span::current - /// [entered]: tracing::Span::enter + /// [current]: Span::current() + /// [entered]: Span::enter() #[inline] fn in_current_span(self) -> Instrumented { self.instrument(Span::current()) @@ -240,12 +248,56 @@ pub trait WithSubscriber: Sized { #[cfg(feature = "std-future")] pin_project! { /// A future, stream, sink, or executor that has been instrumented with a `tracing` span. + #[project = InstrumentedProj] + #[project_ref = InstrumentedProjRef] #[derive(Debug, Clone)] pub struct Instrumented { + // `ManuallyDrop` is used here to to enter instrument `Drop` by entering + // `Span` and executing `ManuallyDrop::drop`. #[pin] - inner: T, + inner: ManuallyDrop, span: Span, } + + impl PinnedDrop for Instrumented { + fn drop(this: Pin<&mut Self>) { + let this = this.project(); + let _enter = this.span.enter(); + // SAFETY: 1. `Pin::get_unchecked_mut()` is safe, because this isn't + // different from wrapping `T` in `Option` and calling + // `Pin::set(&mut this.inner, None)`, except avoiding + // additional memory overhead. + // 2. `ManuallyDrop::drop()` is safe, because + // `PinnedDrop::drop()` is guaranteed to be called only + // once. + unsafe { ManuallyDrop::drop(this.inner.get_unchecked_mut()) } + } + } +} + +#[cfg(feature = "std-future")] +impl<'a, T> InstrumentedProj<'a, T> { + /// Get a mutable reference to the [`Span`] a pinned mutable reference to + /// the wrapped type. + fn span_and_inner_pin_mut(self) -> (&'a mut Span, Pin<&'a mut T>) { + // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move + // and `inner` is valid, because `ManuallyDrop::drop` is called + // only inside `Drop` of the `Instrumented`. + let inner = unsafe { self.inner.map_unchecked_mut(|v| &mut **v) }; + (self.span, inner) + } +} + +#[cfg(feature = "std-future")] +impl<'a, T> InstrumentedProjRef<'a, T> { + /// Get a reference to the [`Span`] a pinned reference to the wrapped type. + fn span_and_inner_pin_ref(self) -> (&'a Span, Pin<&'a T>) { + // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move + // and `inner` is valid, because `ManuallyDrop::drop` is called + // only inside `Drop` of the `Instrumented`. + let inner = unsafe { self.inner.map_unchecked(|v| &**v) }; + (self.span, inner) + } } /// A future, stream, sink, or executor that has been instrumented with a `tracing` span. @@ -286,10 +338,10 @@ impl Instrument for T {} impl crate::stdlib::future::Future for Instrumented { type Output = T::Output; - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> crate::stdlib::task::Poll { - let this = self.project(); - let _enter = this.span.enter(); - this.inner.poll(cx) + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> core::task::Poll { + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + inner.poll(cx) } } @@ -346,9 +398,9 @@ impl futures::Stream for Instrumented { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> futures::task::Poll> { - let this = self.project(); - let _enter = this.span.enter(); - T::poll_next(this.inner, cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + T::poll_next(inner, cx) } } @@ -364,33 +416,33 @@ where self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> futures::task::Poll> { - let this = self.project(); - let _enter = this.span.enter(); - T::poll_ready(this.inner, cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + T::poll_ready(inner, cx) } fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> { - let this = self.project(); - let _enter = this.span.enter(); - T::start_send(this.inner, item) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + T::start_send(inner, item) } fn poll_flush( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> futures::task::Poll> { - let this = self.project(); - let _enter = this.span.enter(); - T::poll_flush(this.inner, cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + T::poll_flush(inner, cx) } fn poll_close( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> futures::task::Poll> { - let this = self.project(); - let _enter = this.span.enter(); - T::poll_close(this.inner, cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + T::poll_close(inner, cx) } } @@ -419,20 +471,36 @@ impl Instrumented { #[cfg(feature = "std-future")] #[cfg_attr(docsrs, doc(cfg(feature = "std-future")))] pub fn inner_pin_ref(self: Pin<&Self>) -> Pin<&T> { - self.project_ref().inner + self.project_ref().span_and_inner_pin_ref().1 } /// Get a pinned mutable reference to the wrapped type. #[cfg(feature = "std-future")] #[cfg_attr(docsrs, doc(cfg(feature = "std-future")))] pub fn inner_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { - self.project().inner + self.project().span_and_inner_pin_mut().1 } /// Consumes the `Instrumented`, returning the wrapped type. /// /// Note that this drops the span. pub fn into_inner(self) -> T { + #[cfg(feature = "std-future")] + { + // To manually destructure `Instrumented` without `Drop`, we save + // pointers to the fields and use `mem::forget` to leave those pointers + // valid. + let span: *const Span = &self.span; + let inner: *const ManuallyDrop = &self.inner; + mem::forget(self); + // SAFETY: Those pointers are valid for reads, because `Drop` didn't + // run, and properly aligned, because `Instrumented` isn't + // `#[repr(packed)]`. + let _span = unsafe { span.read() }; + let inner = unsafe { inner.read() }; + ManuallyDrop::into_inner(inner) + } + #[cfg(not(feature = "std-future"))] self.inner } } @@ -570,6 +638,8 @@ mod tests { .exit(span::mock().named("foo")) .enter(span::mock().named("foo")) .exit(span::mock().named("foo")) + .enter(span::mock().named("foo")) + .exit(span::mock().named("foo")) .drop_span(span::mock().named("foo")) .done() .run_with_handle(); @@ -589,6 +659,8 @@ mod tests { .exit(span::mock().named("foo")) .enter(span::mock().named("foo")) .exit(span::mock().named("foo")) + .enter(span::mock().named("foo")) + .exit(span::mock().named("foo")) .drop_span(span::mock().named("foo")) .done() .run_with_handle(); @@ -613,6 +685,8 @@ mod tests { .exit(span::mock().named("foo")) .enter(span::mock().named("foo")) .exit(span::mock().named("foo")) + .enter(span::mock().named("foo")) + .exit(span::mock().named("foo")) .drop_span(span::mock().named("foo")) .run_with_handle(); with_default(subscriber, || { diff --git a/tracing-futures/tests/std_future.rs b/tracing-futures/tests/std_future.rs index a8d27bb6ba..da47569de4 100644 --- a/tracing-futures/tests/std_future.rs +++ b/tracing-futures/tests/std_future.rs @@ -1,3 +1,6 @@ +use std::{future::Future, pin::Pin, task}; + +use futures::FutureExt as _; use tracing::Instrument; use tracing::{subscriber::with_default, Level}; use tracing_mock::*; @@ -9,6 +12,8 @@ fn enter_exit_is_reasonable() { .exit(span::mock().named("foo")) .enter(span::mock().named("foo")) .exit(span::mock().named("foo")) + .enter(span::mock().named("foo")) + .exit(span::mock().named("foo")) .drop_span(span::mock().named("foo")) .done() .run_with_handle(); @@ -26,6 +31,8 @@ fn error_ends_span() { .exit(span::mock().named("foo")) .enter(span::mock().named("foo")) .exit(span::mock().named("foo")) + .enter(span::mock().named("foo")) + .exit(span::mock().named("foo")) .drop_span(span::mock().named("foo")) .done() .run_with_handle(); @@ -35,3 +42,57 @@ fn error_ends_span() { }); handle.assert_finished(); } + +#[test] +fn span_on_drop() { + #[derive(Clone, Debug)] + struct AssertSpanOnDrop; + + impl Drop for AssertSpanOnDrop { + fn drop(&mut self) { + tracing::info!("Drop"); + } + } + + struct Fut(Option); + + impl Future for Fut { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _: &mut task::Context<'_>) -> task::Poll { + self.set(Fut(None)); + task::Poll::Ready(()) + } + } + + let subscriber = subscriber::mock() + .enter(span::mock().named("foo")) + .event( + event::mock() + .at_level(Level::INFO), + ) + .exit(span::mock().named("foo")) + .enter(span::mock().named("foo")) + .exit(span::mock().named("foo")) + .drop_span(span::mock().named("foo")) + .enter(span::mock().named("bar")) + .event( + event::mock() + .at_level(Level::INFO), + ) + .exit(span::mock().named("bar")) + .drop_span(span::mock().named("bar")) + .done() + .run(); + + with_default(subscriber, || { + // polled once + Fut(Some(AssertSpanOnDrop)) + .instrument(tracing::span!(Level::TRACE, "foo")) + .now_or_never() + .unwrap(); + + // never polled + drop(Fut(Some(AssertSpanOnDrop)).instrument(tracing::span!(Level::TRACE, "bar"))); + }); +} diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 7cf472048b..234c4ce0d5 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -34,7 +34,8 @@ tracing-attributes = { path = "../tracing-attributes", version = "0.1.23", optio pin-project-lite = "0.2.9" [dev-dependencies] -criterion = { version = "0.3.6", default-features = false } +criterion = { version = "0.3.6", default_features = false } +futures = { version = "0.3.21", default_features = false } log = "0.4.17" tracing-mock = { path = "../tracing-mock" } diff --git a/tracing/src/instrument.rs b/tracing/src/instrument.rs index 46e5f579cd..376a2c903d 100644 --- a/tracing/src/instrument.rs +++ b/tracing/src/instrument.rs @@ -1,10 +1,15 @@ -use crate::stdlib::pin::Pin; -use crate::stdlib::task::{Context, Poll}; -use crate::stdlib::{future::Future, marker::Sized}; + use crate::{ dispatcher::{self, Dispatch}, span::Span, }; +use core::{ + future::Future, + marker::Sized, + mem::{self, ManuallyDrop}, + pin::Pin, + task::{Context, Poll}, +}; use pin_project_lite::pin_project; /// Attaches spans to a [`std::future::Future`]. @@ -18,7 +23,7 @@ pub trait Instrument: Sized { /// `Instrumented` wrapper. /// /// The attached [`Span`] will be [entered] every time the instrumented - /// [`Future`] is polled. + /// [`Future`] is polled or [`Drop`]ped. /// /// # Examples /// @@ -80,14 +85,17 @@ pub trait Instrument: Sized { /// [disabled]: super::Span::is_disabled() /// [`Future`]: std::future::Future fn instrument(self, span: Span) -> Instrumented { - Instrumented { inner: self, span } + Instrumented { + inner: ManuallyDrop::new(self), + span, + } } /// Instruments this type with the [current] [`Span`], returning an /// `Instrumented` wrapper. /// /// The attached [`Span`] will be [entered] every time the instrumented - /// [`Future`] is polled. + /// [`Future`] is polled or [`Drop`]ped. /// /// This can be used to propagate the current span when spawning a new future. /// @@ -252,13 +260,55 @@ pin_project! { /// /// [`Future`]: std::future::Future /// [`Span`]: crate::Span + #[project = InstrumentedProj] + #[project_ref = InstrumentedProjRef] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Instrumented { + // `ManuallyDrop` is used here to to enter instrument `Drop` by entering + // `Span` and executing `ManuallyDrop::drop`. #[pin] - inner: T, + inner: ManuallyDrop, span: Span, } + + impl PinnedDrop for Instrumented { + fn drop(this: Pin<&mut Self>) { + let this = this.project(); + let _enter = this.span.enter(); + // SAFETY: 1. `Pin::get_unchecked_mut()` is safe, because this isn't + // different from wrapping `T` in `Option` and calling + // `Pin::set(&mut this.inner, None)`, except avoiding + // additional memory overhead. + // 2. `ManuallyDrop::drop()` is safe, because + // `PinnedDrop::drop()` is guaranteed to be called only + // once. + unsafe { ManuallyDrop::drop(this.inner.get_unchecked_mut()) } + } + } +} + +impl<'a, T> InstrumentedProj<'a, T> { + /// Get a mutable reference to the [`Span`] a pinned mutable reference to + /// the wrapped type. + fn span_and_inner_pin_mut(self) -> (&'a mut Span, Pin<&'a mut T>) { + // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move + // and `inner` is valid, because `ManuallyDrop::drop` is called + // only inside `Drop` of the `Instrumented`. + let inner = unsafe { self.inner.map_unchecked_mut(|v| &mut **v) }; + (self.span, inner) + } +} + +impl<'a, T> InstrumentedProjRef<'a, T> { + /// Get a reference to the [`Span`] a pinned reference to the wrapped type. + fn span_and_inner_pin_ref(self) -> (&'a Span, Pin<&'a T>) { + // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move + // and `inner` is valid, because `ManuallyDrop::drop` is called + // only inside `Drop` of the `Instrumented`. + let inner = unsafe { self.inner.map_unchecked(|v| &**v) }; + (self.span, inner) + } } // === impl Instrumented === @@ -267,9 +317,9 @@ impl Future for Instrumented { type Output = T::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - let _enter = this.span.enter(); - this.inner.poll(cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + inner.poll(cx) } } @@ -298,19 +348,30 @@ impl Instrumented { /// Get a pinned reference to the wrapped type. pub fn inner_pin_ref(self: Pin<&Self>) -> Pin<&T> { - self.project_ref().inner + self.project_ref().span_and_inner_pin_ref().1 } /// Get a pinned mutable reference to the wrapped type. pub fn inner_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { - self.project().inner + self.project().span_and_inner_pin_mut().1 } /// Consumes the `Instrumented`, returning the wrapped type. /// /// Note that this drops the span. pub fn into_inner(self) -> T { - self.inner + // To manually destructure `Instrumented` without `Drop`, we save + // pointers to the fields and use `mem::forget` to leave those pointers + // valid. + let span: *const Span = &self.span; + let inner: *const ManuallyDrop = &self.inner; + mem::forget(self); + // SAFETY: Those pointers are valid for reads, because `Drop` didn't + // run, and properly aligned, because `Instrumented` isn't + // `#[repr(packed)]`. + let _span = unsafe { span.read() }; + let inner = unsafe { inner.read() }; + ManuallyDrop::into_inner(inner) } } diff --git a/tracing/tests/instrument.rs b/tracing/tests/instrument.rs new file mode 100644 index 0000000000..1c4ebc9ffa --- /dev/null +++ b/tracing/tests/instrument.rs @@ -0,0 +1,65 @@ +// These tests require the thread-local scoped dispatcher, which only works when +// we have a standard library. The behaviour being tested should be the same +// with the standard lib disabled. +#![cfg(feature = "std")] + +use std::{future::Future, pin::Pin, task}; + +use futures::FutureExt as _; +use tracing::{subscriber::with_default, Instrument as _, Level}; +use tracing_mock::*; + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[test] +fn span_on_drop() { + #[derive(Clone, Debug)] + struct AssertSpanOnDrop; + + impl Drop for AssertSpanOnDrop { + fn drop(&mut self) { + tracing::info!("Drop"); + } + } + + struct Fut(Option); + + impl Future for Fut { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _: &mut task::Context<'_>) -> task::Poll { + self.set(Fut(None)); + task::Poll::Ready(()) + } + } + + let subscriber = subscriber::mock() + .enter(span::mock().named("foo")) + .event( + event::mock() + .at_level(Level::INFO), + ) + .exit(span::mock().named("foo")) + .enter(span::mock().named("foo")) + .exit(span::mock().named("foo")) + .drop_span(span::mock().named("foo")) + .enter(span::mock().named("bar")) + .event( + event::mock() + .at_level(Level::INFO), + ) + .exit(span::mock().named("bar")) + .drop_span(span::mock().named("bar")) + .done() + .run(); + + with_default(subscriber, || { + // polled once + Fut(Some(AssertSpanOnDrop)) + .instrument(tracing::span!(Level::TRACE, "foo")) + .now_or_never() + .unwrap(); + + // never polled + drop(Fut(Some(AssertSpanOnDrop)).instrument(tracing::span!(Level::TRACE, "bar"))); + }); +} From b9becf90fe29a72e21cc676e5ac1a57d8f71aab0 Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Fri, 21 Apr 2023 13:47:44 -0700 Subject: [PATCH 139/142] attributes: update UI tests with the latest stable version of Rust (#2568) updated UI tests using TRYBUILD=overwrite with the latest stable version of Rust ## Motivation UI tests are failing on the latest stable version of Rust ## Solution Run `TRYBUILD=overwrite cargo test` to update the effected files. --- tracing-attributes/tests/ui/async_instrument.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tracing-attributes/tests/ui/async_instrument.stderr b/tracing-attributes/tests/ui/async_instrument.stderr index 519b71e045..2c64b0c15e 100644 --- a/tracing-attributes/tests/ui/async_instrument.stderr +++ b/tracing-attributes/tests/ui/async_instrument.stderr @@ -16,7 +16,7 @@ error[E0308]: mismatched types 10 | "" | ^^- help: try using a conversion method: `.to_string()` | | - | expected struct `String`, found `&str` + | expected `String`, found `&str` | note: return type inferred to be `String` here --> tests/ui/async_instrument.rs:9:31 @@ -47,7 +47,7 @@ error[E0308]: mismatched types --> tests/ui/async_instrument.rs:23:5 | 23 | "" - | ^^ expected struct `Wrapper`, found `&str` + | ^^ expected `Wrapper<_>`, found `&str` | = note: expected struct `Wrapper<_>` found reference `&'static str` @@ -79,7 +79,7 @@ error[E0308]: mismatched types 36 | return ""; | ^^- help: try using a conversion method: `.to_string()` | | - | expected struct `String`, found `&str` + | expected `String`, found `&str` | note: return type inferred to be `String` here --> tests/ui/async_instrument.rs:34:28 From 22355704ea0a33300fb959669d76943762383165 Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Fri, 21 Apr 2023 13:59:25 -0700 Subject: [PATCH 140/142] subscriber: add "unicode-case" and "unicode-perl" features to `regex` dependency (#2566) ## Motivation Missing features for the `regex` crate were causing build time errors due to the the use of unicode characters in the regex without using the proper features within the regex crate ## Solution Add the missing feature flags. Fixes #2565 Authored-by: Devin Bidwell --- tracing-subscriber/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 5553fd8737..fa2b083b30 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -43,7 +43,7 @@ tracing-core = { path = "../tracing-core", version = "0.1.30", default-features # only required by the filter feature tracing = { optional = true, path = "../tracing", version = "0.1.35", default-features = false } matchers = { optional = true, version = "0.1.0" } -regex = { optional = true, version = "1", default-features = false, features = ["std"] } +regex = { optional = true, version = "1.6.0", default-features = false, features = ["std", "unicode-case", "unicode-perl"] } smallvec = { optional = true, version = "1.9.0" } once_cell = { optional = true, version = "1.13.0" } From 53989b422c25282dd6fb57d5f8364ae75d74e930 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 21 Apr 2023 16:48:04 -0700 Subject: [PATCH 141/142] chore: fix rustfmt --- tracing-futures/tests/std_future.rs | 10 ++-------- tracing/src/instrument.rs | 1 - tracing/tests/instrument.rs | 10 ++-------- 3 files changed, 4 insertions(+), 17 deletions(-) diff --git a/tracing-futures/tests/std_future.rs b/tracing-futures/tests/std_future.rs index da47569de4..8bc31149c7 100644 --- a/tracing-futures/tests/std_future.rs +++ b/tracing-futures/tests/std_future.rs @@ -67,19 +67,13 @@ fn span_on_drop() { let subscriber = subscriber::mock() .enter(span::mock().named("foo")) - .event( - event::mock() - .at_level(Level::INFO), - ) + .event(event::mock().at_level(Level::INFO)) .exit(span::mock().named("foo")) .enter(span::mock().named("foo")) .exit(span::mock().named("foo")) .drop_span(span::mock().named("foo")) .enter(span::mock().named("bar")) - .event( - event::mock() - .at_level(Level::INFO), - ) + .event(event::mock().at_level(Level::INFO)) .exit(span::mock().named("bar")) .drop_span(span::mock().named("bar")) .done() diff --git a/tracing/src/instrument.rs b/tracing/src/instrument.rs index 376a2c903d..25edc5e61c 100644 --- a/tracing/src/instrument.rs +++ b/tracing/src/instrument.rs @@ -1,4 +1,3 @@ - use crate::{ dispatcher::{self, Dispatch}, span::Span, diff --git a/tracing/tests/instrument.rs b/tracing/tests/instrument.rs index 1c4ebc9ffa..a23769e66f 100644 --- a/tracing/tests/instrument.rs +++ b/tracing/tests/instrument.rs @@ -34,19 +34,13 @@ fn span_on_drop() { let subscriber = subscriber::mock() .enter(span::mock().named("foo")) - .event( - event::mock() - .at_level(Level::INFO), - ) + .event(event::mock().at_level(Level::INFO)) .exit(span::mock().named("foo")) .enter(span::mock().named("foo")) .exit(span::mock().named("foo")) .drop_span(span::mock().named("foo")) .enter(span::mock().named("bar")) - .event( - event::mock() - .at_level(Level::INFO), - ) + .event(event::mock().at_level(Level::INFO)) .exit(span::mock().named("bar")) .drop_span(span::mock().named("bar")) .done() From 0114ec1cf56e01e79b2e429e77c660457711d263 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 21 Apr 2023 18:56:59 -0700 Subject: [PATCH 142/142] subscriber: prepare to release v0.3.17 (#2571) # 0.3.17 (April 21, 2023) This release of `tracing-subscriber` fixes a build error when using `env-filter` with recent versions of the `regex` crate. It also introduces several minor API improvements. ### Fixed - **env-filter**: Add "unicode-case" and "unicode-perl" to the `regex` dependency, fixing a build error with recent versions of `regex` (#2566) - A number of minor documentation typos and other fixes (#2384, #2378, #2368, #2548) ### Added - **filter**: Add `fmt::Display` impl for `filter::Targets` (#2343) - **fmt**: Made `with_ansi(false)` no longer require the "ansi" feature, so that ANSI formatting escapes can be disabled without requiring ANSI-specific dependencies (#2532) ### Changed - **fmt**: Dim targets in the `Compact` formatter, matching the default formatter (#2409) Thanks to @keepsimple1, @andrewhalle, @LeoniePhiline, @LukeMathWalker, @howardjohn, @daxpedda, and @dbidwell94 for contributing to this release! --- tracing-subscriber/CHANGELOG.md | 37 +++++++++++++++++++++++++++++++++ tracing-subscriber/Cargo.toml | 2 +- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/tracing-subscriber/CHANGELOG.md b/tracing-subscriber/CHANGELOG.md index f283dc61e2..9a33e55e23 100644 --- a/tracing-subscriber/CHANGELOG.md +++ b/tracing-subscriber/CHANGELOG.md @@ -1,3 +1,40 @@ +# 0.3.17 (April 21, 2023) + +This release of `tracing-subscriber` fixes a build error when using `env-filter` +with recent versions of the `regex` crate. It also introduces several minor API +improvements. + +### Fixed + +- **env-filter**: Add "unicode-case" and "unicode-perl" to the `regex` + dependency, fixing a build error with recent versions of `regex` ([#2566]) +- A number of minor documentation typos and other fixes ([#2384], [#2378], + [#2368], [#2548]) + +### Added + +- **filter**: Add `fmt::Display` impl for `filter::Targets` ([#2343]) +- **fmt**: Made `with_ansi(false)` no longer require the "ansi" feature, so that + ANSI formatting escapes can be disabled without requiring ANSI-specific + dependencies ([#2532]) + +### Changed + +- **fmt**: Dim targets in the `Compact` formatter, matching the default + formatter ([#2409]) + +Thanks to @keepsimple1, @andrewhalle, @LeoniePhiline, @LukeMathWalker, +@howardjohn, @daxpedda, and @dbidwell94 for contributing to this release! + +[#2566]: https://github.com/tokio-rs/tracing/pull/2566 +[#2384]: https://github.com/tokio-rs/tracing/pull/2384 +[#2378]: https://github.com/tokio-rs/tracing/pull/2378 +[#2368]: https://github.com/tokio-rs/tracing/pull/2368 +[#2548]: https://github.com/tokio-rs/tracing/pull/2548 +[#2343]: https://github.com/tokio-rs/tracing/pull/2343 +[#2532]: https://github.com/tokio-rs/tracing/pull/2532 +[#2409]: https://github.com/tokio-rs/tracing/pull/2409 + # 0.3.16 (October 6, 2022) This release of `tracing-subscriber` fixes a regression introduced in diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index fa2b083b30..3fbeb2feab 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" authors = [ "Eliza Weisman ", "David Barsky ",