create LogMailer for using it in CombinedLogger

This commit is contained in:
jb-alvarado 2022-02-25 16:22:29 +01:00
parent 8a936badaf
commit e6adb5282d
8 changed files with 238 additions and 151 deletions

View File

@ -5,6 +5,59 @@ use simplelog::*;
use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate}; use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate};
// use crate::{Config, SharedLogger};
use log::{Level, LevelFilter, Log, Metadata, Record};
pub struct LogMailer {
level: LevelFilter,
config: Config,
}
impl LogMailer {
pub fn new(log_level: LevelFilter, config: Config) -> Box<LogMailer> {
Box::new(LogMailer {
level: log_level,
config,
})
}
}
impl Log for LogMailer {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
metadata.level() <= self.level
}
fn log(&self, record: &Record<'_>) {
if self.enabled(record.metadata()) {
match record.level() {
Level::Error => {
println!("Send Error Mail: {:?}\n{:?}", record, self.config)
},
Level::Warn => {
println!("Send Warn Mail: {:?}", record.args())
}
_ => ()
}
}
}
fn flush(&self) {}
}
impl SharedLogger for LogMailer {
fn level(&self) -> LevelFilter {
self.level
}
fn config(&self) -> Option<&Config> {
Some(&self.config)
}
fn as_log(self: Box<Self>) -> Box<dyn Log> {
Box::new(*self)
}
}
fn main() { fn main() {
let log = || { let log = || {
FileRotate::new( FileRotate::new(
@ -19,9 +72,11 @@ fn main() {
.set_target_level(LevelFilter::Off) .set_target_level(LevelFilter::Off)
.set_thread_level(LevelFilter::Off) .set_thread_level(LevelFilter::Off)
.set_level_padding(LevelPadding::Left) .set_level_padding(LevelPadding::Left)
.set_time_to_local(true).clone(); .set_time_to_local(true)
.clone();
let term_config = def_config.clone() let term_config = def_config
.clone()
.set_level_color(Level::Debug, Some(Color::Ansi256(12))) .set_level_color(Level::Debug, Some(Color::Ansi256(12)))
.set_level_color(Level::Info, Some(Color::Ansi256(10))) .set_level_color(Level::Info, Some(Color::Ansi256(10)))
.set_level_color(Level::Warn, Some(Color::Ansi256(208))) .set_level_color(Level::Warn, Some(Color::Ansi256(208)))
@ -29,7 +84,13 @@ fn main() {
.set_time_format_str("\x1b[30;1m[%Y-%m-%d %H:%M:%S%.3f]\x1b[0m") .set_time_format_str("\x1b[30;1m[%Y-%m-%d %H:%M:%S%.3f]\x1b[0m")
.build(); .build();
let file_config = def_config.clone() let file_config = def_config
.clone()
.set_time_format_str("[%Y-%m-%d %H:%M:%S%.3f]")
.build();
let mail_config = def_config
.clone()
.set_time_format_str("[%Y-%m-%d %H:%M:%S%.3f]") .set_time_format_str("[%Y-%m-%d %H:%M:%S%.3f]")
.build(); .build();
@ -41,6 +102,7 @@ fn main() {
ColorChoice::Auto, ColorChoice::Auto,
), ),
WriteLogger::new(LevelFilter::Debug, file_config, log()), WriteLogger::new(LevelFilter::Debug, file_config, log()),
LogMailer::new(LevelFilter::Warn, mail_config),
]) ])
.unwrap(); .unwrap();

View File

@ -1,19 +1,23 @@
extern crate log;
extern crate simplelog;
mod filter; mod filter;
mod output; mod output;
mod utils; mod utils;
use simplelog::*;
use crate::output::desktop; use crate::output::desktop;
use crate::utils::{get_config, Messenger}; use crate::utils::{get_config, init_logging};
fn main() { fn main() {
let config = get_config(); let config = get_config();
let msg = Messenger::new(&config); let logging = init_logging(&config);
// msg.debug("this is a debug"); CombinedLogger::init(logging).unwrap();
// msg.info("this is a info");
// msg.warning("this is a warning"); warn!("this is a warning");
// msg.error("this is a error"); error!("this is a error");
// println!("{:#?}", config);
// folder::walk(&config.storage.path, config.storage.shuffle, &config.storage.extensions); // folder::walk(&config.storage.path, config.storage.shuffle, &config.storage.extensions);
@ -29,5 +33,5 @@ fn main() {
// println!("{:#?}", utils::get_sec()); // println!("{:#?}", utils::get_sec());
// println!("{:#?}", utils::get_timestamp()); // println!("{:#?}", utils::get_timestamp());
desktop::play(msg, config); desktop::play(config);
} }

View File

@ -5,10 +5,12 @@ use std::{
time::Duration, time::Duration,
}; };
use crate::utils::{sec_to_time, Config, CurrentProgram, Messenger}; use simplelog::*;
pub fn play(msg: Messenger, config: Config) { use crate::utils::{sec_to_time, Config, CurrentProgram};
let get_source = CurrentProgram::new(&msg, config.clone());
pub fn play(config: Config) {
let get_source = CurrentProgram::new(config.clone());
let dec_settings = config.processing.settings.unwrap(); let dec_settings = config.processing.settings.unwrap();
let ff_log_format = format!("level+{}", config.logging.ffmpeg_level); let ff_log_format = format!("level+{}", config.logging.ffmpeg_level);
let mut enc_cmd = vec![ let mut enc_cmd = vec![
@ -35,7 +37,7 @@ pub fn play(msg: Messenger, config: Config) {
enc_cmd.append(&mut enc_filter.iter().map(String::as_str).collect()); enc_cmd.append(&mut enc_filter.iter().map(String::as_str).collect());
msg.debug(format!("Encoder CMD: <bright-blue>{:?}</>", enc_cmd)); debug!("Encoder CMD: <bright-blue>{:?}</>", enc_cmd);
let mut enc_proc = match Command::new("ffplay") let mut enc_proc = match Command::new("ffplay")
.args(enc_cmd) .args(enc_cmd)
@ -44,7 +46,7 @@ pub fn play(msg: Messenger, config: Config) {
.spawn() .spawn()
{ {
Err(e) => { Err(e) => {
msg.error(format!("couldn't spawn encoder process: {}", e)); error!("couldn't spawn encoder process: {}", e);
panic!("couldn't spawn encoder process: {}", e) panic!("couldn't spawn encoder process: {}", e)
} }
Ok(proc) => proc, Ok(proc) => proc,
@ -52,11 +54,11 @@ pub fn play(msg: Messenger, config: Config) {
for node in get_source { for node in get_source {
// println!("Node begin: {:?}", sec_to_time(node.begin.unwrap())); // println!("Node begin: {:?}", sec_to_time(node.begin.unwrap()));
msg.info(format!( info!(
"Play for <yellow>{}</>: <b><magenta>{}</></b>", "Play for <yellow>{}</>: <b><magenta>{}</></b>",
sec_to_time(node.out - node.seek), sec_to_time(node.out - node.seek),
node.source node.source
)); );
let cmd = node.cmd.unwrap(); let cmd = node.cmd.unwrap();
let filter = node.filter.unwrap(); let filter = node.filter.unwrap();
@ -70,7 +72,7 @@ pub fn play(msg: Messenger, config: Config) {
} }
dec_cmd.append(&mut dec_settings.iter().map(String::as_str).collect()); dec_cmd.append(&mut dec_settings.iter().map(String::as_str).collect());
msg.debug(format!("Decoder CMD: <bright-blue>{:?}</>", dec_cmd)); debug!("Decoder CMD: <bright-blue>{:?}</>", dec_cmd);
let mut dec_proc = match Command::new("ffmpeg") let mut dec_proc = match Command::new("ffmpeg")
.args(dec_cmd) .args(dec_cmd)
@ -79,7 +81,7 @@ pub fn play(msg: Messenger, config: Config) {
.spawn() .spawn()
{ {
Err(e) => { Err(e) => {
msg.error(format!("couldn't spawn decoder process: {}", e)); error!("couldn't spawn decoder process: {}", e);
panic!("couldn't spawn decoder process: {}", e) panic!("couldn't spawn decoder process: {}", e)
} }
Ok(proc) => proc, Ok(proc) => proc,
@ -111,7 +113,7 @@ pub fn play(msg: Messenger, config: Config) {
sleep(Duration::from_secs(1)); sleep(Duration::from_secs(1));
match enc_proc.kill() { match enc_proc.kill() {
Ok(_) => msg.info("Playout done...".into()), Ok(_) => info!("Playout done..."),
Err(e) => panic!("Enc error: {:?}", e), Err(e) => panic!("Enc error: {:?}", e),
} }
} }

View File

@ -1,7 +1,9 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fs::File, path::Path}; use std::{fs::File, path::Path};
use crate::utils::{get_date, get_sec, modified_time, time_to_sec, Config, MediaProbe, Messenger}; use simplelog::*;
use crate::utils::{get_date, get_sec, modified_time, time_to_sec, Config, MediaProbe};
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct Playlist { pub struct Playlist {
@ -29,7 +31,7 @@ pub struct Program {
pub next_ad: Option<bool>, pub next_ad: Option<bool>,
} }
pub fn read_json(msg: &Messenger, config: &Config, seek: bool) -> Playlist { pub fn read_json(config: &Config, seek: bool) -> Playlist {
let mut playlist_path = Path::new(&config.playlist.path).to_owned(); let mut playlist_path = Path::new(&config.playlist.path).to_owned();
let start = &config.playlist.day_start; let start = &config.playlist.day_start;
let length = &config.playlist.length; let length = &config.playlist.length;
@ -53,7 +55,7 @@ pub fn read_json(msg: &Messenger, config: &Config, seek: bool) -> Playlist {
length_sec = time_to_sec(length); length_sec = time_to_sec(length);
} }
msg.info(format!("Read Playlist: <b><magenta>{}</></b>", &current_file)); info!("Read Playlist: <b><magenta>{}</></b>", &current_file);
let modify = modified_time(current_file.clone()); let modify = modified_time(current_file.clone());
let f = File::open(&current_file).expect("Could not open json playlist file."); let f = File::open(&current_file).expect("Could not open json playlist file.");

129
src/utils/logging.rs Normal file
View File

@ -0,0 +1,129 @@
extern crate log;
extern crate simplelog;
use std::path::Path;
use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate};
use log::{Level, LevelFilter, Log, Metadata, Record};
use simplelog::*;
use crate::utils;
pub struct LogMailer {
level: LevelFilter,
config: Config,
}
impl LogMailer {
pub fn new(log_level: LevelFilter, config: Config) -> Box<LogMailer> {
Box::new(LogMailer {
level: log_level,
config,
})
}
}
impl Log for LogMailer {
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
metadata.level() <= self.level
}
fn log(&self, record: &Record<'_>) {
if self.enabled(record.metadata()) {
match record.level() {
Level::Error => {
println!("Send Error Mail: {:?}\n{:?}", record, self.config)
}
Level::Warn => {
println!("Send Warn Mail: {:?}", record.args())
}
_ => (),
}
}
}
fn flush(&self) {}
}
impl SharedLogger for LogMailer {
fn level(&self) -> LevelFilter {
self.level
}
fn config(&self) -> Option<&Config> {
Some(&self.config)
}
fn as_log(self: Box<Self>) -> Box<dyn Log> {
Box::new(*self)
}
}
pub fn init_logging(config: &utils::Config) -> Vec<Box<dyn SharedLogger>> {
let app_config = config.logging.clone();
let mut app_logger: Vec<Box<dyn SharedLogger>> = vec![];
let log_config = simplelog::ConfigBuilder::new()
.set_thread_level(LevelFilter::Off)
.set_target_level(LevelFilter::Off)
.set_level_padding(LevelPadding::Left)
.set_time_to_local(app_config.local_time)
.clone();
if app_config.log_to_file {
let file_config = log_config
.clone()
.set_time_format("[%Y-%m-%d %H:%M:%S%.3f]".into())
.build();
let mut log_path = "logs/ffplayout.log".to_string();
if Path::new(&app_config.log_path).is_dir() {
log_path = Path::new(&app_config.log_path)
.join("ffplayout.log")
.display()
.to_string();
} else if Path::new(&app_config.log_path).is_file() {
log_path = app_config.log_path
} else {
println!("Logging path not exists!")
}
let log = || {
FileRotate::new(
log_path,
AppendCount::new(app_config.backup_count),
ContentLimit::Lines(1000),
Compression::None,
)
};
app_logger.push(WriteLogger::new(LevelFilter::Debug, file_config, log()));
} else {
let term_config = log_config
.clone()
.set_level_color(Level::Debug, Some(Color::Ansi256(12)))
.set_level_color(Level::Info, Some(Color::Ansi256(10)))
.set_level_color(Level::Warn, Some(Color::Ansi256(208)))
.set_level_color(Level::Error, Some(Color::Ansi256(9)))
.set_time_format_str("\x1b[30;1m[%Y-%m-%d %H:%M:%S%.3f]\x1b[0m")
.build();
app_logger.push(TermLogger::new(
LevelFilter::Debug,
term_config,
TerminalMode::Mixed,
ColorChoice::Auto,
));
}
if config.mail.recipient.len() > 3 {
let mail_config = log_config
.clone()
.set_time_format_str("[%Y-%m-%d %H:%M:%S%.3f]")
.build();
app_logger.push(LogMailer::new(LevelFilter::Warn, mail_config));
}
app_logger
}

View File

@ -1,112 +0,0 @@
extern crate log;
extern crate simplelog;
use std::path::Path;
use file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate};
use simplelog::*;
use crate::utils::Config;
#[derive(Debug, Clone)]
pub struct Messenger {
level: String,
// ffmpeg_level: String,
}
impl Messenger {
pub fn new(config: &Config) -> Self {
let conf = config.logging.clone();
let log_config = simplelog::ConfigBuilder::new()
.set_thread_level(LevelFilter::Off)
.set_target_level(LevelFilter::Off)
.set_level_padding(LevelPadding::Left)
.set_time_to_local(conf.local_time)
.clone();
if conf.log_to_file {
let file_config = log_config
.clone()
.set_time_format("[%Y-%m-%d %H:%M:%S%.3f]".into())
.build();
let mut log_path = "logs/ffplayout.log".to_string();
if Path::new(&conf.log_path).is_dir() {
log_path = Path::new(&conf.log_path)
.join("ffplayout.log")
.display()
.to_string();
} else if Path::new(&conf.log_path).is_file() {
log_path = conf.log_path
} else {
println!("Logging path not exists!")
}
let log = || {
FileRotate::new(
log_path,
AppendCount::new(conf.backup_count),
ContentLimit::Lines(1000),
Compression::None,
)
};
WriteLogger::init(LevelFilter::Debug, file_config, log()).unwrap();
} else {
let term_config = log_config
.clone()
.set_level_color(Level::Debug, Some(Color::Ansi256(12)))
.set_level_color(Level::Info, Some(Color::Ansi256(10)))
.set_level_color(Level::Warn, Some(Color::Ansi256(208)))
.set_level_color(Level::Error, Some(Color::Ansi256(9)))
.set_time_format_str("\x1b[30;1m[%Y-%m-%d %H:%M:%S%.3f]\x1b[0m")
.build();
TermLogger::init(
LevelFilter::Debug,
term_config,
TerminalMode::Mixed,
ColorChoice::Auto,
)
.unwrap();
}
Messenger {
level: conf.log_level,
// ffmpeg_level: conf.ffmpeg_level,
}
}
pub fn debug(&self, msg: String) {
if self.level.to_lowercase() == "debug".to_string() {
debug!("{}", msg)
}
}
pub fn info(&self, msg: String) {
if self.level.to_lowercase() == "debug".to_string()
|| self.level.to_lowercase() == "info".to_string()
{
info!("{}", msg)
}
}
pub fn warning(&self, msg: String) {
if self.level.to_lowercase() == "debug".to_string()
|| self.level.to_lowercase() == "info".to_string()
|| self.level.to_lowercase() == "warning".to_string()
{
warn!("{}", msg)
}
}
pub fn error(&self, msg: String) {
if self.level.to_lowercase() == "debug".to_string()
|| self.level.to_lowercase() == "info".to_string()
|| self.level.to_lowercase() == "warning".to_string()
|| self.level.to_lowercase() == "error".to_string()
{
error!("{}", msg)
}
}
}

View File

@ -7,14 +7,14 @@ use std::{fs::metadata, process, time, time::UNIX_EPOCH};
mod arg_parse; mod arg_parse;
mod config; mod config;
mod json_reader; mod json_reader;
mod messenger; mod logging;
mod playlist; mod playlist;
pub use arg_parse::get_args; pub use arg_parse::get_args;
pub use config::{get_config, Config}; pub use config::{get_config, Config};
// pub use folder::walk; // pub use folder::walk;
pub use json_reader::{read_json, Program}; pub use json_reader::{read_json, Program};
pub use messenger::Messenger; pub use logging::init_logging;
pub use playlist::CurrentProgram; pub use playlist::CurrentProgram;
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]

View File

@ -1,15 +1,16 @@
use std::path::Path; use std::path::Path;
use simplelog::*;
use crate::utils::{ use crate::utils::{
check_sync, gen_dummy, get_delta, check_sync, gen_dummy, get_delta,
json_reader::{read_json, Program}, json_reader::{read_json, Program},
modified_time, Config, MediaProbe, Messenger, modified_time, Config, MediaProbe,
}; };
use crate::filter::filter_chains; use crate::filter::filter_chains;
pub struct CurrentProgram { pub struct CurrentProgram {
msg: Messenger,
config: Config, config: Config,
json_mod: String, json_mod: String,
json_path: String, json_path: String,
@ -19,11 +20,10 @@ pub struct CurrentProgram {
} }
impl CurrentProgram { impl CurrentProgram {
pub fn new(msg: &Messenger, config: Config) -> Self { pub fn new(config: Config) -> Self {
let json = read_json(&msg, &config, true); let json = read_json(&config, true);
Self { Self {
msg: msg.clone(),
config: config, config: config,
json_mod: json.modified.unwrap(), json_mod: json.modified.unwrap(),
json_path: json.current_file.unwrap(), json_path: json.current_file.unwrap(),
@ -38,7 +38,7 @@ impl CurrentProgram {
if !mod_time.unwrap().to_string().eq(&self.json_mod) { if !mod_time.unwrap().to_string().eq(&self.json_mod) {
// when playlist has changed, reload it // when playlist has changed, reload it
let json = read_json(&self.msg, &self.config, false); let json = read_json(&self.config, false);
self.json_mod = json.modified.unwrap(); self.json_mod = json.modified.unwrap();
self.nodes = json.program.into(); self.nodes = json.program.into();
@ -68,8 +68,8 @@ impl Iterator for CurrentProgram {
last = true last = true
} }
self.msg.debug(format!("Last: <b><magenta>{}</></b>", self.nodes[self.idx - 1].source)); debug!("Last: <b><magenta>{}</></b>", self.nodes[self.idx - 1].source);
self.msg.debug(format!("Next: <b><magenta>{}</></b>", self.nodes[self.idx + 1].source)); debug!("Next: <b><magenta>{}</></b>", self.nodes[self.idx + 1].source);
self.idx += 1; self.idx += 1;
@ -80,7 +80,7 @@ impl Iterator for CurrentProgram {
if !self.init { if !self.init {
let delta = get_delta(&current.begin.unwrap(), &self.config); let delta = get_delta(&current.begin.unwrap(), &self.config);
self.msg.debug(format!("Delta: <yellow>{delta}</>")); debug!("Delta: <yellow>{delta}</>");
check_sync(delta, &self.config); check_sync(delta, &self.config);
} }
@ -88,7 +88,7 @@ impl Iterator for CurrentProgram {
self.append_probe(&mut current); self.append_probe(&mut current);
self.add_filter(&mut current, last, next); self.add_filter(&mut current, last, next);
} else { } else {
self.msg.error(format!("File not found: {}", current.source)); error!("File not found: {}", current.source);
let dummy = gen_dummy(current.out - current.seek, &self.config); let dummy = gen_dummy(current.out - current.seek, &self.config);
current.source = dummy.0; current.source = dummy.0;
current.cmd = Some(dummy.1); current.cmd = Some(dummy.1);
@ -105,7 +105,7 @@ impl Iterator for CurrentProgram {
last = true last = true
} }
let json = read_json(&self.msg, &self.config, false); let json = read_json(&self.config, false);
self.json_mod = json.modified.unwrap(); self.json_mod = json.modified.unwrap();
self.json_path = json.current_file.unwrap(); self.json_path = json.current_file.unwrap();
self.nodes = json.program.into(); self.nodes = json.program.into();
@ -119,7 +119,7 @@ impl Iterator for CurrentProgram {
if !self.init { if !self.init {
let delta = get_delta(&current.begin.unwrap(), &self.config); let delta = get_delta(&current.begin.unwrap(), &self.config);
self.msg.debug(format!("Delta: {delta}")); debug!("Delta: {delta}");
check_sync(delta, &self.config); check_sync(delta, &self.config);
} }
@ -127,7 +127,7 @@ impl Iterator for CurrentProgram {
self.append_probe(&mut current); self.append_probe(&mut current);
self.add_filter(&mut current, last, next); self.add_filter(&mut current, last, next);
} else { } else {
self.msg.error(format!("File not found: {}", current.source)); error!("File not found: {}", current.source);
let dummy = gen_dummy(current.out - current.seek, &self.config); let dummy = gen_dummy(current.out - current.seek, &self.config);
current.source = dummy.0; current.source = dummy.0;
current.cmd = Some(dummy.1); current.cmd = Some(dummy.1);