pub use crate::logger_handle::LogSpecSubscriber;
use crate::{
log_specification::Space,
logger::{create_specfile_watcher, synchronize_subscriber_with_specfile},
writers::{FileLogWriterBuilder, FileLogWriterHandle},
};
use crate::{FlexiLoggerError, LogSpecification};
use notify_debouncer_mini::{notify::RecommendedWatcher, Debouncer};
use std::path::{Path, PathBuf};
use tracing_subscriber::{EnvFilter, FmtSubscriber};
#[cfg(feature = "specfile_without_notification")]
#[cfg_attr(docsrs, doc(cfg(feature = "specfile")))]
pub fn subscribe_to_specfile<P: AsRef<Path>>(
specfile: P,
reloader: Box<dyn Fn(LogSpecification) + Send + Sync>,
initial_logspec: LogSpecification,
) -> Result<Option<Debouncer<RecommendedWatcher>>, FlexiLoggerError> {
let specfile = specfile.as_ref();
let mut subscriber = TraceLogSpecSubscriber::new(reloader, initial_logspec);
synchronize_subscriber_with_specfile(&mut subscriber, specfile)?;
if cfg!(feature = "specfile") {
Ok(Some(create_specfile_watcher(specfile, subscriber)?))
} else {
Ok(None)
}
}
#[cfg(feature = "specfile_without_notification")]
struct TraceLogSpecSubscriber {
initial_logspec: LogSpecification,
update: Box<dyn Fn(LogSpecification) + Send + Sync>,
}
impl TraceLogSpecSubscriber {
#[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 TraceLogSpecSubscriber {
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())
}
}
pub struct SpecFileNotifier {
_watcher: Option<Debouncer<RecommendedWatcher>>,
}
pub fn setup_tracing(
initial_logspec: LogSpecification,
o_specfile: Option<&PathBuf>,
flwb: FileLogWriterBuilder,
config: &FormatConfig,
) -> Result<(FileLogWriterHandle, SpecFileNotifier), FlexiLoggerError> {
let (file_writer, fw_handle) = flwb.try_build_with_handle()?;
if config.with_time {
let subscriber_builder = FmtSubscriber::builder()
.with_writer(move || file_writer.clone())
.with_ansi(config.with_ansi)
.with_env_filter(EnvFilter::from_default_env())
.with_file(config.with_file)
.with_filter_reloading()
.with_level(config.with_level)
.with_line_number(config.with_line_number)
.with_target(config.with_target)
.with_thread_ids(config.with_thread_ids)
.with_thread_names(config.with_thread_names)
.with_thread_ids(config.with_thread_ids)
.with_thread_names(config.with_thread_names)
.with_env_filter(LogSpecAsFilter(initial_logspec.clone()))
.with_filter_reloading();
let spec_file_notifier = SpecFileNotifier {
_watcher: match o_specfile {
Some(specfile) => {
let reload_handle = Box::new(subscriber_builder.reload_handle());
subscribe_to_specfile(
specfile,
Box::new(move |logspec| {
{ reload_handle.reload(LogSpecAsFilter(logspec)) }.unwrap();
}),
initial_logspec,
)?
}
None => None,
},
};
tracing::subscriber::set_global_default(subscriber_builder.finish())?;
Ok((fw_handle, spec_file_notifier))
} else {
let subscriber_builder = FmtSubscriber::builder()
.without_time()
.with_writer(move || file_writer.clone())
.with_ansi(config.with_ansi)
.with_env_filter(EnvFilter::from_default_env())
.with_file(config.with_file)
.with_filter_reloading()
.with_level(config.with_level)
.with_line_number(config.with_line_number)
.with_target(config.with_target)
.with_thread_ids(config.with_thread_ids)
.with_thread_names(config.with_thread_names)
.with_thread_ids(config.with_thread_ids)
.with_thread_names(config.with_thread_names)
.with_env_filter(LogSpecAsFilter(initial_logspec.clone()))
.with_filter_reloading();
let spec_file_notifier = SpecFileNotifier {
_watcher: match o_specfile {
Some(specfile) => {
let reload_handle = Box::new(subscriber_builder.reload_handle());
subscribe_to_specfile(
specfile,
Box::new(move |logspec| {
{ reload_handle.reload(LogSpecAsFilter(logspec)) }.unwrap();
}),
initial_logspec,
)?
}
None => None,
},
};
tracing::subscriber::set_global_default(subscriber_builder.finish())?;
Ok((fw_handle, spec_file_notifier))
}
}
struct LogSpecAsFilter(pub LogSpecification);
impl From<LogSpecAsFilter> for EnvFilter {
fn from(wrapped_logspec: LogSpecAsFilter) -> Self {
Self::new(wrapped_logspec.to_string())
}
}
impl std::fmt::Display for LogSpecAsFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.to_string_int(f, Space::No)
}
}
#[allow(clippy::struct_excessive_bools)]
pub struct FormatConfig {
with_ansi: bool,
with_file: bool,
with_level: bool,
with_line_number: bool,
with_target: bool,
with_thread_ids: bool,
with_thread_names: bool,
with_time: bool,
}
impl Default for FormatConfig {
fn default() -> Self {
Self {
with_ansi: false,
with_file: false,
with_level: true,
with_line_number: true,
with_target: false,
with_thread_ids: false,
with_thread_names: false,
with_time: true,
}
}
}
impl FormatConfig {
#[must_use]
pub fn with_ansi(mut self, with_ansi: bool) -> Self {
self.with_ansi = with_ansi;
self
}
#[must_use]
pub fn with_file(mut self, with_file: bool) -> Self {
self.with_file = with_file;
self
}
#[must_use]
pub fn with_level(mut self, with_level: bool) -> Self {
self.with_level = with_level;
self
}
#[must_use]
pub fn with_line_number(mut self, with_line_number: bool) -> Self {
self.with_line_number = with_line_number;
self
}
#[must_use]
pub fn with_target(mut self, with_target: bool) -> Self {
self.with_target = with_target;
self
}
#[must_use]
pub fn with_thread_ids(mut self, with_thread_ids: bool) -> Self {
self.with_thread_ids = with_thread_ids;
self
}
#[must_use]
pub fn with_thread_names(mut self, with_thread_names: bool) -> Self {
self.with_thread_names = with_thread_names;
self
}
#[must_use]
pub fn with_time(mut self, with_time: bool) -> Self {
self.with_time = with_time;
self
}
}