slog_term/
lib.rs

1// {{{ Module docs
2//! `slog-rs`'s `Drain` for terminal output
3//!
4//! This crate implements output formatting targeting logging to
5//! terminal/console/shell or similar text-based IO.
6//!
7//! **Warning**: `slog-term` (like `slog-rs` itself) is fast, modular and
8//! extensible.  It comes with a price: a lot of details (*that you don't care
9//! about
10//! right now and think they are stupid, until you actually do and then you are
11//! happy that someone thought of them for you*) are being taken into
12//! consideration. Anyway, **if you just want to get a logging to terminal
13//! working with `slog`**, consider using a wrapper crate like
14//! [sloggers](https://docs.rs/sloggers/) instead.
15//!
16//! **Note**: A lot of users gets bitten by the fact that
17//! `slog::Logger::root(...)` requires a drain that is
18//! safe to send and share across threads (`Send+Sync`). With shared resource
19//! like terminal or a file to which you log, a synchronization needs to be
20//! taken care of. If you get compilation errors around `Sync` or `Send` you
21//! are doing something wrong around it.
22//!
23//! Using `Decorator` open trait, user can implement outputting
24//! using different colors, terminal types and so on.
25//!
26//! # Synchronization via `PlainSyncDecorator`
27//!
28//! This logger works by synchronizing on the IO directly in
29//! `PlainSyncDecorator`.  The formatting itself is thread-safe.
30//!
31//! ```
32//! use slog::*;
33//!
34//! let plain = slog_term::PlainSyncDecorator::new(std::io::stdout());
35//! let logger = Logger::root(
36//!     slog_term::FullFormat::new(plain)
37//!     .build().fuse(), o!()
38//! );
39//!
40//! info!(logger, "Logging ready!");
41//! ```
42//!
43//! # Synchronization via `slog_async`
44//!
45//! This drain puts logging into a separate thread via `slog_async::Async`:
46//! formatting and writing to terminal is happening in a one dedicated thread,
47//! so no further synchronization is required.
48//!
49//! ```
50//! use slog::{Drain, o, info};
51//!
52//! let decorator = slog_term::TermDecorator::new().build();
53//! let drain = slog_term::CompactFormat::new(decorator).build().fuse();
54//! let drain = slog_async::Async::new(drain).build().fuse();
55//!
56//! let log = slog::Logger::root(drain, o!());
57//!
58//! info!(log, "Logging ready!");
59//! ```
60//!
61//! # Synchronization via `Mutex`
62//!
63//! This drain synchronizes by wrapping everything in a big mutex (yes,
64//! `Mutex<Drain>` implements a `Drain` trait). This is kind of slow, but in
65//! scripting languages like Ruby or Python pretty much the whole code is
66//! running in a one
67//! huge mutex and noone seems to mind, so I'm sure you're going to get away
68//! with this. Personally, I am a bit sad, that I've spent so much effort to
69//! give you tools to make your code as efficient as possible, and you choose
70//! this. ಠ_ಠ . But I'm here to serve, not to tell you what to do.
71//!
72//! ```
73//! use slog::{Drain, o, info};
74//!
75//! let decorator = slog_term::TermDecorator::new().build();
76//! let drain = slog_term::CompactFormat::new(decorator).build();
77//! let drain = std::sync::Mutex::new(drain).fuse();
78//!
79//! let log = slog::Logger::root(drain, o!());
80//!
81//! info!(log, "Logging ready!");
82//! ```
83// }}}
84
85// {{{ Imports & meta
86#![warn(missing_docs)]
87
88use slog::Drain;
89use slog::Key;
90use slog::*;
91use std::cell::RefCell;
92use std::io::Write as IoWrite;
93use std::panic::{RefUnwindSafe, UnwindSafe};
94use std::result;
95use std::{fmt, io, mem, sync};
96
97// TODO: Should probably look into `std::io::IsTerminal` if/when that becomes stable
98// See tracking issue rust-lang/rust#98070
99//
100// This should really be an issue we file on the `is-terminal` crate
101use is_terminal::IsTerminal;
102// }}}
103
104// {{{ Decorator
105/// Output decorator
106///
107/// Trait implementing strategy of output formating in terms of IO,
108/// colors, etc.
109pub trait Decorator {
110    /// Get a `RecordDecorator` for a given `record`
111    ///
112    /// This allows `Decorator` to have on-stack data per processed `Record`s
113    ///
114    fn with_record<F>(
115        &self,
116        _record: &Record,
117        _logger_values: &OwnedKVList,
118        f: F,
119    ) -> io::Result<()>
120    where
121        F: FnOnce(&mut dyn RecordDecorator) -> io::Result<()>;
122}
123
124impl<T: ?Sized> Decorator for Box<T>
125where
126    T: Decorator,
127{
128    fn with_record<F>(
129        &self,
130        record: &Record,
131        logger_kv: &OwnedKVList,
132        f: F,
133    ) -> io::Result<()>
134    where
135        F: FnOnce(&mut dyn RecordDecorator) -> io::Result<()>,
136    {
137        (**self).with_record(record, logger_kv, f)
138    }
139}
140
141/// Per-record decorator
142pub trait RecordDecorator: io::Write {
143    /// Reset formatting to defaults
144    fn reset(&mut self) -> io::Result<()>;
145
146    /// Format normal text
147    fn start_whitespace(&mut self) -> io::Result<()> {
148        self.reset()
149    }
150
151    /// Format `Record` message
152    fn start_msg(&mut self) -> io::Result<()> {
153        self.reset()
154    }
155
156    /// Format timestamp
157    fn start_timestamp(&mut self) -> io::Result<()> {
158        self.reset()
159    }
160
161    /// Format `Record` level
162    fn start_level(&mut self) -> io::Result<()> {
163        self.reset()
164    }
165
166    /// Format a comma between key-value pairs
167    fn start_comma(&mut self) -> io::Result<()> {
168        self.reset()
169    }
170
171    /// Format key
172    fn start_key(&mut self) -> io::Result<()> {
173        self.reset()
174    }
175
176    /// Format a value
177    fn start_value(&mut self) -> io::Result<()> {
178        self.reset()
179    }
180
181    /// Format a file location
182    fn start_location(&mut self) -> io::Result<()> {
183        self.reset()
184    }
185
186    /// Format value
187    fn start_separator(&mut self) -> io::Result<()> {
188        self.reset()
189    }
190}
191
192impl RecordDecorator for Box<dyn RecordDecorator> {
193    fn reset(&mut self) -> io::Result<()> {
194        (**self).reset()
195    }
196    fn start_whitespace(&mut self) -> io::Result<()> {
197        (**self).start_whitespace()
198    }
199
200    /// Format `Record` message
201    fn start_msg(&mut self) -> io::Result<()> {
202        (**self).start_msg()
203    }
204
205    /// Format timestamp
206    fn start_timestamp(&mut self) -> io::Result<()> {
207        (**self).start_timestamp()
208    }
209
210    /// Format `Record` level
211    fn start_level(&mut self) -> io::Result<()> {
212        (**self).start_level()
213    }
214
215    /// Format `Record` message
216    fn start_comma(&mut self) -> io::Result<()> {
217        (**self).start_comma()
218    }
219
220    /// Format key
221    fn start_key(&mut self) -> io::Result<()> {
222        (**self).start_key()
223    }
224
225    /// Format value
226    fn start_value(&mut self) -> io::Result<()> {
227        (**self).start_value()
228    }
229
230    /// Format file location
231    fn start_location(&mut self) -> io::Result<()> {
232        (**self).start_location()
233    }
234
235    /// Format value
236    fn start_separator(&mut self) -> io::Result<()> {
237        (**self).start_separator()
238    }
239}
240// }}}
241
242// {{{ Misc
243/// Returns `true` if message was not empty
244pub fn print_msg_header(
245    fn_timestamp: &dyn ThreadSafeTimestampFn<Output = io::Result<()>>,
246    mut rd: &mut dyn RecordDecorator,
247    record: &Record,
248    use_file_location: bool,
249) -> io::Result<bool> {
250    rd.start_timestamp()?;
251    fn_timestamp(&mut rd)?;
252
253    rd.start_whitespace()?;
254    write!(rd, " ")?;
255
256    rd.start_level()?;
257    write!(rd, "{}", record.level().as_short_str())?;
258
259    if use_file_location {
260        rd.start_location()?;
261        write!(
262            rd,
263            "[{}:{}:{}]",
264            record.location().file,
265            record.location().line,
266            record.location().column
267        )?;
268    }
269
270    rd.start_whitespace()?;
271    write!(rd, " ")?;
272
273    rd.start_msg()?;
274    let mut count_rd = CountingWriter::new(&mut rd);
275    write!(count_rd, "{}", record.msg())?;
276    Ok(count_rd.count() != 0)
277}
278
279// }}}
280
281// {{{ Header Printer
282/// Threadsafe header formatting function type
283///
284/// To satify `slog-rs` thread and unwind safety requirements, the
285/// bounds expressed by this trait need to satisfied for a function
286/// to be used in timestamp formatting.
287pub trait ThreadSafeHeaderFn:
288    Fn(
289        &dyn ThreadSafeTimestampFn<Output = io::Result<()>>,
290        &mut dyn RecordDecorator,
291        &Record,
292        bool,
293    ) -> io::Result<bool>
294    + Send
295    + Sync
296    + UnwindSafe
297    + RefUnwindSafe
298    + 'static
299{
300}
301
302impl<F> ThreadSafeHeaderFn for F
303where
304    F: Fn(
305            &dyn ThreadSafeTimestampFn<Output = io::Result<()>>,
306            &mut dyn RecordDecorator,
307            &Record,
308            bool,
309        ) -> io::Result<bool>
310        + Send
311        + Sync,
312    F: UnwindSafe + RefUnwindSafe + 'static,
313    F: ?Sized,
314{
315}
316
317// }}}
318
319// {{{ Term
320/// Terminal-output formatting `Drain`
321///
322/// **Note**: logging to `FullFormat` drain is thread-safe, since every
323/// line of output is formatted independently. However, the underlying
324/// IO, needs to be synchronized.
325pub struct FullFormat<D>
326where
327    D: Decorator,
328{
329    decorator: D,
330    fn_timestamp: Box<dyn ThreadSafeTimestampFn<Output = io::Result<()>>>,
331    use_original_order: bool,
332    use_file_location: bool,
333    header_printer: Box<dyn ThreadSafeHeaderFn>,
334}
335
336/// Streamer builder
337pub struct FullFormatBuilder<D>
338where
339    D: Decorator,
340{
341    decorator: D,
342    fn_timestamp: Box<dyn ThreadSafeTimestampFn<Output = io::Result<()>>>,
343    original_order: bool,
344    file_location: bool,
345    header_printer: Box<dyn ThreadSafeHeaderFn>,
346}
347
348impl<D> FullFormatBuilder<D>
349where
350    D: Decorator,
351{
352    /// Use the UTC time zone for the timestamp
353    pub fn use_utc_timestamp(mut self) -> Self {
354        self.fn_timestamp = Box::new(timestamp_utc);
355        self
356    }
357
358    /// Use the local time zone for the timestamp (default)
359    pub fn use_local_timestamp(mut self) -> Self {
360        self.fn_timestamp = Box::new(timestamp_local);
361        self
362    }
363
364    /// Provide a custom function to generate the timestamp
365    pub fn use_custom_timestamp<F>(mut self, f: F) -> Self
366    where
367        F: ThreadSafeTimestampFn,
368    {
369        self.fn_timestamp = Box::new(f);
370        self
371    }
372
373    /// Enable the file location in log in this format [file:line:column]
374    pub fn use_file_location(mut self) -> Self {
375        self.file_location = true;
376        self
377    }
378
379    /// Use the original ordering of key-value pairs
380    ///
381    /// By default, key-values are printed in a reversed order. This option will
382    /// change it to the order in which key-values were added.
383    pub fn use_original_order(mut self) -> Self {
384        self.original_order = true;
385        self
386    }
387
388    /// Provide a function that print the header
389    ///
390    /// If not used, `slog_term::print_msg_header` will be used.
391    ///
392    /// The header is the part before the log message and key-values. It usually contains the time,
393    /// the log level.
394    ///
395    /// The default function:
396    /// ```compile_fail
397    /// pub fn print_msg_header(
398    ///     fn_timestamp: &dyn ThreadSafeTimestampFn<Output = io::Result<()>>,
399    ///     mut rd: &mut dyn RecordDecorator,
400    ///     record: &Record,
401    ///     use_file_location: bool,
402    /// ) -> io::Result<bool> {
403    ///     rd.start_timestamp()?;
404    ///     fn_timestamp(&mut rd)?;
405    ///
406    ///     rd.start_whitespace()?;
407    ///     write!(rd, " ")?;
408    ///
409    ///     rd.start_level()?;
410    ///     write!(rd, "{}", record.level().as_short_str())?;
411    ///
412    ///     if use_file_location {
413    ///         rd.start_location()?;
414    ///         write!(
415    ///             rd,
416    ///             "[{}:{}:{}]",
417    ///             record.location().file,
418    ///             record.location().line,
419    ///             record.location().column
420    ///         )?;
421    ///     }
422    ///
423    ///     rd.start_whitespace()?;
424    ///     write!(rd, " ")?;
425    ///
426    ///     rd.start_msg()?;
427    ///     let mut count_rd = CountingWriter::new(&mut rd);
428    ///     write!(count_rd, "{}", record.msg())?;
429    ///     Ok(count_rd.count() != 0)
430    /// }
431    /// ```
432    ///
433    /// produces this output:
434    /// ```text
435    /// Oct 19 09:20:37.962 INFO an event log, my_key: my_value
436    /// ```
437    ///
438    /// the `Oct 19 09:20:37.962 INFO` part is the header.
439    pub fn use_custom_header_print<F>(mut self, f: F) -> Self
440    where
441        F: ThreadSafeHeaderFn,
442    {
443        self.header_printer = Box::new(f);
444        self
445    }
446
447    /// Build `FullFormat`
448    pub fn build(self) -> FullFormat<D> {
449        FullFormat {
450            decorator: self.decorator,
451            fn_timestamp: self.fn_timestamp,
452            use_original_order: self.original_order,
453            use_file_location: self.file_location,
454            header_printer: self.header_printer,
455        }
456    }
457}
458
459impl<D> Drain for FullFormat<D>
460where
461    D: Decorator,
462{
463    type Ok = ();
464    type Err = io::Error;
465
466    fn log(
467        &self,
468        record: &Record,
469        values: &OwnedKVList,
470    ) -> result::Result<Self::Ok, Self::Err> {
471        self.format_full(record, values)
472    }
473}
474
475impl<D> FullFormat<D>
476where
477    D: Decorator,
478{
479    /// New `TermBuilder`
480    #[allow(clippy::new_ret_no_self)]
481    pub fn new(d: D) -> FullFormatBuilder<D> {
482        FullFormatBuilder {
483            fn_timestamp: Box::new(timestamp_local),
484            decorator: d,
485            original_order: false,
486            file_location: false,
487            header_printer: Box::new(print_msg_header),
488        }
489    }
490
491    fn format_full(
492        &self,
493        record: &Record,
494        values: &OwnedKVList,
495    ) -> io::Result<()> {
496        self.decorator.with_record(record, values, |decorator| {
497            let header_printer = &self.header_printer;
498            let comma_needed = header_printer(
499                &*self.fn_timestamp,
500                decorator,
501                record,
502                self.use_file_location,
503            )?;
504
505            {
506                let mut serializer = Serializer::new(
507                    decorator,
508                    comma_needed,
509                    self.use_original_order,
510                );
511
512                record.kv().serialize(record, &mut serializer)?;
513
514                values.serialize(record, &mut serializer)?;
515
516                serializer.finish()?;
517            }
518
519            decorator.start_whitespace()?;
520            writeln!(decorator)?;
521
522            decorator.flush()?;
523
524            Ok(())
525        })
526    }
527}
528// }}}
529
530// {{{ CompactFormat
531/// Compact terminal-output formatting `Drain`
532///
533/// **Note**: Compact logging format is not `Sync` (thread-safe) and needs to be
534/// synchronized externally, as current output depends on the previous one.
535///
536/// Put it into a `std::sync::Mutex` or `slog_async::Async` worker-thread to
537/// serialize accesses to it.
538pub struct CompactFormat<D>
539where
540    D: Decorator,
541{
542    decorator: D,
543    history: RefCell<Vec<(Vec<u8>, Vec<u8>)>>,
544    fn_timestamp: Box<dyn ThreadSafeTimestampFn<Output = io::Result<()>>>,
545    header_printer: Box<dyn ThreadSafeHeaderFn>,
546}
547
548/// Streamer builder
549pub struct CompactFormatBuilder<D>
550where
551    D: Decorator,
552{
553    decorator: D,
554    fn_timestamp: Box<dyn ThreadSafeTimestampFn<Output = io::Result<()>>>,
555    header_printer: Box<dyn ThreadSafeHeaderFn>,
556}
557
558impl<D> CompactFormatBuilder<D>
559where
560    D: Decorator,
561{
562    /// Use the UTC time zone for the timestamp
563    pub fn use_utc_timestamp(mut self) -> Self {
564        self.fn_timestamp = Box::new(timestamp_utc);
565        self
566    }
567
568    /// Use the local time zone for the timestamp (default)
569    pub fn use_local_timestamp(mut self) -> Self {
570        self.fn_timestamp = Box::new(timestamp_local);
571        self
572    }
573
574    /// Provide a custom function to generate the timestamp
575    pub fn use_custom_timestamp<F>(mut self, f: F) -> Self
576    where
577        F: ThreadSafeTimestampFn,
578    {
579        self.fn_timestamp = Box::new(f);
580        self
581    }
582
583    /// Provide a function that print the header
584    ///
585    /// If not used, `slog_term::print_msg_header` will be used
586    pub fn use_custom_header_print<F>(mut self, f: F) -> Self
587    where
588        F: ThreadSafeHeaderFn,
589    {
590        self.header_printer = Box::new(f);
591        self
592    }
593
594    /// Build the streamer
595    pub fn build(self) -> CompactFormat<D> {
596        CompactFormat {
597            decorator: self.decorator,
598            fn_timestamp: self.fn_timestamp,
599            history: RefCell::new(vec![]),
600            header_printer: self.header_printer,
601        }
602    }
603}
604
605impl<D> Drain for CompactFormat<D>
606where
607    D: Decorator,
608{
609    type Ok = ();
610    type Err = io::Error;
611
612    fn log(
613        &self,
614        record: &Record,
615        values: &OwnedKVList,
616    ) -> result::Result<Self::Ok, Self::Err> {
617        self.format_compact(record, values)
618    }
619}
620
621impl<D> CompactFormat<D>
622where
623    D: Decorator,
624{
625    /// New `CompactFormatBuilder`
626    #[allow(clippy::new_ret_no_self)]
627    pub fn new(d: D) -> CompactFormatBuilder<D> {
628        CompactFormatBuilder {
629            fn_timestamp: Box::new(timestamp_local),
630            decorator: d,
631            header_printer: Box::new(print_msg_header),
632        }
633    }
634
635    fn format_compact(
636        &self,
637        record: &Record,
638        values: &OwnedKVList,
639    ) -> io::Result<()> {
640        self.decorator.with_record(record, values, |decorator| {
641            let indent = {
642                let mut history_ref = self.history.borrow_mut();
643                let mut serializer =
644                    CompactFormatSerializer::new(decorator, &mut *history_ref);
645
646                values.serialize(record, &mut serializer)?;
647
648                serializer.finish()?
649            };
650
651            decorator.start_whitespace()?;
652
653            for _ in 0..indent {
654                write!(decorator, " ")?;
655            }
656
657            let header_printer = &self.header_printer;
658            let comma_needed =
659                header_printer(&*self.fn_timestamp, decorator, record, false)?;
660
661            {
662                let mut serializer =
663                    Serializer::new(decorator, comma_needed, false);
664
665                record.kv().serialize(record, &mut serializer)?;
666
667                serializer.finish()?;
668            }
669
670            decorator.start_whitespace()?;
671            writeln!(decorator)?;
672
673            decorator.flush()?;
674
675            Ok(())
676        })
677    }
678}
679// }}}
680
681// {{{ Serializer
682/// Serializer for the lines
683pub struct Serializer<'a> {
684    comma_needed: bool,
685    decorator: &'a mut dyn RecordDecorator,
686    reverse: bool,
687    stack: Vec<(String, String)>,
688}
689
690impl<'a> Serializer<'a> {
691    /// Create `Serializer` instance
692    pub fn new(
693        d: &'a mut dyn RecordDecorator,
694        comma_needed: bool,
695        reverse: bool,
696    ) -> Self {
697        Serializer {
698            comma_needed,
699            decorator: d,
700            reverse,
701            stack: vec![],
702        }
703    }
704
705    fn maybe_print_comma(&mut self) -> io::Result<()> {
706        if self.comma_needed {
707            self.decorator.start_comma()?;
708            write!(self.decorator, ", ")?;
709        }
710        self.comma_needed |= true;
711        Ok(())
712    }
713
714    /// Write out all the whole stack
715    pub fn finish(mut self) -> io::Result<()> {
716        loop {
717            if let Some((k, v)) = self.stack.pop() {
718                self.maybe_print_comma()?;
719                self.decorator.start_key()?;
720                write!(self.decorator, "{}", k)?;
721                write!(self.decorator, ":")?;
722                self.decorator.start_whitespace()?;
723                write!(self.decorator, " ")?;
724                self.decorator.start_value()?;
725                write!(self.decorator, "{}", v)?;
726            } else {
727                return Ok(());
728            }
729        }
730    }
731}
732
733impl<'a> Drop for Serializer<'a> {
734    fn drop(&mut self) {
735        if !self.stack.is_empty() {
736            panic!("stack not empty");
737        }
738    }
739}
740
741macro_rules! s(
742    ($s:expr, $k:expr, $v:expr) => {
743
744        if $s.reverse {
745            $s.stack.push(($k.into(), format!("{}", $v)));
746        } else {
747        $s.maybe_print_comma()?;
748        $s.decorator.start_key()?;
749        write!($s.decorator, "{}", $k)?;
750        $s.decorator.start_separator()?;
751        write!($s.decorator, ":")?;
752        $s.decorator.start_whitespace()?;
753        write!($s.decorator, " ")?;
754        $s.decorator.start_value()?;
755        write!($s.decorator, "{}", $v)?;
756        }
757    };
758);
759
760impl<'a> slog::ser::Serializer for Serializer<'a> {
761    fn emit_none(&mut self, key: Key) -> slog::Result {
762        s!(self, key, "None");
763        Ok(())
764    }
765    fn emit_unit(&mut self, key: Key) -> slog::Result {
766        s!(self, key, "()");
767        Ok(())
768    }
769
770    fn emit_bool(&mut self, key: Key, val: bool) -> slog::Result {
771        s!(self, key, val);
772        Ok(())
773    }
774
775    fn emit_char(&mut self, key: Key, val: char) -> slog::Result {
776        s!(self, key, val);
777        Ok(())
778    }
779
780    fn emit_usize(&mut self, key: Key, val: usize) -> slog::Result {
781        s!(self, key, val);
782        Ok(())
783    }
784    fn emit_isize(&mut self, key: Key, val: isize) -> slog::Result {
785        s!(self, key, val);
786        Ok(())
787    }
788
789    fn emit_u8(&mut self, key: Key, val: u8) -> slog::Result {
790        s!(self, key, val);
791        Ok(())
792    }
793    fn emit_i8(&mut self, key: Key, val: i8) -> slog::Result {
794        s!(self, key, val);
795        Ok(())
796    }
797    fn emit_u16(&mut self, key: Key, val: u16) -> slog::Result {
798        s!(self, key, val);
799        Ok(())
800    }
801    fn emit_i16(&mut self, key: Key, val: i16) -> slog::Result {
802        s!(self, key, val);
803        Ok(())
804    }
805    fn emit_u32(&mut self, key: Key, val: u32) -> slog::Result {
806        s!(self, key, val);
807        Ok(())
808    }
809    fn emit_i32(&mut self, key: Key, val: i32) -> slog::Result {
810        s!(self, key, val);
811        Ok(())
812    }
813    fn emit_f32(&mut self, key: Key, val: f32) -> slog::Result {
814        s!(self, key, val);
815        Ok(())
816    }
817    fn emit_u64(&mut self, key: Key, val: u64) -> slog::Result {
818        s!(self, key, val);
819        Ok(())
820    }
821    fn emit_i64(&mut self, key: Key, val: i64) -> slog::Result {
822        s!(self, key, val);
823        Ok(())
824    }
825    fn emit_f64(&mut self, key: Key, val: f64) -> slog::Result {
826        s!(self, key, val);
827        Ok(())
828    }
829    fn emit_str(&mut self, key: Key, val: &str) -> slog::Result {
830        s!(self, key, val);
831        Ok(())
832    }
833    fn emit_arguments(
834        &mut self,
835        key: Key,
836        val: &fmt::Arguments,
837    ) -> slog::Result {
838        s!(self, key, val);
839        Ok(())
840    }
841    #[cfg(feature = "nested-values")]
842    fn emit_serde(
843        &mut self,
844        key: Key,
845        val: &dyn slog::SerdeValue,
846    ) -> slog::Result {
847        let mut writer = Vec::new();
848        serde::ser::Serialize::serialize(
849            val.as_serde(),
850            &mut serde_json::Serializer::new(&mut writer),
851        )
852        .map_err(std::io::Error::from)?;
853        let val =
854            std::str::from_utf8(&writer).expect("serde JSON is always UTF-8");
855        s!(self, key, val);
856        Ok(())
857    }
858}
859// }}}
860
861// {{{ CompactFormatSerializer
862/// The Compact format serializer
863pub struct CompactFormatSerializer<'a> {
864    decorator: &'a mut dyn RecordDecorator,
865    history: &'a mut Vec<(Vec<u8>, Vec<u8>)>,
866    buf: Vec<(Vec<u8>, Vec<u8>)>,
867}
868
869impl<'a> CompactFormatSerializer<'a> {
870    /// Create `CompactFormatSerializer` instance
871    pub fn new(
872        d: &'a mut dyn RecordDecorator,
873        history: &'a mut Vec<(Vec<u8>, Vec<u8>)>,
874    ) -> Self {
875        CompactFormatSerializer {
876            decorator: d,
877            history,
878            buf: vec![],
879        }
880    }
881
882    /// Write out all the whole stack
883    pub fn finish(&mut self) -> io::Result<usize> {
884        let mut indent = 0;
885
886        for mut buf in self.buf.drain(..).rev() {
887            let (print, trunc, push) =
888                if let Some(prev) = self.history.get_mut(indent) {
889                    if *prev != buf {
890                        *prev = mem::take(&mut buf);
891                        (true, true, false)
892                    } else {
893                        (false, false, false)
894                    }
895                } else {
896                    (true, false, true)
897                };
898
899            if push {
900                self.history.push(mem::take(&mut buf));
901            }
902
903            if trunc {
904                self.history.truncate(indent + 1);
905            }
906
907            if print {
908                let &(ref k, ref v) =
909                    self.history.get(indent).expect("assertion failed");
910                self.decorator.start_whitespace()?;
911                for _ in 0..indent {
912                    write!(self.decorator, " ")?;
913                }
914                self.decorator.start_key()?;
915                self.decorator.write_all(k)?;
916                self.decorator.start_separator()?;
917                write!(self.decorator, ":")?;
918                self.decorator.start_whitespace()?;
919                write!(self.decorator, " ")?;
920                self.decorator.start_value()?;
921                self.decorator.write_all(v)?;
922
923                self.decorator.start_whitespace()?;
924                writeln!(self.decorator)?;
925            }
926
927            indent += 1;
928        }
929
930        Ok(indent)
931    }
932}
933
934macro_rules! cs(
935    ($s:expr, $k:expr, $v:expr) => {
936
937        let mut k = vec!();
938        let mut v = vec!();
939        write!(&mut k, "{}", $k)?;
940        write!(&mut v, "{}", $v)?;
941        $s.buf.push((k, v));
942    };
943);
944
945impl<'a> slog::ser::Serializer for CompactFormatSerializer<'a> {
946    fn emit_none(&mut self, key: Key) -> slog::Result {
947        cs!(self, key, "None");
948        Ok(())
949    }
950    fn emit_unit(&mut self, key: Key) -> slog::Result {
951        cs!(self, key, "()");
952        Ok(())
953    }
954
955    fn emit_bool(&mut self, key: Key, val: bool) -> slog::Result {
956        cs!(self, key, val);
957        Ok(())
958    }
959
960    fn emit_char(&mut self, key: Key, val: char) -> slog::Result {
961        cs!(self, key, val);
962        Ok(())
963    }
964
965    fn emit_usize(&mut self, key: Key, val: usize) -> slog::Result {
966        cs!(self, key, val);
967        Ok(())
968    }
969    fn emit_isize(&mut self, key: Key, val: isize) -> slog::Result {
970        cs!(self, key, val);
971        Ok(())
972    }
973
974    fn emit_u8(&mut self, key: Key, val: u8) -> slog::Result {
975        cs!(self, key, val);
976        Ok(())
977    }
978    fn emit_i8(&mut self, key: Key, val: i8) -> slog::Result {
979        cs!(self, key, val);
980        Ok(())
981    }
982    fn emit_u16(&mut self, key: Key, val: u16) -> slog::Result {
983        cs!(self, key, val);
984        Ok(())
985    }
986    fn emit_i16(&mut self, key: Key, val: i16) -> slog::Result {
987        cs!(self, key, val);
988        Ok(())
989    }
990    fn emit_u32(&mut self, key: Key, val: u32) -> slog::Result {
991        cs!(self, key, val);
992        Ok(())
993    }
994    fn emit_i32(&mut self, key: Key, val: i32) -> slog::Result {
995        cs!(self, key, val);
996        Ok(())
997    }
998    fn emit_f32(&mut self, key: Key, val: f32) -> slog::Result {
999        cs!(self, key, val);
1000        Ok(())
1001    }
1002    fn emit_u64(&mut self, key: Key, val: u64) -> slog::Result {
1003        cs!(self, key, val);
1004        Ok(())
1005    }
1006    fn emit_i64(&mut self, key: Key, val: i64) -> slog::Result {
1007        cs!(self, key, val);
1008        Ok(())
1009    }
1010    fn emit_f64(&mut self, key: Key, val: f64) -> slog::Result {
1011        cs!(self, key, val);
1012        Ok(())
1013    }
1014    fn emit_str(&mut self, key: Key, val: &str) -> slog::Result {
1015        cs!(self, key, val);
1016        Ok(())
1017    }
1018    fn emit_arguments(
1019        &mut self,
1020        key: Key,
1021        val: &fmt::Arguments,
1022    ) -> slog::Result {
1023        cs!(self, key, val);
1024        Ok(())
1025    }
1026}
1027// }}}
1028
1029// {{{ CountingWriter
1030/// Wrapper for `Write` types that counts total bytes written.
1031pub struct CountingWriter<'a> {
1032    wrapped: &'a mut dyn io::Write,
1033    count: usize,
1034}
1035
1036impl<'a> CountingWriter<'a> {
1037    /// Create `CountingWriter` instance
1038    pub fn new(wrapped: &'a mut dyn io::Write) -> CountingWriter {
1039        CountingWriter { wrapped, count: 0 }
1040    }
1041
1042    /// Returns the count of the total bytes written.
1043    pub fn count(&self) -> usize {
1044        self.count
1045    }
1046}
1047
1048impl<'a> io::Write for CountingWriter<'a> {
1049    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1050        self.wrapped.write(buf).map(|n| {
1051            self.count += n;
1052            n
1053        })
1054    }
1055
1056    fn flush(&mut self) -> io::Result<()> {
1057        self.wrapped.flush()
1058    }
1059
1060    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
1061        self.wrapped.write_all(buf).map(|_| {
1062            self.count += buf.len();
1063        })
1064    }
1065}
1066// }}}
1067
1068// {{{ Timestamp
1069/// Threadsafe timestamp formatting function type
1070///
1071/// To satify `slog-rs` thread and unwind safety requirements, the
1072/// bounds expressed by this trait need to satisfied for a function
1073/// to be used in timestamp formatting.
1074pub trait ThreadSafeTimestampFn:
1075    Fn(&mut dyn io::Write) -> io::Result<()>
1076    + Send
1077    + Sync
1078    + UnwindSafe
1079    + RefUnwindSafe
1080    + 'static
1081{
1082}
1083
1084impl<F> ThreadSafeTimestampFn for F
1085where
1086    F: Fn(&mut dyn io::Write) -> io::Result<()> + Send + Sync,
1087    F: UnwindSafe + RefUnwindSafe + 'static,
1088    F: ?Sized,
1089{
1090}
1091
1092const TIMESTAMP_FORMAT: &[time::format_description::FormatItem] = time::macros::format_description!("[month repr:short] [day] [hour repr:24]:[minute]:[second].[subsecond digits:3]");
1093
1094/// Default local timezone timestamp function
1095///
1096/// The exact format used, is still subject to change.
1097pub fn timestamp_local(io: &mut dyn io::Write) -> io::Result<()> {
1098    let now: time::OffsetDateTime = std::time::SystemTime::now().into();
1099    write!(
1100        io,
1101        "{}",
1102        now.format(TIMESTAMP_FORMAT)
1103            .map_err(convert_time_fmt_error)?
1104    )
1105}
1106
1107/// Default UTC timestamp function
1108///
1109/// The exact format used, is still subject to change.
1110pub fn timestamp_utc(io: &mut dyn io::Write) -> io::Result<()> {
1111    let now = time::OffsetDateTime::now_utc();
1112    write!(
1113        io,
1114        "{}",
1115        now.format(TIMESTAMP_FORMAT)
1116            .map_err(convert_time_fmt_error)?
1117    )
1118}
1119fn convert_time_fmt_error(cause: time::error::Format) -> io::Error {
1120    io::Error::new(io::ErrorKind::Other, cause)
1121}
1122
1123// }}}
1124
1125// {{{ Plain
1126
1127/// Plain (no-op) `Decorator` implementation
1128///
1129/// This decorator doesn't do any coloring, and doesn't do any synchronization
1130/// between threads, so is not `Sync`. It is however useful combined with
1131/// `slog_async::Async` drain, as `slog_async::Async` uses only one thread,
1132/// and thus requires only `Send` from `Drain`s it wraps.
1133///
1134/// ```
1135/// use slog::*;
1136/// use slog_async::Async;
1137///
1138/// let decorator = slog_term::PlainDecorator::new(std::io::stdout());
1139/// let drain = Async::new(
1140///        slog_term::FullFormat::new(decorator).build().fuse()
1141/// )
1142/// .build()
1143/// .fuse();
1144/// ```
1145
1146pub struct PlainDecorator<W>(RefCell<W>)
1147where
1148    W: io::Write;
1149
1150impl<W> PlainDecorator<W>
1151where
1152    W: io::Write,
1153{
1154    /// Create `PlainDecorator` instance
1155    pub fn new(io: W) -> Self {
1156        PlainDecorator(RefCell::new(io))
1157    }
1158}
1159
1160impl<W> Decorator for PlainDecorator<W>
1161where
1162    W: io::Write,
1163{
1164    fn with_record<F>(
1165        &self,
1166        _record: &Record,
1167        _logger_values: &OwnedKVList,
1168        f: F,
1169    ) -> io::Result<()>
1170    where
1171        F: FnOnce(&mut dyn RecordDecorator) -> io::Result<()>,
1172    {
1173        f(&mut PlainRecordDecorator(&self.0))
1174    }
1175}
1176
1177/// Record decorator used by `PlainDecorator`
1178pub struct PlainRecordDecorator<'a, W: 'a>(&'a RefCell<W>)
1179where
1180    W: io::Write;
1181
1182impl<'a, W> io::Write for PlainRecordDecorator<'a, W>
1183where
1184    W: io::Write,
1185{
1186    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1187        self.0.borrow_mut().write(buf)
1188    }
1189
1190    fn flush(&mut self) -> io::Result<()> {
1191        self.0.borrow_mut().flush()
1192    }
1193}
1194
1195impl<'a, W> Drop for PlainRecordDecorator<'a, W>
1196where
1197    W: io::Write,
1198{
1199    fn drop(&mut self) {
1200        let _ = self.flush();
1201    }
1202}
1203
1204impl<'a, W> RecordDecorator for PlainRecordDecorator<'a, W>
1205where
1206    W: io::Write,
1207{
1208    fn reset(&mut self) -> io::Result<()> {
1209        Ok(())
1210    }
1211}
1212
1213// }}}
1214
1215// {{{ PlainSync
1216/// PlainSync `Decorator` implementation
1217///
1218/// This implementation is exactly like `PlainDecorator` but it takes care
1219/// of synchronizing writes to `io`.
1220///
1221/// ```
1222/// use slog::*;
1223///
1224/// let plain = slog_term::PlainSyncDecorator::new(std::io::stdout());
1225/// let root = Logger::root(
1226///     slog_term::FullFormat::new(plain).build().fuse(), o!()
1227/// );
1228/// ```
1229pub struct PlainSyncDecorator<W>(sync::Arc<sync::Mutex<W>>)
1230where
1231    W: io::Write;
1232
1233impl<W> PlainSyncDecorator<W>
1234where
1235    W: io::Write,
1236{
1237    /// Create `PlainSyncDecorator` instance
1238    pub fn new(io: W) -> Self {
1239        PlainSyncDecorator(sync::Arc::new(sync::Mutex::new(io)))
1240    }
1241}
1242
1243impl<W> Decorator for PlainSyncDecorator<W>
1244where
1245    W: io::Write,
1246{
1247    fn with_record<F>(
1248        &self,
1249        _record: &Record,
1250        _logger_values: &OwnedKVList,
1251        f: F,
1252    ) -> io::Result<()>
1253    where
1254        F: FnOnce(&mut dyn RecordDecorator) -> io::Result<()>,
1255    {
1256        f(&mut PlainSyncRecordDecorator {
1257            io: self.0.clone(),
1258            buf: vec![],
1259        })
1260    }
1261}
1262
1263/// `RecordDecorator` used by `PlainSyncDecorator`
1264pub struct PlainSyncRecordDecorator<W>
1265where
1266    W: io::Write,
1267{
1268    io: sync::Arc<sync::Mutex<W>>,
1269    buf: Vec<u8>,
1270}
1271
1272impl<W> io::Write for PlainSyncRecordDecorator<W>
1273where
1274    W: io::Write,
1275{
1276    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1277        self.buf.write(buf)
1278    }
1279
1280    fn flush(&mut self) -> io::Result<()> {
1281        if self.buf.is_empty() {
1282            return Ok(());
1283        }
1284
1285        let mut io = self.io.lock().map_err(|_| {
1286            io::Error::new(io::ErrorKind::Other, "mutex locking error")
1287        })?;
1288
1289        io.write_all(&self.buf)?;
1290        self.buf.clear();
1291        io.flush()
1292    }
1293}
1294
1295impl<W> Drop for PlainSyncRecordDecorator<W>
1296where
1297    W: io::Write,
1298{
1299    fn drop(&mut self) {
1300        let _ = self.flush();
1301    }
1302}
1303
1304impl<W> RecordDecorator for PlainSyncRecordDecorator<W>
1305where
1306    W: io::Write,
1307{
1308    fn reset(&mut self) -> io::Result<()> {
1309        Ok(())
1310    }
1311}
1312
1313// }}}
1314
1315// {{{ TermDecorator
1316
1317/// Any type of a terminal supported by `term` crate
1318// TODO: https://github.com/Stebalien/term/issues/70
1319enum AnyTerminal {
1320    /// Stdout terminal
1321    Stdout {
1322        term: Box<term::StdoutTerminal>,
1323        supports_reset: bool,
1324        supports_color: bool,
1325        supports_bold: bool,
1326    },
1327    /// Stderr terminal
1328    Stderr {
1329        term: Box<term::StderrTerminal>,
1330        supports_reset: bool,
1331        supports_color: bool,
1332        supports_bold: bool,
1333    },
1334    FallbackStdout,
1335    FallbackStderr,
1336}
1337
1338impl AnyTerminal {
1339    fn should_use_color(&self) -> bool {
1340        match *self {
1341            AnyTerminal::Stdout { .. } => std::io::stdout().is_terminal(),
1342            AnyTerminal::Stderr { .. } => std::io::stderr().is_terminal(),
1343            AnyTerminal::FallbackStdout => false,
1344            AnyTerminal::FallbackStderr => false,
1345        }
1346    }
1347}
1348
1349/// `TermDecorator` builder
1350pub struct TermDecoratorBuilder {
1351    use_stderr: bool,
1352    color: Option<bool>,
1353}
1354
1355impl TermDecoratorBuilder {
1356    fn new() -> Self {
1357        TermDecoratorBuilder {
1358            use_stderr: true,
1359            color: None,
1360        }
1361    }
1362
1363    /// Output to `stderr`
1364    pub fn stderr(mut self) -> Self {
1365        self.use_stderr = true;
1366        self
1367    }
1368
1369    /// Output to `stdout`
1370    pub fn stdout(mut self) -> Self {
1371        self.use_stderr = false;
1372        self
1373    }
1374
1375    /// Force colored output
1376    pub fn force_color(mut self) -> Self {
1377        self.color = Some(true);
1378        self
1379    }
1380
1381    /// Force plain output
1382    pub fn force_plain(mut self) -> Self {
1383        self.color = Some(false);
1384        self
1385    }
1386
1387    /// Try to build `TermDecorator`
1388    ///
1389    /// Unlike `build` this will not fall-back to raw `stdout`/`stderr`
1390    /// if it wasn't able to use terminal and its features directly
1391    /// (eg. if `TERM` env. was not set).
1392    pub fn try_build(self) -> Option<TermDecorator> {
1393        let io = if self.use_stderr {
1394            term::stderr().map(|t| {
1395                let supports_reset = t.supports_reset();
1396                let supports_color = t.supports_color();
1397                let supports_bold = t.supports_attr(term::Attr::Bold);
1398                AnyTerminal::Stderr {
1399                    term: t,
1400                    supports_reset,
1401                    supports_color,
1402                    supports_bold,
1403                }
1404            })
1405        } else {
1406            term::stdout().map(|t| {
1407                let supports_reset = t.supports_reset();
1408                let supports_color = t.supports_color();
1409                let supports_bold = t.supports_attr(term::Attr::Bold);
1410                AnyTerminal::Stdout {
1411                    term: t,
1412                    supports_reset,
1413                    supports_color,
1414                    supports_bold,
1415                }
1416            })
1417        };
1418
1419        io.map(|io| {
1420            let use_color = self.color.unwrap_or_else(|| io.should_use_color());
1421            TermDecorator {
1422                use_color,
1423                term: RefCell::new(io),
1424            }
1425        })
1426    }
1427
1428    /// Build `TermDecorator`
1429    ///
1430    /// Unlike `try_build` this it will fall-back to using plain `stdout`/`stderr`
1431    /// if it wasn't able to use terminal directly.
1432    pub fn build(self) -> TermDecorator {
1433        let io = if self.use_stderr {
1434            term::stderr()
1435                .map(|t| {
1436                    let supports_reset = t.supports_reset();
1437                    let supports_color = t.supports_color();
1438                    let supports_bold = t.supports_attr(term::Attr::Bold);
1439                    AnyTerminal::Stderr {
1440                        term: t,
1441                        supports_reset,
1442                        supports_color,
1443                        supports_bold,
1444                    }
1445                })
1446                .unwrap_or(AnyTerminal::FallbackStderr)
1447        } else {
1448            term::stdout()
1449                .map(|t| {
1450                    let supports_reset = t.supports_reset();
1451                    let supports_color = t.supports_color();
1452                    let supports_bold = t.supports_attr(term::Attr::Bold);
1453                    AnyTerminal::Stdout {
1454                        term: t,
1455                        supports_reset,
1456                        supports_color,
1457                        supports_bold,
1458                    }
1459                })
1460                .unwrap_or(AnyTerminal::FallbackStdout)
1461        };
1462
1463        let use_color = self.color.unwrap_or_else(|| io.should_use_color());
1464        TermDecorator {
1465            term: RefCell::new(io),
1466            use_color,
1467        }
1468    }
1469}
1470
1471/// `Decorator` implemented using `term` crate
1472///
1473/// This decorator will add nice formatting to the logs it's outputting. It's
1474/// based on `term` crate.
1475///
1476/// It does not deal with serialization so is `!Sync`. Run in a separate thread
1477/// with `slog_async::Async`.
1478pub struct TermDecorator {
1479    term: RefCell<AnyTerminal>,
1480    use_color: bool,
1481}
1482
1483impl TermDecorator {
1484    /// Start building `TermDecorator`
1485    #[allow(clippy::new_ret_no_self)]
1486    pub fn new() -> TermDecoratorBuilder {
1487        TermDecoratorBuilder::new()
1488    }
1489
1490    /// `Level` color
1491    ///
1492    /// Standard level to Unix color conversion used by `TermDecorator`
1493    pub fn level_to_color(level: slog::Level) -> u16 {
1494        match level {
1495            Level::Critical => 5,
1496            Level::Error => 1,
1497            Level::Warning => 3,
1498            Level::Info => 2,
1499            Level::Debug => 6,
1500            Level::Trace => 4,
1501        }
1502    }
1503}
1504
1505impl Decorator for TermDecorator {
1506    fn with_record<F>(
1507        &self,
1508        record: &Record,
1509        _logger_values: &OwnedKVList,
1510        f: F,
1511    ) -> io::Result<()>
1512    where
1513        F: FnOnce(&mut dyn RecordDecorator) -> io::Result<()>,
1514    {
1515        let mut term = self.term.borrow_mut();
1516        let mut deco = TermRecordDecorator {
1517            term: &mut *term,
1518            level: record.level(),
1519            use_color: self.use_color,
1520        };
1521        {
1522            f(&mut deco)
1523        }
1524    }
1525}
1526
1527/// Record decorator used by `TermDecorator`
1528pub struct TermRecordDecorator<'a> {
1529    term: &'a mut AnyTerminal,
1530    level: slog::Level,
1531    use_color: bool,
1532}
1533
1534impl<'a> io::Write for TermRecordDecorator<'a> {
1535    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1536        match *self.term {
1537            AnyTerminal::Stdout { ref mut term, .. } => term.write(buf),
1538            AnyTerminal::Stderr { ref mut term, .. } => term.write(buf),
1539            AnyTerminal::FallbackStdout => std::io::stdout().write(buf),
1540            AnyTerminal::FallbackStderr => std::io::stderr().write(buf),
1541        }
1542    }
1543
1544    fn flush(&mut self) -> io::Result<()> {
1545        match *self.term {
1546            AnyTerminal::Stdout { ref mut term, .. } => term.flush(),
1547            AnyTerminal::Stderr { ref mut term, .. } => term.flush(),
1548            AnyTerminal::FallbackStdout => std::io::stdout().flush(),
1549            AnyTerminal::FallbackStderr => std::io::stderr().flush(),
1550        }
1551    }
1552}
1553
1554impl<'a> Drop for TermRecordDecorator<'a> {
1555    fn drop(&mut self) {
1556        let _ = self.flush();
1557    }
1558}
1559
1560fn term_error_to_io_error(e: term::Error) -> io::Error {
1561    match e {
1562        term::Error::Io(e) => e,
1563        e => io::Error::new(io::ErrorKind::Other, format!("term error: {}", e)),
1564    }
1565}
1566
1567impl<'a> RecordDecorator for TermRecordDecorator<'a> {
1568    fn reset(&mut self) -> io::Result<()> {
1569        if !self.use_color {
1570            return Ok(());
1571        }
1572        match *self.term {
1573            AnyTerminal::Stdout {
1574                ref mut term,
1575                supports_reset,
1576                ..
1577            } if supports_reset => term.reset(),
1578            AnyTerminal::Stderr {
1579                ref mut term,
1580                supports_reset,
1581                ..
1582            } if supports_reset => term.reset(),
1583            _ => Ok(()),
1584        }
1585        .map_err(term_error_to_io_error)
1586    }
1587
1588    fn start_level(&mut self) -> io::Result<()> {
1589        if !self.use_color {
1590            return Ok(());
1591        }
1592        let color = TermDecorator::level_to_color(self.level);
1593        match *self.term {
1594            AnyTerminal::Stdout {
1595                ref mut term,
1596                supports_color,
1597                ..
1598            } if supports_color => term.fg(color as term::color::Color),
1599            AnyTerminal::Stderr {
1600                ref mut term,
1601                supports_color,
1602                ..
1603            } if supports_color => term.fg(color as term::color::Color),
1604            _ => Ok(()),
1605        }
1606        .map_err(term_error_to_io_error)
1607    }
1608
1609    fn start_key(&mut self) -> io::Result<()> {
1610        if !self.use_color {
1611            return Ok(());
1612        }
1613        match self.term {
1614            &mut AnyTerminal::Stdout {
1615                ref mut term,
1616                supports_color,
1617                supports_bold,
1618                ..
1619            } => {
1620                if supports_bold {
1621                    term.attr(term::Attr::Bold)
1622                } else if supports_color {
1623                    term.fg(term::color::BRIGHT_WHITE)
1624                } else {
1625                    Ok(())
1626                }
1627            }
1628            &mut AnyTerminal::Stderr {
1629                ref mut term,
1630                supports_color,
1631                supports_bold,
1632                ..
1633            } => {
1634                if supports_bold {
1635                    term.attr(term::Attr::Bold)
1636                } else if supports_color {
1637                    term.fg(term::color::BRIGHT_WHITE)
1638                } else {
1639                    Ok(())
1640                }
1641            }
1642            &mut AnyTerminal::FallbackStdout
1643            | &mut AnyTerminal::FallbackStderr => Ok(()),
1644        }
1645        .map_err(term_error_to_io_error)
1646    }
1647
1648    fn start_msg(&mut self) -> io::Result<()> {
1649        // msg is just like key
1650        self.start_key()
1651    }
1652}
1653
1654// }}}
1655
1656// {{{ TestStdoutWriter
1657/// Replacement for `std::io::stdout()` for when output capturing by rust's test
1658/// harness is required.
1659///
1660/// # Note
1661///
1662/// Due to the way that output capturing works in Rust, using this class has no effect
1663/// if the logger is later passed to another thread that is not controlled by Rust's
1664/// testing framework.
1665/// See [rust-lang/rust#42474](https://github.com/rust-lang/rust/issues/42474) for reference.
1666///
1667/// For this reason, combining this drain with [Async](https://github.com/slog-rs/async), for example, has no effect.
1668///
1669/// # Example
1670///
1671/// ```
1672/// # use slog::{Drain, info, o, Logger};
1673/// #[test]
1674/// fn test_logger() {
1675///     let logger = {
1676///         let decorator = slog_term::PlainSyncDecorator::new(slog_term::TestStdoutWriter);
1677///         let drain = slog_term::FullFormat::new(decorator).build().fuse();
1678///
1679///         Logger::root_typed(drain, o!())
1680///     };
1681///     info!(logger, "Hi from logger test");
1682/// }
1683/// ```
1684pub struct TestStdoutWriter;
1685
1686impl io::Write for TestStdoutWriter {
1687    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
1688        print!(
1689            "{}",
1690            std::str::from_utf8(data)
1691                .map_err(|x| io::Error::new(io::ErrorKind::InvalidData, x))?
1692        );
1693        Ok(data.len())
1694    }
1695    fn flush(&mut self) -> io::Result<()> {
1696        io::stdout().flush()
1697    }
1698}
1699// }}}
1700
1701// {{{ Helpers
1702/// Create a `CompactFormat` drain with default settings
1703pub fn term_compact() -> CompactFormat<TermDecorator> {
1704    let decorator = TermDecorator::new().build();
1705    CompactFormat::new(decorator).build()
1706}
1707
1708/// Create a `FullFormat` drain with default settings
1709pub fn term_full() -> FullFormat<TermDecorator> {
1710    let decorator = TermDecorator::new().build();
1711    FullFormat::new(decorator).build()
1712}
1713
1714// }}}
1715
1716#[cfg(test)]
1717mod tests {
1718    use super::*;
1719    #[test]
1720    fn test_logger() {
1721        let logger = {
1722            let decorator = PlainSyncDecorator::new(TestStdoutWriter);
1723            let drain = FullFormat::new(decorator).build().fuse();
1724
1725            slog::Logger::root_typed(drain, o!())
1726        };
1727        info!(logger, "Hi from logger test");
1728    }
1729}
1730// vim: foldmethod=marker foldmarker={{{,}}}