flexi_logger/
write_mode.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
use crate::ZERO_DURATION;
use std::time::Duration;

/// Default buffer capacity (8k), when buffering is used.
pub const DEFAULT_BUFFER_CAPACITY: usize = 8 * 1024;

/// Default flush interval (1s), when flushing is used.
pub const DEFAULT_FLUSH_INTERVAL: Duration = Duration::from_secs(1);

/// Default size of the message pool;
/// a higher value could further reduce allocations during log file rotation and cleanup.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub const DEFAULT_POOL_CAPA: usize = 50;

/// Default capacity for the message buffers;
/// a higher value reduces allocations when longer log lines are used.
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
pub const DEFAULT_MESSAGE_CAPA: usize = 200;

/// Describes whether the log output should be written synchronously or asynchronously,
/// and if and how I/O should be buffered and flushed.
///
/// Is used in [`Logger::write_mode`](struct.Logger.html#method.write_mode).
///
/// Buffering reduces the program's I/O overhead, and thus increases overall performance,
/// which can become relevant if logging is used heavily.
/// On the other hand, if logging is used with low frequency,
/// buffering can defer the appearance of log lines significantly,
/// so regular flushing is usually advisable with buffering.
///
/// **Note** that for all options except `Direct` you should keep the
/// [`LoggerHandle`](struct.LoggerHandle.html) alive
/// up to the very end of your program to ensure that all buffered log lines are flushed out
/// (which happens automatically when the [`LoggerHandle`](struct.LoggerHandle.html) is dropped)
/// before the program terminates.
/// [See here for an example](code_examples/index.html#choose-the-write-mode).
///
/// **Note** further that flushing uses an extra thread (with minimal stack).
///
/// The console is a slow output device (at least on Windows).
/// With `WriteMode::Async` it can happen that in phases with vast log output
/// the log lines appear significantly later than they were written.
/// Also, a final printing phase is possible at the end of the program when the logger handle
/// is dropped (and all output is flushed automatically).
///
/// `WriteMode::Direct` (i.e. without buffering) is the slowest option with all output devices,
/// showing that buffered I/O pays off.
///
/// Using `log_to_stdout()` and then redirecting the output to a file can make things faster,
/// likely because the operating system's adds buffering,
/// but is still significantly slower than writing to files directly.
///
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum WriteMode {
    /// Do not buffer (default).
    ///
    /// Every log line is directly written to the output, without buffering.
    /// This allows seeing new log lines in real time, and does not need additional threads.
    Direct,

    /// Do not buffer and support `cargo test`'s capture.
    ///
    /// Much like `Direct`, just a bit slower, and allows
    /// `cargo test` to capture log output and print it only for failing tests.
    SupportCapture,

    /// Same as `BufferAndFlushWith` with default capacity ([`DEFAULT_BUFFER_CAPACITY`])
    /// and default interval ([`DEFAULT_FLUSH_INTERVAL`]).
    BufferAndFlush,

    /// Buffer and flush with given buffer capacity and flush interval.
    BufferAndFlushWith(
        /// Buffer capacity.
        usize,
        /// Flush interval.
        Duration,
    ),

    /// Same as `BufferDontFlushWith` with default capacity ([`DEFAULT_BUFFER_CAPACITY`]).
    BufferDontFlush,

    /// Buffer with given buffer capacity, but don't flush.
    ///
    /// This might be handy if you want to minimize I/O effort and don't want to create
    /// the extra thread for flushing and don't care if log lines appear with delay.
    BufferDontFlushWith(
        /// Buffer capacity.
        usize,
    ),

    /// Same as `AsyncWith`, using default values for all parameters.
    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
    #[cfg(feature = "async")]
    Async,

    /// Log lines are sent through an unbounded channel to an output thread, which
    /// does the I/O, and, if `log_to_file()` is chosen, also the rotation and the cleanup.
    ///
    /// Uses buffered output to reduce overhead, and a bounded message pool to reduce allocations.
    /// The log output is flushed regularly with the given interval.
    ///
    /// See [here](code_examples/index.html#choose-the-write-mode) for an example.
    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
    #[cfg(feature = "async")]
    AsyncWith {
        /// Capacity of the pool for the message buffers.
        pool_capa: usize,
        /// Capacity of an individual message buffer.
        message_capa: usize,
        /// The interval for flushing the output.
        ///
        /// With `Duration::ZERO` flushing is suppressed.
        flush_interval: Duration,
    },
}
impl WriteMode {
    pub(crate) fn effective_write_mode(&self) -> EffectiveWriteMode {
        match *self {
            Self::Direct | Self::SupportCapture => EffectiveWriteMode::Direct,
            Self::BufferDontFlush => {
                EffectiveWriteMode::BufferDontFlushWith(DEFAULT_BUFFER_CAPACITY)
            }
            Self::BufferDontFlushWith(duration) => {
                EffectiveWriteMode::BufferDontFlushWith(duration)
            }
            Self::BufferAndFlush => EffectiveWriteMode::BufferAndFlushWith(DEFAULT_BUFFER_CAPACITY),
            Self::BufferAndFlushWith(bufsize, _duration) => {
                EffectiveWriteMode::BufferAndFlushWith(bufsize)
            }
            #[cfg(feature = "async")]
            Self::Async => EffectiveWriteMode::AsyncWith {
                pool_capa: DEFAULT_POOL_CAPA,
                message_capa: DEFAULT_MESSAGE_CAPA,
                flush_interval: DEFAULT_FLUSH_INTERVAL,
            },
            #[cfg(feature = "async")]
            Self::AsyncWith {
                pool_capa,
                message_capa,
                flush_interval,
            } => EffectiveWriteMode::AsyncWith {
                pool_capa,
                message_capa,
                flush_interval,
            },
        }
    }
    pub(crate) fn without_flushing(&self) -> WriteMode {
        match self {
            Self::Direct
            | Self::SupportCapture
            | Self::BufferDontFlush
            | Self::BufferDontFlushWith(_) => *self,
            Self::BufferAndFlush => Self::BufferDontFlush,
            Self::BufferAndFlushWith(bufsize, _) => Self::BufferDontFlushWith(*bufsize),
            #[cfg(feature = "async")]
            Self::Async => Self::AsyncWith {
                pool_capa: DEFAULT_POOL_CAPA,
                message_capa: DEFAULT_MESSAGE_CAPA,
                flush_interval: ZERO_DURATION,
            },
            #[cfg(feature = "async")]
            Self::AsyncWith {
                pool_capa,
                message_capa,
                flush_interval: _,
            } => Self::AsyncWith {
                pool_capa: *pool_capa,
                message_capa: *message_capa,
                flush_interval: ZERO_DURATION,
            },
        }
    }
    pub(crate) fn buffersize(&self) -> Option<usize> {
        match self.effective_write_mode() {
            EffectiveWriteMode::Direct => None,
            EffectiveWriteMode::BufferAndFlushWith(bufsize)
            | EffectiveWriteMode::BufferDontFlushWith(bufsize) => Some(bufsize),
            #[cfg(feature = "async")]
            EffectiveWriteMode::AsyncWith {
                pool_capa: _,
                message_capa: _,
                flush_interval: _,
            } => None,
        }
    }
    pub(crate) fn get_flush_interval(&self) -> Duration {
        match self {
            Self::Direct
            | Self::SupportCapture
            | Self::BufferDontFlush
            | Self::BufferDontFlushWith(_) => ZERO_DURATION,
            Self::BufferAndFlush => DEFAULT_FLUSH_INTERVAL,
            #[cfg(feature = "async")]
            Self::Async => DEFAULT_FLUSH_INTERVAL,
            Self::BufferAndFlushWith(_, flush_interval) => *flush_interval,
            #[cfg(feature = "async")]
            Self::AsyncWith {
                pool_capa: _,
                message_capa: _,
                flush_interval,
            } => *flush_interval,
        }
    }
}

pub(crate) enum EffectiveWriteMode {
    Direct,
    BufferAndFlushWith(usize),
    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
    #[cfg(feature = "async")]
    AsyncWith {
        /// Capacity of the pool for the message buffers.
        pool_capa: usize,
        /// Capacity of an individual message buffer.
        message_capa: usize,
        /// The interval for flushing the output.
        ///
        /// With `Duration::ZERO` flushing is suppressed.
        flush_interval: Duration,
    },
    BufferDontFlushWith(usize),
}