Let's say I have a function that returns a Vec<Box<dyn Subscriber>> and I want to combine them into a single one to be set as the default subscriber.
How to do this?
I'm trying to do something like this, but I cannot make the types match:
pub fn init_log(subscribers: Vec<Box<dyn Subscriber>>) -> Result<(), Error> {
use tracing_subscriber::prelude::*;
let mut layers: Vec<Box<dyn Layer<dyn Subscriber>>> = Vec::new();
for subscriber in subscribers.iter().drain(..) {
let layer: Box<dyn Layer<dyn Subscriber>> =
Box::new(tracing_subscriber::layer::Identity::new().with_subscriber(subscriber));
layers.push(layer);
}
let init_layer: Box<dyn Layer<dyn Subscriber>> =
Box::new(tracing_subscriber::layer::Identity::new());
let acc_subscriber: Layered<Box<dyn Layer<dyn Subscriber>>, _> =
tracing_subscriber::fmt().finish().with(init_layer);
let composed = layers
.drain(..)
.into_iter()
.fold(acc_subscriber, |acc, layer| acc.with(layer));
tracing::subscriber::set_global_default(composed);
Ok(())
}
error[E0277]: the trait bound `Box<dyn tracing::Subscriber>: tracing::Subscriber` is not satisfied
--> jormungandr\src\settings\logging.rs:97:85
|
97 | Box::new(tracing_subscriber::layer::Identity::new().with_subscriber(subscriber));
| ^^^^^^^^^^ the trait `tracing::Subscriber` is not implemented for `Box<dyn tracing::Subscriber>`
I think this is fundamentally at odds with what a Subscriber is tasked with handling. I think the module documentation for Layers puts it clearly:
The
Subscribertrait intracing-corerepresents the complete set of functionality required to consumetracinginstrumentation. This means that a singleSubscriberinstance is a self-contained implementation of a complete strategy for collecting traces; but it also means that theSubscribertrait cannot easily be composed with otherSubscribers.In particular,
Subscribers are responsible for generating span IDs and assigning them to spans. Since these IDs must uniquely identify a span within the context of the current trace, this means that there may only be a singleSubscriberfor a given thread at any point in time — otherwise, there would be no authoritative source of span IDs.On the other hand, the majority of the
Subscribertrait’s functionality is composable: any number of subscribers may observe events, span entry and exit, and so on, provided that there is a single authoritative source of span IDs. TheLayertrait represents this composable subset of theSubscriberbehavior; it can observe events and spans, but does not assign IDs.
So if at all possible, try to generalize your logging configuration over Layers instead of Subscribers. There is even a impl Layer for Vec<L> where L: Layer implementation already.
I think this expressibility and composiblity is the reason why most Related Crates are implemented as Layers instead of Subscribers. Even some of those listed as "implements a subscriber" use layers (like tracing-gelf or tracing-forest).