mirror of
https://github.com/tokio-rs/tokio.git
synced 2025-09-28 12:10:37 +00:00
trace-core: Add overrideable downcasting to Subscriber
s (#974)
## Motivation In order to implement "out of band" `Subscriber` APIs in third-party subscriber implementations (see [this comment]) users may want to downcast the current `Dispatch` to a concrete subscriber type. For example, in a library for integrating `tokio-trace` with a fancy new (hypothetical) distributed tracing technology "ElizaTracing", which uses 256-bit span IDs, we might expect to see a function like this: ```rust pub fn correlate(tt: tokio_trace::span::Id, et: elizatracing::SpanId) { tokio_trace::dispatcher::with(|c| { if let Some(s) = c.downcast_ref::<elizatracing::Subscriber>() { s.do_elizatracing_correlation_magic(tt, et); } }); } ``` This allows users to correlate `tokio-trace` IDs with IDs in the distributed tracing system without having to pass a special handle to the subscriber through application code (as one is already present in thread-local storage, but with its type erased). ## Solution This branch makes the following changes: * Add an object-safe `downcast_raw` method to the `Subscriber` trait, taking a `TypeId` and returning an `*const ()` if the type ID matches the subscriber's type ID, or `None` if it does not, and * Add `is<T>` and `downcast_ref<T>` functions to `Subscriber` and `Dispatch`, using `downcast_raw`. Unlike the approach implemented in #950, the `downcast_raw` method is object-safe, since it takes a `TypeId` rather than a type _parameter_ and returns a void pointer rather than an `&T`. This means that `Subscriber` implementations can override this method if necessary. For example, a `Subscriber` that fans out to multiple component subscribers can downcast to their component parts, and "chained" or "middleware" subscribers, which wrap an inner `Subscriber` and modify its behaviour somehow, can downcast to the inner type if they choose to. [this comment]: https://github.com/tokio-rs/tokio/issues/932#issuecomment-469473501 [`std::error::Error`'s]: https://doc.rust-lang.org/1.33.0/src/std/error.rs.html#204 Refs: #950, #953, https://github.com/tokio-rs/tokio/issues/948#issuecomment-469444293 Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This commit is contained in:
parent
30330da11a
commit
9c5cad037f
@ -6,6 +6,7 @@ use {
|
||||
};
|
||||
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::RefCell,
|
||||
fmt,
|
||||
sync::{Arc, Weak},
|
||||
@ -226,6 +227,20 @@ impl Dispatch {
|
||||
pub fn drop_span(&self, id: span::Id) {
|
||||
self.subscriber.drop_span(id)
|
||||
}
|
||||
|
||||
/// Returns `true` if this `Dispatch` forwards to a `Subscriber` of type
|
||||
/// `T`.
|
||||
#[inline]
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
Subscriber::is::<T>(&*self.subscriber)
|
||||
}
|
||||
|
||||
/// Returns some reference to the `Subscriber` this `Dispatch` forwards to
|
||||
/// if it is of type `T`, or `None` if it isn't.
|
||||
#[inline]
|
||||
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
|
||||
Subscriber::downcast_ref(&*self.subscriber)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Dispatch {
|
||||
@ -275,3 +290,20 @@ impl Registrar {
|
||||
self.0.upgrade().map(|s| s.register_callsite(metadata))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn dispatch_is() {
|
||||
let dispatcher = Dispatch::new(NoSubscriber);
|
||||
assert!(dispatcher.is::<NoSubscriber>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dispatch_downcasts() {
|
||||
let dispatcher = Dispatch::new(NoSubscriber);
|
||||
assert!(dispatcher.downcast_ref::<NoSubscriber>().is_some());
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
//! Subscribers collect and record trace data.
|
||||
use {span, Event, Metadata};
|
||||
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
ptr,
|
||||
};
|
||||
|
||||
/// Trait representing the functions required to collect trace data.
|
||||
///
|
||||
/// Crates that provide implementations of methods for collecting or recording
|
||||
@ -262,6 +267,61 @@ pub trait Subscriber: 'static {
|
||||
fn drop_span(&self, id: span::Id) {
|
||||
let _ = id;
|
||||
}
|
||||
|
||||
// === Downcasting methods ================================================
|
||||
|
||||
/// If `self` is the same type as the provided `TypeId`, returns an untyped
|
||||
/// `*const` pointer to that type. Otherwise, returns `None`.
|
||||
///
|
||||
/// If you wish to downcast a `Subscriber`, it is strongly advised to use
|
||||
/// the safe API provided by [`downcast_ref`] instead.
|
||||
///
|
||||
/// This API is required for `downcast_raw` to be a trait method; a method
|
||||
/// signature like [`downcast_ref`] (with a generic type parameter) is not
|
||||
/// object-safe, and thus cannot be a trait method for `Subscriber`. This
|
||||
/// means that if we only exposed `downcast_ref`, `Subscriber`
|
||||
/// implementations could not override the downcasting behavior
|
||||
///
|
||||
/// This method may be overridden by "fan out" or "chained" subscriber
|
||||
/// implementations which consist of multiple composed types. Such
|
||||
/// subscribers might allow `downcast_raw` by returning references to those
|
||||
/// component if they contain components with the given `TypeId`.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The [`downcast_ref`] method expects that the pointer returned by
|
||||
/// `downcast_raw` is non-null and points to a valid instance of the type
|
||||
/// with the provided `TypeId`. Failure to ensure this will result in
|
||||
/// undefined behaviour, so implementing `downcast_raw` is unsafe.
|
||||
///
|
||||
/// [`downcast_ref`]: #method.downcast_ref
|
||||
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
|
||||
if id == TypeId::of::<Self>() {
|
||||
Some(self as *const Self as *const ())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Subscriber {
|
||||
/// Returns `true` if this `Subscriber` is the same type as `T`.
|
||||
pub fn is<T: Any>(&self) -> bool {
|
||||
self.downcast_ref::<T>().is_some()
|
||||
}
|
||||
|
||||
/// Returns some reference to this `Subscriber` value if it is of type `T`,
|
||||
/// or `None` if it isn't.
|
||||
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
|
||||
unsafe {
|
||||
let raw = self.downcast_raw(TypeId::of::<T>())?;
|
||||
if raw == ptr::null() {
|
||||
None
|
||||
} else {
|
||||
Some(&*(raw as *const _))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates a [`Subscriber`]'s interest in a particular callsite.
|
||||
|
Loading…
x
Reference in New Issue
Block a user