use crate::build::BuilderCommon;
#[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::{Drain, Logger};
use slog_term::{self, CompactFormat, FullFormat, PlainDecorator, TermDecorator};
use std::fmt::Debug;
use std::io;
#[derive(Debug)]
pub struct TerminalLoggerBuilder {
common: BuilderCommon,
format: Format,
timezone: TimeZone,
destination: Destination,
}
impl TerminalLoggerBuilder {
pub fn new() -> Self {
TerminalLoggerBuilder {
common: BuilderCommon::default(),
format: Format::default(),
timezone: TimeZone::default(),
destination: Destination::default(),
}
}
pub fn format(&mut self, format: Format) -> &mut Self {
self.format = format;
self
}
pub fn source_location(&mut self, source_location: SourceLocation) -> &mut Self {
self.common.source_location = source_location;
self
}
pub fn overflow_strategy(&mut self, overflow_strategy: OverflowStrategy) -> &mut Self {
self.common.overflow_strategy = overflow_strategy;
self
}
pub fn timezone(&mut self, timezone: TimeZone) -> &mut Self {
self.timezone = timezone;
self
}
pub fn destination(&mut self, destination: Destination) -> &mut Self {
self.destination = destination;
self
}
pub fn level(&mut self, severity: Severity) -> &mut Self {
self.common.level = severity;
self
}
pub fn channel_size(&mut self, channel_size: usize) -> &mut Self {
self.common.channel_size = channel_size;
self
}
#[cfg(feature = "slog-kvfilter")]
pub fn kvfilter(&mut self, parameters: KVFilterParameters) -> &mut Self {
self.common.kvfilterparameters = Some(parameters);
self
}
}
impl Default for TerminalLoggerBuilder {
fn default() -> Self {
Self::new()
}
}
impl Build for TerminalLoggerBuilder {
fn build(&self) -> Result<Logger> {
let decorator = self.destination.to_decorator();
let timestamp = misc::timezone_to_timestamp_fn(self.timezone);
let logger = match self.format {
Format::Full => {
let format = FullFormat::new(decorator).use_custom_timestamp(timestamp);
self.common.build_with_drain(format.build())
}
Format::Compact => {
let format = CompactFormat::new(decorator).use_custom_timestamp(timestamp);
self.common.build_with_drain(format.build())
}
#[cfg(feature = "json")]
Format::Json => match self.destination {
Destination::Stdout => self.common.build_with_drain(
slog_json::Json::new(std::io::stdout())
.set_flush(true)
.add_default_keys()
.build(),
),
Destination::Stderr => self.common.build_with_drain(
slog_json::Json::new(std::io::stderr())
.set_flush(true)
.add_default_keys()
.build(),
),
},
};
Ok(logger)
}
}
impl BuildWithCustomFormat for TerminalLoggerBuilder {
type Decorator = TerminalLoggerDecorator;
fn build_with_custom_format<F, D>(&self, f: F) -> Result<Logger>
where
F: FnOnce(Self::Decorator) -> Result<D>,
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))
}
}
pub struct TerminalLoggerDecorator(Decorator);
impl slog_term::Decorator for TerminalLoggerDecorator {
fn with_record<F>(
&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(Default, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Destination {
Stdout,
#[default]
Stderr,
}
impl Destination {
fn to_decorator(self) -> Decorator {
let maybe_term_decorator = match self {
Destination::Stdout => TermDecorator::new().stdout().try_build(),
Destination::Stderr => TermDecorator::new().stderr().try_build(),
};
maybe_term_decorator
.map(Decorator::Term)
.unwrap_or_else(|| match self {
Destination::Stdout => Decorator::PlainStdout(PlainDecorator::new(io::stdout())),
Destination::Stderr => Decorator::PlainStderr(PlainDecorator::new(io::stderr())),
})
}
}
enum Decorator {
Term(TermDecorator),
PlainStdout(PlainDecorator<io::Stdout>),
PlainStderr(PlainDecorator<io::Stderr>),
}
impl slog_term::Decorator for Decorator {
fn with_record<F>(
&self,
record: &slog::Record,
logger_values: &slog::OwnedKVList,
f: F,
) -> io::Result<()>
where
F: FnOnce(&mut dyn slog_term::RecordDecorator) -> io::Result<()>,
{
match *self {
Decorator::Term(ref d) => d.with_record(record, logger_values, f),
Decorator::PlainStdout(ref d) => d.with_record(record, logger_values, f),
Decorator::PlainStderr(ref d) => d.with_record(record, logger_values, f),
}
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[non_exhaustive]
pub struct TerminalLoggerConfig {
#[serde(default)]
pub level: Severity,
#[serde(default)]
pub format: Format,
#[serde(default)]
pub source_location: SourceLocation,
#[serde(default)]
pub timezone: TimeZone,
#[serde(default)]
pub destination: Destination,
#[serde(default = "default_channel_size")]
pub channel_size: usize,
#[serde(default)]
pub overflow_strategy: OverflowStrategy,
}
impl TerminalLoggerConfig {
pub fn new() -> Self {
Default::default()
}
}
impl Config for TerminalLoggerConfig {
type Builder = TerminalLoggerBuilder;
fn try_to_builder(&self) -> Result<Self::Builder> {
let mut builder = TerminalLoggerBuilder::new();
builder.level(self.level);
builder.format(self.format);
builder.source_location(self.source_location);
builder.timezone(self.timezone);
builder.destination(self.destination);
builder.channel_size(self.channel_size);
builder.overflow_strategy(self.overflow_strategy);
Ok(builder)
}
}
fn default_channel_size() -> usize {
1024
}