anki/rslib/src/log.rs

90 lines
2.8 KiB
Rust
Raw Normal View History

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
pub use slog::{debug, error, Logger};
use slog::{slog_o, Drain};
use slog_async::OverflowStrategy;
use std::fs::OpenOptions;
2020-03-10 01:33:14 +01:00
use std::{fs, io};
const LOG_ROTATE_BYTES: u64 = 50 * 1024 * 1024;
pub(crate) fn terminal() -> Logger {
let decorator = slog_term::TermDecorator::new().build();
let drain = slog_term::FullFormat::new(decorator).build().fuse();
let drain = slog_envlogger::new(drain);
let drain = slog_async::Async::new(drain)
.chan_size(1_024)
.overflow_strategy(OverflowStrategy::Block)
.build()
.fuse();
Logger::root(drain, slog_o!())
}
2020-03-10 01:33:14 +01:00
fn file(path: &str) -> io::Result<Logger> {
maybe_rotate_log(path)?;
let file = OpenOptions::new().create(true).append(true).open(path)?;
let decorator = slog_term::PlainSyncDecorator::new(file);
let drain = slog_term::FullFormat::new(decorator).build().fuse();
let drain = slog_envlogger::new(drain);
2020-04-06 07:57:13 +02:00
if std::env::var("LOGTERM").is_ok() {
// log to the terminal as well
let decorator = slog_term::TermDecorator::new().build();
let term_drain = slog_term::FullFormat::new(decorator).build().fuse();
let term_drain = slog_envlogger::new(term_drain);
let joined_drain = slog::Duplicate::new(drain, term_drain).fuse();
let drain = slog_async::Async::new(joined_drain)
.chan_size(1_024)
.overflow_strategy(OverflowStrategy::Block)
.build()
.fuse();
Ok(Logger::root(drain, slog_o!()))
} else {
let drain = slog_async::Async::new(drain)
.chan_size(1_024)
.overflow_strategy(OverflowStrategy::Block)
.build()
.fuse();
Ok(Logger::root(drain, slog_o!()))
}
}
2020-03-10 01:33:14 +01:00
fn maybe_rotate_log(path: &str) -> io::Result<()> {
let current_bytes = match fs::metadata(path) {
Ok(meta) => meta.len(),
Err(e) => {
if e.kind() == io::ErrorKind::NotFound {
0
} else {
return Err(e);
}
}
};
if current_bytes < LOG_ROTATE_BYTES {
return Ok(());
}
let path2 = format!("{}.1", path);
let path3 = format!("{}.2", path);
// if a rotated file already exists, rename it
if let Err(e) = fs::rename(&path2, &path3) {
if e.kind() != io::ErrorKind::NotFound {
return Err(e);
}
}
// and rotate the primary log
fs::rename(path, path2)
}
/// Get a logger, logging to a file if a path was provided, otherwise terminal.
2020-03-10 01:33:14 +01:00
pub(crate) fn default_logger(path: Option<&str>) -> io::Result<Logger> {
Ok(match path {
Some(path) => file(path)?,
None => terminal(),
})
}