diff --git a/src/build.rs b/src/build.rs index 2be1baa..b6121ad 100644 --- a/src/build.rs +++ b/src/build.rs @@ -12,6 +12,7 @@ use slog::{Drain, FnValue, Logger}; use slog_async::Async; #[cfg(feature = "slog-kvfilter")] use slog_kvfilter::KVFilter; +use slog_term::Decorator; use std::fmt::Debug; use std::panic::{RefUnwindSafe, UnwindSafe}; @@ -21,6 +22,19 @@ pub trait Build { fn build(&self) -> Result; } +/// This trait allows to build a logger instance with a custom format (i.e., [`Drain`]}. +pub trait BuildWithCustomFormat { + /// [`Decorator`] type generated by the logger builder. + type Decorator: Decorator; + + /// Builds a logger with a custom format. + fn build_with_custom_format(&self, f: F) -> Result + where + F: FnOnce(Self::Decorator) -> Result, + D: Drain + Send + 'static, + D::Err: Debug; +} + /// Logger builder. #[derive(Debug)] #[allow(clippy::large_enum_variant)] diff --git a/src/file.rs b/src/file.rs index f2f8b32..482a5d3 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,16 +1,16 @@ //! File logger. use crate::build::BuilderCommon; -use crate::misc; use crate::permissions::restrict_file_permissions; #[cfg(feature = "slog-kvfilter")] use crate::types::KVFilterParameters; use crate::types::{Format, OverflowStrategy, Severity, SourceLocation, TimeZone}; +use crate::{misc, BuildWithCustomFormat}; use crate::{Build, Config, ErrorKind, Result}; use chrono::{DateTime, Local, TimeZone as ChronoTimeZone, Utc}; #[cfg(feature = "libflate")] use libflate::gzip::Encoder as GzipEncoder; use serde::{Deserialize, Serialize}; -use slog::Logger; +use slog::{Drain, Logger}; use slog_term::{CompactFormat, FullFormat, PlainDecorator}; use std::fmt::Debug; use std::fs::{self, File, OpenOptions}; @@ -178,6 +178,37 @@ impl Build for FileLoggerBuilder { Ok(logger) } } +impl BuildWithCustomFormat for FileLoggerBuilder { + type Decorator = FileLoggerDecorator; + + fn build_with_custom_format(&self, f: F) -> Result + where + F: FnOnce(Self::Decorator) -> Result, + D: Drain + Send + 'static, + D::Err: Debug, + { + let decorator = FileLoggerDecorator(PlainDecorator::new(self.appender.clone())); + let drain = track!(f(decorator))?; + Ok(self.common.build_with_drain(drain)) + } +} + +/// [`slog_term::Decorator`] implementation for [`FileLoggerBuilder`]. +pub struct FileLoggerDecorator(PlainDecorator); + +impl slog_term::Decorator for FileLoggerDecorator { + fn with_record( + &self, + record: &slog::Record, + logger_values: &slog::OwnedKVList, + f: F, + ) -> io::Result<()> + where + F: FnOnce(&mut dyn slog_term::RecordDecorator) -> io::Result<()>, + { + self.0.with_record(record, logger_values, f) + } +} #[derive(Debug)] struct FileAppender { diff --git a/src/lib.rs b/src/lib.rs index b5ff470..4244f6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,7 +48,7 @@ extern crate slog; #[macro_use] extern crate trackable; -pub use build::{Build, LoggerBuilder}; +pub use build::{Build, BuildWithCustomFormat, LoggerBuilder}; pub use config::{Config, LoggerConfig}; pub use error::{Error, ErrorKind}; pub use misc::set_stdlog_logger; diff --git a/src/terminal.rs b/src/terminal.rs index c685fc7..73a79dc 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -1,12 +1,12 @@ //! Terminal logger. use crate::build::BuilderCommon; -use crate::misc; #[cfg(feature = "slog-kvfilter")] use crate::types::KVFilterParameters; use crate::types::{Format, OverflowStrategy, Severity, SourceLocation, TimeZone}; +use crate::{misc, BuildWithCustomFormat}; use crate::{Build, Config, Result}; use serde::{Deserialize, Serialize}; -use slog::Logger; +use slog::{Drain, Logger}; use slog_term::{self, CompactFormat, FullFormat, PlainDecorator, TermDecorator}; use std::fmt::Debug; use std::io; @@ -120,6 +120,37 @@ impl Build for TerminalLoggerBuilder { Ok(logger) } } +impl BuildWithCustomFormat for TerminalLoggerBuilder { + type Decorator = TerminalLoggerDecorator; + + fn build_with_custom_format(&self, f: F) -> Result + where + F: FnOnce(Self::Decorator) -> Result, + D: Drain + Send + 'static, + D::Err: Debug, + { + let decorator = TerminalLoggerDecorator(self.destination.to_decorator()); + let drain = track!(f(decorator))?; + Ok(self.common.build_with_drain(drain)) + } +} + +/// [`slog_term::Decorator`] implementation for [`TerminalLoggerBuilder`]. +pub struct TerminalLoggerDecorator(Decorator); + +impl slog_term::Decorator for TerminalLoggerDecorator { + fn with_record( + &self, + record: &slog::Record, + logger_values: &slog::OwnedKVList, + f: F, + ) -> io::Result<()> + where + F: FnOnce(&mut dyn slog_term::RecordDecorator) -> io::Result<()>, + { + self.0.with_record(record, logger_values, f) + } +} /// The destination to which log records will be outputted. ///