use std::path::{Path, PathBuf};
use uninode::UniNode;
use tracing::Metadata;
use tracing_appender::non_blocking::{NonBlocking, WorkerGuard};
use tracing_appender::rolling::{RollingFileAppender, Rotation};
use tracing_subscriber::fmt::writer::{BoxMakeWriter, MakeWriter};
use super::LoggerError;
struct NonBlokingWriter {
writer: NonBlocking,
_guard: WorkerGuard,
}
impl<'a> MakeWriter<'a> for NonBlokingWriter {
type Writer = NonBlocking;
fn make_writer(&self) -> Self::Writer {
self.writer.make_writer()
}
fn make_writer_for(&'a self, meta: &Metadata) -> Self::Writer {
self.writer.make_writer_for(meta)
}
}
fn create_console_appender(cfg: &UniNode) -> Result<BoxMakeWriter, LoggerError> {
if cfg.find_bool("sync").unwrap_or(false) {
Ok(BoxMakeWriter::new(std::io::stdout))
} else {
let (non_blocking, guard) = tracing_appender::non_blocking(std::io::stdout());
let writer = NonBlokingWriter {
writer: non_blocking,
_guard: guard,
};
Ok(BoxMakeWriter::new(writer))
}
}
struct BlockingFileWriter {
rotation: Rotation,
directory: PathBuf,
file_name_prefix: PathBuf,
}
impl<'a> MakeWriter<'a> for BlockingFileWriter {
type Writer = RollingFileAppender;
fn make_writer(&self) -> Self::Writer {
RollingFileAppender::new(
self.rotation.clone(),
self.directory.clone(),
self.file_name_prefix.clone(),
)
}
}
fn create_file_appender(cfg: &UniNode) -> Result<BoxMakeWriter, LoggerError> {
let file_path = cfg
.find_str("path")
.map(Path::new)
.ok_or(LoggerError::InvalidOuputPath)?;
let dir = file_path
.parent()
.and_then(|dir| dir.canonicalize().ok())
.ok_or(LoggerError::InvalidOuputPath)?;
let file = file_path
.file_name()
.map(Path::new)
.ok_or(LoggerError::InvalidOuputPath)?;
let rotation = cfg
.find_str("rotation")
.map(|v| v.to_lowercase())
.unwrap_or_else(|| String::from("never"));
let rotation = match rotation.as_str() {
"minutely" => Rotation::MINUTELY,
"hourly" => Rotation::HOURLY,
"daily" => Rotation::DAILY,
_ => Rotation::NEVER,
};
if cfg.find_bool("sync").unwrap_or(false) {
let writer = BlockingFileWriter {
rotation,
directory: dir,
file_name_prefix: file.to_path_buf(),
};
Ok(BoxMakeWriter::new(writer))
} else {
let file_appender = RollingFileAppender::new(rotation, dir, file);
let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
Ok(BoxMakeWriter::new(NonBlokingWriter {
writer: non_blocking,
_guard: guard,
}))
}
}
pub fn create_appender(cfg: &UniNode) -> Result<BoxMakeWriter, LoggerError> {
let appender = cfg
.find_str("appender")
.map(|v| v.to_lowercase())
.ok_or(LoggerError::NotDefineAppender)?;
match appender.as_str() {
"file" => create_file_appender(cfg),
"console" => create_console_appender(cfg),
_ => Err(LoggerError::UnknownAppender(appender)),
}
}