use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use tracing_subscriber::layer::Context;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::Layer;
pub struct StatusLogLayer {
file: Arc<Mutex<File>>,
}
pub struct StatusLogHandle {
pub path: PathBuf,
}
pub fn create() -> std::io::Result<(StatusLogLayer, StatusLogHandle)> {
create_with_path(super::log_dirs::status_log_path())
}
pub fn create_with_path(path: PathBuf) -> std::io::Result<(StatusLogLayer, StatusLogHandle)> {
let file = File::create(&path)?;
let layer = StatusLogLayer {
file: Arc::new(Mutex::new(file)),
};
let handle = StatusLogHandle { path };
Ok((layer, handle))
}
impl<S> Layer<S> for StatusLogLayer
where
S: tracing::Subscriber + for<'a> LookupSpan<'a>,
{
fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, S>) {
let target = event.metadata().target();
if target != "status" {
return;
}
let mut visitor = StringVisitor::default();
event.record(&mut visitor);
let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
let line = format!("{} {}\n", timestamp, visitor.0);
if let Ok(mut file) = self.file.lock() {
let _ = file.write_all(line.as_bytes());
let _ = file.flush();
}
}
}
#[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);
}
}
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
if field.name() == "message" {
self.0 = value.to_string();
}
}
}