|
215 | 215 | //! ```
|
216 | 216 | //!
|
217 | 217 | //! If a [`Layer`] may be one of several different types, note that [`Box<dyn
|
218 |
| -//! Layer<C> + Send + Sync>` implements `Layer`][box-impl]. |
| 218 | +//! Layer<S> + Send + Sync>` implements `Layer`][box-impl]. |
219 | 219 | //! This may be used to erase the type of a [`Layer`].
|
220 | 220 | //!
|
221 | 221 | //! For example, a function that configures a [`Layer`] to log to one of
|
222 |
| -//! several outputs might return a `Box<dyn Layer<C> + Send + Sync + 'static>`: |
| 222 | +//! several outputs might return a `Box<dyn Layer<S> + Send + Sync + 'static>`: |
223 | 223 | //! ```
|
224 | 224 | //! use tracing_subscriber::{
|
225 | 225 | //! Layer,
|
|
267 | 267 | //! The [`Layer::boxed`] method is provided to make boxing a `Layer`
|
268 | 268 | //! more convenient, but [`Box::new`] may be used as well.
|
269 | 269 | //!
|
| 270 | +//! When the number of `Layer`s varies at runtime, note that a |
| 271 | +//! [`Vec<L> where L: `Layer`` also implements `Layer`][vec-impl]. This |
| 272 | +//! can be used to add a variable number of `Layer`s to a `Subscriber`: |
| 273 | +//! |
| 274 | +//! ``` |
| 275 | +//! use tracing_subscriber::{Layer, prelude::*}; |
| 276 | +//! struct MyLayer { |
| 277 | +//! // ... |
| 278 | +//! } |
| 279 | +//! # impl MyLayer { fn new() -> Self { Self {} }} |
| 280 | +//! |
| 281 | +//! impl<S: tracing_core::Subscriber> Layer<S> for MyLayer { |
| 282 | +//! // ... |
| 283 | +//! } |
| 284 | +//! |
| 285 | +//! /// Returns how many layers we need |
| 286 | +//! fn how_many_layers() -> usize { |
| 287 | +//! // ... |
| 288 | +//! # 3 |
| 289 | +//! } |
| 290 | +//! |
| 291 | +//! // Create a variable-length `Vec` of layers |
| 292 | +//! let mut layers = Vec::new(); |
| 293 | +//! for _ in 0..how_many_layers() { |
| 294 | +//! layers.push(MyLayer::new()); |
| 295 | +//! } |
| 296 | +//! |
| 297 | +//! tracing_subscriber::registry() |
| 298 | +//! .with(layers) |
| 299 | +//! .init(); |
| 300 | +//! ``` |
| 301 | +//! |
| 302 | +//! If a variable number of `Layer` is needed and those `Layer`s have |
| 303 | +//! different types, a `Vec` of [boxed `Layer` trait objects][box-impl] may |
| 304 | +//! be used. For example: |
| 305 | +//! |
| 306 | +//! ``` |
| 307 | +//! use tracing_subscriber::{filter::LevelFilter, Layer, prelude::*}; |
| 308 | +//! use std::fs::File; |
| 309 | +//! # fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 310 | +//! struct Config { |
| 311 | +//! enable_log_file: bool, |
| 312 | +//! enable_stdout: bool, |
| 313 | +//! enable_stderr: bool, |
| 314 | +//! // ... |
| 315 | +//! } |
| 316 | +//! # impl Config { |
| 317 | +//! # fn from_config_file()-> Result<Self, Box<dyn std::error::Error>> { |
| 318 | +//! # // don't enable the log file so that the example doesn't actually create it |
| 319 | +//! # Ok(Self { enable_log_file: false, enable_stdout: true, enable_stderr: true }) |
| 320 | +//! # } |
| 321 | +//! # } |
| 322 | +//! |
| 323 | +//! let cfg = Config::from_config_file()?; |
| 324 | +//! |
| 325 | +//! // Based on our dynamically loaded config file, create any number of layers: |
| 326 | +//! let mut layers = Vec::new(); |
| 327 | +//! |
| 328 | +//! if cfg.enable_log_file { |
| 329 | +//! let file = File::create("myapp.log")?; |
| 330 | +//! let layer = tracing_subscriber::fmt::layer() |
| 331 | +//! .with_thread_names(true) |
| 332 | +//! .with_target(true) |
| 333 | +//! .json() |
| 334 | +//! .with_writer(file) |
| 335 | +//! // Box the layer as a type-erased trait object, so that it can |
| 336 | +//! // be pushed to the `Vec`. |
| 337 | +//! .boxed(); |
| 338 | +//! layers.push(layer); |
| 339 | +//! } |
| 340 | +//! |
| 341 | +//! if cfg.enable_stdout { |
| 342 | +//! let layer = tracing_subscriber::fmt::layer() |
| 343 | +//! .pretty() |
| 344 | +//! .with_filter(LevelFilter::INFO) |
| 345 | +//! // Box the layer as a type-erased trait object, so that it can |
| 346 | +//! // be pushed to the `Vec`. |
| 347 | +//! .boxed(); |
| 348 | +//! layers.push(layer); |
| 349 | +//! } |
| 350 | +//! |
| 351 | +//! if cfg.enable_stdout { |
| 352 | +//! let layer = tracing_subscriber::fmt::layer() |
| 353 | +//! .with_target(false) |
| 354 | +//! .with_filter(LevelFilter::WARN) |
| 355 | +//! // Box the layer as a type-erased trait object, so that it can |
| 356 | +//! // be pushed to the `Vec`. |
| 357 | +//! .boxed(); |
| 358 | +//! layers.push(layer); |
| 359 | +//! } |
| 360 | +//! |
| 361 | +//! tracing_subscriber::registry() |
| 362 | +//! .with(layers) |
| 363 | +//! .init(); |
| 364 | +//!# Ok(()) } |
| 365 | +//! ``` |
| 366 | +//! |
| 367 | +//! Finally, if the number of layers _changes_ at runtime, a `Vec` of |
| 368 | +//! subscribers can be used alongside the [`reload`](crate::reload) module to |
| 369 | +//! add or remove subscribers dynamically at runtime. |
| 370 | +//! |
270 | 371 | //! [option-impl]: Layer#impl-Layer<S>-for-Option<L>
|
271 | 372 | //! [box-impl]: Layer#impl-Layer%3CS%3E-for-Box%3Cdyn%20Layer%3CS%3E%20+%20Send%20+%20Sync%3E
|
272 | 373 | //! [prelude]: crate::prelude
|
@@ -1060,6 +1161,8 @@ where
|
1060 | 1161 |
|
1061 | 1162 | feature! {
|
1062 | 1163 | #![all(feature = "registry", feature = "std")]
|
| 1164 | + use alloc::vec::Vec; |
| 1165 | + |
1063 | 1166 | /// A per-[`Layer`] filter that determines whether a span or event is enabled
|
1064 | 1167 | /// for an individual layer.
|
1065 | 1168 | ///
|
@@ -1491,6 +1594,120 @@ feature! {
|
1491 | 1594 | {
|
1492 | 1595 | layer_impl_body! {}
|
1493 | 1596 | }
|
| 1597 | + |
| 1598 | + |
| 1599 | + |
| 1600 | + impl<S, L> Layer<S> for Vec<L> |
| 1601 | + where |
| 1602 | + L: Layer<S>, |
| 1603 | + S: Subscriber, |
| 1604 | + { |
| 1605 | + |
| 1606 | + fn on_layer(&mut self, subscriber: &mut S) { |
| 1607 | + for l in self { |
| 1608 | + l.on_layer(subscriber); |
| 1609 | + } |
| 1610 | + } |
| 1611 | + |
| 1612 | + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { |
| 1613 | + // Return highest level of interest. |
| 1614 | + let mut interest = Interest::never(); |
| 1615 | + for l in self { |
| 1616 | + let new_interest = l.register_callsite(metadata); |
| 1617 | + if (interest.is_sometimes() && new_interest.is_always()) |
| 1618 | + || (interest.is_never() && !new_interest.is_never()) |
| 1619 | + { |
| 1620 | + interest = new_interest; |
| 1621 | + } |
| 1622 | + } |
| 1623 | + |
| 1624 | + interest |
| 1625 | + } |
| 1626 | + |
| 1627 | + fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { |
| 1628 | + self.iter().all(|l| l.enabled(metadata, ctx.clone())) |
| 1629 | + } |
| 1630 | + |
| 1631 | + fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { |
| 1632 | + for l in self { |
| 1633 | + l.on_new_span(attrs, id, ctx.clone()); |
| 1634 | + } |
| 1635 | + } |
| 1636 | + |
| 1637 | + fn max_level_hint(&self) -> Option<LevelFilter> { |
| 1638 | + let mut max_level = LevelFilter::ERROR; |
| 1639 | + for l in self { |
| 1640 | + // NOTE(eliza): this is slightly subtle: if *any* layer |
| 1641 | + // returns `None`, we have to return `None`, assuming there is |
| 1642 | + // no max level hint, since that particular layer cannot |
| 1643 | + // provide a hint. |
| 1644 | + let hint = l.max_level_hint()?; |
| 1645 | + max_level = core::cmp::max(hint, max_level); |
| 1646 | + } |
| 1647 | + Some(max_level) |
| 1648 | + } |
| 1649 | + |
| 1650 | + fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { |
| 1651 | + for l in self { |
| 1652 | + l.on_record(span, values, ctx.clone()) |
| 1653 | + } |
| 1654 | + } |
| 1655 | + |
| 1656 | + fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: Context<'_, S>) { |
| 1657 | + for l in self { |
| 1658 | + l.on_follows_from(span, follows, ctx.clone()); |
| 1659 | + } |
| 1660 | + } |
| 1661 | + |
| 1662 | + fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { |
| 1663 | + for l in self { |
| 1664 | + l.on_event(event, ctx.clone()); |
| 1665 | + } |
| 1666 | + } |
| 1667 | + |
| 1668 | + fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { |
| 1669 | + for l in self { |
| 1670 | + l.on_enter(id, ctx.clone()); |
| 1671 | + } |
| 1672 | + } |
| 1673 | + |
| 1674 | + fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { |
| 1675 | + for l in self { |
| 1676 | + l.on_exit(id, ctx.clone()); |
| 1677 | + } |
| 1678 | + } |
| 1679 | + |
| 1680 | + fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { |
| 1681 | + for l in self { |
| 1682 | + l.on_close(id.clone(), ctx.clone()); |
| 1683 | + } |
| 1684 | + } |
| 1685 | + |
| 1686 | + #[doc(hidden)] |
| 1687 | + unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { |
| 1688 | + // If downcasting to `Self`, return a pointer to `self`. |
| 1689 | + if id == TypeId::of::<Self>() { |
| 1690 | + return Some(self as *const _ as *const ()); |
| 1691 | + } |
| 1692 | + |
| 1693 | + // Someone is looking for per-layer filters. But, this `Vec` |
| 1694 | + // might contain layers with per-layer filters *and* |
| 1695 | + // layers without filters. It should only be treated as a |
| 1696 | + // per-layer-filtered layer if *all* its layers have |
| 1697 | + // per-layer filters. |
| 1698 | + // XXX(eliza): it's a bummer we have to do this linear search every |
| 1699 | + // time. It would be nice if this could be cached, but that would |
| 1700 | + // require replacing the `Vec` impl with an impl for a newtype... |
| 1701 | + if filter::is_plf_downcast_marker(id) && self.iter().any(|s| s.downcast_raw(id).is_none()) { |
| 1702 | + return None; |
| 1703 | + } |
| 1704 | + |
| 1705 | + // Otherwise, return the first child of `self` that downcaasts to |
| 1706 | + // the selected type, if any. |
| 1707 | + // XXX(eliza): hope this is reasonable lol |
| 1708 | + self.iter().find_map(|l| l.downcast_raw(id)) |
| 1709 | + } |
| 1710 | + } |
1494 | 1711 | }
|
1495 | 1712 |
|
1496 | 1713 | // === impl SubscriberExt ===
|
|
0 commit comments