use std::{
borrow::Cow,
cmp, fmt, fs, io,
io::Write,
sync::{mpsc::Sender, Arc, Mutex},
};
#[cfg(feature = "date-based")]
use std::path::{Path, PathBuf};
#[cfg(all(not(windows), any(feature = "syslog-4", feature = "syslog-6")))]
use std::collections::HashMap;
#[cfg(all(not(windows), feature = "syslog-7"))]
use std::collections::BTreeMap;
use log::Log;
use crate::{log_impl, Filter, FormatCallback, Formatter};
#[cfg(feature = "date-based")]
use crate::log_impl::DateBasedState;
#[cfg(all(not(windows), feature = "syslog-4"))]
use crate::{Syslog4Rfc3164Logger, Syslog4Rfc5424Logger, Syslog4TransformFn};
#[cfg(all(not(windows), feature = "syslog-6"))]
use crate::{Syslog6Rfc3164Logger, Syslog6Rfc5424Logger, Syslog6TransformFn};
#[cfg(all(not(windows), feature = "syslog-7"))]
use crate::{Syslog7Rfc3164Logger, Syslog7Rfc5424Logger, Syslog7TransformFn};
#[must_use = "this is only a logger configuration and must be consumed with into_log() or apply()"]
pub struct Dispatch {
format: Option<Box<Formatter>>,
children: Vec<OutputInner>,
default_level: log::LevelFilter,
levels: Vec<(Cow<'static, str>, log::LevelFilter)>,
filters: Vec<Box<Filter>>,
}
#[derive(Clone)]
pub struct SharedDispatch {
inner: Arc<log_impl::Dispatch>,
min_level: log::LevelFilter,
}
impl Dispatch {
#[inline]
pub fn new() -> Self {
Dispatch {
format: None,
children: Vec::new(),
default_level: log::LevelFilter::Trace,
levels: Vec::new(),
filters: Vec::new(),
}
}
#[inline]
pub fn format<F>(mut self, formatter: F) -> Self
where
F: Fn(FormatCallback, &fmt::Arguments, &log::Record) + Sync + Send + 'static,
{
self.format = Some(Box::new(formatter));
self
}
#[inline]
pub fn chain<T: Into<Output>>(mut self, logger: T) -> Self {
self.children.push(logger.into().0);
self
}
#[inline]
pub fn level(mut self, level: log::LevelFilter) -> Self {
self.default_level = level;
self
}
#[inline]
pub fn level_for<T: Into<Cow<'static, str>>>(
mut self,
module: T,
level: log::LevelFilter,
) -> Self {
let module = module.into();
if let Some((index, _)) = self
.levels
.iter()
.enumerate()
.find(|(_, (name, _))| *name == module)
{
self.levels.remove(index);
}
self.levels.push((module, level));
self
}
#[inline]
pub fn filter<F>(mut self, filter: F) -> Self
where
F: Fn(&log::Metadata) -> bool + Send + Sync + 'static,
{
self.filters.push(Box::new(filter));
self
}
pub fn into_shared(self) -> SharedDispatch {
let (min_level, dispatch) = self.into_dispatch();
SharedDispatch {
inner: Arc::new(dispatch),
min_level,
}
}
fn into_dispatch(self) -> (log::LevelFilter, log_impl::Dispatch) {
let Dispatch {
format,
children,
default_level,
levels,
mut filters,
} = self;
let mut max_child_level = log::LevelFilter::Off;
let output = children
.into_iter()
.flat_map(|child| match child {
OutputInner::Stdout { stream, line_sep } => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Stdout(log_impl::Stdout {
stream,
line_sep,
}))
}
OutputInner::Stderr { stream, line_sep } => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Stderr(log_impl::Stderr {
stream,
line_sep,
}))
}
OutputInner::File { stream, line_sep } => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::File(log_impl::File {
stream: Mutex::new(io::BufWriter::new(stream)),
line_sep,
}))
}
OutputInner::Writer { stream, line_sep } => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Writer(log_impl::Writer {
stream: Mutex::new(stream),
line_sep,
}))
}
#[cfg(all(not(windows), feature = "reopen-03"))]
OutputInner::Reopen { stream, line_sep } => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Reopen(log_impl::Reopen {
stream: Mutex::new(stream),
line_sep,
}))
}
#[cfg(all(not(windows), feature = "reopen-1"))]
OutputInner::Reopen1 { stream, line_sep } => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Reopen1(log_impl::Reopen1 {
stream: Mutex::new(stream),
line_sep,
}))
}
OutputInner::Sender { stream, line_sep } => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Sender(log_impl::Sender {
stream: Mutex::new(stream),
line_sep,
}))
}
#[cfg(all(not(windows), feature = "syslog-3"))]
OutputInner::Syslog3(log) => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Syslog3(log_impl::Syslog3 { inner: log }))
}
#[cfg(all(not(windows), feature = "syslog-4"))]
OutputInner::Syslog4Rfc3164(logger) => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Syslog4Rfc3164(log_impl::Syslog4Rfc3164 {
inner: Mutex::new(logger),
}))
}
#[cfg(all(not(windows), feature = "syslog-4"))]
OutputInner::Syslog4Rfc5424 { logger, transform } => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Syslog4Rfc5424(log_impl::Syslog4Rfc5424 {
inner: Mutex::new(logger),
transform,
}))
}
#[cfg(all(not(windows), feature = "syslog-6"))]
OutputInner::Syslog6Rfc3164(logger) => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Syslog6Rfc3164(log_impl::Syslog6Rfc3164 {
inner: Mutex::new(logger),
}))
}
#[cfg(all(not(windows), feature = "syslog-6"))]
OutputInner::Syslog6Rfc5424 { logger, transform } => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Syslog6Rfc5424(log_impl::Syslog6Rfc5424 {
inner: Mutex::new(logger),
transform,
}))
}
#[cfg(all(not(windows), feature = "syslog-7"))]
OutputInner::Syslog7Rfc3164(logger) => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Syslog7Rfc3164(log_impl::Syslog7Rfc3164 {
inner: Mutex::new(logger),
}))
}
#[cfg(all(not(windows), feature = "syslog-7"))]
OutputInner::Syslog7Rfc5424 { logger, transform } => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Syslog7Rfc5424(log_impl::Syslog7Rfc5424 {
inner: Mutex::new(logger),
transform,
}))
}
OutputInner::Panic => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::Panic(log_impl::Panic))
}
OutputInner::Dispatch(child_dispatch) => {
let (child_level, child) = child_dispatch.into_dispatch();
if child_level > log::LevelFilter::Off {
max_child_level = cmp::max(max_child_level, child_level);
Some(log_impl::Output::Dispatch(child))
} else {
None
}
}
OutputInner::SharedDispatch(child_dispatch) => {
let SharedDispatch {
inner: child,
min_level: child_level,
} = child_dispatch;
if child_level > log::LevelFilter::Off {
max_child_level = cmp::max(max_child_level, child_level);
Some(log_impl::Output::SharedDispatch(child))
} else {
None
}
}
OutputInner::OtherBoxed(child_log) => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::OtherBoxed(child_log))
}
OutputInner::OtherStatic(child_log) => {
max_child_level = log::LevelFilter::Trace;
Some(log_impl::Output::OtherStatic(child_log))
}
#[cfg(feature = "date-based")]
OutputInner::DateBased { config } => {
max_child_level = log::LevelFilter::Trace;
let config = log_impl::DateBasedConfig::new(
config.line_sep,
config.file_prefix,
config.file_suffix,
if config.utc_time {
log_impl::ConfiguredTimezone::Utc
} else {
log_impl::ConfiguredTimezone::Local
},
);
let computed_suffix = config.compute_current_suffix();
let initial_file = config.open_current_log_file(&computed_suffix).ok();
Some(log_impl::Output::DateBased(log_impl::DateBased {
config,
state: Mutex::new(DateBasedState::new(computed_suffix, initial_file)),
}))
}
})
.collect();
let min_level = levels
.iter()
.map(|t| t.1)
.max()
.map_or(default_level, |lvl| cmp::max(lvl, default_level));
let real_min = cmp::min(min_level, max_child_level);
filters.shrink_to_fit();
let dispatch = log_impl::Dispatch {
output,
default_level,
levels: levels.into(),
format,
filters,
};
(real_min, dispatch)
}
pub fn into_log(self) -> (log::LevelFilter, Box<dyn log::Log>) {
let (level, logger) = self.into_dispatch();
if level == log::LevelFilter::Off {
(level, Box::new(log_impl::Null))
} else {
(level, Box::new(logger))
}
}
pub fn apply(self) -> Result<(), log::SetLoggerError> {
let (max_level, log) = self.into_log();
log::set_boxed_logger(log)?;
log::set_max_level(max_level);
Ok(())
}
}
enum OutputInner {
Stdout {
stream: io::Stdout,
line_sep: Cow<'static, str>,
},
Stderr {
stream: io::Stderr,
line_sep: Cow<'static, str>,
},
File {
stream: fs::File,
line_sep: Cow<'static, str>,
},
Writer {
stream: Box<dyn Write + Send>,
line_sep: Cow<'static, str>,
},
#[cfg(all(not(windows), feature = "reopen-03"))]
Reopen {
stream: reopen03::Reopen<fs::File>,
line_sep: Cow<'static, str>,
},
#[cfg(all(not(windows), feature = "reopen-1"))]
Reopen1 {
stream: reopen1::Reopen<fs::File>,
line_sep: Cow<'static, str>,
},
Sender {
stream: Sender<String>,
line_sep: Cow<'static, str>,
},
Dispatch(Dispatch),
SharedDispatch(SharedDispatch),
OtherBoxed(Box<dyn Log>),
OtherStatic(&'static dyn Log),
#[cfg(all(not(windows), feature = "syslog-3"))]
Syslog3(syslog3::Logger),
#[cfg(all(not(windows), feature = "syslog-4"))]
Syslog4Rfc3164(Syslog4Rfc3164Logger),
#[cfg(all(not(windows), feature = "syslog-4"))]
Syslog4Rfc5424 {
logger: Syslog4Rfc5424Logger,
transform: Box<Syslog4TransformFn>,
},
#[cfg(all(not(windows), feature = "syslog-6"))]
Syslog6Rfc3164(Syslog6Rfc3164Logger),
#[cfg(all(not(windows), feature = "syslog-6"))]
Syslog6Rfc5424 {
logger: Syslog6Rfc5424Logger,
transform: Box<Syslog6TransformFn>,
},
#[cfg(all(not(windows), feature = "syslog-7"))]
Syslog7Rfc3164(Syslog7Rfc3164Logger),
#[cfg(all(not(windows), feature = "syslog-7"))]
Syslog7Rfc5424 {
logger: Syslog7Rfc5424Logger,
transform: Box<Syslog7TransformFn>,
},
Panic,
#[cfg(feature = "date-based")]
DateBased { config: DateBased },
}
pub struct Panic;
pub struct Output(OutputInner);
impl From<Dispatch> for Output {
fn from(log: Dispatch) -> Self {
Output(OutputInner::Dispatch(log))
}
}
impl From<SharedDispatch> for Output {
fn from(log: SharedDispatch) -> Self {
Output(OutputInner::SharedDispatch(log))
}
}
impl From<Box<dyn Log>> for Output {
fn from(log: Box<dyn Log>) -> Self {
Output(OutputInner::OtherBoxed(log))
}
}
impl From<&'static dyn Log> for Output {
fn from(log: &'static dyn Log) -> Self {
Output(OutputInner::OtherStatic(log))
}
}
impl From<fs::File> for Output {
fn from(file: fs::File) -> Self {
Output(OutputInner::File {
stream: file,
line_sep: "\n".into(),
})
}
}
impl From<Box<dyn Write + Send>> for Output {
fn from(writer: Box<dyn Write + Send>) -> Self {
Output(OutputInner::Writer {
stream: writer,
line_sep: "\n".into(),
})
}
}
#[cfg(all(not(windows), feature = "reopen-03"))]
impl From<reopen03::Reopen<fs::File>> for Output {
fn from(reopen: reopen03::Reopen<fs::File>) -> Self {
Output(OutputInner::Reopen {
stream: reopen,
line_sep: "\n".into(),
})
}
}
#[cfg(all(not(windows), feature = "reopen-1"))]
impl From<reopen1::Reopen<fs::File>> for Output {
fn from(reopen: reopen1::Reopen<fs::File>) -> Self {
Output(OutputInner::Reopen1 {
stream: reopen,
line_sep: "\n".into(),
})
}
}
impl From<io::Stdout> for Output {
fn from(stream: io::Stdout) -> Self {
Output(OutputInner::Stdout {
stream,
line_sep: "\n".into(),
})
}
}
impl From<io::Stderr> for Output {
fn from(stream: io::Stderr) -> Self {
Output(OutputInner::Stderr {
stream,
line_sep: "\n".into(),
})
}
}
impl From<Sender<String>> for Output {
fn from(stream: Sender<String>) -> Self {
Output(OutputInner::Sender {
stream,
line_sep: "\n".into(),
})
}
}
#[cfg(all(not(windows), feature = "syslog-3"))]
impl From<syslog3::Logger> for Output {
fn from(log: syslog3::Logger) -> Self {
Output(OutputInner::Syslog3(log))
}
}
#[cfg(all(not(windows), feature = "syslog-3"))]
impl From<Box<syslog3::Logger>> for Output {
fn from(log: Box<syslog3::Logger>) -> Self {
Output(OutputInner::Syslog3(*log))
}
}
#[cfg(all(not(windows), feature = "syslog-4"))]
impl From<Syslog4Rfc3164Logger> for Output {
fn from(log: Syslog4Rfc3164Logger) -> Self {
Output(OutputInner::Syslog4Rfc3164(log))
}
}
#[cfg(all(not(windows), feature = "syslog-6"))]
impl From<Syslog6Rfc3164Logger> for Output {
fn from(log: Syslog6Rfc3164Logger) -> Self {
Output(OutputInner::Syslog6Rfc3164(log))
}
}
#[cfg(all(not(windows), feature = "syslog-7"))]
impl From<Syslog7Rfc3164Logger> for Output {
fn from(log: Syslog7Rfc3164Logger) -> Self {
Output(OutputInner::Syslog7Rfc3164(log))
}
}
impl From<Panic> for Output {
fn from(_: Panic) -> Self {
Output(OutputInner::Panic)
}
}
impl Output {
pub fn file<T: Into<Cow<'static, str>>>(file: fs::File, line_sep: T) -> Self {
Output(OutputInner::File {
stream: file,
line_sep: line_sep.into(),
})
}
pub fn writer<T: Into<Cow<'static, str>>>(writer: Box<dyn Write + Send>, line_sep: T) -> Self {
Output(OutputInner::Writer {
stream: writer,
line_sep: line_sep.into(),
})
}
#[cfg(all(not(windows), feature = "reopen-03"))]
pub fn reopen<T: Into<Cow<'static, str>>>(
reopen: reopen03::Reopen<fs::File>,
line_sep: T,
) -> Self {
Output(OutputInner::Reopen {
stream: reopen,
line_sep: line_sep.into(),
})
}
#[cfg(all(not(windows), feature = "reopen-1"))]
pub fn reopen1<T: Into<Cow<'static, str>>>(
reopen: reopen1::Reopen<fs::File>,
line_sep: T,
) -> Self {
Output(OutputInner::Reopen1 {
stream: reopen,
line_sep: line_sep.into(),
})
}
pub fn stdout<T: Into<Cow<'static, str>>>(line_sep: T) -> Self {
Output(OutputInner::Stdout {
stream: io::stdout(),
line_sep: line_sep.into(),
})
}
pub fn stderr<T: Into<Cow<'static, str>>>(line_sep: T) -> Self {
Output(OutputInner::Stderr {
stream: io::stderr(),
line_sep: line_sep.into(),
})
}
pub fn sender<T: Into<Cow<'static, str>>>(sender: Sender<String>, line_sep: T) -> Self {
Output(OutputInner::Sender {
stream: sender,
line_sep: line_sep.into(),
})
}
#[cfg(all(not(windows), feature = "syslog-4"))]
pub fn syslog_5424<F>(logger: Syslog4Rfc5424Logger, transform: F) -> Self
where
F: Fn(&log::Record) -> (i32, HashMap<String, HashMap<String, String>>, String)
+ Sync
+ Send
+ 'static,
{
Output(OutputInner::Syslog4Rfc5424 {
logger,
transform: Box::new(transform),
})
}
#[cfg(all(not(windows), feature = "syslog-6"))]
pub fn syslog6_5424<F>(logger: Syslog6Rfc5424Logger, transform: F) -> Self
where
F: Fn(&log::Record) -> (u32, HashMap<String, HashMap<String, String>>, String)
+ Sync
+ Send
+ 'static,
{
Output(OutputInner::Syslog6Rfc5424 {
logger,
transform: Box::new(transform),
})
}
#[cfg(all(not(windows), feature = "syslog-7"))]
pub fn syslog7_5424<F>(logger: Syslog7Rfc5424Logger, transform: F) -> Self
where
F: Fn(&log::Record) -> (u32, BTreeMap<String, BTreeMap<String, String>>, String)
+ Sync
+ Send
+ 'static,
{
Output(OutputInner::Syslog7Rfc5424 {
logger,
transform: Box::new(transform),
})
}
pub fn call<F>(func: F) -> Self
where
F: Fn(&log::Record) + Sync + Send + 'static,
{
struct CallShim<F>(F);
impl<F> log::Log for CallShim<F>
where
F: Fn(&log::Record) + Sync + Send + 'static,
{
fn enabled(&self, _: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
(self.0)(record)
}
fn flush(&self) {}
}
Self::from(Box::new(CallShim(func)) as Box<dyn log::Log>)
}
}
impl Default for Dispatch {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for Dispatch {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
struct LevelsDebug<'a>(&'a [(Cow<'static, str>, log::LevelFilter)]);
impl<'a> fmt::Debug for LevelsDebug<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_map()
.entries(self.0.iter().map(|t| (t.0.as_ref(), t.1)))
.finish()
}
}
struct FiltersDebug<'a>(&'a [Box<Filter>]);
impl<'a> fmt::Debug for FiltersDebug<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_list()
.entries(self.0.iter().map(|_| "<filter closure>"))
.finish()
}
}
f.debug_struct("Dispatch")
.field(
"format",
&self.format.as_ref().map(|_| "<formatter closure>"),
)
.field("children", &self.children)
.field("default_level", &self.default_level)
.field("levels", &LevelsDebug(&self.levels))
.field("filters", &FiltersDebug(&self.filters))
.finish()
}
}
impl fmt::Debug for OutputInner {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
OutputInner::Stdout {
ref stream,
ref line_sep,
} => f
.debug_struct("Output::Stdout")
.field("stream", stream)
.field("line_sep", line_sep)
.finish(),
OutputInner::Stderr {
ref stream,
ref line_sep,
} => f
.debug_struct("Output::Stderr")
.field("stream", stream)
.field("line_sep", line_sep)
.finish(),
OutputInner::File {
ref stream,
ref line_sep,
} => f
.debug_struct("Output::File")
.field("stream", stream)
.field("line_sep", line_sep)
.finish(),
OutputInner::Writer { ref line_sep, .. } => f
.debug_struct("Output::Writer")
.field("stream", &"<unknown writer>")
.field("line_sep", line_sep)
.finish(),
#[cfg(all(not(windows), feature = "reopen-03"))]
OutputInner::Reopen { ref line_sep, .. } => f
.debug_struct("Output::Reopen")
.field("stream", &"<unknown reopen file>")
.field("line_sep", line_sep)
.finish(),
#[cfg(all(not(windows), feature = "reopen-1"))]
OutputInner::Reopen1 {
ref line_sep,
ref stream,
} => f
.debug_struct("Output::Reopen1")
.field("stream", stream)
.field("line_sep", line_sep)
.finish(),
OutputInner::Sender {
ref stream,
ref line_sep,
} => f
.debug_struct("Output::Sender")
.field("stream", stream)
.field("line_sep", line_sep)
.finish(),
#[cfg(all(not(windows), feature = "syslog-3"))]
OutputInner::Syslog3(_) => f
.debug_tuple("Output::Syslog3")
.field(&"<unprintable syslog::Logger>")
.finish(),
#[cfg(all(not(windows), feature = "syslog-4"))]
OutputInner::Syslog4Rfc3164 { .. } => f
.debug_tuple("Output::Syslog4Rfc3164")
.field(&"<unprintable syslog::Logger>")
.finish(),
#[cfg(all(not(windows), feature = "syslog-4"))]
OutputInner::Syslog4Rfc5424 { .. } => f
.debug_tuple("Output::Syslog4Rfc5424")
.field(&"<unprintable syslog::Logger>")
.finish(),
#[cfg(all(not(windows), feature = "syslog-6"))]
OutputInner::Syslog6Rfc3164 { .. } => f
.debug_tuple("Output::Syslog6Rfc3164")
.field(&"<unprintable syslog::Logger>")
.finish(),
#[cfg(all(not(windows), feature = "syslog-6"))]
OutputInner::Syslog6Rfc5424 { .. } => f
.debug_tuple("Output::Syslog6Rfc5424")
.field(&"<unprintable syslog::Logger>")
.finish(),
#[cfg(all(not(windows), feature = "syslog-7"))]
OutputInner::Syslog7Rfc3164 { .. } => f
.debug_tuple("Output::Syslog7Rfc3164")
.field(&"<unprintable syslog::Logger>")
.finish(),
#[cfg(all(not(windows), feature = "syslog-7"))]
OutputInner::Syslog7Rfc5424 { .. } => f
.debug_tuple("Output::Syslog7Rfc5424")
.field(&"<unprintable syslog::Logger>")
.finish(),
OutputInner::Dispatch(ref dispatch) => {
f.debug_tuple("Output::Dispatch").field(dispatch).finish()
}
OutputInner::SharedDispatch(_) => f
.debug_tuple("Output::SharedDispatch")
.field(&"<built Dispatch logger>")
.finish(),
OutputInner::OtherBoxed { .. } => f
.debug_tuple("Output::OtherBoxed")
.field(&"<boxed logger>")
.finish(),
OutputInner::OtherStatic { .. } => f
.debug_tuple("Output::OtherStatic")
.field(&"<boxed logger>")
.finish(),
OutputInner::Panic => f.debug_tuple("Output::Panic").finish(),
#[cfg(feature = "date-based")]
OutputInner::DateBased { ref config } => f
.debug_struct("Output::DateBased")
.field("config", config)
.finish(),
}
}
}
impl fmt::Debug for Output {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Debug)]
#[cfg(feature = "date-based")]
pub struct DateBased {
file_prefix: PathBuf,
file_suffix: Cow<'static, str>,
line_sep: Cow<'static, str>,
utc_time: bool,
}
#[cfg(feature = "date-based")]
impl DateBased {
pub fn new<T, U>(file_prefix: T, file_suffix: U) -> Self
where
T: AsRef<Path>,
U: Into<Cow<'static, str>>,
{
DateBased {
utc_time: false,
file_prefix: file_prefix.as_ref().to_owned(),
file_suffix: file_suffix.into(),
line_sep: "\n".into(),
}
}
pub fn line_sep<T>(mut self, line_sep: T) -> Self
where
T: Into<Cow<'static, str>>,
{
self.line_sep = line_sep.into();
self
}
pub fn utc_time(mut self) -> Self {
self.utc_time = true;
self
}
pub fn local_time(mut self) -> Self {
self.utc_time = false;
self
}
}
#[cfg(feature = "date-based")]
impl From<DateBased> for Output {
fn from(config: DateBased) -> Self {
Output(OutputInner::DateBased { config })
}
}