|
19 | 19 | //! [`Subscriber`] behavior; it can _observe_ events and spans, but does not |
20 | 20 | //! assign IDs. |
21 | 21 | //! |
22 | | -//! ## Composing Layers |
| 22 | +//! # Composing Layers |
23 | 23 | //! |
24 | 24 | //! Since a [`Layer`] does not implement a complete strategy for collecting |
25 | 25 | //! traces, it must be composed with a `Subscriber` in order to be used. The |
|
135 | 135 | //! [`Layer::with_subscriber`] as an implementation detail, as `with_subscriber` |
136 | 136 | //! calls must be nested, leading to less clear code for the reader. |
137 | 137 | //! |
| 138 | +//! ## Runtime Configuration With `Layer`s |
| 139 | +//! |
| 140 | +//! In some cases, a particular [`Layer`] may be enabled or disabled based on |
| 141 | +//! runtime configuration. This can introduce challenges, because the type of a |
| 142 | +//! layered [`Subscriber`] depends on which layers are added to it: if an `if` |
| 143 | +//! or `match` expression adds some [`Layer`] implementation in one branch, |
| 144 | +//! and other layers in another, the [`Subscriber`] values returned by those |
| 145 | +//! branches will have different types. For example, the following _will not_ |
| 146 | +//! work: |
| 147 | +//! |
| 148 | +//! ```compile_fail |
| 149 | +//! # fn docs() -> Result<(), Box<dyn std::error::Error + 'static>> { |
| 150 | +//! # struct Config { |
| 151 | +//! # is_prod: bool, |
| 152 | +//! # path: &'static str, |
| 153 | +//! # } |
| 154 | +//! # let cfg = Config { is_prod: false, path: "debug.log" }; |
| 155 | +//! use std::fs::File; |
| 156 | +//! use tracing_subscriber::{Registry, prelude::*}; |
| 157 | +//! |
| 158 | +//! let stdout_log = tracing_subscriber::fmt::layer().pretty(); |
| 159 | +//! let subscriber = Registry::default().with(stdout_log); |
| 160 | +//! |
| 161 | +//! // The compile error will occur here because the if and else |
| 162 | +//! // branches have different (and therefore incompatible) types. |
| 163 | +//! let subscriber = if cfg.is_prod { |
| 164 | +//! let file = File::create(cfg.path)?; |
| 165 | +//! let layer = tracing_subscriber::fmt::layer() |
| 166 | +//! .json() |
| 167 | +//! .with_writer(Arc::new(file)); |
| 168 | +//! layer.with(subscriber) |
| 169 | +//! } else { |
| 170 | +//! layer |
| 171 | +//! }; |
| 172 | +//! |
| 173 | +//! tracing::subscriber::set_global_default(subscriber) |
| 174 | +//! .expect("Unable to set global subscriber"); |
| 175 | +//! # Ok(()) } |
| 176 | +//! ``` |
| 177 | +//! |
| 178 | +//! However, a [`Layer`] wrapped in an [`Option`] [also implements the `Layer` |
| 179 | +//! trait][option-impl]. This allows individual layers to be enabled or disabled at |
| 180 | +//! runtime while always producing a [`Subscriber`] of the same type. For |
| 181 | +//! example: |
| 182 | +//! |
| 183 | +//! ``` |
| 184 | +//! # fn docs() -> Result<(), Box<dyn std::error::Error + 'static>> { |
| 185 | +//! # struct Config { |
| 186 | +//! # is_prod: bool, |
| 187 | +//! # path: &'static str, |
| 188 | +//! # } |
| 189 | +//! # let cfg = Config { is_prod: false, path: "debug.log" }; |
| 190 | +//! use std::fs::File; |
| 191 | +//! use tracing_subscriber::{Registry, prelude::*}; |
| 192 | +//! |
| 193 | +//! let stdout_log = tracing_subscriber::fmt::layer().pretty(); |
| 194 | +//! let subscriber = Registry::default().with(stdout_log); |
| 195 | +//! |
| 196 | +//! // if `cfg.is_prod` is true, also log JSON-formatted logs to a file. |
| 197 | +//! let json_log = if cfg.is_prod { |
| 198 | +//! let file = File::create(cfg.path)?; |
| 199 | +//! let json_log = tracing_subscriber::fmt::layer() |
| 200 | +//! .json() |
| 201 | +//! .with_writer(file); |
| 202 | +//! Some(json_log) |
| 203 | +//! } else { |
| 204 | +//! None |
| 205 | +//! }; |
| 206 | +//! |
| 207 | +//! // If `cfg.is_prod` is false, then `json` will be `None`, and this layer |
| 208 | +//! // will do nothing. However, the subscriber will still have the same type |
| 209 | +//! // regardless of whether the `Option`'s value is `None` or `Some`. |
| 210 | +//! let subscriber = subscriber.with(json_log); |
| 211 | +//! |
| 212 | +//! tracing::subscriber::set_global_default(subscriber) |
| 213 | +//! .expect("Unable to set global subscriber"); |
| 214 | +//! # Ok(()) } |
| 215 | +//! ``` |
| 216 | +//! |
| 217 | +//! If a [`Layer`] may be one of several different types, note that [`Box<dyn |
| 218 | +//! Layer<C> + Send + Sync>` implements `Layer`][box-impl]. |
| 219 | +//! This may be used to erase the type of a [`Layer`]. |
| 220 | +//! |
| 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>`: |
| 223 | +//! ``` |
| 224 | +//! use tracing_subscriber::{ |
| 225 | +//! Layer, |
| 226 | +//! registry::LookupSpan, |
| 227 | +//! prelude::*, |
| 228 | +//! }; |
| 229 | +//! use std::{path::PathBuf, fs::File, io}; |
| 230 | +//! |
| 231 | +//! /// Configures whether logs are emitted to a file, to stdout, or to stderr. |
| 232 | +//! pub enum LogConfig { |
| 233 | +//! File(PathBuf), |
| 234 | +//! Stdout, |
| 235 | +//! Stderr, |
| 236 | +//! } |
| 237 | +//! |
| 238 | +//! impl LogConfig { |
| 239 | +//! pub fn layer<C>(self) -> Box<dyn Layer<C> + Send + Sync + 'static> |
| 240 | +//! where |
| 241 | +//! C: tracing_core::Subscriber + Send + Sync, |
| 242 | +//! for<'a> C: LookupSpan<'a>, |
| 243 | +//! { |
| 244 | +//! // Shared configuration regardless of where logs are output to. |
| 245 | +//! let fmt = tracing_subscriber::fmt::layer() |
| 246 | +//! .with_target(true) |
| 247 | +//! .with_thread_names(true); |
| 248 | +//! |
| 249 | +//! // Configure the writer based on the desired log target: |
| 250 | +//! match self { |
| 251 | +//! LogConfig::File(path) => { |
| 252 | +//! let file = File::create(path).expect("failed to create log file"); |
| 253 | +//! Box::new(fmt.with_writer(file)) |
| 254 | +//! }, |
| 255 | +//! LogConfig::Stdout => Box::new(fmt.with_writer(io::stdout)), |
| 256 | +//! LogConfig::Stderr => Box::new(fmt.with_writer(io::stderr)), |
| 257 | +//! } |
| 258 | +//! } |
| 259 | +//! } |
| 260 | +//! |
| 261 | +//! let config = LogConfig::Stdout; |
| 262 | +//! tracing_subscriber::registry() |
| 263 | +//! .with(config.layer()) |
| 264 | +//! .init(); |
| 265 | +//! ``` |
| 266 | +//! |
| 267 | +//! [prelude]: crate::prelude |
| 268 | +//! [box-impl]: #impl-Layer<S>-for-Box<dyn Layer<S> + Send + Sync> |
138 | 269 | //! [prelude]: crate::prelude |
139 | 270 | //! |
140 | | -//! ## Recording Traces |
| 271 | +//! # Recording Traces |
141 | 272 | //! |
142 | 273 | //! The [`Layer`] trait defines a set of methods for consuming notifications from |
143 | 274 | //! tracing instrumentation, which are generally equivalent to the similarly |
|
146 | 277 | //! information provided by the wrapped subscriber (such as [the current span]) |
147 | 278 | //! to the layer. |
148 | 279 | //! |
149 | | -//! ## Filtering with `Layer`s |
| 280 | +//! # Filtering with `Layer`s |
150 | 281 | //! |
151 | 282 | //! As well as strategies for handling trace events, the `Layer` trait may also |
152 | 283 | //! be used to represent composable _filters_. This allows the determination of |
|
158 | 289 | //! combined with _per-layer filters_ that control what spans and events are |
159 | 290 | //! recorded by those layers. |
160 | 291 | //! |
161 | | -//! ### Global Filtering |
| 292 | +//! ## Global Filtering |
162 | 293 | //! |
163 | 294 | //! A `Layer` that implements a filtering strategy should override the |
164 | 295 | //! [`register_callsite`] and/or [`enabled`] methods. It may also choose to implement |
|
179 | 310 | //! [`Interest::never()`] from its [`register_callsite`] method, filter |
180 | 311 | //! evaluation will short-circuit and the span or event will be disabled. |
181 | 312 | //! |
182 | | -//! ### Per-Layer Filtering |
| 313 | +//! ## Per-Layer Filtering |
183 | 314 | //! |
184 | 315 | //! **Note**: per-layer filtering APIs currently require the [`"registry"` crate |
185 | 316 | //! feature flag][feat] to be enabled. |
|
393 | 524 | //! # Ok(()) } |
394 | 525 | //! ``` |
395 | 526 | //! |
396 | | -//! ## Runtime Configuration With Layers |
397 | | -//! |
398 | | -//! In some cases, a particular [`Layer`] may be enabled or disabled based on |
399 | | -//! runtime configuration. This can introduce challenges, because the type of a |
400 | | -//! layered [`Subscriber`] depends on which layers are added to it: if an `if` |
401 | | -//! or `match` expression adds some [`Layer`]s in one branch and other layers |
402 | | -//! in another, the [`Subscriber`] values returned by those branches will have |
403 | | -//! different types. For example, the following _will not_ work: |
404 | | -//! |
405 | | -//! ```compile_fail |
406 | | -//! # fn docs() -> Result<(), Box<dyn std::error::Error + 'static>> { |
407 | | -//! # struct Config { |
408 | | -//! # is_prod: bool, |
409 | | -//! # path: &'static str, |
410 | | -//! # } |
411 | | -//! # let cfg = Config { is_prod: false, path: "debug.log" }; |
412 | | -//! use std::{fs::File, sync::Arc}; |
413 | | -//! use tracing_subscriber::{Registry, prelude::*}; |
414 | | -//! |
415 | | -//! let stdout_log = tracing_subscriber::fmt::layer().pretty(); |
416 | | -//! let subscriber = Registry::default().with(stdout_log); |
417 | | -//! |
418 | | -//! // The compile error will occur here because the if and else |
419 | | -//! // branches have different (and therefore incompatible) types. |
420 | | -//! let subscriber = if cfg.is_prod { |
421 | | -//! let file = File::create(cfg.path)?; |
422 | | -//! let layer = tracing_subscriber::fmt::layer() |
423 | | -//! .json() |
424 | | -//! .with_writer(Arc::new(file)); |
425 | | -//! subscriber.with(layer) |
426 | | -//! } else { |
427 | | -//! subscriber |
428 | | -//! }; |
429 | | -//! |
430 | | -//! tracing::subscriber::set_global_default(subscriber) |
431 | | -//! .expect("Unable to set global subscriber"); |
432 | | -//! # Ok(()) } |
433 | | -//! ``` |
434 | | -//! |
435 | | -//! However, a [`Layer`] wrapped in an [`Option`] [also implements the `Layer` |
436 | | -//! trait][option-impl]. This allows individual layers to be enabled or disabled at |
437 | | -//! runtime while always producing a [`Subscriber`] of the same type. For |
438 | | -//! example: |
439 | | -//! |
440 | | -//! ``` |
441 | | -//! # fn docs() -> Result<(), Box<dyn std::error::Error + 'static>> { |
442 | | -//! # struct Config { |
443 | | -//! # is_prod: bool, |
444 | | -//! # path: &'static str, |
445 | | -//! # } |
446 | | -//! # let cfg = Config { is_prod: false, path: "debug.log" }; |
447 | | -//! use std::{fs::File, sync::Arc}; |
448 | | -//! use tracing_subscriber::{Registry, prelude::*}; |
449 | | -//! |
450 | | -//! let stdout_log = tracing_subscriber::fmt::layer().pretty(); |
451 | | -//! let subscriber = Registry::default().with(stdout_log); |
452 | | -//! |
453 | | -//! // if `cfg.is_prod` is true, also log JSON-formatted logs to a file. |
454 | | -//! let json_log = if cfg.is_prod { |
455 | | -//! let file = File::create(cfg.path)?; |
456 | | -//! let json_log = tracing_subscriber::fmt::layer() |
457 | | -//! .json() |
458 | | -//! .with_writer(Arc::new(file)); |
459 | | -//! Some(json_log) |
460 | | -//! } else { |
461 | | -//! None |
462 | | -//! }; |
463 | | -//! |
464 | | -//! // If `cfg.is_prod` is false, then `json` will be `None`, and this layer |
465 | | -//! // will do nothing. However, the subscriber will still have the same type |
466 | | -//! // regardless of whether the `Option`'s value is `None` or `Some`. |
467 | | -//! let subscriber = subscriber.with(json_log); |
468 | | -//! |
469 | | -//! tracing::subscriber::set_global_default(subscriber) |
470 | | -//! .expect("Unable to set global subscriber"); |
471 | | -//! # Ok(()) } |
472 | | -//! ``` |
473 | | -//! |
474 | 527 | //! [`Subscriber`]: https://docs.rs/tracing-core/latest/tracing_core/subscriber/trait.Subscriber.html |
475 | 528 | //! [span IDs]: https://docs.rs/tracing-core/latest/tracing_core/span/struct.Id.html |
476 | 529 | //! [the current span]: Context::current_span |
|
0 commit comments