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 Layer
s puts it clearly:
The
Subscriber
trait intracing-core
represents the complete set of functionality required to consumetracing
instrumentation. This means that a singleSubscriber
instance is a self-contained implementation of a complete strategy for collecting traces; but it also means that theSubscriber
trait cannot easily be composed with otherSubscriber
s.In particular,
Subscriber
s 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 singleSubscriber
for 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
Subscriber
trait’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. TheLayer
trait represents this composable subset of theSubscriber
behavior; it can observe events and spans, but does not assign IDs.
So if at all possible, try to generalize your logging configuration over Layer
s instead of Subscriber
s. 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 Layer
s instead of Subscriber
s. Even some of those listed as "implements a subscriber" use layers (like tracing-gelf or tracing-forest).