flexi_logger/
write_mode.rs

1use crate::ZERO_DURATION;
2use std::time::Duration;
3
4/// Default buffer capacity (8k), when buffering is used.
5pub const DEFAULT_BUFFER_CAPACITY: usize = 8 * 1024;
6
7/// Default flush interval (1s), when flushing is used.
8pub const DEFAULT_FLUSH_INTERVAL: Duration = Duration::from_secs(1);
9
10/// Default size of the message pool;
11/// a higher value could further reduce allocations during log file rotation and cleanup.
12#[cfg(feature = "async")]
13#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
14pub const DEFAULT_POOL_CAPA: usize = 50;
15
16/// Default capacity for the message buffers;
17/// a higher value reduces allocations when longer log lines are used.
18#[cfg(feature = "async")]
19#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
20pub const DEFAULT_MESSAGE_CAPA: usize = 200;
21
22/// Describes whether the log output should be written synchronously or asynchronously,
23/// and if and how I/O should be buffered and flushed.
24///
25/// Is used in [`Logger::write_mode`](struct.Logger.html#method.write_mode).
26///
27/// Buffering reduces the program's I/O overhead, and thus increases overall performance,
28/// which can become relevant if logging is used heavily.
29/// On the other hand, if logging is used with low frequency,
30/// buffering can defer the appearance of log lines significantly,
31/// so regular flushing is usually advisable with buffering.
32///
33/// **Note** that for all options except `Direct` you should keep the
34/// [`LoggerHandle`](struct.LoggerHandle.html) alive
35/// up to the very end of your program to ensure that all buffered log lines are flushed out
36/// (which happens automatically when the [`LoggerHandle`](struct.LoggerHandle.html) is dropped)
37/// before the program terminates.
38/// [See here for an example](code_examples/index.html#choose-the-write-mode).
39///
40/// **Note** further that flushing uses an extra thread (with minimal stack).
41///
42/// The console is a slow output device (at least on Windows).
43/// With `WriteMode::Async` it can happen that in phases with vast log output
44/// the log lines appear significantly later than they were written.
45/// Also, a final printing phase is possible at the end of the program when the logger handle
46/// is dropped (and all output is flushed automatically).
47///
48/// `WriteMode::Direct` (i.e. without buffering) is the slowest option with all output devices,
49/// showing that buffered I/O pays off.
50///
51/// Using `log_to_stdout()` and then redirecting the output to a file can make things faster,
52/// likely because the operating system's adds buffering,
53/// but is still significantly slower than writing to files directly.
54///
55#[derive(Copy, Clone, Debug, Eq, PartialEq)]
56pub enum WriteMode {
57    /// Do not buffer (default).
58    ///
59    /// Every log line is directly written to the output, without buffering.
60    /// This allows seeing new log lines in real time, and does not need additional threads.
61    Direct,
62
63    /// Do not buffer and support `cargo test`'s capture.
64    ///
65    /// Much like `Direct`, just a bit slower, and allows
66    /// `cargo test` to capture log output and print it only for failing tests.
67    SupportCapture,
68
69    /// Same as `BufferAndFlushWith` with default capacity ([`DEFAULT_BUFFER_CAPACITY`])
70    /// and default interval ([`DEFAULT_FLUSH_INTERVAL`]).
71    BufferAndFlush,
72
73    /// Buffer and flush with given buffer capacity and flush interval.
74    BufferAndFlushWith(
75        /// Buffer capacity.
76        usize,
77        /// Flush interval.
78        Duration,
79    ),
80
81    /// Same as `BufferDontFlushWith` with default capacity ([`DEFAULT_BUFFER_CAPACITY`]).
82    BufferDontFlush,
83
84    /// Buffer with given buffer capacity, but don't flush.
85    ///
86    /// This might be handy if you want to minimize I/O effort and don't want to create
87    /// the extra thread for flushing and don't care if log lines appear with delay.
88    BufferDontFlushWith(
89        /// Buffer capacity.
90        usize,
91    ),
92
93    /// Same as `AsyncWith`, using default values for all parameters.
94    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
95    #[cfg(feature = "async")]
96    Async,
97
98    /// Log lines are sent through an unbounded channel to an output thread, which
99    /// does the I/O, and, if `log_to_file()` is chosen, also the rotation and the cleanup.
100    ///
101    /// Uses buffered output to reduce overhead, and a bounded message pool to reduce allocations.
102    /// The log output is flushed regularly with the given interval.
103    ///
104    /// See [here](code_examples/index.html#choose-the-write-mode) for an example.
105    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
106    #[cfg(feature = "async")]
107    AsyncWith {
108        /// Capacity of the pool for the message buffers.
109        pool_capa: usize,
110        /// Capacity of an individual message buffer.
111        message_capa: usize,
112        /// The interval for flushing the output.
113        ///
114        /// With `Duration::ZERO` flushing is suppressed.
115        flush_interval: Duration,
116    },
117}
118impl WriteMode {
119    pub(crate) fn effective_write_mode(&self) -> EffectiveWriteMode {
120        match *self {
121            Self::Direct | Self::SupportCapture => EffectiveWriteMode::Direct,
122            Self::BufferDontFlush => {
123                EffectiveWriteMode::BufferDontFlushWith(DEFAULT_BUFFER_CAPACITY)
124            }
125            Self::BufferDontFlushWith(duration) => {
126                EffectiveWriteMode::BufferDontFlushWith(duration)
127            }
128            Self::BufferAndFlush => EffectiveWriteMode::BufferAndFlushWith(DEFAULT_BUFFER_CAPACITY),
129            Self::BufferAndFlushWith(bufsize, _duration) => {
130                EffectiveWriteMode::BufferAndFlushWith(bufsize)
131            }
132            #[cfg(feature = "async")]
133            Self::Async => EffectiveWriteMode::AsyncWith {
134                pool_capa: DEFAULT_POOL_CAPA,
135                message_capa: DEFAULT_MESSAGE_CAPA,
136                flush_interval: DEFAULT_FLUSH_INTERVAL,
137            },
138            #[cfg(feature = "async")]
139            Self::AsyncWith {
140                pool_capa,
141                message_capa,
142                flush_interval,
143            } => EffectiveWriteMode::AsyncWith {
144                pool_capa,
145                message_capa,
146                flush_interval,
147            },
148        }
149    }
150    pub(crate) fn without_flushing(&self) -> WriteMode {
151        match self {
152            Self::Direct
153            | Self::SupportCapture
154            | Self::BufferDontFlush
155            | Self::BufferDontFlushWith(_) => *self,
156            Self::BufferAndFlush => Self::BufferDontFlush,
157            Self::BufferAndFlushWith(bufsize, _) => Self::BufferDontFlushWith(*bufsize),
158            #[cfg(feature = "async")]
159            Self::Async => Self::AsyncWith {
160                pool_capa: DEFAULT_POOL_CAPA,
161                message_capa: DEFAULT_MESSAGE_CAPA,
162                flush_interval: ZERO_DURATION,
163            },
164            #[cfg(feature = "async")]
165            Self::AsyncWith {
166                pool_capa,
167                message_capa,
168                flush_interval: _,
169            } => Self::AsyncWith {
170                pool_capa: *pool_capa,
171                message_capa: *message_capa,
172                flush_interval: ZERO_DURATION,
173            },
174        }
175    }
176    pub(crate) fn buffersize(&self) -> Option<usize> {
177        match self.effective_write_mode() {
178            EffectiveWriteMode::Direct => None,
179            EffectiveWriteMode::BufferAndFlushWith(bufsize)
180            | EffectiveWriteMode::BufferDontFlushWith(bufsize) => Some(bufsize),
181            #[cfg(feature = "async")]
182            EffectiveWriteMode::AsyncWith {
183                pool_capa: _,
184                message_capa: _,
185                flush_interval: _,
186            } => None,
187        }
188    }
189    pub(crate) fn get_flush_interval(&self) -> Duration {
190        match self {
191            Self::Direct
192            | Self::SupportCapture
193            | Self::BufferDontFlush
194            | Self::BufferDontFlushWith(_) => ZERO_DURATION,
195            Self::BufferAndFlush => DEFAULT_FLUSH_INTERVAL,
196            #[cfg(feature = "async")]
197            Self::Async => DEFAULT_FLUSH_INTERVAL,
198            Self::BufferAndFlushWith(_, flush_interval) => *flush_interval,
199            #[cfg(feature = "async")]
200            Self::AsyncWith {
201                pool_capa: _,
202                message_capa: _,
203                flush_interval,
204            } => *flush_interval,
205        }
206    }
207}
208
209pub(crate) enum EffectiveWriteMode {
210    Direct,
211    BufferAndFlushWith(usize),
212    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
213    #[cfg(feature = "async")]
214    AsyncWith {
215        /// Capacity of the pool for the message buffers.
216        pool_capa: usize,
217        /// Capacity of an individual message buffer.
218        message_capa: usize,
219        /// The interval for flushing the output.
220        ///
221        /// With `Duration::ZERO` flushing is suppressed.
222        flush_interval: Duration,
223    },
224    BufferDontFlushWith(usize),
225}