use crate::{
default_format,
writers::{FileLogWriter, FileLogWriterConfig, LogWriter},
Cleanup, Criterion, FileSpec, FlexiLoggerError, FormatFunction, LogfileSelector, Naming,
WriteMode,
};
use std::{path::PathBuf, sync::Arc};
use super::{RotationConfig, State};
#[allow(clippy::struct_excessive_bools, clippy::module_name_repetitions)]
pub struct FileLogWriterBuilder {
cfg_print_message: bool,
cfg_append: bool,
cfg_write_mode: WriteMode,
file_spec: FileSpec,
cfg_o_create_symlink: Option<PathBuf>,
cfg_line_ending: &'static [u8],
format: FormatFunction,
o_rotation_config: Option<RotationConfig>,
max_log_level: log::LevelFilter,
cleanup_in_background_thread: bool,
use_utc: bool,
}
impl FileLogWriterBuilder {
pub(crate) fn new(file_spec: FileSpec) -> Self {
Self {
o_rotation_config: None,
cfg_print_message: false,
file_spec,
cfg_append: false,
cfg_write_mode: WriteMode::Direct,
cfg_o_create_symlink: None,
cfg_line_ending: super::UNIX_LINE_ENDING,
format: default_format,
max_log_level: log::LevelFilter::Trace,
cleanup_in_background_thread: true,
use_utc: false,
}
}
#[must_use]
pub fn print_message(mut self) -> Self {
self.cfg_print_message = true;
self
}
#[must_use]
pub fn format(mut self, format: FormatFunction) -> Self {
self.format = format;
self
}
#[must_use]
pub fn cleanup_in_background_thread(mut self, use_background_thread: bool) -> Self {
self.cleanup_in_background_thread = use_background_thread;
self
}
#[must_use]
pub fn rotate(mut self, criterion: Criterion, naming: Naming, cleanup: Cleanup) -> Self {
self.o_rotation_config = Some(RotationConfig {
criterion,
naming,
cleanup,
});
self.file_spec.if_default_use_timestamp(false);
self
}
#[must_use]
pub(crate) fn file_spec(mut self, mut file_spec: FileSpec) -> Self {
if self.o_rotation_config.is_some() {
file_spec.if_default_use_timestamp(false);
}
self.file_spec = file_spec;
self
}
#[must_use]
pub fn append(mut self) -> Self {
self.cfg_append = true;
self
}
#[must_use]
pub fn max_level(mut self, max_log_level: log::LevelFilter) -> Self {
self.max_log_level = max_log_level;
self
}
#[must_use]
pub fn use_utc(mut self) -> Self {
self.file_spec.use_utc = true;
self.use_utc = true;
self
}
#[must_use]
pub fn create_symlink<P: Into<PathBuf>>(mut self, symlink: P) -> Self {
self.cfg_o_create_symlink = Some(symlink.into());
self
}
#[must_use]
pub fn use_windows_line_ending(mut self) -> Self {
self.cfg_line_ending = super::WINDOWS_LINE_ENDING;
self
}
#[must_use]
pub fn write_mode(mut self, write_mode: WriteMode) -> Self {
self.cfg_write_mode = write_mode;
self
}
pub(crate) fn assert_write_mode(&self, write_mode: WriteMode) -> Result<(), FlexiLoggerError> {
if self.cfg_write_mode == write_mode {
Ok(())
} else {
Err(FlexiLoggerError::Reset)
}
}
#[must_use]
pub(crate) fn get_write_mode(&self) -> &WriteMode {
&self.cfg_write_mode
}
pub fn try_build(self) -> Result<FileLogWriter, FlexiLoggerError> {
Ok(FileLogWriter::new(
self.try_build_state()?,
self.max_log_level,
self.format,
))
}
pub fn try_build_with_handle(
self,
) -> Result<(ArcFileLogWriter, FileLogWriterHandle), FlexiLoggerError> {
Ok(ArcFileLogWriter::new_with_handle(FileLogWriter::new(
self.try_build_state()?,
self.max_log_level,
self.format,
)))
}
pub(super) fn try_build_state(&self) -> Result<State, FlexiLoggerError> {
let parent = self.file_spec.get_directory();
std::fs::create_dir_all(&parent)?;
if !std::fs::metadata(&parent)?.is_dir() {
return Err(FlexiLoggerError::OutputBadDirectory);
}
#[cfg(feature = "async")]
let cleanup_in_background_thread = if let WriteMode::AsyncWith {
pool_capa: _,
message_capa: _,
flush_interval: _,
} = self.cfg_write_mode
{
false
} else {
self.cleanup_in_background_thread
};
#[cfg(not(feature = "async"))]
let cleanup_in_background_thread = self.cleanup_in_background_thread;
Ok(State::new(
FileLogWriterConfig {
print_message: self.cfg_print_message,
append: self.cfg_append,
line_ending: self.cfg_line_ending,
write_mode: self.cfg_write_mode,
file_spec: self.file_spec.clone(),
o_create_symlink: self.cfg_o_create_symlink.clone(),
use_utc: self.use_utc,
},
self.o_rotation_config.clone(),
cleanup_in_background_thread,
))
}
}
impl FileLogWriterBuilder {
#[must_use]
pub fn o_print_message(mut self, print_message: bool) -> Self {
self.cfg_print_message = print_message;
self
}
#[must_use]
pub fn o_rotate(mut self, rotate_config: Option<(Criterion, Naming, Cleanup)>) -> Self {
if let Some((criterion, naming, cleanup)) = rotate_config {
self.o_rotation_config = Some(RotationConfig {
criterion,
naming,
cleanup,
});
self.file_spec.if_default_use_timestamp(false);
} else {
self.o_rotation_config = None;
self.file_spec.if_default_use_timestamp(true);
}
self
}
#[must_use]
pub fn o_append(mut self, append: bool) -> Self {
self.cfg_append = append;
self
}
#[must_use]
pub fn o_create_symlink<S: Into<PathBuf>>(mut self, symlink: Option<S>) -> Self {
self.cfg_o_create_symlink = symlink.map(Into::into);
self
}
}
pub struct ArcFileLogWriter(Arc<FileLogWriter>);
impl ArcFileLogWriter {
pub(crate) fn new_with_handle(flw: FileLogWriter) -> (Self, FileLogWriterHandle) {
let a_flw = Arc::new(flw);
(Self(Arc::clone(&a_flw)), FileLogWriterHandle(a_flw))
}
}
impl std::ops::Deref for ArcFileLogWriter {
type Target = FileLogWriter;
fn deref(&self) -> &FileLogWriter {
&(self.0)
}
}
impl Clone for ArcFileLogWriter {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl std::io::Write for ArcFileLogWriter {
fn write(&mut self, buffer: &[u8]) -> std::result::Result<usize, std::io::Error> {
(*self.0).plain_write(buffer)
}
fn flush(&mut self) -> std::result::Result<(), std::io::Error> {
LogWriter::flush(&*self.0)
}
}
pub struct FileLogWriterHandle(Arc<FileLogWriter>);
impl Drop for FileLogWriterHandle {
fn drop(&mut self) {
self.0.shutdown();
}
}
impl FileLogWriterHandle {
pub fn existing_log_files(
&self,
selector: &LogfileSelector,
) -> Result<Vec<PathBuf>, FlexiLoggerError> {
self.0.existing_log_files(selector)
}
#[doc(hidden)]
pub fn validate_logs(&self, expected: &[(&'static str, &'static str, &'static str)]) {
self.0.validate_logs(expected);
}
}