From eac07a5e87e7b6029486e3c363ae2749bd133dd7 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 3 Feb 2022 10:47:04 -0800 Subject: [PATCH] core: change `metadata::Kind` to a bitflag (#1891) 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 --- tracing-core/src/metadata.rs | 71 ++++++++++++++++++++++++++++-------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/tracing-core/src/metadata.rs b/tracing-core/src/metadata.rs index 2bc15a9820..b6ecaf583e 100644 --- a/tracing-core/src/metadata.rs +++ b/tracing-core/src/metadata.rs @@ -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 @@ -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(")") } }