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 formatting 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 satisfy `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 (k, 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<'a> {
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 satisfy `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/// Convert from [`chrono::DateTime`] into [`time::OffsetDateTime`]
1095fn local_timestamp_from_chrono(
1096    local_time: chrono::DateTime<chrono::Local>,
1097) -> result::Result<time::OffsetDateTime, TimestampError> {
1098    use chrono::Utc;
1099    let offset: chrono::FixedOffset = local_time.fixed_offset().timezone();
1100    let utc_time: chrono::DateTime<Utc> = local_time.to_utc();
1101    #[cfg(test)]
1102    {
1103        if offset.local_minus_utc() == 0 {
1104            assert_eq!(utc_time.to_rfc3339(), local_time.to_rfc3339());
1105        } else {
1106            assert_ne!(utc_time.to_rfc3339(), local_time.to_rfc3339());
1107        }
1108    }
1109    let utc_time: time::OffsetDateTime =
1110        time::OffsetDateTime::from_unix_timestamp(utc_time.timestamp())
1111            .map_err(TimestampError::LocalTimeConversion)?
1112            + time::Duration::nanoseconds(i64::from(
1113                utc_time.timestamp_subsec_nanos(),
1114            ));
1115    Ok(utc_time.to_offset(
1116        time::UtcOffset::from_whole_seconds(offset.local_minus_utc())
1117            .map_err(TimestampError::LocalTimeConversion)?,
1118    ))
1119}
1120
1121/// Default local timezone timestamp function
1122///
1123/// The exact format used, is still subject to change.
1124///
1125/// # Implementation Note
1126/// This requires `chrono` to detect the localtime in a thread-safe manner.
1127/// See the comment on the dependency, PR #44 and PR #48.
1128pub fn timestamp_local(io: &mut dyn io::Write) -> io::Result<()> {
1129    let now = local_timestamp_from_chrono(chrono::Local::now())?;
1130    write!(
1131        io,
1132        "{}",
1133        now.format(TIMESTAMP_FORMAT)
1134            .map_err(TimestampError::Format)?
1135    )
1136}
1137
1138/// Default UTC timestamp function
1139///
1140/// The exact format used, is still subject to change.
1141pub fn timestamp_utc(io: &mut dyn io::Write) -> io::Result<()> {
1142    let now = time::OffsetDateTime::now_utc();
1143    write!(
1144        io,
1145        "{}",
1146        now.format(TIMESTAMP_FORMAT)
1147            .map_err(TimestampError::Format)?
1148    )
1149}
1150
1151/// An internal error that occurs with timestamps
1152#[derive(Debug)]
1153#[non_exhaustive]
1154enum TimestampError {
1155    /// An error formatting the timestamp
1156    Format(time::error::Format),
1157    /// An error that occurred while
1158    /// converting from `chrono::DateTime<Local>` to `time::OffsetDateTime`
1159    LocalTimeConversion(time::error::ComponentRange),
1160}
1161/*
1162 * We could use thiserror here,
1163 * but writing it by hand is almost as good and eliminates a dependency
1164 */
1165impl fmt::Display for TimestampError {
1166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1167        match self {
1168            TimestampError::Format(cause) => {
1169                write!(f, "Failed to format timestamp: {cause}")
1170            }
1171            TimestampError::LocalTimeConversion(cause) => {
1172                write!(f, "Failed to convert local time: {cause}")
1173            }
1174        }
1175    }
1176}
1177impl From<TimestampError> for io::Error {
1178    fn from(cause: TimestampError) -> Self {
1179        io::Error::new(io::ErrorKind::Other, cause)
1180    }
1181}
1182impl std::error::Error for TimestampError {
1183    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
1184        match self {
1185            TimestampError::Format(cause) => Some(cause),
1186            TimestampError::LocalTimeConversion(cause) => Some(cause),
1187        }
1188    }
1189}
1190
1191// }}}
1192
1193// {{{ Plain
1194
1195/// Plain (no-op) `Decorator` implementation
1196///
1197/// This decorator doesn't do any coloring, and doesn't do any synchronization
1198/// between threads, so is not `Sync`. It is however useful combined with
1199/// `slog_async::Async` drain, as `slog_async::Async` uses only one thread,
1200/// and thus requires only `Send` from `Drain`s it wraps.
1201///
1202/// ```
1203/// use slog::*;
1204/// use slog_async::Async;
1205///
1206/// let decorator = slog_term::PlainDecorator::new(std::io::stdout());
1207/// let drain = Async::new(
1208///        slog_term::FullFormat::new(decorator).build().fuse()
1209/// )
1210/// .build()
1211/// .fuse();
1212/// ```
1213pub struct PlainDecorator<W>(RefCell<W>)
1214where
1215    W: io::Write;
1216
1217impl<W> PlainDecorator<W>
1218where
1219    W: io::Write,
1220{
1221    /// Create `PlainDecorator` instance
1222    pub fn new(io: W) -> Self {
1223        PlainDecorator(RefCell::new(io))
1224    }
1225}
1226
1227impl<W> Decorator for PlainDecorator<W>
1228where
1229    W: io::Write,
1230{
1231    fn with_record<F>(
1232        &self,
1233        _record: &Record,
1234        _logger_values: &OwnedKVList,
1235        f: F,
1236    ) -> io::Result<()>
1237    where
1238        F: FnOnce(&mut dyn RecordDecorator) -> io::Result<()>,
1239    {
1240        f(&mut PlainRecordDecorator(&self.0))
1241    }
1242}
1243
1244/// Record decorator used by `PlainDecorator`
1245pub struct PlainRecordDecorator<'a, W: 'a>(&'a RefCell<W>)
1246where
1247    W: io::Write;
1248
1249impl<'a, W> io::Write for PlainRecordDecorator<'a, W>
1250where
1251    W: io::Write,
1252{
1253    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1254        self.0.borrow_mut().write(buf)
1255    }
1256
1257    fn flush(&mut self) -> io::Result<()> {
1258        self.0.borrow_mut().flush()
1259    }
1260}
1261
1262impl<'a, W> Drop for PlainRecordDecorator<'a, W>
1263where
1264    W: io::Write,
1265{
1266    fn drop(&mut self) {
1267        let _ = self.flush();
1268    }
1269}
1270
1271impl<'a, W> RecordDecorator for PlainRecordDecorator<'a, W>
1272where
1273    W: io::Write,
1274{
1275    fn reset(&mut self) -> io::Result<()> {
1276        Ok(())
1277    }
1278}
1279
1280// }}}
1281
1282// {{{ PlainSync
1283/// PlainSync `Decorator` implementation
1284///
1285/// This implementation is exactly like `PlainDecorator` but it takes care
1286/// of synchronizing writes to `io`.
1287///
1288/// ```
1289/// use slog::*;
1290///
1291/// let plain = slog_term::PlainSyncDecorator::new(std::io::stdout());
1292/// let root = Logger::root(
1293///     slog_term::FullFormat::new(plain).build().fuse(), o!()
1294/// );
1295/// ```
1296pub struct PlainSyncDecorator<W>(sync::Arc<sync::Mutex<W>>)
1297where
1298    W: io::Write;
1299
1300impl<W> PlainSyncDecorator<W>
1301where
1302    W: io::Write,
1303{
1304    /// Create `PlainSyncDecorator` instance
1305    pub fn new(io: W) -> Self {
1306        PlainSyncDecorator(sync::Arc::new(sync::Mutex::new(io)))
1307    }
1308}
1309
1310impl<W> Decorator for PlainSyncDecorator<W>
1311where
1312    W: io::Write,
1313{
1314    fn with_record<F>(
1315        &self,
1316        _record: &Record,
1317        _logger_values: &OwnedKVList,
1318        f: F,
1319    ) -> io::Result<()>
1320    where
1321        F: FnOnce(&mut dyn RecordDecorator) -> io::Result<()>,
1322    {
1323        f(&mut PlainSyncRecordDecorator {
1324            io: self.0.clone(),
1325            buf: vec![],
1326        })
1327    }
1328}
1329
1330/// `RecordDecorator` used by `PlainSyncDecorator`
1331pub struct PlainSyncRecordDecorator<W>
1332where
1333    W: io::Write,
1334{
1335    io: sync::Arc<sync::Mutex<W>>,
1336    buf: Vec<u8>,
1337}
1338
1339impl<W> io::Write for PlainSyncRecordDecorator<W>
1340where
1341    W: io::Write,
1342{
1343    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1344        self.buf.write(buf)
1345    }
1346
1347    fn flush(&mut self) -> io::Result<()> {
1348        if self.buf.is_empty() {
1349            return Ok(());
1350        }
1351
1352        let mut io = self.io.lock().map_err(|_| {
1353            io::Error::new(io::ErrorKind::Other, "mutex locking error")
1354        })?;
1355
1356        io.write_all(&self.buf)?;
1357        self.buf.clear();
1358        io.flush()
1359    }
1360}
1361
1362impl<W> Drop for PlainSyncRecordDecorator<W>
1363where
1364    W: io::Write,
1365{
1366    fn drop(&mut self) {
1367        let _ = self.flush();
1368    }
1369}
1370
1371impl<W> RecordDecorator for PlainSyncRecordDecorator<W>
1372where
1373    W: io::Write,
1374{
1375    fn reset(&mut self) -> io::Result<()> {
1376        Ok(())
1377    }
1378}
1379
1380// }}}
1381
1382// {{{ TermDecorator
1383
1384/// Any type of a terminal supported by `term` crate
1385// TODO: https://github.com/Stebalien/term/issues/70
1386enum AnyTerminal {
1387    /// Stdout terminal
1388    Stdout {
1389        term: Box<term::StdoutTerminal>,
1390        supports_reset: bool,
1391        supports_color: bool,
1392        supports_bold: bool,
1393    },
1394    /// Stderr terminal
1395    Stderr {
1396        term: Box<term::StderrTerminal>,
1397        supports_reset: bool,
1398        supports_color: bool,
1399        supports_bold: bool,
1400    },
1401    FallbackStdout,
1402    FallbackStderr,
1403}
1404
1405impl AnyTerminal {
1406    fn should_use_color(&self) -> bool {
1407        // Respect NO_COLOR <https://no-color.org/>
1408        if std::env::var_os("NO_COLOR").map_or(false, |x| !x.is_empty()) {
1409            return false;
1410        }
1411        match *self {
1412            AnyTerminal::Stdout { .. } => std::io::stdout().is_terminal(),
1413            AnyTerminal::Stderr { .. } => std::io::stderr().is_terminal(),
1414            AnyTerminal::FallbackStdout => false,
1415            AnyTerminal::FallbackStderr => false,
1416        }
1417    }
1418}
1419
1420/// `TermDecorator` builder
1421pub struct TermDecoratorBuilder {
1422    use_stderr: bool,
1423    color: Option<bool>,
1424}
1425
1426impl TermDecoratorBuilder {
1427    fn new() -> Self {
1428        TermDecoratorBuilder {
1429            use_stderr: true,
1430            color: None,
1431        }
1432    }
1433
1434    /// Output to `stderr`
1435    pub fn stderr(mut self) -> Self {
1436        self.use_stderr = true;
1437        self
1438    }
1439
1440    /// Output to `stdout`
1441    pub fn stdout(mut self) -> Self {
1442        self.use_stderr = false;
1443        self
1444    }
1445
1446    /// Force colored output
1447    pub fn force_color(mut self) -> Self {
1448        self.color = Some(true);
1449        self
1450    }
1451
1452    /// Force plain output
1453    pub fn force_plain(mut self) -> Self {
1454        self.color = Some(false);
1455        self
1456    }
1457
1458    /// Try to build `TermDecorator`
1459    ///
1460    /// Unlike `build` this will not fall-back to raw `stdout`/`stderr`
1461    /// if it wasn't able to use terminal and its features directly
1462    /// (eg. if `TERM` env. was not set).
1463    pub fn try_build(self) -> Option<TermDecorator> {
1464        let io = if self.use_stderr {
1465            term::stderr().map(|t| {
1466                let supports_reset = t.supports_reset();
1467                let supports_color = t.supports_color();
1468                let supports_bold = t.supports_attr(term::Attr::Bold);
1469                AnyTerminal::Stderr {
1470                    term: t,
1471                    supports_reset,
1472                    supports_color,
1473                    supports_bold,
1474                }
1475            })
1476        } else {
1477            term::stdout().map(|t| {
1478                let supports_reset = t.supports_reset();
1479                let supports_color = t.supports_color();
1480                let supports_bold = t.supports_attr(term::Attr::Bold);
1481                AnyTerminal::Stdout {
1482                    term: t,
1483                    supports_reset,
1484                    supports_color,
1485                    supports_bold,
1486                }
1487            })
1488        };
1489
1490        io.map(|io| {
1491            let use_color = self.color.unwrap_or_else(|| io.should_use_color());
1492            TermDecorator {
1493                use_color,
1494                term: RefCell::new(io),
1495            }
1496        })
1497    }
1498
1499    /// Build `TermDecorator`
1500    ///
1501    /// Unlike `try_build` this it will fall-back to using plain `stdout`/`stderr`
1502    /// if it wasn't able to use terminal directly.
1503    pub fn build(self) -> TermDecorator {
1504        let io = if self.use_stderr {
1505            term::stderr()
1506                .map(|t| {
1507                    let supports_reset = t.supports_reset();
1508                    let supports_color = t.supports_color();
1509                    let supports_bold = t.supports_attr(term::Attr::Bold);
1510                    AnyTerminal::Stderr {
1511                        term: t,
1512                        supports_reset,
1513                        supports_color,
1514                        supports_bold,
1515                    }
1516                })
1517                .unwrap_or(AnyTerminal::FallbackStderr)
1518        } else {
1519            term::stdout()
1520                .map(|t| {
1521                    let supports_reset = t.supports_reset();
1522                    let supports_color = t.supports_color();
1523                    let supports_bold = t.supports_attr(term::Attr::Bold);
1524                    AnyTerminal::Stdout {
1525                        term: t,
1526                        supports_reset,
1527                        supports_color,
1528                        supports_bold,
1529                    }
1530                })
1531                .unwrap_or(AnyTerminal::FallbackStdout)
1532        };
1533
1534        let use_color = self.color.unwrap_or_else(|| io.should_use_color());
1535        TermDecorator {
1536            term: RefCell::new(io),
1537            use_color,
1538        }
1539    }
1540}
1541
1542/// `Decorator` implemented using `term` crate
1543///
1544/// This decorator will add nice formatting to the logs it's outputting. It's
1545/// based on `term` crate.
1546///
1547/// It does not deal with serialization so is `!Sync`. Run in a separate thread
1548/// with `slog_async::Async`.
1549pub struct TermDecorator {
1550    term: RefCell<AnyTerminal>,
1551    use_color: bool,
1552}
1553
1554impl TermDecorator {
1555    /// Start building `TermDecorator`
1556    #[allow(clippy::new_ret_no_self)]
1557    pub fn new() -> TermDecoratorBuilder {
1558        TermDecoratorBuilder::new()
1559    }
1560
1561    /// `Level` color
1562    ///
1563    /// Standard level to Unix color conversion used by `TermDecorator`
1564    pub fn level_to_color(level: slog::Level) -> u16 {
1565        match level {
1566            Level::Critical => 5,
1567            Level::Error => 1,
1568            Level::Warning => 3,
1569            Level::Info => 2,
1570            Level::Debug => 6,
1571            Level::Trace => 4,
1572        }
1573    }
1574}
1575
1576impl Decorator for TermDecorator {
1577    fn with_record<F>(
1578        &self,
1579        record: &Record,
1580        _logger_values: &OwnedKVList,
1581        f: F,
1582    ) -> io::Result<()>
1583    where
1584        F: FnOnce(&mut dyn RecordDecorator) -> io::Result<()>,
1585    {
1586        let mut term = self.term.borrow_mut();
1587        let mut deco = TermRecordDecorator {
1588            term: &mut term,
1589            level: record.level(),
1590            use_color: self.use_color,
1591        };
1592        {
1593            f(&mut deco)
1594        }
1595    }
1596}
1597
1598/// Record decorator used by `TermDecorator`
1599pub struct TermRecordDecorator<'a> {
1600    term: &'a mut AnyTerminal,
1601    level: slog::Level,
1602    use_color: bool,
1603}
1604
1605impl<'a> io::Write for TermRecordDecorator<'a> {
1606    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1607        match *self.term {
1608            AnyTerminal::Stdout { ref mut term, .. } => term.write(buf),
1609            AnyTerminal::Stderr { ref mut term, .. } => term.write(buf),
1610            AnyTerminal::FallbackStdout => std::io::stdout().write(buf),
1611            AnyTerminal::FallbackStderr => std::io::stderr().write(buf),
1612        }
1613    }
1614
1615    fn flush(&mut self) -> io::Result<()> {
1616        match *self.term {
1617            AnyTerminal::Stdout { ref mut term, .. } => term.flush(),
1618            AnyTerminal::Stderr { ref mut term, .. } => term.flush(),
1619            AnyTerminal::FallbackStdout => std::io::stdout().flush(),
1620            AnyTerminal::FallbackStderr => std::io::stderr().flush(),
1621        }
1622    }
1623}
1624
1625impl<'a> Drop for TermRecordDecorator<'a> {
1626    fn drop(&mut self) {
1627        let _ = self.flush();
1628    }
1629}
1630
1631fn term_error_to_io_error(e: term::Error) -> io::Error {
1632    match e {
1633        term::Error::Io(e) => e,
1634        e => io::Error::new(io::ErrorKind::Other, format!("term error: {}", e)),
1635    }
1636}
1637
1638impl<'a> RecordDecorator for TermRecordDecorator<'a> {
1639    fn reset(&mut self) -> io::Result<()> {
1640        if !self.use_color {
1641            return Ok(());
1642        }
1643        match *self.term {
1644            AnyTerminal::Stdout {
1645                ref mut term,
1646                supports_reset,
1647                ..
1648            } if supports_reset => term.reset(),
1649            AnyTerminal::Stderr {
1650                ref mut term,
1651                supports_reset,
1652                ..
1653            } if supports_reset => term.reset(),
1654            _ => Ok(()),
1655        }
1656        .map_err(term_error_to_io_error)
1657    }
1658
1659    fn start_level(&mut self) -> io::Result<()> {
1660        if !self.use_color {
1661            return Ok(());
1662        }
1663        let color = TermDecorator::level_to_color(self.level);
1664        match *self.term {
1665            AnyTerminal::Stdout {
1666                ref mut term,
1667                supports_color,
1668                ..
1669            } if supports_color => term.fg(color as term::color::Color),
1670            AnyTerminal::Stderr {
1671                ref mut term,
1672                supports_color,
1673                ..
1674            } if supports_color => term.fg(color as term::color::Color),
1675            _ => Ok(()),
1676        }
1677        .map_err(term_error_to_io_error)
1678    }
1679
1680    fn start_key(&mut self) -> io::Result<()> {
1681        if !self.use_color {
1682            return Ok(());
1683        }
1684        match self.term {
1685            &mut AnyTerminal::Stdout {
1686                ref mut term,
1687                supports_color,
1688                supports_bold,
1689                ..
1690            } => {
1691                if supports_bold {
1692                    term.attr(term::Attr::Bold)
1693                } else if supports_color {
1694                    term.fg(term::color::BRIGHT_WHITE)
1695                } else {
1696                    Ok(())
1697                }
1698            }
1699            &mut AnyTerminal::Stderr {
1700                ref mut term,
1701                supports_color,
1702                supports_bold,
1703                ..
1704            } => {
1705                if supports_bold {
1706                    term.attr(term::Attr::Bold)
1707                } else if supports_color {
1708                    term.fg(term::color::BRIGHT_WHITE)
1709                } else {
1710                    Ok(())
1711                }
1712            }
1713            &mut AnyTerminal::FallbackStdout
1714            | &mut AnyTerminal::FallbackStderr => Ok(()),
1715        }
1716        .map_err(term_error_to_io_error)
1717    }
1718
1719    fn start_msg(&mut self) -> io::Result<()> {
1720        // msg is just like key
1721        self.start_key()
1722    }
1723}
1724
1725// }}}
1726
1727// {{{ TestStdoutWriter
1728/// Replacement for `std::io::stdout()` for when output capturing by rust's test
1729/// harness is required.
1730///
1731/// # Note
1732///
1733/// Due to the way that output capturing works in Rust, using this class has no effect
1734/// if the logger is later passed to another thread that is not controlled by Rust's
1735/// testing framework.
1736/// See [rust-lang/rust#42474](https://github.com/rust-lang/rust/issues/42474) for reference.
1737///
1738/// For this reason, combining this drain with [Async](https://github.com/slog-rs/async), for example, has no effect.
1739///
1740/// # Example
1741///
1742/// ```
1743/// # use slog::{Drain, info, o, Logger};
1744/// #[test]
1745/// fn test_logger() {
1746///     let logger = {
1747///         let decorator = slog_term::PlainSyncDecorator::new(slog_term::TestStdoutWriter);
1748///         let drain = slog_term::FullFormat::new(decorator).build().fuse();
1749///
1750///         Logger::root_typed(drain, o!())
1751///     };
1752///     info!(logger, "Hi from logger test");
1753/// }
1754/// ```
1755pub struct TestStdoutWriter;
1756
1757impl io::Write for TestStdoutWriter {
1758    fn write(&mut self, data: &[u8]) -> io::Result<usize> {
1759        print!(
1760            "{}",
1761            std::str::from_utf8(data)
1762                .map_err(|x| io::Error::new(io::ErrorKind::InvalidData, x))?
1763        );
1764        Ok(data.len())
1765    }
1766    fn flush(&mut self) -> io::Result<()> {
1767        io::stdout().flush()
1768    }
1769}
1770// }}}
1771
1772// {{{ Helpers
1773/// Create a `CompactFormat` drain with default settings
1774pub fn term_compact() -> CompactFormat<TermDecorator> {
1775    let decorator = TermDecorator::new().build();
1776    CompactFormat::new(decorator).build()
1777}
1778
1779/// Create a `FullFormat` drain with default settings
1780pub fn term_full() -> FullFormat<TermDecorator> {
1781    let decorator = TermDecorator::new().build();
1782    FullFormat::new(decorator).build()
1783}
1784
1785// }}}
1786
1787#[cfg(test)]
1788mod tests {
1789    use super::*;
1790    #[test]
1791    fn test_logger() {
1792        let logger = {
1793            let decorator = PlainSyncDecorator::new(TestStdoutWriter);
1794            let drain = FullFormat::new(decorator).build().fuse();
1795
1796            slog::Logger::root_typed(drain, o!())
1797        };
1798        info!(logger, "Hi from logger test");
1799    }
1800}
1801// vim: foldmethod=marker foldmarker={{{,}}}