# 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
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 <eliza@buoyant.io>
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.
# 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
## 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.
## 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`
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.
# 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<S: Subscriber + ?Sized>` and
`Arc<S: Subscriber + ?Sized>` ([#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
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 <eliza@buoyant.io>
## 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
## 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.
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`.
## 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<T: Value>
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.
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).
## 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
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`.
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.
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 <eliza@buoyant.io>
## Motivation
Currently on `v0.1.x`, the global callsite registry is implemented as a
`Mutex<Vec<&'static dyn Callsite>>`. 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<Vec<...>>`. 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<Vec<dyn Dispatch>>` 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 <eliza@buoyant.io>
* docs: `cargo intraconv` for more intra-doc links
... also it deleted some redundant ones, and it got some things wrong,
and it was gonna delete some of the cross-crate docs.rs links we can't
do as intra-doc links so I passed `--no-favored`.
* docs: convert https:// links to std/core/alloc to intra-doc links
Note that this adds some more broken intra doc links when building
without std support, but that was already a thing and I expect people
who build their own docs without std support can handle it.
This time I gave up on sed and used ruby.
find -name '*.rs' -exec ruby -i -p blah.rb {} +
with
$_.gsub!(%r{
https://doc\.rust-lang\.org/
(?: stable/)?
((?:core | std | alloc)(?:/\S+?)*)
/(\w+)\.(\w+)\.html}x
) {
path, kind, name = $~.captures
suffix = case kind
when 'method' then '()'
when 'macro' then '!'
else ''
end
r = [path.gsub('/', '::'), '::', name, suffix].join
STDERR.puts [path, kind, name, suffix, r].inspect
r
}
$_.gsub!(%r{
https://doc\.rust-lang\.org/
(?: stable/)?
((?: core | std | alloc)(?:/\S+?)*)
/(?:
index\.html
| $
| (?= \#)
)}x
) {
path, _ = $~.captures
r = path.gsub('/', '::')
STDERR.puts [path, r].inspect
r
}
* docs: more cross-crate intra-doc links
cargo intraconv doesn't seem to get them reliably and also plenty of
links to other crates aren't actually intra-doc because they're in
crates that don't depend (or only dev-depend, or only conditionally
depend) on those crates, so this wasn't very automated.
I tried to only convert docs.rs links to unconditional dependencies to
intra-crate links, but it's possible that some slipped through in either
direction.
This commit adds a `Value` implementation for `Box<T> where T: Value`.
This is *primarily* intended to make `Box<dyn Error + ...>` implement
`Value`, building on the `Value` impls for `dyn Error + ...` added in
#2066, but it may be useful for other boxed values as well.
Refs: #1308
## Motivation
#940, I guess. I kept running into the odd broken link in the docs and
eventually realized it's because a lot of stuff is reexported in parent
modules and so the file path based relative links couldn't possibly work
in all contexts. Looks like master already underwent this treatment but
I suspect this is easier than backporting.
## Solution
Intra-doc links seem pretty good.
I started with
```
find -name \*.rs -exec sed -i -e '
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\.\./\w\+\.\(\w\+\)\.html@\1super::\2@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\.\./\(\w\+\)/\w\+\.\(\w\+\)\.html@\1super::\2::\3@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\.\./\.\./\w\+\.\(\w\+\)\.html@\1super::super::\2@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\.\./\.\./\(\w\+\)/\w\+\.\(\w\+\)\.html@\1super::super::\2::\3@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\.\./\(\w\+\)/index\.html@\1super::\2@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\.\./index\.html@\1super@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\.\./\(\w\+\)/\?$@\1super::\2@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\./\w\+\.\(\w\+\)\.html@\1self::\2@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\./\(\w\+\)/\w\+\.\(\w\+\)\.html@\1self::\2::\3@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\./\(\w\+\)/index\.html@\1self::\2@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\./index\.html@\1self@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\./\(\w\+\)/\?$@\1self::\2@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\w\+\.\(\w\+\)\.html@\1self::\2@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\(\w\+\)/\w\+\.\(\w\+\)\.html@\1self::\2::\3@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\(\w\+\)/index\.html@\1self::\2@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?index\.html@\1self@;
s@\(//. \[[^]]*\]:\s\+\)[:"]\?\(\w\+\)/\?$@\1self::\2@;
s@\(//. \[[^]]*\]:\s\+[A-Za-z_0-9:]\+\)#method\.\(\w\+\)@\1::\2@;
' {} +
```
and then removed redundant `self::`s when I realized you don't actually
need a `::` in the links, and fixed stuff up by hand as I ran into
errors from
```
x='--cfg docsrs --cfg tracing_unstable'; RUSTFLAGS=$x RUSTDOCFLAGS=$x cargo doc --all-features
```
I hope that's roughly how the docs are supposed to work.
I understand this is a relatively big unsolicited change in that it
touches a whole lot of files (definitely went further than I originally
intended), I'm happy to revise or split or reduce scope of this PR as
desired.
# 0.1.25 (April 12, 2022)
This release adds additional `Value` implementations for
`std::error::Error` trait objects with auto trait bounds (`Send` and
`Sync`), as Rust will not auto-coerce trait objects. Additionally, it
fixes a bug when setting scoped dispatchers that was introduced in the
previous release ([v0.1.24]).
### Added
- `Value` implementations for `dyn Error + Send + 'static`, `dyn Error +
Send + Sync + 'static`, `dyn Error + Sync + 'static` ([#2066])
### Fixed
- Failure to use the global default dispatcher if a thread has set a
scoped default prior to setting the global default, and unset the
scoped default after setting the global default ([#2065])
Thanks to @lilyball for contributing to this release!
[v0.1.24]: https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.24
[#2066]: https://github.com/tokio-rs/tracing/pull/2066
[#2065]: https://github.com/tokio-rs/tracing/pull/2065
## Motivation
`Value` was already implemented for `dyn Error + 'static`, but rust
doesn't silently coerce trait objects. This means that passing an error
of type `dyn Error + Send + Sync + 'static` would not work. This is
related to #1308.
## Solution
Add impls for `dyn Error + …` variants for `Send`, `Sync`, and `Send +
Sync`. These extra impls just delegate to the existing `dyn Error +
'static` impl.
Also update one of the examples to use `dyn Error + Send + Sync` to
demonstrate that this works.
Refs: #1308
## Motivation
PR https://github.com/tokio-rs/tracing/pull/2001 introduced --- or rather, _uncovered_ --- a bug which occurs
when a global default subscriber is set *after* a scoped default has
been set.
When the scoped default guard is dropped, it resets the
thread-local default cell to whatever subscriber was the default when
the scoped default was set. This allows nesting scoped default contexts.
However, when there was *no* default subscriber when the `DefaultGuard`
was created, it sets the "previous" subscriber as `NoSubscriber`. This
means dropping a `DefaultGuard` that was created before any other
subscriber was set as default will reset that thread's default to
`NoSubscriber`. Because https://github.com/tokio-rs/tracing/pull/2001 changed the dispatcher module to stop
using `NoSubscriber` as a placeholder for "use the global default if one
exists", this means that the global default is permanently clobbered on
the thread that set the scoped default prior to setting the global one.
## Solution
This PR changes the behavior when creating a `DefaultGuard` when no
default has been set. Instead of populating the "previous" dispatcher
with `NoSubscriber`, it instead leaves the `DefaultGuard` with a `None`.
When the `DefaultGuard` is dropped, if the subscriber is `None`, it will
just clear the thread-local cell, rather than setting it to
`NoSubscriber`. This way, the next time the cell is accessed, we will
check if a global default exists to populate the thread-local, and
everything works correctly. As a side benefit, this also makes the code
a bit simpler!
I've also added a test reproducing the bug.
This PR is against `v0.1.x` rather than `master`, because the issue does
not exist on `master` due to other implementation differences in v0.2.
We may want to forward-port the test to guard against future
regressions, though.
Fixes#2050
This adds a minimal-versions check to the tracing project. Adapted from
`tokio-rs/tokio`. Adding this avoids breaking downstream dependencies
from accidentally under-constraining minimal versions of dependencies
when they depend on tracing.
I've currently just introduced the check. I will try to and do encourage
others to add patches to fix this where possible since it can be a fair
bit of work to chase down a version of all dependencies that passes
minimal-versions and is msrv. I've also seen some really odd
windows-specific issues (which are not being tested for here).
This is currently only testing `tracing`, `tracing-core`, and
`tracing-subscriber`. Packages such as `tracing-futures` are proving to
be a bit harder to deal with due to having features which enable very
old dependencies.
Steps to test the build minimal versions locally:
```sh
cargo install cargo-hack
rustup default nightly
cargo hack --remove-dev-deps --workspace
cargo update -Z minimal-versions
cargo hack check --all-features --ignore-private
```
CC: tokio-rs/tokio#4513
## Motivation
Currently, compiling `tracing-core` with `default-features = false`
(i.e. for `no_std` targets) emits a few warnings. This is due to the
spinlock implementation's use of the deprecated `atomic::spin_loop_hint`
function (renamed to `hint::spin_loop`), and the use of deprecated
`compare_and_swap` instead of `compare_exchange` methods. Now that our
MSRV is 1.49 (the version in which `hint::spin_loop` was stabilized), we
can fix these warnings.
## Solution
This branch replaces the deprecated APIs.
Also, I noticed that one of the tests emits unused-imports warnings with
`--no-default-features`. This is because the actual tests are feature
flagged to require `std`, but the module itself doesn't, so the imports
are just hanging out and not getting used for anything. I went ahead and
fixed that as well.
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
## Motivation
Currently, it is not actually possible to use `set_default(NoSubscriber)`
or similar to temporarily disable the global default subscriber (see
#1999).
This is because `NoSubscriber` is currently used as a placeholder value
when the thread-local cell that stores the current scoped default
subscriber is initialized. Therefore, we currently check if the current
scoped subscriber is `NoSubscriber`, and if it is, we fall back to
returning the global default instead.
This was fine, _when `NoSubscriber` was a private internal type only_.
However, PR #1549 makes `NoSubscriber` into a public API type. When users
can publicly construct `NoSubscriber` instances, it makes sense to want
to be able to use `NoSubscriber` to disable the current subscriber. This
is not possible when there is a global default set, because the local
default being `NoSubscriber` will cause the global default to be
returned.
## Solution
This branch changes the thread-local cell to store an `Option<Dispatch>`
instead, and use the `None` case to indicate no local default is set.
This way, when the local default is explicitly set to `NoSubscriber`, we
will return `NoSubscriber` rather than falling back.
This may also be a slight performance improvement, because we now check
if there's no global default by checking if the `Option` is `None`,
rather than downcasting it to a `NoSubscriber`.
I've also added a test reproducing #1999.
Fixes#1999
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This branch removes some unnecessary uses of the `format_args!`
and `write!` macros in `tracing-core`. Using `fmt::Display::fmt` and
similar rather than the macros may be slightly more efficient.
Co-authored-by: David Barsky <me@davidbarsky.com>
## Motivation
Disabled spans introduce a non-trivial amount of overhead, even when no
`tracing` subscriber is in use. This is primarily due to the need to
create and then drop the empty `Span` struct even when the span is
disabled.
While thinking about #1970 a bit, I noticed that one source of overhead
is that dropping a disabled span always causes a function call, even
when the span is empty. This could be avoided.
## Solution
In this branch, I've changed the `Drop` impls for `Span`, `Entered`, and
`EnteredSpan` to be `#[inline(always)]`. In the always-inlined
functions, we perform a check for whether or not the span is empty, and
if it is not empty, we call into the dispatcher method to drop the span
or guard. The dispatcher methods are no longer inlined. Now, the
function call only occurs when the span _is_ enabled, rather than always
occurring in the `Drop` call. This significantly reduces the overhead
for holding a disabled span, or a disabled `Entered` guard, in a scope.
Also, the `log` integration when dropping a span would always check if
the span had metadata, even when `log` is disabled. This means we would
do an extra branch that wasn't necessary. I moved that into the macro
that guards for whether or not the `log` crate is enabled, which also
significantly reduces overhead.
This change reduces the overhead of a disabled span by 50-70%, per the
`no_subscriber.rs` benchmarks. I also improved those benchmarks a
bit to test more cases, in order to find the precise difference in overhead
between just constructing `Span::none()` and the actual `span!` macros.
<details>
<summary><code>no_subscriber.rs</code> benchmark results</summary>
```
no_subscriber/span time: [696.37 ps 696.53 ps 696.73 ps]
change: [-50.599% -50.577% -50.544%] (p = 0.00 < 0.05)
Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
4 (4.00%) high mild
4 (4.00%) high severe
no_subscriber/span_enter
time: [465.58 ps 466.35 ps 467.61 ps]
change: [-71.350% -71.244% -71.138%] (p = 0.00 < 0.05)
Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
4 (4.00%) high mild
2 (2.00%) high severe
no_subscriber/empty_span
time: [226.15 ps 226.73 ps 227.36 ps]
change: [-84.404% -84.045% -83.663%] (p = 0.00 < 0.05)
Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
5 (5.00%) high mild
1 (1.00%) high severe
no_subscriber/empty_struct
time: [693.32 ps 693.76 ps 694.30 ps]
change: [+1.7164% +1.9701% +2.2540%] (p = 0.00 < 0.05)
Performance has regressed.
Found 8 outliers among 100 measurements (8.00%)
5 (5.00%) high mild
3 (3.00%) high severe
no_subscriber/event time: [294.32 ps 301.68 ps 310.85 ps]
change: [+0.3073% +2.1111% +4.1919%] (p = 0.03 < 0.05)
Change within noise threshold.
Found 16 outliers among 100 measurements (16.00%)
2 (2.00%) high mild
14 (14.00%) high severe
no_subscriber/relaxed_load
time: [463.24 ps 463.74 ps 464.33 ps]
change: [+1.4046% +1.6735% +1.9366%] (p = 0.00 < 0.05)
Performance has regressed.
Found 16 outliers among 100 measurements (16.00%)
1 (1.00%) low severe
6 (6.00%) high mild
9 (9.00%) high severe
no_subscriber/acquire_load
time: [465.28 ps 465.68 ps 466.08 ps]
change: [+0.6837% +1.1755% +1.6034%] (p = 0.00 < 0.05)
Change within noise threshold.
Found 8 outliers among 100 measurements (8.00%)
4 (4.00%) high mild
4 (4.00%) high severe
no_subscriber/log time: [231.11 ps 231.27 ps 231.45 ps]
change: [-4.4700% -2.3810% -0.9164%] (p = 0.00 < 0.05)
Change within noise threshold.
Found 17 outliers among 100 measurements (17.00%)
3 (3.00%) low mild
8 (8.00%) high mild
6 (6.00%) high severe
no_subscriber_field/span
time: [1.6334 ns 1.6343 ns 1.6354 ns]
change: [-12.401% -12.337% -12.279%] (p = 0.00 < 0.05)
Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
1 (1.00%) high mild
no_subscriber_field/event
time: [461.54 ps 461.84 ps 462.14 ps]
change: [-0.3654% +0.1235% +0.5557%] (p = 0.62 > 0.05)
No change in performance detected.
Found 6 outliers among 100 measurements (6.00%)
3 (3.00%) high mild
3 (3.00%) high severe
no_subscriber_field/log time: [463.52 ps 463.98 ps 464.49 ps]
change: [+0.3011% +0.8645% +1.6355%] (p = 0.01 < 0.05)
Change within noise threshold.
Found 18 outliers among 100 measurements (18.00%)
4 (4.00%) low mild
10 (10.00%) high mild
4 (4.00%) high severe
```
</details>
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This updates all crates' MSRVs to 1.49 if they were not already greater
than that (`tracing-appender` is at 1.53). Rust 1.49+ is required to
update `parking_lot` to v0.12 (see #1878). Also, `futures-task` (which I
believe is only needed as a transitive dep) now needs 1.45+, so this
also fixes our CI build.
Because `tracing-opentelemetry` previously required 1.46.0, it had a
separate CI MSRV job. Since 1.49.0 is greater than 1.46.0, the separate
check for `tracing-opentelemetry` is no longer needed.
In the process, I removed deprecated uses of
`core::atomic::spin_loop_hint`, which is replaced with
`core::hint::spin_loop` in 1.49.
This changes the `Kind` type to a bitflag, in order to represent
callsites that are hints but also count as spans or events. The goal
here is to allow variants of the `enabled!` macro that specifically
check if a span would be enabled, or if an event would be enabled.
See [this comment][1] for details.
This does not actually implement the `enabled!` variants, just the
`Kind` representation change. This way, we can add to the `enabled!`
macro in a subsequent `tracing` release without having to change
`tracing-core` again.
I went with the bitflag representation rather than adding a bool to the
`KindInner::Span` and `KindInner::Event` enum variants because it felt a
bit simpler and maybe more performant, although I don't think it's
particularly important to micro-optimize here. I'd consider changing
this to an enum-based representation if people think it's significantly
easier to understand.
[1]: https://github.com/tokio-rs/tracing/pull/1821#discussion_r784174046
## Motivation
Fixes: #196, #1739
Previously, the `field` token trees would be substituted into the log
macro invocation and the `ValueSet`, potentially meaning they are
executed more than once.
## Solution
This changes the `event!` macro (and all the upstream macros like `info!`),
so that it only uses each field once when the log feature is enabled. It
does this by generating the log using the `ValueSet` meaning the token
trees are only substituted once.
* ci: add `tracing_unstable` to CI
Currently, the `valuable` support requires the `tracing_unstable` cfg to
be set. Because none of our current CI jobs set this, we aren't
currently testing that code, and have no way of even ensuring that it
compiles. This is Bad.
This PR adds a CI job to run tests with the unstable cfg enabled.
* core: fix wrong `self` types with `valuable`
This should fix the build.
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Depends on #1881
Right now, the `valuable` stuff isn't very discoverable --- enabling the
feature just adds some trait impls and stuff that aren't particularly
visible in the documentation. This PR adds some top-level docs on using
`valuable`. In particular:
- Added a section to the `tracing` and `tracing-core` lib.rs docs
explaining the unstable features versioning policy and how to turn on
unstable features
- Added a section in the `field` module that explains how to use
`valuable` to record fields.
- It turns out the `tracing::field` module didn't really have docs,
since it doesn't re-export the `tracing_core::field` module but
re-exports its _types_ in a new module (because it adds a trait). It
had a single line of docs that just said something about "structured
key-value data". I fixed this by coping the docs from `tracing-core`.
:/
- Enabled unstable features in the documentation on docs.rs and netlify.
In #1881, I accidentally had the `Visit` trait take a `&Value<'_>`. This
was probably wrong, since `Value` is `Copy` and should only be a couple
words, one of which might be a pointer...so it's probably better to just
pass the `pointer + enum descriminant` (or `integer + enum descriminant`)
instead of passing `pointer to (pointer + enum descriminant)`.
This probably also makes the API a little nicer.
This changes the `valuable` integration so that `record_value` takes
instances of `valuable::Value<'_>`, rather than `&dyn
valuable::Valuable` trait objects. The primary advantage of this is that
a `Value` can be produced by calling `Valuable::as_value`, so it allows
users to write code like this:
```rust
#[derive(Valuable, Debug)]
struct Foo {
// ...
}
tracing::trace!(foo = foo.as_value());
```
rather than this:
```rust
#[derive(Valuable, Debug)]
struct Foo {
// ...
}
tracing::trace!(foo = tracing::field::valuable(&foo));
```
which feels a bit more ergonomic. It also simplifies the code in
`tracing-core`, since we no longer need our own `ValuableValue` wrapper
type to turn things into trait objects.
It might also reduce boilerplate a bit on the visitor side, as
`as_value()` doesn't have to be called on the trait object, although
that's probably not as big a deal.
I didn't remove the `field::valuable` function, as I thought it was nice
to have for consistency with the existing `field::debug` and
`field::display` functions.
## Performance Considerations
@carllerche pointed out that a `Value<'_>` might be slightly more bytes
to pass on the stack than a trait object (always two words). I believe
this is only the case when the `Value` is a `Listable`, `Enumerable`,
`Structable`, `Mappable`, or `Tupleable`, where the `Value` would be an
enum descriminant _and_ a wide pointer to a trait object. However, in
the cases where the value is a primitive, `Value` will be two words if
the primitive is word-sized (e.g. `u64` on 64-bit platforms), for the
enum descriminant + the value, or one word if the primitive is smaller
than word size (`bool`, `char`, etc). Also, for primitive `Value`s,
there's no pointer dereference, which the trait object always requires.
I'm not sure how the enum dispatch compares to vtable dispatch when
calling `visit` on the value. However, if the `tracing` visitor is going
to call `as_value()` on the recorded value, this approach is better,
because calling `as_value()` in the macro _prior_ to recording the
span/event will use the statically dispatched `as_value()` impl on a
known type, rather than the the dynamically dispatched `as_value()` impl
on the trait object. Since `as_value` impls are generally quite trivial,
I'd guess they usually (always?) will get inlined, which is never
possible with the dynamically dispatched call after passing a trait
object into `tracing`.
In practice I'm not sure if there's a huge perf diff either way, but it
was interesting to think through the implications.
Backports #1821 to `v0.1.x`.
## Motivation
Closes: #1668
My usecase is different than the referenced "avoid doing something
expensive to log": I want to guard turning on `debug` mode for an ffi'd
library, based on some `target` that represents the "module" we care
about.
## Solution
The macro is very similar to `event!`, but adds a few simplistic cases,
and generates ever so slightly different code (to return the correct
value always. It also skips anything to do with `tracing-log`. I
considered (and tried), to share the impl between `event!` and
`enabled!`, but must confess I am not good at macros and got stuck. I
think they are sufficiently different, where copied impls, is easier to
read. We already manage
Co-authored-by: Eliza Weisman <eliza@buoyant.io>
Co-authored-by: David Barsky <me@davidbarsky.com>
This branch adds initial support for using the [`valuable`] crate as an
opt-in `Value` type in `tracing`. `valuable` provides a mechanism for
defining custom ways to record user-implemented types, as well as
structured recording of standard library data structures such as maps,
arrays, and sets.
For more details, see the tracking issue #1570.
In `tracing` v0.2, the intent is for `valuable` to replace the existing
`tracing_core::field::Value` trait. However, in v0.1.x, `valuable`
support must be added in a backwards-compatible way, so recording types
that implement `valuable::Valueable` currently requires opting in using
a `field::valuable` wrapper function.
Since this is the first release of `valuable` and the API is still
stabilizing, we don't want to tie `tracing-core`'s stability to
`valuable`'s. Therefore, the valuable dependency is feature-flagged
*and* also requires `RUSTFLAGS="--cfg tracing_unstable"`.
[`valuable`]: https://github.com/tokio-rs/valuable
Co-authored-by: Daniel McKenna <daniel@emotech.co>
Co-authored-by: David Barsky <me@davidbarsky.com>
Co-authored-by: Eliza Weisman <eliza@buoyant.io>
Depends on #1737
This branch removes all remaining `extern crate` statements. Most of
these are in old code and were not removed when updating to Rust 2018.
Whoops!
Our MSRV no longer requires `extern crate`, so we don't need these. The
exception is `extern crate` statements for `std` and `alloc`, which are
still the way these libraries are included explicitly when building for
`no_std` platforms.
In some cases, the tests had to explicitly import the `span!` and
`event!` macros at every use, because their names conflict with the
`span` and `event` modules in the test support code. Oh well.
Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Some of the examples still include `#[macro_use] extern crate`
statements for importing macros from `tracing` or `tracing-core`. On a
recent nightly, this results in import conflicts with the implicit
import of the documented crate in doctests:
https://github.com/tokio-rs/tracing/runs/4279736243?check_suite_focus=true
This commit removes all the `extern crate` statements from doctests. Our
MSRV is new enough that `extern crate` is not required on any of the Rust
versions we support.
Signed-off-by: Eliza Weisman <eliza@buoyant.io>