1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
use merfolk::{ interfaces::{Backend, Frontend}, Call, Reply, }; use wildmatch::WildMatch; use std::{marker::PhantomData, sync::Arc}; use anyhow::Result; use thiserror::Error; use log::{Level, Metadata, Record}; #[derive(Debug, Error)] pub enum Error { #[error("backend error: {0}")] FromBackend(#[from] anyhow::Error), #[error("could not set logger: {0}")] SetLogger(#[from] log::SetLoggerError), #[error("no sink provided in init()")] NoSink, #[error("error parsing nog level {level}")] LevelParseError { level: String }, } struct LoggerInstance { level: Level, ignore_targets: Arc<Option<Vec<&'static str>>>, allow_targets: Arc<Option<Vec<&'static str>>>, #[allow(clippy::type_complexity)] call: Arc<dyn Fn(&Record) + 'static + Send + Sync>, } #[derive(derive_builder::Builder)] #[builder(pattern = "owned")] pub struct Logger<'a, B: Backend> { #[builder(setter(into, strip_option), default = "None")] level: Option<Level>, #[builder(setter(name = "sink_setter", strip_option), private, default = "None")] sink: Option<Box<dyn Fn(Level, String)>>, #[builder(setter(name = "ignore_targets_setter", strip_option), private, default = "Arc::new(None)")] ignore_targets: Arc<Option<Vec<&'static str>>>, #[builder(setter(name = "allow_targets_setter", strip_option), private, default = "Arc::new(None)")] allow_targets: Arc<Option<Vec<&'static str>>>, #[builder(private, default = "PhantomData")] __phantom: std::marker::PhantomData<&'a B>, } impl<'a, B: Backend> LoggerBuilder<'a, B> { pub fn sink<S: 'static + Fn(Level, String)>(self, value: S) -> Self { self.sink_setter(Box::new(value)) } pub fn ignore_targets(self, mut value: Vec<&'static str>) -> Self { value.push("merfolk"); self.ignore_targets_setter(Arc::new(Some(value))) } pub fn allow_targets(self, value: Vec<&'static str>) -> Self { self.allow_targets_setter(Arc::new(Some(value))) } } impl<'a, B: Backend> Logger<'a, B> { pub fn builder() -> LoggerBuilder<'a, B> { LoggerBuilder::default() } } unsafe impl<'a, T: Backend> Send for Logger<'a, T> {} impl log::Log for LoggerInstance { fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= self.level && !metadata.target().is_empty() && if let Some(ignore_targets) = self.ignore_targets.as_ref() { !ignore_targets.iter().any(|t| WildMatch::new(t).is_match(&metadata.target())) } else { true } && if let Some(allow_targets) = self.allow_targets.as_ref() { allow_targets.iter().any(|t| WildMatch::new(t).is_match(&metadata.target())) } else { true } } fn log(&self, record: &Record) { if self.enabled(record.metadata()) { self.call.as_ref()(record); } } fn flush(&self) {} } impl<'a, B: Backend> Frontend for Logger<'a, B> { type Backend = B; fn register<T>(&mut self, caller: T) -> Result<()> where T: Fn(Call<B::Intermediate>) -> Result<Reply<B::Intermediate>> + 'static + Send + Sync, { if let Some(level) = self.level { log::set_boxed_logger(Box::new(LoggerInstance { level, ignore_targets: self.ignore_targets.clone(), allow_targets: self.allow_targets.clone(), call: Arc::new(move |record: &Record| { let args = match B::serialize(&record.args().to_string()) { Ok(ser) => ser, Err(err) => B::serialize(&err.to_string()).unwrap(), }; caller(Call { procedure: record.level().to_string(), payload: args, }) .ok(); }), })) .map_err(Error::SetLogger)?; log::set_max_level(level.to_level_filter()); } Ok(()) } fn receive(&self, call: Call<B::Intermediate>) -> Result<Reply<B::Intermediate>> { self.sink.as_ref().ok_or(Error::NoSink)?( match call.procedure.as_str() { "ERROR" => Level::Error, "WARN" => Level::Warn, "INFO" => Level::Info, "DEBUG" => Level::Debug, "TRACE" => Level::Trace, err => return Err(Error::LevelParseError { level: err.to_string() }.into()), }, B::deserialize(&call.payload)?, ); Ok(Reply { payload: B::serialize(&())? }) } }