1use crate::build::BuilderCommon;
3#[cfg(feature = "slog-kvfilter")]
4use crate::types::KVFilterParameters;
5use crate::types::{Format, OverflowStrategy, Severity, SourceLocation, TimeZone};
6use crate::{misc, BuildWithCustomFormat};
7use crate::{Build, Config, Result};
8use serde::{Deserialize, Serialize};
9use slog::{Drain, Logger};
10use slog_term::{self, CompactFormat, FullFormat, PlainDecorator, TermDecorator};
11use std::fmt::Debug;
12use std::io;
13
14#[derive(Debug)]
18pub struct TerminalLoggerBuilder {
19 common: BuilderCommon,
20 format: Format,
21 timezone: TimeZone,
22 destination: Destination,
23}
24impl TerminalLoggerBuilder {
25 pub fn new() -> Self {
27 TerminalLoggerBuilder {
28 common: BuilderCommon::default(),
29 format: Format::default(),
30 timezone: TimeZone::default(),
31 destination: Destination::default(),
32 }
33 }
34
35 pub fn format(&mut self, format: Format) -> &mut Self {
37 self.format = format;
38 self
39 }
40
41 pub fn source_location(&mut self, source_location: SourceLocation) -> &mut Self {
43 self.common.source_location = source_location;
44 self
45 }
46
47 pub fn overflow_strategy(&mut self, overflow_strategy: OverflowStrategy) -> &mut Self {
49 self.common.overflow_strategy = overflow_strategy;
50 self
51 }
52
53 pub fn timezone(&mut self, timezone: TimeZone) -> &mut Self {
55 self.timezone = timezone;
56 self
57 }
58
59 pub fn destination(&mut self, destination: Destination) -> &mut Self {
61 self.destination = destination;
62 self
63 }
64
65 pub fn level(&mut self, severity: Severity) -> &mut Self {
67 self.common.level = severity;
68 self
69 }
70
71 pub fn channel_size(&mut self, channel_size: usize) -> &mut Self {
73 self.common.channel_size = channel_size;
74 self
75 }
76
77 #[cfg(feature = "slog-kvfilter")]
81 pub fn kvfilter(&mut self, parameters: KVFilterParameters) -> &mut Self {
82 self.common.kvfilterparameters = Some(parameters);
83 self
84 }
85}
86impl Default for TerminalLoggerBuilder {
87 fn default() -> Self {
88 Self::new()
89 }
90}
91impl Build for TerminalLoggerBuilder {
92 fn build(&self) -> Result<Logger> {
93 let decorator = self.destination.to_decorator();
94 let timestamp = misc::timezone_to_timestamp_fn(self.timezone);
95 let logger = match self.format {
96 Format::Full => {
97 let format = FullFormat::new(decorator).use_custom_timestamp(timestamp);
98 self.common.build_with_drain(format.build())
99 }
100 Format::Compact => {
101 let format = CompactFormat::new(decorator).use_custom_timestamp(timestamp);
102 self.common.build_with_drain(format.build())
103 }
104 #[cfg(feature = "json")]
105 Format::Json => match self.destination {
106 Destination::Stdout => self.common.build_with_drain(
107 slog_json::Json::new(std::io::stdout())
108 .set_flush(true)
109 .add_default_keys()
110 .build(),
111 ),
112 Destination::Stderr => self.common.build_with_drain(
113 slog_json::Json::new(std::io::stderr())
114 .set_flush(true)
115 .add_default_keys()
116 .build(),
117 ),
118 },
119 };
120 Ok(logger)
121 }
122}
123impl BuildWithCustomFormat for TerminalLoggerBuilder {
124 type Decorator = TerminalLoggerDecorator;
125
126 fn build_with_custom_format<F, D>(&self, f: F) -> Result<Logger>
127 where
128 F: FnOnce(Self::Decorator) -> Result<D>,
129 D: Drain + Send + 'static,
130 D::Err: Debug,
131 {
132 let decorator = TerminalLoggerDecorator(self.destination.to_decorator());
133 let drain = track!(f(decorator))?;
134 Ok(self.common.build_with_drain(drain))
135 }
136}
137
138pub struct TerminalLoggerDecorator(Decorator);
140
141impl slog_term::Decorator for TerminalLoggerDecorator {
142 fn with_record<F>(
143 &self,
144 record: &slog::Record,
145 logger_values: &slog::OwnedKVList,
146 f: F,
147 ) -> io::Result<()>
148 where
149 F: FnOnce(&mut dyn slog_term::RecordDecorator) -> io::Result<()>,
150 {
151 self.0.with_record(record, logger_values, f)
152 }
153}
154
155#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
167#[serde(rename_all = "lowercase")]
168pub enum Destination {
169 Stdout,
171
172 #[default]
174 Stderr,
175}
176impl Destination {
177 fn to_decorator(self) -> Decorator {
178 let maybe_term_decorator = match self {
179 Destination::Stdout => TermDecorator::new().stdout().try_build(),
180 Destination::Stderr => TermDecorator::new().stderr().try_build(),
181 };
182 maybe_term_decorator
183 .map(Decorator::Term)
184 .unwrap_or_else(|| match self {
185 Destination::Stdout => Decorator::PlainStdout(PlainDecorator::new(io::stdout())),
186 Destination::Stderr => Decorator::PlainStderr(PlainDecorator::new(io::stderr())),
187 })
188 }
189}
190
191enum Decorator {
192 Term(TermDecorator),
193 PlainStdout(PlainDecorator<io::Stdout>),
194 PlainStderr(PlainDecorator<io::Stderr>),
195}
196impl slog_term::Decorator for Decorator {
197 fn with_record<F>(
198 &self,
199 record: &slog::Record,
200 logger_values: &slog::OwnedKVList,
201 f: F,
202 ) -> io::Result<()>
203 where
204 F: FnOnce(&mut dyn slog_term::RecordDecorator) -> io::Result<()>,
205 {
206 match *self {
207 Decorator::Term(ref d) => d.with_record(record, logger_values, f),
208 Decorator::PlainStdout(ref d) => d.with_record(record, logger_values, f),
209 Decorator::PlainStderr(ref d) => d.with_record(record, logger_values, f),
210 }
211 }
212}
213
214#[derive(Debug, Default, Clone, Serialize, Deserialize)]
216#[non_exhaustive]
217pub struct TerminalLoggerConfig {
218 #[serde(default)]
220 pub level: Severity,
221
222 #[serde(default)]
224 pub format: Format,
225
226 #[serde(default)]
228 pub source_location: SourceLocation,
229
230 #[serde(default)]
232 pub timezone: TimeZone,
233
234 #[serde(default)]
236 pub destination: Destination,
237
238 #[serde(default = "default_channel_size")]
240 pub channel_size: usize,
241
242 #[serde(default)]
248 pub overflow_strategy: OverflowStrategy,
249}
250impl TerminalLoggerConfig {
251 pub fn new() -> Self {
253 Default::default()
254 }
255}
256impl Config for TerminalLoggerConfig {
257 type Builder = TerminalLoggerBuilder;
258 fn try_to_builder(&self) -> Result<Self::Builder> {
259 let mut builder = TerminalLoggerBuilder::new();
260 builder.level(self.level);
261 builder.format(self.format);
262 builder.source_location(self.source_location);
263 builder.timezone(self.timezone);
264 builder.destination(self.destination);
265 builder.channel_size(self.channel_size);
266 builder.overflow_strategy(self.overflow_strategy);
267 Ok(builder)
268 }
269}
270
271fn default_channel_size() -> usize {
272 1024
273}