core: consider changing Subscriber::new_span
to return Option<Id>
#924
Labels
crate/core
Related to the `tracing-core` crate
meta/breaking
This is a breaking change, and should wait until the next breaking release.
needs/design
Additional design discussion is required.
Milestone
Feature Request
Crates
tracing-core
Motivation
Currently, spans and events may be disabled by a subscriber by returning
Interest::never
fromSubscriber::register_callsite
, or by returningfalse
fromSubscriber::enabled
. However, in some cases, aSubscriber
may also wish to disable a span or event based on the values of its fields.For performance reasons, the values of span/event fields are not available in
enabled
(orregister_callsite
, of course) — these methods are called before constructing theValueSet
for an instance of a span/event, since their purpose is to allow skippingValueSet
construction for unwanted spans or events. This means that if aSubscriber
decides it doesn't want something based on the values of its fields, it has to choose to ignore that span or event in itsnew_span
andevent
methods.For events, this is fine — the
Subscriber::event
method doesn't return anything, so if a subscriber decides it doesn't want an event inside theevent
method, it can just...not record it, and return immediately. On the other hand, this is not possible for spans.Subscriber::new_span
is required to return a span ID for the new span, and once it returns anId
, the span will be created with thatId
and a reference to the subscriber (anArc
clone of itsDispatch
).If the goal is just to avoid printing/recording data related to that span, the subscriber could add its
Id
to a list of disabled spans, and ignore it in all subsequent calls toenter
,exit
,record
,follows_from
,clone_span
, andtry_close
. This is not a great option for performance reasons, though: once the span is created, it will store aDispatch
clone, so cloning it will bump theDispatch
's ref count; callingenter
and other methods on theSpan
will dereference the dispatcher and call methods on it that would not be called if the span was disabled, and so on.Furthermore, the
Subscriber
now needs some way of tracking which IDs are disabled. A naive solution, which I imagine would be the obvious one to most people, is to store some kind of set of disabled spans in the subscriber. BecauseSubscriber
s may be shared by multiple threads, this might mean aRwLock<HashSet<Id>>
or something...and in order to ignore disabled spans correctly, every call toenter
,exit
,record
,record_follows_from
,clone_span
, andtry_close
would require checking if the provided ID is in that set. This has a massive performance implication, by forcing every span-notification method to contend a lock. This means that disabling a span might actually cost more than enabling it, if it is disabled innew_span
.Of course, there are better approaches: since the subscriber controls ID assignment, it could just flip a bit in the ID that indicates that the span is disabled, and test if that bit is set when IDs are passed to its other methods. But this is a much less obvious solution, so we are running the risk of users doing the obvious thing rather than the performant one. And, there is still the overhead of calling the subscriber methods in the first place, which could be avoided if the span was properly disabled.
Proposal
I propose changing
new_span
to returnOption<Id>
rather thanId
. If a subscriber doesn't like a span's fields that are passed tonew_span
, it can simply returnNone
, and the span will be disabled. It won't clone the subscriber'sDispatch
ref, and all subsequent calls toenter
andrecord
,clone
s and drops of clones, etc, will all be no-ops that don't access the subscriber.Since
Id
is represented by aNonZeroU64
, it will always benefit from niche optimization when stored in anOption
, so this is essentially the same as reserving the least-significant bit as a bit flag as in the approach I discussed above. However, the differences are:Id
, allowing for slightly more ID values to exist concurrently (this doesn't matter all that much, sinceu64::MAX >> 2
is still a huge number...).Dispatch
for disabled spans (which checking a bit flag in the subscriber does not).We would want to ensure that the performance difference between returning
None
fromnew_span
and returningfalse
fromenabled
orInterest::never
fromregister_callsite
is documented, so thatSubscriber
implementations know to only filter innew_span
when data from a span's fields is necessary to perform filtering.Of course, changing the method signature is inherently a breaking change, so if we make this change, it would be part of
tracing-core
0.2.Alternatives
We could, uh, not do this, and leave it up to the user to implement one of the less efficient approaches I mentioned above.
The text was updated successfully, but these errors were encountered: