1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
//! ## Use `flexi_logger` functionality with [`tracing`](https://docs.rs/tracing/latest/tracing/).
//!
//! [`tracing`](https://docs.rs/tracing/latest/tracing/) is an alternative to
//! [`log`](https://docs.rs/log/latest/log/).
//! It has a similar base architecture, but is optimized for supporting async apps,
//! which adds complexity due to the need to manage contexts.
//! [`tracing-subscriber`](https://docs.rs/tracing/latest/tracing-subscriber/)
//! facilitates contributing "backends", and is used in the example below to plug
//! `flexi_logger`-functionality into `tracing`.
//!
//! **The content of this module is a first attempt to support such an integration.
//! Every feedback is highly appreciated.**
//!
//! ### Example
//!
//! The following example uses a `FileLogWriter` as trace writer,
//! and `flexi_logger`'s specfile handling to adapt `tracing` dynamically,
//! while your program is running.
//! The code is a bit cumbersome, maybe there are (oor will be) easier ways to achieve the same.
//!
//! Precondition: add these entries to your `Cargo.toml`:
//! ```toml
//! flexi_logger = {version = "0.19", features = ["trc"]}
//! tracing = "0.1"
//! tracing-subscriber = {version = "0.2.20", features = ["env-filter"]}
//! ```
//!
//! ```rust,ignore
//! # #[cfg(feature = "specfile_without_notification")]
//! # {
//! # use std::error::Error;
//! use flexi_logger::{
//! trc::{subscribe_to_specfile, BasicLogSpecSubscriber, LogSpecAsFilter},
//! writers::FileLogWriter,
//! Age, Cleanup, Criterion, FileSpec, LogSpecification, Naming, WriteMode,
//! };
//!
//! use tracing::{debug, info, trace, warn};
//! use tracing_subscriber::FmtSubscriber;
//!
//! # fn main() -> Result<(), Box<dyn Error>> {
//! // Prepare a `FileLogWriter` and a handle to it, and keep the handle alive
//! // until the program ends (it will flush and shutdown the `FileLogWriter` when dropped).
//! // For the `FileLogWriter`, use the settings that fit your needs
//! let (file_writer, _fw_handle) = FileLogWriter::builder(FileSpec::default())
//! .rotate(
//! // If the program runs long enough,
//! Criterion::Age(Age::Day), // - create a new file every day
//! Naming::Timestamps, // - let the rotated files have a timestamp in their name
//! Cleanup::KeepLogFiles(7), // - keep at most seven log files
//! )
//! .write_mode(WriteMode::Async)
//! .try_build_with_handle()
//! .unwrap();
//!
//! // Set up subscriber that makes use of the file writer, with some hardcoded initial log spec
//! let initial_logspec = LogSpecification::info();
//! let subscriber_builder = FmtSubscriber::builder()
//! .with_writer(move || file_writer.clone())
//! .with_env_filter(LogSpecAsFilter(initial_logspec.clone()))
//! .with_filter_reloading();
//!
//! // Set up specfile tracking and subscribe
//! let reload_handle = Box::new(subscriber_builder.reload_handle());
//! subscribe_to_specfile(
//! "trcspecfile.toml",
//! BasicLogSpecSubscriber::new(
//! Box::new(move |logspec| reload_handle.reload(LogSpecAsFilter(logspec)).unwrap()),
//! initial_logspec,
//! ),
//! )
//! .unwrap();
//!
//! // Get ready to trace
//! tracing::subscriber::set_global_default(subscriber_builder.finish())
//! .expect("setting default subscriber failed");
//!
//! // now do what you really want to do...
//! # Ok(())}}
//! ```
//!
pub use crate::logger_handle::LogSpecSubscriber;
use crate::{FlexiLoggerError, LogSpecification};
use std::path::Path;
use tracing_subscriber::EnvFilter;
/// Helper struct for using [`LogSpecification`] as filter in `tracing`.
pub struct LogSpecAsFilter(pub LogSpecification);
impl From<LogSpecAsFilter> for EnvFilter {
fn from(my_filter: LogSpecAsFilter) -> Self {
Self::new(my_filter.0.to_string())
}
}
/// Allows registering a `LogSpecSubscriber` to a specfile.
///
/// Every update to the specfile will be noticed (via crate `notify`),
/// the file will be re-read, and the `LogSpecSubscriber` will be updated.
///
/// # Errors
///
/// Several variants of [`FlexiLoggerError`] can occur.
#[cfg(feature = "specfile_without_notification")]
pub fn subscribe_to_specfile<P: AsRef<Path>, H: LogSpecSubscriber>(
specfile: P,
subscriber: H,
) -> Result<(), FlexiLoggerError> {
crate::logger::subscribe_to_specfile(specfile, subscriber)
}
/// Helper struct that can be registered in
/// [`subscribe_to_specfile`](fn.subscribe_to_specfile.html) to get
/// informed about updates to the specfile,
/// and can be registered in `tracing` to forward such updates.
#[cfg(feature = "specfile_without_notification")]
pub struct BasicLogSpecSubscriber {
initial_logspec: LogSpecification,
update: Box<(dyn Fn(LogSpecification) + Send + Sync)>,
}
impl BasicLogSpecSubscriber {
/// Factory method.
///
/// # Parameters
/// `initial_logspec`: used to initialize the logspec file if it does not yet exist
///
/// update: Closure that implements the update of the log specification to some consumer
#[must_use]
pub fn new(
update: Box<(dyn Fn(LogSpecification) + Send + Sync)>,
initial_logspec: LogSpecification,
) -> Self {
Self {
initial_logspec,
update,
}
}
}
#[cfg(feature = "specfile_without_notification")]
impl LogSpecSubscriber for BasicLogSpecSubscriber {
fn set_new_spec(&mut self, logspec: LogSpecification) -> Result<(), FlexiLoggerError> {
(self.update)(logspec);
Ok(())
}
fn initial_spec(&self) -> Result<LogSpecification, FlexiLoggerError> {
Ok(self.initial_logspec.clone())
}
}
