Skip to content

Commit

Permalink
core: change metadata::Kind to a bitflag (#1891)
Browse files Browse the repository at this point in the history
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]: #1821 (comment)
  • Loading branch information
hawkw committed Feb 3, 2022
1 parent 8147827 commit f1d8f41
Showing 1 changed file with 56 additions and 15 deletions.
71 changes: 56 additions & 15 deletions tracing-core/src/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,9 @@ pub struct Metadata<'a> {
}

/// Indicates whether the callsite is a span or event.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Kind(KindInner);
#[derive(Clone, Eq, PartialEq)]
pub struct Kind(u8);

/// Describes the level of verbosity of a span or event.
///
/// # Comparing Levels
Expand Down Expand Up @@ -367,38 +368,78 @@ impl<'a> fmt::Debug for Metadata<'a> {
}
}

#[derive(Clone, Debug, Eq, PartialEq)]
enum KindInner {
Event,
Span,
Hint,
}

impl Kind {
const EVENT_BIT: u8 = 1 << 0;
const SPAN_BIT: u8 = 1 << 1;
const HINT_BIT: u8 = 1 << 2;

/// `Event` callsite
pub const EVENT: Kind = Kind(KindInner::Event);
pub const EVENT: Kind = Kind(Self::EVENT_BIT);

/// `Span` callsite
pub const SPAN: Kind = Kind(KindInner::Span);
pub const SPAN: Kind = Kind(Self::SPAN_BIT);

/// `enabled!` callsite. [`Subscriber`][`crate::subscriber::Subscriber`]s can assume
/// this `Kind` means they will never recieve a
/// full event with this [`Metadata`].
pub const HINT: Kind = Kind(KindInner::Hint);
pub const HINT: Kind = Kind(Self::HINT_BIT);

/// Return true if the callsite kind is `Span`
pub fn is_span(&self) -> bool {
matches!(self, Kind(KindInner::Span))
self.0 & Self::SPAN_BIT == Self::SPAN_BIT
}

/// Return true if the callsite kind is `Event`
pub fn is_event(&self) -> bool {
matches!(self, Kind(KindInner::Event))
self.0 & Self::EVENT_BIT == Self::EVENT_BIT
}

/// Return true if the callsite kind is `Hint`
pub fn is_hint(&self) -> bool {
matches!(self, Kind(KindInner::Hint))
self.0 & Self::HINT_BIT == Self::HINT_BIT
}

/// Sets that this `Kind` is a [hint](Self::HINT).
///
/// This can be called on [`SPAN`](Self::SPAN) and [`EVENT`](Self::EVENT)
/// kinds to construct a hint callsite that also counts as a span or event.
pub const fn hint(self) -> Self {
Self(self.0 | Self::HINT_BIT)
}
}

impl fmt::Debug for Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Kind(")?;
let mut has_bits = false;
let mut write_bit = |name: &str| {
if has_bits {
f.write_str(" | ")?;
}
f.write_str(name)?;
has_bits = true;
Ok(())
};

if self.is_event() {
write_bit("EVENT")?;
}

if self.is_span() {
write_bit("SPAN")?;
}

if self.is_hint() {
write_bit("HINT")?;
}

// if none of the expected bits were set, something is messed up, so
// just print the bits for debugging purposes
if !has_bits {
write!(f, "{:#b}", self.0)?;
}

f.write_str(")")
}
}

Expand Down

0 comments on commit f1d8f41

Please sign in to comment.