use std::sync::{Arc, Mutex};
use crate::{DaemonRef, DaemonResult, Factory};
use super::Daemon;
use slog::{self, debug, Never, SendSyncRefUnwindSafeDrain};
use slog::{o, Drain, Logger};
use slog_async;
use slog_term;
pub struct RootLog {
pub root: Logger,
}
impl RootLog {
pub fn get_factory_logger(&self, host: &str) -> Logger {
self.root
.new(o! {"daemon"=>"factory".to_string(), "host"=>host.to_string()})
}
pub fn get_ambient_logger(&self, daemon: &str) -> Logger {
self.root
.new(o! {"daemon"=>daemon.to_string(), "host"=>"*".to_string()})
}
pub fn get_test_logger(&self, test: &str) -> Logger {
self.root
.new(o! {"daemon"=>test.to_string(), "host"=>"*".to_string()})
}
pub fn test_instance() -> Arc<RootLog> {
Arc::new(Self::build(true))
}
pub fn runtime_instance() -> Arc<RootLog> {
Arc::new(Self::build(false))
}
fn build(test: bool) -> RootLog {
if std::env::var("RUST_LOG").is_err() {
std::env::set_var("RUST_LOG", "info")
}
let io = slog_term::TestStdoutWriter;
let root = if test {
let decorator = slog_term::PlainDecorator::new(io );
let drain = slog_term::FullFormat::new(decorator).build();
let drain = slog_envlogger::new(Mutex::new(drain)).fuse();
slog::Logger::root(drain, o!())
} else {
let drain = slog_json::Json::default(std::io::stdout()).map(slog::Fuse);
let drain = slog_async::Async::new(drain).build().fuse();
let drain = slog_envlogger::new(drain);
slog::Logger::root(drain, o!())
};
Self { root }
}
}
#[async_trait::async_trait]
impl<E> Daemon<E> for RootLog {
fn name(&self) -> &'static str {
"root_log"
}
async fn prepare(&self) -> DaemonResult<(), E> {
Ok(())
}
async fn run(&self) -> DaemonResult<(), E> {
Ok(())
}
async fn stop(&self) -> DaemonResult<(), E> {
Ok(())
}
}
pub struct NodeLog {
log: Logger<Arc<dyn SendSyncRefUnwindSafeDrain<Ok = (), Err = Never>>>,
}
pub trait LogConfig {
fn name(&self) -> &str;
}
pub trait LogDaemons<E: std::fmt::Debug + Send + Sync + 'static>: Default {
type LogConfig: LogConfig;
fn root_log(factory: &Factory<Self, E>) -> DaemonResult<DaemonRef<RootLog>, E>;
fn node_log(factory: &Factory<Self, E>) -> DaemonResult<DaemonRef<NodeLog>, E>;
fn local_config(factory: &Factory<Self, E>) -> DaemonResult<DaemonRef<Self::LogConfig>, E>;
}
impl NodeLog {
pub fn new<E: std::fmt::Debug + Send + Sync + 'static, Daemons: LogDaemons<E>>(
factory: &Factory<Daemons, E>,
) -> DaemonResult<Self, E> {
let config = Daemons::local_config(factory)?;
let root = Daemons::root_log(factory)?;
let log = root.root.new(o!("host"=>config.name().to_string()));
debug!(log, "Initialized log");
Ok(Self { log })
}
pub fn logger(&self, name: &str) -> Logger {
self.log.new(o! {"daemon"=>name.to_string()})
}
}
#[async_trait::async_trait]
impl<E> Daemon<E> for NodeLog {
fn name(&self) -> &'static str {
"node_log"
}
async fn prepare(&self) -> DaemonResult<(), E> {
Ok(())
}
async fn run(&self) -> DaemonResult<(), E> {
Ok(())
}
async fn stop(&self) -> DaemonResult<(), E> {
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
use serde::Serialize;
use slog::info;
use slog_derive::SerdeValue;
#[derive(Debug, Default, Clone, Serialize, SerdeValue)]
struct Test {
json: serde_json::Value,
}
#[test]
fn log_json() -> DaemonResult<(), String> {
let the_file = r#"{
"FirstName": "John",
"LastName": "Doe",
"Age": 43,
"Address": {
"Street": "Downing Street 10",
"City": "London",
"Country": "Great Britain"
},
"PhoneNumbers": [
"+44 1234567",
"+44 2345678"
]
}"#;
let json: serde_json::Value =
serde_json::from_str(the_file).expect("JSON was not well-formatted");
let log = RootLog::test_instance().get_factory_logger("test");
info!(log, "hello test"; "t"=>Test{ json });
Ok(())
}
}