tracing/tracing-core
Hayden Stainsby 8a25a16873
core: fix missed register_callsite error (#2938)
There are 2 triggers which will cause a subscriber to receive a call to
`Subscriber::register_callsite` for a specific callsite.
1. The first time the event or span at that callsite is executed.
2. When a new subscriber is added or removed (for example, calls to
   `set_default` or `with_default`)

It is trigger (2) that will cause a new subscriber to receive
`Subscriber::register_callsite` for all the callsites which had already
been registered before it became active.

When a callsite is registered for trigger (1), the callsite starts in
state `UNREGISTERED`.

The first thread to encounter the callsite will transition it to
`REGISTERING` and determine the overall interest for the callsite by
registering with all known dispatchers (which will call into
`Subscriber::register_callsite`).

Once that is complete, the callsite is added to the list of all known
callsites and its state is transitioned to `REGISTERED`.

is (re)built for all known dispatchers. The callsite starts in state
`UNREGISTERED`. The  This calls down into
`Subscriber::register_callsite` for each subscriber. Once that is
complete, the callsite is added to the global list of known callsites.

While the callsite interest is being rebuilt, other threads that
encounter the callsite will be given `Interest::sometimes()` until the
registration is complete. However, if a new subscriber is added during
this window, all the interest for all callsites will be rebuilt, but
because the new callsite (in state `REGISTERING`) won't be included
because it isn't yet in the global list of callsites.

This can cause a case where that new subscriber being added won't
receive `Subscriber::register_callsite` before it receives the subsequent
call to `Subscriber::event` or `Subscriber::new_span`.

The documentation on [Registering Callsites] is not very explicit on
this point, but it does suggest that `Subscriber::register_callsite`
will be called before the call to either `Subscriber::event` or
`Subscriber::new_span`, and the current behavior can break this implicit
contract.

[Registering Callsites]: https://docs.rs/tracing-core/0.1.32/tracing_core/callsite/index.html#registering-callsites

This change swaps the order of rebuilding the callsite interest and
adding the callsite to the global list so that the callsite gets pushed
first, avoiding this window in which a subscriber won't get a call to
`register_callsite`.

As such, a callsite may have its interest read before it is set. In this
case, the existing implementation will return `Interest::sometimes()`
for the `DefaultCallsite` implementation. Other implementations (outside
of the `tracing` project) may perform this differently, but in this
case, there is no documented guarantee regarding the ordering.

A regression test is included which provokes the race condition 100% of
the time before the changes in this fix.

Fixes: #2743

Co-authored-by: David Barsky <me@davidbarsky.com>
2024-11-25 12:10:36 +01:00
..
2024-09-24 16:27:33 -04:00
2023-11-07 13:37:19 -08:00

Tracing — Structured, application-level diagnostics

tracing-core

Core primitives for application-level tracing.

Crates.io Documentation Documentation (master) MIT licensed Build Status Discord chat

Documentation | Chat

Overview

tracing is a framework for instrumenting Rust programs to collect structured, event-based diagnostic information. This crate defines the core primitives of tracing.

The crate provides:

  • span::Id identifies a span within the execution of a program.

  • Event represents a single event within a trace.

  • Subscriber, the trait implemented to collect trace data.

  • Metadata and Callsite provide information describing spans and events.

  • Field, FieldSet, Value, and ValueSet represent the structured data attached to spans and events.

  • Dispatch allows spans and events to be dispatched to Subscribers.

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.63+

Usage

Application authors will typically not use this crate directly. Instead, they will use the tracing crate, which provides a much more fully-featured API. However, this crate's API will change very infrequently, so it may be used when dependencies must be very stable.

Subscriber implementations may depend on tracing-core rather than tracing, as the additional APIs provided by tracing are primarily useful for instrumenting libraries and applications, and are generally not necessary for Subscriber implementations.

Crate Feature Flags

The following crate feature flags are available:

  • std: Depend on the Rust standard library (enabled by default).

    no_std users may disable this feature with default-features = false:

    [dependencies]
    tracing-core = { version = "0.1.31", default-features = false }
    

    Note:tracing-core's no_std support requires liballoc.

Supported Rust Versions

Tracing is built against the latest stable release. The minimum supported version is 1.63. The current Tracing version is not guaranteed to build on Rust versions earlier than the minimum supported version.

Tracing follows the same compiler support policies as the rest of the Tokio project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable compiler version is 1.69, the minimum supported version will not be increased past 1.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.

License

This project is licensed under the MIT license.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Tokio by you, shall be licensed as MIT, without any additional terms or conditions.