use notify_rust::{Notification, Timeout, Urgency};
use tracing::{field::Visit, Level};
use tracing_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{filter, fmt, layer::SubscriberExt, EnvFilter, Layer};
use crate::{cli::Cli, error::Result};
pub fn init_tracing(cli: &Cli) -> Result<WorkerGuard> {
let (file_writer, _guard) =
tracing_appender::non_blocking(tracing_appender::rolling::never(
xdg::BaseDirectories::new()?.create_cache_directory("handlr")?,
"handlr.log",
));
let env_filter = || {
EnvFilter::builder()
.with_default_directive(cli.verbosity.tracing_level_filter().into())
.from_env_lossy()
};
tracing::subscriber::set_global_default(
tracing_subscriber::registry()
.with(
fmt::Layer::new()
.with_writer(std::io::stderr)
.with_filter(env_filter()),
)
.with(fmt::Layer::new().with_writer(file_writer).with_ansi(false))
.with(
cli.show_notifications()
.then_some(NotificationLayer.with_filter(env_filter())),
)
.with(
filter::Targets::new()
.with_target("handlr", Level::TRACE)
.with_target("tracing_unwrap", Level::WARN),
),
)?;
Ok(_guard)
}
struct NotificationLayer;
impl<S> Layer<S> for NotificationLayer
where
S: tracing::Subscriber,
{
#[mutants::skip] fn on_event(
&self,
event: &tracing::Event,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
let mut message = String::new();
event.record(&mut NotificationVisitor(&mut message));
if message.is_empty() {
message = "No message, see ~/.cache/handlr/handlr.log for details"
.to_string()
}
let (level, icon, urgency) = match *event.metadata().level() {
tracing::Level::ERROR => {
("error".to_string(), "dialog-error", Urgency::Critical)
}
tracing::Level::WARN => {
("warning".to_string(), "dialog-warning", Urgency::Normal)
}
l => (
l.as_str().to_lowercase(),
"dialog-information",
Urgency::Low,
),
};
Notification::new()
.summary(&format!("handlr {}", level))
.body(&message)
.icon(icon)
.appname("handlr")
.timeout(Timeout::Milliseconds(10_000))
.urgency(urgency)
.show()
.expect("handlr error: Could not issue dbus notification");
}
}
struct NotificationVisitor<'a>(&'a mut String);
impl Visit for NotificationVisitor<'_> {
#[mutants::skip] fn record_debug(
&mut self,
field: &tracing::field::Field,
value: &dyn std::fmt::Debug,
) {
if field.name() == "message" {
self.0.push_str(&format!("{:?}", value));
}
}
}