use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use tracing::Level;
use tracing_subscriber::layer::Context;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::Layer;
pub struct WarningLogLayer {
file: Arc<Mutex<File>>,
sender: mpsc::Sender<()>,
}
pub struct WarningLogHandle {
pub receiver: mpsc::Receiver<()>,
pub path: PathBuf,
}
pub fn create() -> std::io::Result<(WarningLogLayer, WarningLogHandle)> {
create_with_path(
std::env::temp_dir().join(format!("fresh-warnings-{}.log", std::process::id())),
)
}
pub fn create_with_path(path: PathBuf) -> std::io::Result<(WarningLogLayer, WarningLogHandle)> {
let file = File::create(&path)?;
let (sender, receiver) = mpsc::channel();
let layer = WarningLogLayer {
file: Arc::new(Mutex::new(file)),
sender,
};
let handle = WarningLogHandle { receiver, path };
Ok((layer, handle))
}
impl<S> Layer<S> for WarningLogLayer
where
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
{
fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) {
let level = *event.metadata().level();
if level > Level::WARN {
return;
}
let mut visitor = StringVisitor::default();
event.record(&mut visitor);
let line = format!(
"{} {} {}: {}\n",
chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"),
level,
event.metadata().target(),
visitor.0
);
if let Ok(mut file) = self.file.lock() {
let _ = file.write_all(line.as_bytes());
let _ = file.flush();
}
let _ = self.sender.send(());
}
}
#[derive(Default)]
struct StringVisitor(String);
impl tracing::field::Visit for StringVisitor {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
if field.name() == "message" {
self.0 = format!("{:?}", value);
} else if !self.0.is_empty() {
self.0.push_str(&format!(" {}={:?}", field.name(), value));
} else {
self.0 = format!("{}={:?}", field.name(), value);
}
}
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
if field.name() == "message" {
self.0 = value.to_string();
} else if !self.0.is_empty() {
self.0.push_str(&format!(" {}={}", field.name(), value));
} else {
self.0 = format!("{}={}", field.name(), value);
}
}
}