use log::{Level, Metadata, Record}; use logtail_poster::Egress; use std::{ env, sync::{Arc, Mutex}, }; pub struct LogtailLogger { ing: Arc>, threshold: Level, } #[derive(serde::Serialize)] struct LogData<'a> { level: &'a str, target: &'a str, module_path: &'a str, file: &'a str, line: u32, message: String, } impl log::Log for LogtailLogger { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= self.threshold } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { let ld = LogData { level: record.level().as_str(), target: record.target(), module_path: record.module_path().unwrap_or("???"), file: record.file().unwrap_or("???"), line: record.line().unwrap_or(0), message: format!("{}", record.args()), }; if let Ok(val) = serde_json::to_value(&ld) { if let Ok(mut ing) = self.ing.lock() { if let Err(why) = ing.send(val) { eprintln!("logtail_facade::LogtailLogger::log: can't send json value to buffer: {}", why); } } } } } fn flush(&self) {} } pub fn init(collection: String) -> Result> { let cfg = logtail_poster::Config::load(collection.clone())?; let target = env::var("TS_LOG_TARGET") .unwrap_or(logtail_poster::DEFAULT_HOST.to_string()) .to_string(); let (mut ing, eg) = logtail_poster::Builder::default() .collection(collection) .base_url(target) .buffer_size(256) .private_id(cfg.private_id()) .build()?; let threshold = if cfg!(debug_assertions) { Level::Debug } else { Level::Info }; ing.send(serde_json::to_value(&ProgramStarted { msg: "Program started".to_string(), })?)?; let ing = Arc::new(Mutex::new(ing.clone())); let logger = LogtailLogger { ing: ing.clone(), threshold: threshold.clone(), }; log::set_boxed_logger(Box::new(logger)) .map(|()| log::set_max_level(threshold.to_level_filter()))?; Ok(eg) } #[derive(serde::Serialize)] struct ProgramStarted { msg: String, }