use crate::actor::{Actor, All, BoxActorProd, ChannelMsg, Context, DeadLetter, Props, Subscribe};
use crate::actor_ref::{ActorRef, BasicActorRef, Tell};
use config::Config;
use slog::{info, o, Drain, Level, Logger, Never, OwnedKVList, Record};
use std::str::FromStr;
#[derive(Clone)]
pub struct LoggerConfig {
time_fmt: String,
date_fmt: String,
log_fmt: String,
filter: Vec<String>,
level: Level,
}
impl<'a> From<&'a Config> for LoggerConfig {
fn from(config: &Config) -> Self {
Self {
time_fmt: config.get_str("log.time_format").unwrap(),
date_fmt: config.get_str("log.date_format").unwrap(),
log_fmt: config.get_str("log.log_format").unwrap(),
filter: config
.get_array("log.filter")
.unwrap_or_else(|_| vec![])
.into_iter()
.map(|e| e.to_string())
.collect(),
level: config
.get_str("log.level")
.map(|l| Level::from_str(&l).unwrap_or(Level::Info))
.unwrap_or(Level::Info),
}
}
}
pub(crate) fn default_log(cfg: &Config) -> Logger {
let cfg = LoggerConfig::from(cfg);
let drain = DefaultConsoleLogger::new(cfg.clone())
.filter_level(cfg.level)
.fuse();
let logger = Logger::root(drain, o!());
let _scope_guard = slog_scope::set_global_logger(logger.clone());
let _log_guard = slog_stdlog::init();
logger
}
struct DefaultConsoleLogger {
cfg: LoggerConfig,
}
impl DefaultConsoleLogger {
const fn new(cfg: LoggerConfig) -> Self {
Self { cfg }
}
}
impl Drain for DefaultConsoleLogger {
type Ok = ();
type Err = Never;
fn log(&self, record: &Record, _values: &OwnedKVList) -> Result<Self::Ok, Self::Err> {
let now = chrono::Utc::now();
let filter_match = self.cfg.filter.iter().any(|f| record.module().contains(f));
if !filter_match {
println!(
"{} {} {} [{}] {}",
now.format(&self.cfg.date_fmt),
now.format(&self.cfg.time_fmt),
record.level().as_short_str(),
record.module(),
record.msg()
);
}
Ok(())
}
}
pub struct DeadLetterLogger {
dl_chan: ActorRef<ChannelMsg<DeadLetter>>,
logger: Logger,
}
impl DeadLetterLogger {
fn new((dl_chan, logger): (ActorRef<ChannelMsg<DeadLetter>>, Logger)) -> Self {
Self { dl_chan, logger }
}
pub fn props(dl_chan: &ActorRef<ChannelMsg<DeadLetter>>, logger: Logger) -> BoxActorProd<Self> {
Props::new_args(Self::new, (dl_chan.clone(), logger))
}
}
impl Actor for DeadLetterLogger {
type Msg = DeadLetter;
fn pre_start(&mut self, ctx: &Context<Self::Msg>) {
let sub = Box::new(ctx.myself());
self.dl_chan.tell(
Subscribe {
topic: All.into(),
actor: sub,
},
None,
);
}
fn recv(&mut self, _: &Context<Self::Msg>, msg: Self::Msg, _: Option<BasicActorRef>) {
info!(
self.logger,
"DeadLetter: {:?} => {:?} ({:?})", msg.sender, msg.recipient, msg.msg
)
}
}