use tracing::Subscriber;
use tracing_subscriber::{fmt::format::FmtSpan, layer::Layer, reload, reload::Handle, EnvFilter};
use crate::util::formats::{ActyxOSResult, LogSeverity};
trait ReloadHandle {
fn reload(&self, new_filter: EnvFilter) -> Result<(), reload::Error>;
}
const DEFAULT_ENV: &str = "RUST_LOG";
impl<L, S> ReloadHandle for Handle<L, S>
where
L: From<EnvFilter> + Layer<S> + 'static,
S: Subscriber,
{
fn reload(&self, new_filter: EnvFilter) -> Result<(), reload::Error> {
self.reload(new_filter)
}
}
pub struct LoggingSink {
configured_level: LogSeverity,
level_from_env: bool,
filter_handle: Box<dyn ReloadHandle + Send>,
}
impl LoggingSink {
pub fn new(level: LogSeverity, log_no_color: bool, log_as_json: bool) -> Self {
let (filter, level_from_env) = match EnvFilter::try_from_default_env() {
Ok(filter) => (filter, true),
Err(e) => {
if std::env::var(EnvFilter::DEFAULT_ENV).is_ok() {
eprintln!("tracing: falling back to {}, error parsing RUST_LOG: {}", level, e);
}
(EnvFilter::new(level.to_string()), false)
}
};
let log_color = !log_no_color;
let builder = tracing_subscriber::FmtSubscriber::builder().with_span_events(FmtSpan::ENTER | FmtSpan::CLOSE);
let (subscriber, filter_handle): (
Box<dyn Subscriber + Sync + Send + 'static>,
Box<dyn ReloadHandle + Send>,
) = if log_as_json {
let builder = builder
.json()
.flatten_event(true)
.with_env_filter(filter)
.with_ansi(log_color)
.with_writer(std::io::stderr)
.with_filter_reloading();
let filter_handle = Box::new(builder.reload_handle());
let subscriber = builder.finish();
#[cfg(target_os = "android")]
let subscriber = tracing_android::layer("com.actyx").unwrap().with_subscriber(subscriber);
let sub = Box::new(subscriber);
(sub, filter_handle)
} else {
let builder = builder
.with_env_filter(filter)
.with_ansi(log_color)
.with_writer(std::io::stderr)
.with_filter_reloading();
let filter_handle = Box::new(builder.reload_handle());
let subscriber = builder.finish();
#[cfg(target_os = "android")]
let subscriber = tracing_android::layer("com.actyx").unwrap().with_subscriber(subscriber);
let sub = Box::new(subscriber);
(sub, filter_handle)
};
if tracing::subscriber::set_global_default(subscriber).is_err() {
eprintln!("`tracing::subscriber::set_global_default` has been called more than once!");
tracing::error!("`tracing::subscriber::set_global_default` has been called more than once!");
}
Self {
configured_level: level,
level_from_env,
filter_handle,
}
}
pub fn set_level(&mut self, level: LogSeverity) -> ActyxOSResult<()> {
if self.configured_level != level {
self.configured_level = level;
if self.level_from_env {
tracing::info!(
"Ignoring set log level \"{}\", as the log filter is set via the \"{}\" environment variable (\"{}\")",
self.configured_level,
DEFAULT_ENV,
std::env::var(DEFAULT_ENV).unwrap_or_else(|_| "".into())
);
} else {
let new_filter = EnvFilter::new(self.configured_level.to_string());
if let Err(e) = self.filter_handle.reload(new_filter) {
eprintln!(
"Error installing new EnvFilter with severity {}: {}",
self.configured_level, e
);
tracing::error!(
"Error installing new EnvFilter with severity {}: {}",
self.configured_level,
e
);
}
}
}
Ok(())
}
}