/rust/registry/src/index.crates.io-1949cf8c6b5b557f/flexi_logger-0.27.4/src/logger.rs
Line | Count | Source |
1 | | use crate::{ |
2 | | filter::LogLineFilter, |
3 | | flexi_logger::FlexiLogger, |
4 | | formats::default_format, |
5 | | primary_writer::PrimaryWriter, |
6 | | threads::start_flusher_thread, |
7 | | util::set_error_channel, |
8 | | writers::{FileLogWriter, FileLogWriterBuilder, LogWriter}, |
9 | | Cleanup, Criterion, DeferredNow, FileSpec, FlexiLoggerError, FormatFunction, LogSpecification, |
10 | | LoggerHandle, Naming, WriteMode, |
11 | | }; |
12 | | use log::LevelFilter; |
13 | | use std::{ |
14 | | collections::HashMap, |
15 | | path::PathBuf, |
16 | | sync::{Arc, RwLock}, |
17 | | time::Duration, |
18 | | }; |
19 | | |
20 | | #[cfg(feature = "is-terminal")] |
21 | | use crate::formats::AdaptiveFormat; |
22 | | #[cfg(feature = "is-terminal")] |
23 | | use is_terminal::IsTerminal; |
24 | | |
25 | | #[cfg(feature = "specfile_without_notification")] |
26 | | use {crate::logger_handle::LogSpecSubscriber, std::io::Read, std::path::Path}; |
27 | | |
28 | | #[cfg(feature = "specfile")] |
29 | | use { |
30 | | crate::util::{eprint_err, ErrorCode}, |
31 | | notify_debouncer_mini::{ |
32 | | new_debouncer, |
33 | | notify::{RecommendedWatcher, RecursiveMode}, |
34 | | DebounceEventResult, Debouncer, |
35 | | }, |
36 | | }; |
37 | | |
38 | | /// The entry-point for using `flexi_logger`. |
39 | | /// |
40 | | /// `Logger` is a builder class that allows you to |
41 | | /// * specify your desired (initial) loglevel-specification |
42 | | /// * either as a String ([`Logger::try_with_str`]) |
43 | | /// * or by providing it in the environment ([`Logger::try_with_env`]), |
44 | | /// * or by combining both options ([`Logger::try_with_env_or_str`]), |
45 | | /// * or by building a [`LogSpecification`] programmatically ([`Logger::with`]), |
46 | | /// * use the desired configuration methods, |
47 | | /// * and finally start the logger with |
48 | | /// |
49 | | /// * [`Logger::start`], or |
50 | | /// * [`Logger::start_with_specfile`]. |
51 | | /// |
52 | | /// # Usage |
53 | | /// |
54 | | /// See [`code_examples`](code_examples/index.html) for a comprehensive list of usage possibilities. |
55 | | pub struct Logger { |
56 | | spec: LogSpecification, |
57 | | log_target: LogTarget, |
58 | | duplicate_err: Duplicate, |
59 | | duplicate_out: Duplicate, |
60 | | format_for_file: FormatFunction, |
61 | | format_for_stderr: FormatFunction, |
62 | | format_for_stdout: FormatFunction, |
63 | | format_for_writer: FormatFunction, |
64 | | #[cfg(feature = "colors")] |
65 | | o_palette: Option<String>, |
66 | | flush_interval: std::time::Duration, |
67 | | flwb: FileLogWriterBuilder, |
68 | | other_writers: HashMap<String, Box<dyn LogWriter>>, |
69 | | filter: Option<Box<dyn LogLineFilter + Send + Sync>>, |
70 | | error_channel: ErrorChannel, |
71 | | use_utc: bool, |
72 | | } |
73 | | |
74 | | enum LogTarget { |
75 | | StdErr, |
76 | | StdOut, |
77 | | Multi(bool, Option<Box<dyn LogWriter>>), |
78 | | } |
79 | | |
80 | | /// Create a Logger instance and define how to access the (initial) |
81 | | /// loglevel-specification. |
82 | | impl Logger { |
83 | | /// Creates a Logger that you provide with an explicit [`LogSpecification`]. |
84 | | #[must_use] |
85 | 0 | pub fn with(logspec: LogSpecification) -> Self { |
86 | 0 | Self::from_spec_and_errs(logspec) |
87 | 0 | } |
88 | | |
89 | | /// Creates a Logger that reads the [`LogSpecification`] from a `String` or `&str`. |
90 | | /// See [`LogSpecification`] for the syntax. |
91 | | /// |
92 | | /// # Errors |
93 | | /// |
94 | | /// `FlexiLoggerError::Parse` if the String uses an erroneous syntax. |
95 | 0 | pub fn try_with_str<S: AsRef<str>>(s: S) -> Result<Self, FlexiLoggerError> { |
96 | 0 | Ok(Self::from_spec_and_errs(LogSpecification::parse( |
97 | 0 | s.as_ref(), |
98 | 0 | )?)) |
99 | 0 | } |
100 | | |
101 | | /// Creates a Logger that reads the [`LogSpecification`] from the environment variable |
102 | | /// `RUST_LOG`. |
103 | | /// |
104 | | /// Note that if `RUST_LOG` is not set, nothing is logged. |
105 | | /// |
106 | | /// # Errors |
107 | | /// |
108 | | /// `FlexiLoggerError::Parse` if the value of `RUST_LOG` is malformed. |
109 | 0 | pub fn try_with_env() -> Result<Self, FlexiLoggerError> { |
110 | 0 | Ok(Self::from_spec_and_errs(LogSpecification::env()?)) |
111 | 0 | } |
112 | | |
113 | | /// Creates a Logger that reads the [`LogSpecification`] from the environment variable |
114 | | /// `RUST_LOG`, or derives it from the given `String`, if `RUST_LOG` is not set. |
115 | | /// |
116 | | /// # Errors |
117 | | /// |
118 | | /// `FlexiLoggerError::Parse` if the chosen value is malformed. |
119 | 0 | pub fn try_with_env_or_str<S: AsRef<str>>(s: S) -> Result<Self, FlexiLoggerError> { |
120 | 0 | Ok(Self::from_spec_and_errs(LogSpecification::env_or_parse(s)?)) |
121 | 0 | } |
122 | | |
123 | 0 | fn from_spec_and_errs(spec: LogSpecification) -> Self { |
124 | | #[cfg(feature = "colors")] |
125 | | #[cfg(windows)] |
126 | | { |
127 | | nu_ansi_term::enable_ansi_support().ok(); |
128 | | } |
129 | | |
130 | | Self { |
131 | 0 | spec, |
132 | 0 | log_target: LogTarget::StdErr, |
133 | 0 | duplicate_err: Duplicate::None, |
134 | 0 | duplicate_out: Duplicate::None, |
135 | 0 | format_for_file: default_format, |
136 | | |
137 | | #[cfg(feature = "colors")] |
138 | 0 | format_for_stdout: AdaptiveFormat::Default.format_function( |
139 | 0 | if cfg!(feature = "is-terminal") { |
140 | 0 | std::io::stdout().is_terminal() |
141 | | } else { |
142 | 0 | false |
143 | | }, |
144 | | ), |
145 | | #[cfg(feature = "colors")] |
146 | 0 | format_for_stderr: AdaptiveFormat::Default.format_function( |
147 | 0 | if cfg!(feature = "is-terminal") { |
148 | 0 | std::io::stderr().is_terminal() |
149 | | } else { |
150 | 0 | false |
151 | | }, |
152 | | ), |
153 | | |
154 | | #[cfg(not(feature = "colors"))] |
155 | | format_for_stdout: default_format, |
156 | | #[cfg(not(feature = "colors"))] |
157 | | format_for_stderr: default_format, |
158 | | |
159 | 0 | format_for_writer: default_format, |
160 | | #[cfg(feature = "colors")] |
161 | 0 | o_palette: None, |
162 | 0 | flush_interval: Duration::from_secs(0), |
163 | 0 | flwb: FileLogWriter::builder(FileSpec::default()), |
164 | 0 | other_writers: HashMap::<String, Box<dyn LogWriter>>::new(), |
165 | 0 | filter: None, |
166 | 0 | error_channel: ErrorChannel::default(), |
167 | | use_utc: false, |
168 | | } |
169 | 0 | } |
170 | | } |
171 | | |
172 | | /// Simple methods for influencing the behavior of the Logger. |
173 | | impl Logger { |
174 | | /// Log is written to stderr (which is the default). |
175 | | #[must_use] |
176 | 0 | pub fn log_to_stderr(mut self) -> Self { |
177 | 0 | self.log_target = LogTarget::StdErr; |
178 | 0 | self |
179 | 0 | } |
180 | | |
181 | | /// Log is written to stdout. |
182 | | #[must_use] |
183 | 0 | pub fn log_to_stdout(mut self) -> Self { |
184 | 0 | self.log_target = LogTarget::StdOut; |
185 | 0 | self |
186 | 0 | } |
187 | | |
188 | | /// Log is written to a file. |
189 | | /// |
190 | | /// See [`FileSpec`] for details about the filename pattern. |
191 | | /// |
192 | | /// You can duplicate to stdout and stderr, and you can add additional writers. |
193 | | #[must_use] |
194 | 0 | pub fn log_to_file(mut self, file_spec: FileSpec) -> Self { |
195 | 0 | self.log_target = LogTarget::Multi(true, None); |
196 | 0 | self.flwb = self.flwb.file_spec(file_spec); |
197 | 0 | self |
198 | 0 | } |
199 | | |
200 | | /// Log is written to the provided writer. |
201 | | /// |
202 | | /// You can duplicate to stdout and stderr, and you can add additional writers. |
203 | | #[must_use] |
204 | 0 | pub fn log_to_writer(mut self, w: Box<dyn LogWriter>) -> Self { |
205 | 0 | self.log_target = LogTarget::Multi(false, Some(w)); |
206 | 0 | self |
207 | 0 | } |
208 | | |
209 | | /// Log is written to a file, as with [`Logger::log_to_file`], _and_ to an alternative |
210 | | /// [`LogWriter`] implementation. |
211 | | /// |
212 | | /// And you can duplicate to stdout and stderr, and you can add additional writers. |
213 | | #[must_use] |
214 | 0 | pub fn log_to_file_and_writer(mut self, file_spec: FileSpec, w: Box<dyn LogWriter>) -> Self { |
215 | 0 | self.log_target = LogTarget::Multi(true, Some(w)); |
216 | 0 | self.flwb = self.flwb.file_spec(file_spec); |
217 | 0 | self |
218 | 0 | } |
219 | | |
220 | | /// Log is processed, including duplication, but not written to any destination. |
221 | | /// |
222 | | /// This can be useful e.g. for running application tests with all log-levels active and still |
223 | | /// avoiding tons of log files etc. |
224 | | /// Such tests ensure that the log calls which are normally not active |
225 | | /// will not cause undesired side-effects when activated |
226 | | /// (note that the log macros may prevent arguments of inactive log-calls from being evaluated). |
227 | | /// |
228 | | /// Or, if you want to get logs both to stdout and stderr, but nowhere else, |
229 | | /// then use this option and combine it with |
230 | | /// [`Logger::duplicate_to_stdout`] and [`Logger::duplicate_to_stderr`]. |
231 | | #[must_use] |
232 | 0 | pub fn do_not_log(mut self) -> Self { |
233 | 0 | self.log_target = LogTarget::Multi(false, None); |
234 | 0 | self |
235 | 0 | } |
236 | | |
237 | | /// Makes the logger print an info message to stdout with the name of the logfile |
238 | | /// when a logfile is opened for writing. |
239 | | #[must_use] |
240 | 0 | pub fn print_message(mut self) -> Self { |
241 | 0 | self.flwb = self.flwb.print_message(); |
242 | 0 | self |
243 | 0 | } |
244 | | |
245 | | /// Makes the logger write messages with the specified minimum severity additionally to stderr. |
246 | | /// |
247 | | /// Does not work with [`Logger::log_to_stdout`] or [`Logger::log_to_stderr`]. |
248 | | #[must_use] |
249 | 0 | pub fn duplicate_to_stderr(mut self, dup: Duplicate) -> Self { |
250 | 0 | self.duplicate_err = dup; |
251 | 0 | self |
252 | 0 | } |
253 | | |
254 | | /// Makes the logger write messages with the specified minimum severity additionally to stdout. |
255 | | /// |
256 | | /// Does not work with [`Logger::log_to_stdout`] or [`Logger::log_to_stderr`]. |
257 | | #[must_use] |
258 | 0 | pub fn duplicate_to_stdout(mut self, dup: Duplicate) -> Self { |
259 | 0 | self.duplicate_out = dup; |
260 | 0 | self |
261 | 0 | } |
262 | | |
263 | | /// Makes the logger use the provided format function for all messages |
264 | | /// that are written to files, stderr, stdout, or to an additional writer. |
265 | | /// |
266 | | /// You can either choose one of the provided log-line formatters, |
267 | | /// or you create and use your own format function with the signature <br> |
268 | | /// ```rust |
269 | | /// fn my_format( |
270 | | /// write: &mut dyn std::io::Write, |
271 | | /// now: &mut flexi_logger::DeferredNow, |
272 | | /// record: &log::Record, |
273 | | /// ) -> std::io::Result<()> |
274 | | /// # {unimplemented!("")} |
275 | | /// ``` |
276 | | /// |
277 | | /// By default, [`default_format`] is used for output to files and to custom writers, |
278 | | /// and [`AdaptiveFormat::Default`] is used for output to `stderr` and `stdout`. |
279 | | /// If the feature `colors` is switched off, [`default_format`] is used for all outputs. |
280 | | #[must_use] |
281 | 0 | pub fn format(mut self, format: FormatFunction) -> Self { |
282 | 0 | self.format_for_file = format; |
283 | 0 | self.format_for_stderr = format; |
284 | 0 | self.format_for_stdout = format; |
285 | 0 | self.format_for_writer = format; |
286 | 0 | self |
287 | 0 | } |
288 | | |
289 | | /// Makes the logger use the provided format function for messages |
290 | | /// that are written to files. |
291 | | /// |
292 | | /// Regarding the default, see [`Logger::format`]. |
293 | | #[must_use] |
294 | 0 | pub fn format_for_files(mut self, format: FormatFunction) -> Self { |
295 | 0 | self.format_for_file = format; |
296 | 0 | self |
297 | 0 | } |
298 | | |
299 | | /// Makes the logger use the provided format function for messages |
300 | | /// that are written to stderr. |
301 | | /// |
302 | | /// Regarding the default, see [`Logger::format`]. |
303 | | #[must_use] |
304 | 0 | pub fn format_for_stderr(mut self, format_function: FormatFunction) -> Self { |
305 | 0 | self.format_for_stderr = format_function; |
306 | 0 | self |
307 | 0 | } |
308 | | |
309 | | /// Makes the logger use the specified format for messages that are written to `stderr`. |
310 | | /// Coloring is used if `stderr` is a tty. |
311 | | /// |
312 | | /// Regarding the default, see [`Logger::format`]. |
313 | | #[cfg_attr(docsrs, doc(cfg(feature = "is-terminal")))] |
314 | | #[cfg(feature = "is-terminal")] |
315 | | #[must_use] |
316 | 0 | pub fn adaptive_format_for_stderr(mut self, adaptive_format: AdaptiveFormat) -> Self { |
317 | 0 | self.format_for_stderr = adaptive_format.format_function(std::io::stderr().is_terminal()); |
318 | 0 | self |
319 | 0 | } |
320 | | |
321 | | /// Makes the logger use the provided format function to format messages |
322 | | /// that are written to stdout. |
323 | | /// |
324 | | /// Regarding the default, see [`Logger::format`]. |
325 | | #[must_use] |
326 | 0 | pub fn format_for_stdout(mut self, format_function: FormatFunction) -> Self { |
327 | 0 | self.format_for_stdout = format_function; |
328 | 0 | self |
329 | 0 | } |
330 | | |
331 | | /// Makes the logger use the specified format for messages that are written to `stdout`. |
332 | | /// Coloring is used if `stdout` is a tty. |
333 | | /// |
334 | | /// Regarding the default, see [`Logger::format`]. |
335 | | #[cfg_attr(docsrs, doc(cfg(feature = "is-terminal")))] |
336 | | #[cfg(feature = "is-terminal")] |
337 | | #[must_use] |
338 | 0 | pub fn adaptive_format_for_stdout(mut self, adaptive_format: AdaptiveFormat) -> Self { |
339 | 0 | self.format_for_stdout = adaptive_format.format_function(std::io::stdout().is_terminal()); |
340 | 0 | self |
341 | 0 | } |
342 | | |
343 | | /// Allows specifying a format function for an additional writer. |
344 | | /// Note that it is up to the implementation of the additional writer |
345 | | /// whether it evaluates this setting or not. |
346 | | /// |
347 | | /// Regarding the default, see [`Logger::format`]. |
348 | | #[must_use] |
349 | 0 | pub fn format_for_writer(mut self, format: FormatFunction) -> Self { |
350 | 0 | self.format_for_writer = format; |
351 | 0 | self |
352 | 0 | } |
353 | | |
354 | | /// Sets the color palette for function [`style`](crate::style), which is used in the |
355 | | /// provided coloring format functions. |
356 | | /// |
357 | | /// The palette given here overrides the default palette. |
358 | | /// |
359 | | /// The palette is specified in form of a String that contains a semicolon-separated list |
360 | | /// of numbers (0..=255) and/or dashes (´-´). |
361 | | /// The first five values denote the fixed color that is |
362 | | /// used for coloring `error`, `warn`, `info`, `debug`, and `trace` messages. |
363 | | /// |
364 | | /// The String `"196;208;-;7;8"` describes the default palette, where color 196 is |
365 | | /// used for error messages, and so on. The `-` means that no coloring is done, |
366 | | /// i.e., with `"-;-;-;-;-"` all coloring is switched off. |
367 | | /// |
368 | | /// Prefixing a number with 'b' makes the output being written in bold. |
369 | | /// The String `"b1;3;2;4;6"` e.g. describes the palette used by `env_logger`. |
370 | | /// |
371 | | /// The palette can further be overridden at runtime by setting the environment variable |
372 | | /// `FLEXI_LOGGER_PALETTE` to a palette String. This allows adapting the used text colors to |
373 | | /// differently colored terminal backgrounds. |
374 | | /// |
375 | | /// For your convenience, if you want to specify your own palette, |
376 | | /// you can produce a colored list with all 255 colors with `cargo run --example colors`. |
377 | | #[cfg_attr(docsrs, doc(cfg(feature = "colors")))] |
378 | | #[cfg(feature = "colors")] |
379 | | #[must_use] |
380 | 0 | pub fn set_palette(mut self, palette: String) -> Self { |
381 | 0 | self.o_palette = Some(palette); |
382 | 0 | self |
383 | 0 | } |
384 | | |
385 | | /// Prevent indefinite growth of the log file by applying file rotation |
386 | | /// and a clean-up strategy for older log files. |
387 | | /// |
388 | | /// By default, the log file is fixed while your program is running and will grow indefinitely. |
389 | | /// With this option being used, when the log file reaches the specified criterion, |
390 | | /// the file will be closed and a new file will be opened. |
391 | | /// |
392 | | /// Note that also the filename pattern changes: |
393 | | /// |
394 | | /// - by default, no timestamp is added to the filename if rotation is used |
395 | | /// - the logs are always written to a file with infix `_rCURRENT` |
396 | | /// - when the rotation criterion is fulfilled, it is closed and renamed to a file |
397 | | /// with another infix (see `Naming`), |
398 | | /// and then the logging continues again to the (fresh) file with infix `_rCURRENT`. |
399 | | /// |
400 | | /// Example: |
401 | | /// |
402 | | /// After some logging with your program `my_prog` and rotation with `Naming::Numbers`, |
403 | | /// you will find files like |
404 | | /// |
405 | | /// ```text |
406 | | /// my_prog_r00000.log |
407 | | /// my_prog_r00001.log |
408 | | /// my_prog_r00002.log |
409 | | /// my_prog_rCURRENT.log |
410 | | /// ``` |
411 | | /// |
412 | | /// ## Parameters |
413 | | /// |
414 | | /// `criterion` defines *when* the log file should be rotated, based on its size or age. |
415 | | /// See [`Criterion`] for details. |
416 | | /// |
417 | | /// `naming` defines the naming convention for the rotated log files. |
418 | | /// See [`Naming`] for details. |
419 | | /// |
420 | | /// `cleanup` defines the strategy for dealing with older files. |
421 | | /// See [`Cleanup`] for details. |
422 | | #[must_use] |
423 | 0 | pub fn rotate(mut self, criterion: Criterion, naming: Naming, cleanup: Cleanup) -> Self { |
424 | 0 | self.flwb = self.flwb.rotate(criterion, naming, cleanup); |
425 | 0 | self |
426 | 0 | } |
427 | | |
428 | | /// When [`Logger::rotate`] is used with some [`Cleanup`] variant other than [`Cleanup::Never`], |
429 | | /// then this method can be used to define |
430 | | /// if the cleanup activities (finding files, deleting files, evtl compressing files) are |
431 | | /// delegated to a background thread (which is the default, |
432 | | /// to minimize the blocking impact to your application caused by IO operations), |
433 | | /// or whether they are done synchronously in the current log-call. |
434 | | /// |
435 | | /// If you call this method with `use_background_thread = false`, |
436 | | /// the cleanup is done synchronously. |
437 | | #[must_use] |
438 | 0 | pub fn cleanup_in_background_thread(mut self, use_background_thread: bool) -> Self { |
439 | 0 | self.flwb = self |
440 | 0 | .flwb |
441 | 0 | .cleanup_in_background_thread(use_background_thread); |
442 | 0 | self |
443 | 0 | } |
444 | | |
445 | | /// Apply the provided filter before really writing log lines. |
446 | | /// |
447 | | /// See the documentation of module [`filter`](crate::filter) for a usage example. |
448 | | #[must_use] |
449 | 0 | pub fn filter(mut self, filter: Box<dyn LogLineFilter + Send + Sync>) -> Self { |
450 | 0 | self.filter = Some(filter); |
451 | 0 | self |
452 | 0 | } |
453 | | |
454 | | /// Makes the logger append to the specified output file, if it exists already; |
455 | | /// by default, the file would be truncated. |
456 | | /// |
457 | | /// This option only has an effect if logs are written to files, but |
458 | | /// it will hardly make an effect if [`FileSpec::suppress_timestamp`] is not used. |
459 | | #[must_use] |
460 | 0 | pub fn append(mut self) -> Self { |
461 | 0 | self.flwb = self.flwb.append(); |
462 | 0 | self |
463 | 0 | } |
464 | | |
465 | | /// Makes the logger use UTC timestamps rather than local timestamps. |
466 | | #[must_use] |
467 | 0 | pub fn use_utc(mut self) -> Self { |
468 | 0 | self.use_utc = true; |
469 | 0 | self |
470 | 0 | } |
471 | | |
472 | | /// The specified path will be used on unix systems to create a symbolic link |
473 | | /// to the current log file. |
474 | | /// |
475 | | /// This option has no effect on filesystems where symlinks are not supported, |
476 | | /// and it only has an effect if logs are written to files. |
477 | | /// |
478 | | /// ### Example |
479 | | /// |
480 | | /// You can use the symbolic link to follow the log output with `tail`, |
481 | | /// even if the log files are rotated. |
482 | | /// |
483 | | /// Assuming you use `create_symlink("link_to_log_file")`, then use: |
484 | | /// |
485 | | /// ```text |
486 | | /// tail --follow=name --max-unchanged-stats=1 --retry link_to_log_file |
487 | | /// ``` |
488 | | /// |
489 | | #[must_use] |
490 | 0 | pub fn create_symlink<P: Into<PathBuf>>(mut self, symlink: P) -> Self { |
491 | 0 | self.flwb = self.flwb.create_symlink(symlink); |
492 | 0 | self |
493 | 0 | } |
494 | | |
495 | | /// Registers a [`LogWriter`] implementation under the given target name. |
496 | | /// |
497 | | /// The target name must not start with an underscore. |
498 | | /// See module [`writers`](crate::writers) for more details. |
499 | | #[must_use] |
500 | 0 | pub fn add_writer<S: Into<String>>( |
501 | 0 | mut self, |
502 | 0 | target_name: S, |
503 | 0 | writer: Box<dyn LogWriter>, |
504 | 0 | ) -> Self { |
505 | 0 | self.other_writers.insert(target_name.into(), writer); |
506 | 0 | self |
507 | 0 | } |
508 | | |
509 | | /// Sets the write mode for the logger. |
510 | | /// |
511 | | /// See [`WriteMode`] for more (important!) details. |
512 | | #[must_use] |
513 | 0 | pub fn write_mode(mut self, write_mode: WriteMode) -> Self { |
514 | 0 | self.flwb = self.flwb.write_mode(write_mode.without_flushing()); |
515 | 0 | self.flush_interval = write_mode.get_flush_interval(); |
516 | 0 | self |
517 | 0 | } |
518 | | |
519 | | /// Use Windows line endings, rather than just `\n`. |
520 | | #[must_use] |
521 | 0 | pub fn use_windows_line_ending(mut self) -> Self { |
522 | 0 | self.flwb = self.flwb.use_windows_line_ending(); |
523 | 0 | self |
524 | 0 | } |
525 | | |
526 | | /// Define the output channel for `flexi_logger`'s own error messages. |
527 | | /// |
528 | | /// These are only written if `flexi_logger` cannot do what it is supposed to do, |
529 | | /// so under normal circumstances no single message shuld appear. |
530 | | /// |
531 | | /// By default these error messages are printed to `stderr`. |
532 | | #[must_use] |
533 | 0 | pub fn error_channel(mut self, error_channel: ErrorChannel) -> Self { |
534 | 0 | self.error_channel = error_channel; |
535 | 0 | self |
536 | 0 | } |
537 | | } |
538 | | |
539 | | /// Enum for defining the output channel for `flexi_logger`'s own error messages. |
540 | | /// |
541 | | /// These are only written if `flexi_logger` cannot do what it is supposed to do, |
542 | | /// so under normal circumstances no single message shuld appear. |
543 | | /// |
544 | | /// By default these error messages are printed to `stderr`. |
545 | | #[derive(Debug, Default)] |
546 | | pub enum ErrorChannel { |
547 | | /// Write `flexi_logger`'s own error messages to `stderr`. |
548 | | #[default] |
549 | | StdErr, |
550 | | /// Write `flexi_logger`'s own error messages to `stdout`. |
551 | | StdOut, |
552 | | /// Write `flexi_logger`'s own error messages to the specified file. |
553 | | File(PathBuf), |
554 | | /// Don't write `flexi_logger`'s own error messages. |
555 | | DevNull, |
556 | | } |
557 | | |
558 | | /// Alternative set of methods to control the behavior of the Logger. |
559 | | /// Use these methods when you want to control the settings flexibly, |
560 | | /// e.g. with commandline arguments via `docopts` or `clap`. |
561 | | impl Logger { |
562 | | /// With true, makes the logger print an info message to stdout, each time |
563 | | /// when a new file is used for log-output. |
564 | | #[must_use] |
565 | 0 | pub fn o_print_message(mut self, print_message: bool) -> Self { |
566 | 0 | self.flwb = self.flwb.o_print_message(print_message); |
567 | 0 | self |
568 | 0 | } |
569 | | |
570 | | /// By default, and with None, the log file will grow indefinitely. |
571 | | /// If a `rotate_config` is set, when the log file reaches or exceeds the specified size, |
572 | | /// the file will be closed and a new file will be opened. |
573 | | /// Also the filename pattern changes: instead of the timestamp, a serial number |
574 | | /// is included into the filename. |
575 | | /// |
576 | | /// The size is given in bytes, e.g. `o_rotate_over_size(Some(1_000))` will rotate |
577 | | /// files once they reach a size of 1 kB. |
578 | | /// |
579 | | /// The cleanup strategy allows delimiting the used space on disk. |
580 | | #[must_use] |
581 | 0 | pub fn o_rotate(mut self, rotate_config: Option<(Criterion, Naming, Cleanup)>) -> Self { |
582 | 0 | self.flwb = self.flwb.o_rotate(rotate_config); |
583 | 0 | self |
584 | 0 | } |
585 | | |
586 | | /// If append is set to true, makes the logger append to the specified output file, if it exists. |
587 | | /// By default, or with false, the file would be truncated. |
588 | | /// |
589 | | /// This option only has an effect if logs are written to files, |
590 | | /// and it will hardly make an effect if `suppress_timestamp()` is not used. |
591 | | #[must_use] |
592 | 0 | pub fn o_append(mut self, append: bool) -> Self { |
593 | 0 | self.flwb = self.flwb.o_append(append); |
594 | 0 | self |
595 | 0 | } |
596 | | |
597 | | /// If a String is specified, it will be used on unix systems to create in the current folder |
598 | | /// a symbolic link with this name to the current log file. |
599 | | /// |
600 | | /// This option only has an effect on unix systems and if logs are written to files. |
601 | | #[must_use] |
602 | 0 | pub fn o_create_symlink<P: Into<PathBuf>>(mut self, symlink: Option<P>) -> Self { |
603 | 0 | self.flwb = self.flwb.o_create_symlink(symlink); |
604 | 0 | self |
605 | 0 | } |
606 | | } |
607 | | |
608 | | /// Finally, start logging, optionally with a spec-file. |
609 | | impl Logger { |
610 | | /// Consumes the Logger object and initializes `flexi_logger`. |
611 | | /// |
612 | | /// **Keep the [`LoggerHandle`] alive up to the very end of your program!** |
613 | | /// Dropping the [`LoggerHandle`] flushes and shuts down [`FileLogWriter`]s |
614 | | /// and other [`LogWriter`]s, and then may prevent further logging! |
615 | | /// This should happen immediately before the program terminates, but not earlier. |
616 | | /// |
617 | | /// Dropping the [`LoggerHandle`] is uncritical |
618 | | /// only with [`Logger::log_to_stdout`] or [`Logger::log_to_stderr`]. |
619 | | /// |
620 | | /// The [`LoggerHandle`] also allows updating the log specification programmatically, |
621 | | /// e.g. to intensify logging for (buggy) parts of a (test) program, etc. |
622 | | /// |
623 | | /// # Example |
624 | | /// |
625 | | /// ```rust |
626 | | /// use flexi_logger::{Logger,WriteMode, FileSpec}; |
627 | | /// fn main() -> Result<(), Box<dyn std::error::Error>> { |
628 | | /// let _logger = Logger::try_with_str("info")? |
629 | | /// .log_to_file(FileSpec::default()) |
630 | | /// .write_mode(WriteMode::BufferAndFlush) |
631 | | /// .start()?; |
632 | | /// |
633 | | /// // ... do all your work and join back all threads whose logs you want to see ... |
634 | | /// |
635 | | /// Ok(()) |
636 | | /// } |
637 | | /// ``` |
638 | | /// |
639 | | /// # Errors |
640 | | /// |
641 | | /// Several variants of [`FlexiLoggerError`] can occur. |
642 | 0 | pub fn start(self) -> Result<LoggerHandle, FlexiLoggerError> { |
643 | 0 | let (boxed_logger, handle) = self.build()?; |
644 | 0 | log::set_boxed_logger(boxed_logger)?; |
645 | 0 | Ok(handle) |
646 | 0 | } |
647 | | |
648 | | /// Builds a boxed logger and a `LoggerHandle` for it, |
649 | | /// but does not initialize the global logger. |
650 | | /// |
651 | | /// The returned boxed logger implements the [`Log`](log::Log) trait |
652 | | /// and can be installed manually or nested within another logger. |
653 | | /// |
654 | | /// **Keep the [`LoggerHandle`] alive up to the very end of your program!** |
655 | | /// See [`Logger::start`] for more details. |
656 | | /// |
657 | | /// # Errors |
658 | | /// |
659 | | /// Several variants of [`FlexiLoggerError`] can occur. |
660 | 0 | pub fn build(mut self) -> Result<(Box<dyn log::Log>, LoggerHandle), FlexiLoggerError> { |
661 | | #[cfg(feature = "colors")] |
662 | 0 | crate::formats::set_palette(&self.o_palette)?; |
663 | | |
664 | 0 | if self.use_utc { |
665 | 0 | self.flwb = self.flwb.use_utc(); |
666 | 0 | } |
667 | | |
668 | 0 | let a_primary_writer = Arc::new(match self.log_target { |
669 | | LogTarget::StdOut => { |
670 | 0 | if let WriteMode::SupportCapture = self.flwb.get_write_mode() { |
671 | 0 | PrimaryWriter::test(true, self.format_for_stdout) |
672 | | } else { |
673 | 0 | PrimaryWriter::stdout(self.format_for_stdout, self.flwb.get_write_mode()) |
674 | | } |
675 | | } |
676 | | LogTarget::StdErr => { |
677 | 0 | if let WriteMode::SupportCapture = self.flwb.get_write_mode() { |
678 | 0 | PrimaryWriter::test(false, self.format_for_stderr) |
679 | | } else { |
680 | 0 | PrimaryWriter::stderr(self.format_for_stderr, self.flwb.get_write_mode()) |
681 | | } |
682 | | } |
683 | 0 | LogTarget::Multi(use_file, mut o_writer) => PrimaryWriter::multi( |
684 | 0 | self.duplicate_err, |
685 | 0 | self.duplicate_out, |
686 | 0 | WriteMode::SupportCapture == *self.flwb.get_write_mode(), |
687 | 0 | self.format_for_stderr, |
688 | 0 | self.format_for_stdout, |
689 | 0 | if use_file { |
690 | 0 | Some(Box::new( |
691 | 0 | self.flwb.format(self.format_for_file).try_build()?, |
692 | | )) |
693 | | } else { |
694 | 0 | None |
695 | | }, |
696 | | { |
697 | 0 | if let Some(ref mut writer) = o_writer { |
698 | 0 | writer.format(self.format_for_writer); |
699 | 0 | } |
700 | 0 | o_writer |
701 | | }, |
702 | | ), |
703 | | }); |
704 | | |
705 | 0 | let a_other_writers = Arc::new(self.other_writers); |
706 | | |
707 | 0 | if self.flush_interval.as_secs() != 0 || self.flush_interval.subsec_nanos() != 0 { |
708 | 0 | start_flusher_thread( |
709 | 0 | Arc::clone(&a_primary_writer), |
710 | 0 | Arc::clone(&a_other_writers), |
711 | 0 | self.flush_interval, |
712 | 0 | )?; |
713 | 0 | } |
714 | | |
715 | 0 | let max_level = self.spec.max_level(); |
716 | 0 | let a_l_spec = Arc::new(RwLock::new(self.spec)); |
717 | 0 | set_error_channel(self.error_channel); |
718 | | |
719 | | // initialize the lazy_statics in DeferredNow before threads are spawned |
720 | 0 | if self.use_utc { |
721 | 0 | DeferredNow::force_utc(); |
722 | 0 | } |
723 | 0 | let mut now = DeferredNow::new(); |
724 | 0 | now.now(); |
725 | | |
726 | 0 | let flexi_logger = FlexiLogger::new( |
727 | 0 | Arc::clone(&a_l_spec), |
728 | 0 | Arc::clone(&a_primary_writer), |
729 | 0 | Arc::clone(&a_other_writers), |
730 | 0 | self.filter, |
731 | | ); |
732 | | |
733 | 0 | let handle = LoggerHandle::new(a_l_spec, a_primary_writer, a_other_writers); |
734 | 0 | handle.reconfigure(max_level); |
735 | 0 | Ok((Box::new(flexi_logger), handle)) |
736 | 0 | } |
737 | | |
738 | | /// Consumes the Logger object and initializes `flexi_logger` in a way that |
739 | | /// subsequently the log specification can be updated, |
740 | | /// while the program is running, by editing a file. |
741 | | /// |
742 | | /// Uses the spec that was given to the factory method ([`Logger::with`] etc) |
743 | | /// as initial spec and then tries to read the logspec from a file. |
744 | | /// |
745 | | /// If the file does not exist, `flexi_logger` creates the file and fills it |
746 | | /// with the initial spec (and in the respective file format, of course). |
747 | | /// |
748 | | /// **Keep the returned [`LoggerHandle`] alive up to the very end of your program!** |
749 | | /// See [`Logger::start`] for more details. |
750 | | /// |
751 | | /// # Feature dependency |
752 | | /// |
753 | | /// The implementation of this configuration method uses some additional crates |
754 | | /// that you might not want to depend on with your program if you don't use this functionality. |
755 | | /// For that reason the method is only available if you activate the |
756 | | /// `specfile` feature. See the usage section on |
757 | | /// [crates.io](https://crates.io/crates/flexi_logger) for details. |
758 | | /// |
759 | | /// # Usage |
760 | | /// |
761 | | /// A logger initialization like |
762 | | /// |
763 | | /// ```rust,no_run |
764 | | /// use flexi_logger::Logger; |
765 | | /// Logger::try_with_str("info") |
766 | | /// .unwrap() |
767 | | /// // more logger configuration |
768 | | /// .start_with_specfile("logspecification.toml"); |
769 | | /// ``` |
770 | | /// |
771 | | /// will create the file `logspecification.toml` (if it does not yet exist) with this content: |
772 | | /// |
773 | | /// ```toml |
774 | | /// ### Optional: Default log level |
775 | | /// global_level = 'info' |
776 | | /// ### Optional: specify a regular expression to suppress all messages that don't match |
777 | | /// #global_pattern = 'foo' |
778 | | /// |
779 | | /// ### Specific log levels per module are optionally defined in this section |
780 | | /// [modules] |
781 | | /// #'mod1' = 'warn' |
782 | | /// #'mod2' = 'debug' |
783 | | /// #'mod2::mod3' = 'trace' |
784 | | /// ``` |
785 | | /// |
786 | | /// You can subsequently edit and modify the file according to your needs, |
787 | | /// while the program is running, and it will immediately take your changes into account. |
788 | | /// |
789 | | /// Currently only toml-files are supported, the file suffix thus must be `.toml`. |
790 | | /// |
791 | | /// The initial spec remains valid if the file cannot be read. |
792 | | /// |
793 | | /// If you update the specfile subsequently while the program is running, `flexi_logger` |
794 | | /// re-reads it automatically and adapts its behavior according to the new content. |
795 | | /// If the file cannot be read anymore, e.g. because the format is not correct, the |
796 | | /// previous logspec remains active. |
797 | | /// If the file is corrected subsequently, the log spec update will work again. |
798 | | /// |
799 | | /// # Errors |
800 | | /// |
801 | | /// Several variants of [`FlexiLoggerError`] can occur. |
802 | | #[cfg_attr(docsrs, doc(cfg(feature = "specfile")))] |
803 | | #[cfg(feature = "specfile_without_notification")] |
804 | | pub fn start_with_specfile<P: AsRef<Path>>( |
805 | | self, |
806 | | specfile: P, |
807 | | ) -> Result<LoggerHandle, FlexiLoggerError> { |
808 | | let (boxed_logger, handle) = self.build_with_specfile(specfile)?; |
809 | | log::set_boxed_logger(boxed_logger)?; |
810 | | Ok(handle) |
811 | | } |
812 | | |
813 | | /// Builds a boxed logger and a `LoggerHandle` for it, |
814 | | /// but does not initialize the global logger. |
815 | | /// |
816 | | /// See also [`Logger::start`] and [`Logger::start_with_specfile`]. |
817 | | /// for the properties of the returned logger. |
818 | | /// |
819 | | /// # Errors |
820 | | /// |
821 | | /// Several variants of [`FlexiLoggerError`] can occur. |
822 | | #[cfg_attr(docsrs, doc(cfg(feature = "specfile")))] |
823 | | #[cfg(feature = "specfile_without_notification")] |
824 | | pub fn build_with_specfile<P: AsRef<Path>>( |
825 | | self, |
826 | | specfile: P, |
827 | | ) -> Result<(Box<dyn log::Log>, LoggerHandle), FlexiLoggerError> { |
828 | | let (boxed_log, mut handle) = self.build()?; |
829 | | |
830 | | let specfile = specfile.as_ref(); |
831 | | synchronize_subscriber_with_specfile(&mut handle.writers_handle, specfile)?; |
832 | | |
833 | | #[cfg(feature = "specfile")] |
834 | | { |
835 | | handle.ao_specfile_watcher = Some(Arc::new(create_specfile_watcher( |
836 | | specfile, |
837 | | handle.writers_handle.clone(), |
838 | | )?)); |
839 | | } |
840 | | |
841 | | Ok((boxed_log, handle)) |
842 | | } |
843 | | } |
844 | | |
845 | | // Reread the specfile when it was updated |
846 | | #[cfg(feature = "specfile")] |
847 | | pub(crate) fn create_specfile_watcher<S: LogSpecSubscriber>( |
848 | | specfile: &Path, |
849 | | mut subscriber: S, |
850 | | ) -> Result<Debouncer<RecommendedWatcher>, FlexiLoggerError> { |
851 | | let specfile = specfile |
852 | | .canonicalize() |
853 | | .map_err(FlexiLoggerError::SpecfileIo)?; |
854 | | let clone = specfile.clone(); |
855 | | let parent = clone.parent().unwrap(/*cannot fail*/); |
856 | | |
857 | | let mut debouncer = new_debouncer( |
858 | | std::time::Duration::from_millis(1000), |
859 | | None, // <--- goes away with notify-debouncer-mini version 0.4 |
860 | | move |res: DebounceEventResult| match res { |
861 | | Ok(events) => events.iter().for_each(|e| { |
862 | | if e.path |
863 | | .canonicalize() |
864 | | .map(|x| x == specfile) |
865 | | .unwrap_or(false) |
866 | | { |
867 | | log_spec_string_from_file(&specfile) |
868 | | .map_err(FlexiLoggerError::SpecfileIo) |
869 | | .and_then(LogSpecification::from_toml) |
870 | | .and_then(|spec| subscriber.set_new_spec(spec)) |
871 | | .map_err(|e| { |
872 | | eprint_err( |
873 | | ErrorCode::LogSpecFile, |
874 | | "continuing with previous log specification, because \ |
875 | | rereading the log specification file failed", |
876 | | &e, |
877 | | ); |
878 | | }) |
879 | | .ok(); |
880 | | } |
881 | | }), |
882 | | Err(errors) => errors.iter().for_each(|e| { |
883 | | eprint_err( |
884 | | ErrorCode::LogSpecFile, |
885 | | "error while watching the specfile", |
886 | | &e, |
887 | | ); |
888 | | }), |
889 | | // Err(e) => eprint_err( |
890 | | // ErrorCode::LogSpecFile, |
891 | | // "error while watching the specfile", |
892 | | // &e, |
893 | | // ), |
894 | | }, |
895 | | ) |
896 | | .unwrap(); |
897 | | |
898 | | debouncer |
899 | | .watcher() |
900 | | .watch(parent, RecursiveMode::NonRecursive) |
901 | | .unwrap(); |
902 | | |
903 | | Ok(debouncer) |
904 | | } |
905 | | |
906 | | // If the specfile exists, read the file and update the subscriber's logspec from it; |
907 | | // otherwise try to create the file, with the current spec as content, under the specified name. |
908 | | #[cfg(feature = "specfile_without_notification")] |
909 | | pub(crate) fn synchronize_subscriber_with_specfile<S: LogSpecSubscriber>( |
910 | | subscriber: &mut S, |
911 | | specfile: &Path, |
912 | | ) -> Result<(), FlexiLoggerError> { |
913 | | if specfile |
914 | | .extension() |
915 | | .unwrap_or_else(|| std::ffi::OsStr::new("")) |
916 | | .to_str() |
917 | | .unwrap_or("") |
918 | | != "toml" |
919 | | { |
920 | | return Err(FlexiLoggerError::SpecfileExtension( |
921 | | "only spec files with extension toml are supported", |
922 | | )); |
923 | | } |
924 | | |
925 | | if Path::is_file(specfile) { |
926 | | let s = log_spec_string_from_file(specfile).map_err(FlexiLoggerError::SpecfileIo)?; |
927 | | subscriber.set_new_spec(LogSpecification::from_toml(s)?)?; |
928 | | } else { |
929 | | if let Some(specfolder) = specfile.parent() { |
930 | | std::fs::DirBuilder::new() |
931 | | .recursive(true) |
932 | | .create(specfolder) |
933 | | .map_err(FlexiLoggerError::SpecfileIo)?; |
934 | | } |
935 | | let mut file = std::fs::OpenOptions::new() |
936 | | .write(true) |
937 | | .create_new(true) |
938 | | .open(specfile) |
939 | | .map_err(FlexiLoggerError::SpecfileIo)?; |
940 | | |
941 | | subscriber.initial_spec()?.to_toml(&mut file)?; |
942 | | } |
943 | | Ok(()) |
944 | | } |
945 | | |
946 | | #[cfg(feature = "specfile_without_notification")] |
947 | | pub(crate) fn log_spec_string_from_file<P: AsRef<Path>>( |
948 | | specfile: P, |
949 | | ) -> Result<String, std::io::Error> { |
950 | | let mut buf = String::new(); |
951 | | let mut file = std::fs::File::open(specfile)?; |
952 | | file.read_to_string(&mut buf)?; |
953 | | Ok(buf) |
954 | | } |
955 | | |
956 | | /// Used to control which messages are to be duplicated to stderr, when `log_to_file()` is used. |
957 | | #[derive(Debug, Clone, Copy)] |
958 | | pub enum Duplicate { |
959 | | /// No messages are duplicated. |
960 | | None = 0, |
961 | | /// Only error messages are duplicated. |
962 | | Error = 1, |
963 | | /// Error and warn messages are duplicated. |
964 | | Warn = 2, |
965 | | /// Error, warn, and info messages are duplicated. |
966 | | Info = 3, |
967 | | /// Error, warn, info, and debug messages are duplicated. |
968 | | Debug = 4, |
969 | | /// All messages are duplicated. |
970 | | Trace = 5, |
971 | | /// All messages are duplicated. |
972 | | All = 6, |
973 | | } |
974 | | impl From<u8> for Duplicate { |
975 | 0 | fn from(val: u8) -> Self { |
976 | 0 | match val { |
977 | 0 | 0 => Duplicate::None, |
978 | 0 | 1 => Duplicate::Error, |
979 | 0 | 2 => Duplicate::Warn, |
980 | 0 | 3 => Duplicate::Info, |
981 | 0 | 4 => Duplicate::Debug, |
982 | 0 | 5 => Duplicate::Trace, |
983 | 0 | 6 => Duplicate::All, |
984 | 0 | _ => unreachable!(), |
985 | | } |
986 | 0 | } |
987 | | } |
988 | | |
989 | | impl From<LevelFilter> for Duplicate { |
990 | 0 | fn from(level: LevelFilter) -> Self { |
991 | 0 | match level { |
992 | 0 | LevelFilter::Off => Duplicate::None, |
993 | 0 | LevelFilter::Error => Duplicate::Error, |
994 | 0 | LevelFilter::Warn => Duplicate::Warn, |
995 | 0 | LevelFilter::Info => Duplicate::Info, |
996 | 0 | LevelFilter::Debug => Duplicate::Debug, |
997 | 0 | LevelFilter::Trace => Duplicate::Trace, |
998 | | } |
999 | 0 | } |
1000 | | } |
1001 | | impl From<Duplicate> for LevelFilter { |
1002 | 0 | fn from(level: Duplicate) -> Self { |
1003 | 0 | match level { |
1004 | 0 | Duplicate::None => LevelFilter::Off, |
1005 | 0 | Duplicate::Error => LevelFilter::Error, |
1006 | 0 | Duplicate::Warn => LevelFilter::Warn, |
1007 | 0 | Duplicate::Info => LevelFilter::Info, |
1008 | 0 | Duplicate::Debug => LevelFilter::Debug, |
1009 | 0 | Duplicate::Trace | Duplicate::All => LevelFilter::Trace, |
1010 | | } |
1011 | 0 | } |
1012 | | } |