use std::io;
use tokio::sync::mpsc;
pub struct LogEntry {
pub level: String,
pub message: String,
}
#[derive(Clone)]
struct HostLogWriter {
tx: mpsc::UnboundedSender<LogEntry>,
level: &'static str,
}
impl HostLogWriter {
fn new(tx: mpsc::UnboundedSender<LogEntry>, level: &'static str) -> Self {
Self { tx, level }
}
}
impl io::Write for HostLogWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let message = String::from_utf8_lossy(buf).trim_end().to_string();
if !message.is_empty() {
let _ = self.tx.send(LogEntry {
level: self.level.to_string(),
message,
});
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for HostLogWriter {
type Writer = HostLogWriter;
fn make_writer(&'a self) -> Self::Writer {
self.clone()
}
}
pub fn init_logging() -> mpsc::UnboundedReceiver<LogEntry> {
use tracing_subscriber::filter::{filter_fn, LevelFilter};
use tracing_subscriber::prelude::*;
use tracing_subscriber::Registry;
let (log_tx, log_rx) = mpsc::unbounded_channel::<LogEntry>();
let _ = Registry::default()
.with(
tracing_subscriber::fmt::layer()
.with_writer(io::stderr)
.with_filter(LevelFilter::INFO),
)
.with(
tracing_subscriber::fmt::layer()
.with_writer(HostLogWriter::new(log_tx.clone(), "warn"))
.with_filter(filter_fn(|metadata| {
*metadata.level() == tracing::Level::WARN
})),
)
.with(
tracing_subscriber::fmt::layer()
.with_writer(HostLogWriter::new(log_tx.clone(), "error"))
.with_filter(filter_fn(|metadata| {
*metadata.level() == tracing::Level::ERROR
})),
)
.try_init();
log_rx
}