Skip to content

Instantly share code, notes, and snippets.

@thebluefish
Last active November 3, 2020 03:39
Show Gist options
  • Save thebluefish/6346dda79d898f7bcd4ae58bc868551c to your computer and use it in GitHub Desktop.
Save thebluefish/6346dda79d898f7bcd4ae58bc868551c to your computer and use it in GitHub Desktop.
A simple fern-based logging implementation demonstrating formatting for multiple outputs
use colored::*;
use log::*;
use std::{fs, thread::ThreadId};
/// Please ignore the evil that is parsing debug output
fn parse_thread_id(id: &ThreadId) -> String {
let id_str = format!("{:?}", id);
let parsed = (|| {
let start_idx = id_str.find('(')?;
let end_idx = id_str.rfind(')')?;
Some(id_str[start_idx+1..end_idx].to_owned())
})();
parsed.unwrap_or(id_str)
}
/// app_name **must** match Cargo.toml's package.name, or else app-level logging won't work properly!
pub fn setup(app_name: impl AsRef<str>) -> Result<(), fern::InitError> {
let log_root = format_args!("logs/{}", app_name.as_ref()).to_string();
fs::create_dir_all(log_root.clone())?;
log_panics::init();
// Pretty print for stdout
// Logging lib errors and all app logs
let stdout_dispatch = fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{}[{}][{}][{}] {}",
chrono::Utc::now().format("[%Y-%m-%d][%H:%M:%S]"),
parse_thread_id(&std::thread::current().id()),
match record.level() {
Level::Error => format!("{}", record.level()).red(),
Level::Warn => format!("{}", record.level()).red().italic(),
Level::Info => format!("{}", record.level()).cyan(),
Level::Debug => format!("{}", record.level()).yellow(),
Level::Trace => format!("{}", record.level()).normal(),
},
record.target(),
message
))
})
.level(LevelFilter::Error)
.level_for(app_name.as_ref().to_string(), LevelFilter::Trace)
.chain(std::io::stdout());
let log_file_root = format!("{}/{}.{}", log_root.clone(), app_name.as_ref().to_string(), chrono::Utc::now().format("%Y_%m_%d"));
// Produce a file that contains all app logs
let out_file_dispatch = fern::Dispatch::new()
.level(LevelFilter::Off)
.level_for(app_name.as_ref().to_string(), LevelFilter::Trace)
.chain(fern::log_file(format!("{}.log", log_file_root))?);
// Produce a separate file that contains all debug and error logs
let debug_file_dispatch = fern::Dispatch::new()
.level(LevelFilter::Debug)
.chain(fern::log_file(format!("{}.debug.log", log_file_root))?);
// Produce a separate file that contains all logs
let full_file_dispatch = fern::Dispatch::new()
.chain(fern::log_file(format!("{}.full.log", log_file_root))?);
let files_dispatch = fern::Dispatch::new()
// Format for files
.format(|out, message, record| {
out.finish(format_args!(
"{}[{}][{}][{}] {}",
chrono::Utc::now().format("[%Y-%m-%d %H:%M:%S]"),
parse_thread_id(&std::thread::current().id()),
record.level(),
record.target(),
message
))
})
.chain(out_file_dispatch)
.chain(debug_file_dispatch)
.chain(full_file_dispatch)
;
// This is the main logging dispatch
fern::Dispatch::new()
.chain(stdout_dispatch)
.chain(files_dispatch)
.apply()?;
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment