Skip to main content

dsi_progress_logger/
lib.rs

1/*
2 * SPDX-FileCopyrightText: 2023 Inria
3 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
4 * SPDX-FileCopyrightText: 2024 Fondation Inria
5 *
6 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
7 */
8
9#![doc = include_str!("../README.md")]
10
11use log::{Level, debug, error, info, log, trace, warn};
12use num_format::{Locale, ToFormattedString};
13use pluralizer::pluralize;
14use std::fmt::{Arguments, Display, Formatter, Result};
15use std::sync::{Arc, Mutex};
16use std::time::{Duration, Instant};
17use sysinfo::{MemoryRefreshKind, Pid, ProcessRefreshKind, ProcessesToUpdate, RefreshKind, System};
18mod utils;
19pub use utils::*;
20
21/// Logging trait.
22///
23/// To log the progress of an activity, you call [`start`](ProgressLog::start).
24/// Then, each time you want to mark progress, you call
25/// [`update`](ProgressLog::update), which increases the item counter, and will
26/// log progress information if enough time has passed since the last log.
27/// [`light_update`](ProgressLog::light_update) will perform a time check only
28/// on a subset of updates (e.g., for [`ProgressLogger`], multiples of
29/// [`LIGHT_UPDATE_MASK`](ProgressLogger::LIGHT_UPDATE_MASK) + 1); it should be
30/// used when the activity has an extremely low cost that is comparable to that
31/// of the time check (a call to [`Instant::now()`]) itself.
32///
33/// A few setters can be called at any time to customize the logger (e.g.,
34/// [`item_name`](ProgressLog::item_name),
35/// [`log_interval`](ProgressLog::log_interval),
36/// [`expected_updates`](ProgressLog::expected_updates), etc.). The setters take
37/// and return a mutable reference to the logger, so you must first assign the
38/// logger to a variable, and then you can chain-call the setters on the
39/// variable in fluent style. The disadvantage of this approach is that you must
40/// assign the logger to a variable, but the advantage is that you can call any
41/// setter without having to reassign the variable holding the logger.
42///
43/// It is also possible to log used and free memory at each log interval by
44/// calling [`display_memory`](ProgressLog::display_memory). Memory is read from
45/// system data by the [`sysinfo`] crate, and will be updated at each log
46/// interval (note that this will slightly slow down the logging process).
47/// However, never use this feature in a
48/// [`rayon`](https://crates.io/crates/rayon) environment if another crate in
49/// your compilation unit depends on
50/// [`sysinfo`](https://crates.io/crates/sysinfo)'s (default) `multithread`
51/// feature, as [this can lead to a
52/// deadlock](https://github.com/rayon-rs/rayon/issues/592) .
53///
54///
55/// At any time, displaying the progress logger will give you time information
56/// up to the present. However, since it is impossible to update the memory
57/// information from the [`Display::fmt`] implementation, you should call
58/// [`refresh`](ProgressLog::refresh) before displaying the logger on your own.
59///
60/// When the activity is over, you call [`stop`](ProgressLog::stop), which fixes
61/// the final time, and possibly display again the logger.
62/// [`done`](ProgressLog::done) will stop the logger, print `Completed.`, and
63/// display the final stats.
64///
65/// After you finish a run of the progress logger, can call
66/// [`start`](ProgressLog::start) again measure another activity.
67///
68/// As explained in the [crate documentation](crate), we suggest using `&mut
69/// impl ProgressLog` to pass a logger as an argument, to be able to use
70/// optional logging.
71///
72/// # Examples
73///
74/// See the [`ProgressLogger`] documentation.
75pub trait ProgressLog {
76    /// The type returned by [`concurrent`](ProgressLog::concurrent).
77    type Concurrent: ConcurrentProgressLog;
78
79    /// Force a log of `self` assuming `now` is the current time.
80    ///
81    /// This is a low-level method that should not be called directly.
82    fn log(&mut self, now: Instant);
83
84    /// Log `self` if it is time to log.
85    ///
86    /// This is a low-level method that should not be called directly.
87    fn log_if(&mut self, now: Instant);
88
89    /// Set the display of memory information.
90    ///
91    /// Memory information include:
92    /// - the [resident-set size](sysinfo::Process::memory) of the process that
93    ///   created the logger;
94    /// - the [virtual-memory size](sysinfo::Process::virtual_memory) of the
95    ///   process that created the logger;
96    /// - the [available memory](sysinfo::System::available_memory);
97    /// - the [free memory](sysinfo::System::free_memory);
98    /// - the [total amount](sysinfo::System::total_memory) of memory.
99    ///
100    /// Never use this feature in a [`rayon`](https://crates.io/crates/rayon)
101    /// environment if another crate in your compilation unit depends on
102    /// [`sysinfo`](https://crates.io/crates/sysinfo)'s (default) `multithread`
103    /// feature, as [this can lead to a
104    /// deadlock](https://github.com/rayon-rs/rayon/issues/592) .
105    fn display_memory(&mut self, display_memory: bool) -> &mut Self;
106
107    /// Set the name of an item.
108    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self;
109
110    /// Set the log interval.
111    fn log_interval(&mut self, log_interval: Duration) -> &mut Self;
112
113    /// Set the expected number of updates.
114    ///
115    /// If not [`None`], the logger will display the percentage of completion
116    /// and an estimate of the time to completion.
117    ///
118    /// Note that you do not need to use a [`Some`]
119    /// wrapper around the argument, as the method will automatically convert it
120    /// to an [`Option`] type.
121    fn expected_updates(&mut self, expected_updates: impl Into<Option<usize>>) -> &mut Self;
122
123    /// Set the time unit to use for speed.
124    ///
125    /// If not [`None`], the logger will always display the speed in this unit
126    /// instead of making a choice of readable unit based on the elapsed time.
127    /// Moreover, large numbers will not be thousands separated. This behavior
128    /// is useful when the output of the logger must be parsed.
129    ///
130    /// Note that you do not need to use a [`Some`]
131    /// wrapper around the argument, as the method will automatically convert it
132    /// to an [`Option`] type.
133    fn time_unit(&mut self, time_unit: impl Into<Option<TimeUnit>>) -> &mut Self;
134
135    /// Set whether to display additionally the speed achieved during the last
136    /// log interval.
137    fn local_speed(&mut self, local_speed: bool) -> &mut Self;
138
139    /// Set the [`mod@log`] target.
140    ///
141    /// This should often be the path of the module logging progress, which is
142    /// obtained with [`std::module_path!`].
143    ///
144    /// Note that the macro [`progress_logger!`] sets this field automatically
145    /// to [`std::module_path!`].
146    ///
147    /// # Examples
148    ///
149    /// ```rust
150    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
151    /// use dsi_progress_logger::prelude::*;
152    ///
153    ///    env_logger::builder()
154    ///        .filter_level(log::LevelFilter::Info)
155    ///        .try_init()?;
156    ///
157    /// let mut pl = ProgressLogger::default();
158    /// pl.item_name("pumpkin");
159    /// pl.log_target(std::module_path!());
160    /// pl.start("Smashing pumpkins from a module...");
161    /// for _ in 0..100 {
162    ///    // do something on each pumpkin
163    ///    pl.update();
164    /// }
165    /// pl.done();
166    /// #     Ok(())
167    /// # }
168    /// ```
169    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self;
170
171    /// Set the [`mod@log`] level used for progress messages.
172    ///
173    /// By default, progress messages are logged at the
174    /// [`Info`](`log::Level::Info`) level.
175    fn log_level(&mut self, log_level: Level) -> &mut Self;
176
177    /// Add a value to the counter.
178    ///
179    /// This method is mainly useful for wrappers or to implement a custom
180    /// update strategy.
181    fn add_to_count(&mut self, count: usize);
182
183    /// Start the logger, displaying the given message.
184    ///
185    /// You can pass the empty string to display nothing.
186    fn start(&mut self, msg: impl AsRef<str>);
187
188    /// Increase the count and check whether it is time to log.
189    fn update(&mut self);
190
191    /// Set the count and check whether it is time to log.
192    fn update_with_count(&mut self, count: usize) {
193        self.update_with_count_and_time(count, Instant::now());
194    }
195
196    /// Set the count and check whether it is time to log, given the current
197    /// time.
198    ///
199    /// This method is mainly useful for wrappers that want to avoid unnecessary
200    /// calls to [`Instant::now`].
201    fn update_with_count_and_time(&mut self, count: usize, now: Instant);
202
203    /// Increase the count but checks whether it is time to log only after an
204    /// implementation-defined number of calls.
205    ///
206    /// Useful for very short activities with respect to which checking the
207    /// time is expensive.
208    fn light_update(&mut self);
209
210    /// Increase the count and forces a log.
211    fn update_and_display(&mut self);
212
213    /// Stop the logger, fixing the final time.
214    fn stop(&mut self);
215
216    /// Stop the logger, print `Completed.`, and display the final stats. The
217    /// number of expected updates will be cleared.
218    fn done(&mut self);
219
220    /// Stop the logger, sets the count, prints `Completed.`, and displays the
221    /// final stats. The number of expected updates will be cleared.
222    ///
223    /// This method is particularly useful in two circumstances:
224    /// * you have updated the logger with some approximate values (e.g., in a
225    ///   multicore computation) but before printing the final stats you want
226    ///   the internal counter to contain an exact value;
227    /// * you have used the logger as a handy timer, calling just
228    ///   [`start`](ProgressLog::start) and this method.
229    fn done_with_count(&mut self, count: usize);
230
231    /// Return the elapsed time since the logger was started, or `None` if the
232    /// logger has not been started.
233    fn elapsed(&self) -> Option<Duration>;
234
235    /// Return the last count the logger has been set to.
236    ///
237    /// Note that you can call this method even after the logger has been
238    /// [stopped](ProgressLog::stop).
239    fn count(&self) -> usize;
240
241    /// Refresh memory information, if previously requested with
242    /// [`display_memory`](#method.display_memory). You do not need to call this
243    /// method unless you display the logger manually.
244    fn refresh(&mut self);
245
246    /// Output the given message at the [trace](`log::Level::Trace`) level.
247    ///
248    /// See [`info`](ProgressLog::info) for an example.
249    fn trace(&self, args: Arguments<'_>);
250
251    /// Output the given message at the [debug](`log::Level::Debug`) level.
252    ///
253    /// See [`info`](ProgressLog::info) for an example.
254    fn debug(&self, args: Arguments<'_>);
255
256    /// Output the given message at the [info](`log::Level::Info`) level.
257    ///
258    /// For maximum flexibility, this method takes as argument the result of a
259    /// [`std::format_args!`] macro. Note that there will be no output if the
260    /// logger is [`None`].
261    ///
262    /// # Examples
263    ///
264    /// ```rust
265    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
266    /// use dsi_progress_logger::*;
267    ///
268    /// env_logger::builder()
269    ///     .filter_level(log::LevelFilter::Info)
270    ///     .try_init()?;
271    ///
272    /// let logger_name = "my_logger";
273    /// let mut pl = progress_logger![];
274    /// pl.info(format_args!("My logger named {}", logger_name));
275    /// #     Ok(())
276    /// # }
277    /// ```
278    fn info(&self, args: Arguments<'_>);
279
280    /// Output the given message at the [warn](`log::Level::Warn`) level.
281    ///
282    /// See [`info`](ProgressLog::info) for an example.
283    fn warn(&self, args: Arguments<'_>);
284
285    /// Output the given message at the [error](`log::Level::Error`) level.
286    ///
287    /// See [`info`](ProgressLog::info) for an example.
288    fn error(&self, args: Arguments<'_>);
289
290    /// Return a concurrent copy of the logger.
291    ///
292    /// Some methods require both sequential and concurrent logging. To keep
293    /// optional logging efficient, we suggest in this cases to use `&mut impl
294    /// ProgressLog` to pass a logger as an argument, and then creating a
295    /// concurrent copy of the logger with this method. If the original logger
296    /// is `None`, the concurrent copy will be `None` as well.
297    ///
298    /// Note that the result of the method is a copy—it will not share the state
299    /// of the original logger.
300    ///
301    /// Concurrent logger implementations can just return a duplicate of
302    /// themselves. [`dup`](ConcurrentProgressLog::dup).
303    fn concurrent(&self) -> Self::Concurrent;
304}
305
306/// Concurrent logging trait.
307///
308/// This trait extends [`ProgressLog`] by adding a
309/// [`dup`](ConcurrentProgressLog::dup) method that duplicates the logger and
310/// adding the [`Clone`], [`Sync`], and [`Send`] traits.
311///
312/// By contract, [`Clone`] implementations must return a new logger updating the
313/// same internal state, so you can easily use a [`ConcurrentProgressLog`] in
314/// methods like
315/// [`rayon::ParallelIterator::for_each_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.for_each_with),
316/// [`rayon::ParallelIterator::map_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.map_with),
317/// and so on. In a [`rayon`](https://docs.rs/rayon) environment, however, you
318/// cannot use [`display_memory`](ProgressLog::display_memory) if another crate
319/// in your compilation unit depends on
320/// [`sysinfo`](https://crates.io/crates/sysinfo)'s (default) `multithread`
321/// feature, as [this can lead to a
322/// deadlock](https://github.com/rayon-rs/rayon/issues/592) .
323///
324/// Note that [`ProgressLogger`]'s [`Clone`
325/// implementation](ProgressLogger#impl-Clone-for-ProgressLogger) has a
326/// completely different semantics.
327///
328/// As explained in the [crate documentation](crate), we suggest using `&mut
329/// Self::Concurrent` to pass a concurrent logger as an argument, to be able to
330/// use optional logging.
331///
332/// # Examples
333///
334/// See the [`ConcurrentWrapper`] documentation.
335pub trait ConcurrentProgressLog: ProgressLog + Sync + Send + Clone {
336    /// The type returned by [`dup`](ConcurrentProgressLog::dup).
337    type Duplicated: ConcurrentProgressLog;
338
339    /// Duplicate the concurrent progress logger, obtaining a new one.
340    ///
341    /// Note that the this method has the same semantics of [`ProgressLogger`'s
342    /// `Clone` implementation](ProgressLogger#impl-Clone-for-ProgressLogger),
343    /// but in a [`ConcurrentProgressLog`] by contract [cloning must generate
344    /// copies with the same underlying logger](ConcurrentProgressLog).
345    fn dup(&self) -> Self::Duplicated;
346}
347
348impl<P: ProgressLog> ProgressLog for &mut P {
349    type Concurrent = P::Concurrent;
350
351    fn log(&mut self, now: Instant) {
352        (**self).log(now);
353    }
354
355    fn log_if(&mut self, now: Instant) {
356        (**self).log_if(now);
357    }
358
359    fn add_to_count(&mut self, count: usize) {
360        (**self).add_to_count(count);
361    }
362
363    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
364        (**self).display_memory(display_memory);
365        self
366    }
367
368    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
369        (**self).item_name(item_name);
370        self
371    }
372
373    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
374        (**self).log_interval(log_interval);
375        self
376    }
377
378    fn expected_updates(&mut self, expected_updates: impl Into<Option<usize>>) -> &mut Self {
379        (**self).expected_updates(expected_updates.into());
380        self
381    }
382
383    fn time_unit(&mut self, time_unit: impl Into<Option<TimeUnit>>) -> &mut Self {
384        (**self).time_unit(time_unit.into());
385        self
386    }
387
388    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
389        (**self).local_speed(local_speed);
390        self
391    }
392
393    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
394        (**self).log_target(target);
395        self
396    }
397
398    fn log_level(&mut self, log_level: Level) -> &mut Self {
399        (**self).log_level(log_level);
400        self
401    }
402
403    fn start(&mut self, msg: impl AsRef<str>) {
404        (**self).start(msg);
405    }
406
407    fn update(&mut self) {
408        (**self).update();
409    }
410
411    fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
412        (**self).update_with_count_and_time(count, now);
413    }
414
415    fn light_update(&mut self) {
416        (**self).light_update();
417    }
418
419    fn update_and_display(&mut self) {
420        (**self).update_and_display();
421    }
422
423    fn stop(&mut self) {
424        (**self).stop();
425    }
426
427    fn done(&mut self) {
428        (**self).done();
429    }
430
431    fn done_with_count(&mut self, count: usize) {
432        (**self).done_with_count(count);
433    }
434
435    fn elapsed(&self) -> Option<Duration> {
436        (**self).elapsed()
437    }
438
439    fn count(&self) -> usize {
440        (**self).count()
441    }
442
443    fn refresh(&mut self) {
444        (**self).refresh();
445    }
446
447    fn trace(&self, args: Arguments<'_>) {
448        (**self).trace(args);
449    }
450
451    fn debug(&self, args: Arguments<'_>) {
452        (**self).debug(args);
453    }
454
455    fn info(&self, args: Arguments<'_>) {
456        (**self).info(args);
457    }
458
459    fn warn(&self, args: Arguments<'_>) {
460        (**self).warn(args);
461    }
462
463    fn error(&self, args: Arguments<'_>) {
464        (**self).error(args);
465    }
466
467    fn concurrent(&self) -> Self::Concurrent {
468        (**self).concurrent()
469    }
470}
471
472impl<P: ProgressLog> ProgressLog for Option<P> {
473    type Concurrent = Option<P::Concurrent>;
474
475    fn log(&mut self, now: Instant) {
476        if let Some(pl) = self {
477            pl.log(now);
478        }
479    }
480
481    fn log_if(&mut self, now: Instant) {
482        if let Some(pl) = self {
483            pl.log_if(now);
484        }
485    }
486
487    fn add_to_count(&mut self, count: usize) {
488        if let Some(pl) = self {
489            pl.add_to_count(count);
490        }
491    }
492
493    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
494        if let Some(pl) = self {
495            pl.display_memory(display_memory);
496        }
497        self
498    }
499
500    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
501        if let Some(pl) = self {
502            pl.item_name(item_name);
503        }
504        self
505    }
506
507    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
508        if let Some(pl) = self {
509            pl.log_interval(log_interval);
510        }
511        self
512    }
513
514    fn expected_updates(&mut self, expected_updates: impl Into<Option<usize>>) -> &mut Self {
515        if let Some(pl) = self {
516            pl.expected_updates(expected_updates.into());
517        }
518        self
519    }
520
521    fn time_unit(&mut self, time_unit: impl Into<Option<TimeUnit>>) -> &mut Self {
522        if let Some(pl) = self {
523            pl.time_unit(time_unit.into());
524        }
525        self
526    }
527
528    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
529        if let Some(pl) = self {
530            pl.local_speed(local_speed);
531        }
532        self
533    }
534
535    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
536        if let Some(pl) = self {
537            pl.log_target(target);
538        }
539        self
540    }
541
542    fn log_level(&mut self, log_level: Level) -> &mut Self {
543        if let Some(pl) = self {
544            pl.log_level(log_level);
545        }
546        self
547    }
548
549    fn start(&mut self, msg: impl AsRef<str>) {
550        if let Some(pl) = self {
551            pl.start(msg);
552        }
553    }
554
555    fn update(&mut self) {
556        if let Some(pl) = self {
557            pl.update();
558        }
559    }
560
561    fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
562        if let Some(pl) = self {
563            pl.update_with_count_and_time(count, now);
564        }
565    }
566
567    fn light_update(&mut self) {
568        if let Some(pl) = self {
569            pl.light_update();
570        }
571    }
572
573    fn update_and_display(&mut self) {
574        if let Some(pl) = self {
575            pl.update_and_display();
576        }
577    }
578
579    fn stop(&mut self) {
580        if let Some(pl) = self {
581            pl.stop();
582        }
583    }
584
585    fn done(&mut self) {
586        if let Some(pl) = self {
587            pl.done();
588        }
589    }
590
591    fn done_with_count(&mut self, count: usize) {
592        if let Some(pl) = self {
593            pl.done_with_count(count);
594        }
595    }
596
597    fn elapsed(&self) -> Option<Duration> {
598        self.as_ref().and_then(|pl| pl.elapsed())
599    }
600
601    fn count(&self) -> usize {
602        self.as_ref().map(|pl| pl.count()).unwrap_or(0)
603    }
604
605    fn refresh(&mut self) {
606        if let Some(pl) = self {
607            pl.refresh();
608        }
609    }
610
611    fn trace(&self, args: Arguments<'_>) {
612        if let Some(pl) = self {
613            pl.trace(args);
614        }
615    }
616
617    fn debug(&self, args: Arguments<'_>) {
618        if let Some(pl) = self {
619            pl.debug(args);
620        }
621    }
622
623    fn info(&self, args: Arguments<'_>) {
624        if let Some(pl) = self {
625            pl.info(args);
626        }
627    }
628
629    fn warn(&self, args: Arguments<'_>) {
630        if let Some(pl) = self {
631            pl.warn(args);
632        }
633    }
634
635    fn error(&self, args: Arguments<'_>) {
636        if let Some(pl) = self {
637            pl.error(args);
638        }
639    }
640
641    fn concurrent(&self) -> Self::Concurrent {
642        self.as_ref().map(|pl| pl.concurrent())
643    }
644}
645
646impl<P: ConcurrentProgressLog> ConcurrentProgressLog for Option<P> {
647    type Duplicated = Option<P::Duplicated>;
648
649    fn dup(&self) -> Self::Duplicated {
650        self.as_ref().map(|pl| pl.dup())
651    }
652}
653
654/// An implementation of [`ProgressLog`] with output generated using the
655/// [`log`](https://docs.rs/log) crate at a configurable level (default:
656/// `info`).
657///
658/// Instances can be created by using fluent setters, or by using the
659/// [`progress_logger`] macro.
660///
661/// You can [clone](#impl-Clone-for-ProgressLogger) a logger to create a new one
662/// with the same setup but with all the counters reset. This behavior is useful
663/// when you want to configure a logger and then use its configuration for other
664/// loggers.
665///
666/// # Examples
667///
668/// A typical call sequence to a progress logger is as follows:
669///
670/// ```rust
671/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
672/// use dsi_progress_logger::prelude::*;
673///
674/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
675///
676/// let mut pl = ProgressLogger::default();
677/// pl.item_name("pumpkin");
678/// pl.start("Smashing pumpkins...");
679/// for _ in 0..100 {
680///    // do something on each pumpkin
681///    pl.update();
682/// }
683/// pl.done();
684/// #     Ok(())
685/// # }
686/// ```
687///
688/// The [`progress_logger`] macro will create the progress logger for you and
689/// set its [`log_target`](ProgressLog::log_target) to [`std::module_path!()`],
690/// which is usually what you want. You can also call any setter with a
691/// key-value syntax:
692///
693/// ```rust
694/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
695/// use dsi_progress_logger::prelude::*;
696///
697/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
698///
699/// let mut pl = progress_logger![item_name="pumpkin"];
700/// pl.start("Smashing pumpkins...");
701/// for _ in 0..100 {
702///    // do something on each pumpkin
703///    pl.update();
704/// }
705/// pl.done();
706/// #     Ok(())
707/// # }
708/// ```
709///
710/// A progress logger can also be used as a handy timer:
711///
712/// ```rust
713/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
714/// use dsi_progress_logger::prelude::*;
715///
716/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
717///
718/// let mut pl = progress_logger![item_name="pumpkin"];
719/// pl.start("Smashing pumpkins...");
720/// for _ in 0..100 {
721///    // do something on each pumpkin
722/// }
723/// pl.done_with_count(100);
724/// #     Ok(())
725/// # }
726/// ```
727///
728/// This progress logger will display information about memory usage:
729///
730/// ```rust
731/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
732/// use dsi_progress_logger::prelude::*;
733///
734/// env_logger::builder().filter_level(log::LevelFilter::Info).try_init()?;
735///
736/// let mut pl = progress_logger![display_memory=true];
737/// #     Ok(())
738/// # }
739/// ```
740pub struct ProgressLogger {
741    /// The name of an item. Defaults to `item`.
742    item_name: String,
743    /// The pluralized name of an item. Defaults to `items`. It is quite
744    /// expensive to compute with [`pluralize`], hence the caching.
745    items_name: String,
746    /// The log interval. Defaults to 10 seconds.
747    log_interval: Duration,
748    /// The expected number of updates. If set, the logger will display the percentage of completion and
749    /// an estimate of the time to completion.
750    expected_updates: Option<usize>,
751    /// The time unit to use for speed. If set, the logger will always display the speed in this unit
752    /// instead of making a choice of readable unit based on the elapsed time. Moreover, large numbers
753    /// will not be thousands separated. This is useful when the output of the logger must be parsed.
754    time_unit: Option<TimeUnit>,
755    /// Display additionally the speed achieved during the last log interval.
756    local_speed: bool,
757    /// [`mod@log`] target
758    ///
759    /// This is often the path of the module logging progress.
760    log_target: String,
761    /// [`mod@log`] level for progress messages. Defaults to [`Level::Info`].
762    log_level: Level,
763    /// When the logger was started.
764    start_time: Option<Instant>,
765    /// The last time we logged the activity (to compute speed).
766    last_log_time: Instant,
767    /// The next time we will log the activity.
768    next_log_time: Instant,
769    /// When the logger was stopped.
770    stop_time: Option<Instant>,
771    /// The number of items.
772    count: usize,
773    /// The number of items at the last log (to compute speed).
774    last_count: usize,
775    /// Display additionally the amount of used and free memory using this [`sysinfo::System`]
776    system: Option<System>,
777    /// The pid of the current process
778    pid: Pid,
779}
780
781impl Default for ProgressLogger {
782    /// Create a default [`ProgressLogger`] with a log interval of 10 seconds and
783    /// item name set to “item”.
784    fn default() -> Self {
785        Self {
786            item_name: "item".into(),
787            items_name: "items".into(),
788            log_interval: Duration::from_secs(10),
789            expected_updates: None,
790            time_unit: None,
791            local_speed: false,
792            log_target: std::env::current_exe()
793                .ok()
794                .and_then(|path| {
795                    path.file_name()
796                        .and_then(|s| s.to_owned().into_string().ok())
797                })
798                .unwrap_or_else(|| "main".to_string()),
799            log_level: Level::Info,
800            start_time: None,
801            last_log_time: Instant::now(),
802            next_log_time: Instant::now(),
803            stop_time: None,
804            count: 0,
805            last_count: 0,
806            system: None,
807            pid: Pid::from(std::process::id() as usize),
808        }
809    }
810}
811
812impl Clone for ProgressLogger {
813    /// Clone the logger, returning a logger with the same setup but with all the
814    /// counters reset.
815    #[allow(clippy::manual_map)]
816    fn clone(&self) -> Self {
817        Self {
818            item_name: self.item_name.clone(),
819            items_name: self.items_name.clone(),
820            log_interval: self.log_interval,
821            time_unit: self.time_unit,
822            local_speed: self.local_speed,
823            log_level: self.log_level,
824            log_target: self.log_target.clone(),
825            system: match self.system {
826                Some(_) => Some(System::new_with_specifics(
827                    RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()),
828                )),
829                None => None,
830            },
831            ..ProgressLogger::default()
832        }
833    }
834}
835
836/// Macro to create a [`ProgressLogger`] with default log target set to
837/// [`std::module_path!`], and key-value pairs instead of setters.
838///
839/// # Examples
840///
841///
842/// ```rust
843/// use dsi_progress_logger::prelude::*;
844///
845/// let mut pl = progress_logger![item_name="pumpkin", display_memory=true];
846/// ```
847#[macro_export]
848macro_rules! progress_logger {
849    ($($method:ident = $arg:expr),* $(,)?) => {
850        {
851            let mut pl = $crate::ProgressLogger::default();
852            $crate::ProgressLog::log_target(&mut pl, ::std::module_path!());
853            $(
854                $crate::ProgressLog::$method(&mut pl, $arg);
855            )*
856            pl
857        }
858    }
859}
860
861impl ProgressLogger {
862    /// Calls to [light_update](ProgressLog::light_update) will cause a call to
863    /// [`Instant::now`] only if the current count is a multiple of this mask
864    /// plus one.
865    pub const LIGHT_UPDATE_MASK: usize = (1 << 20) - 1;
866
867    fn fmt_timing_speed(&self, f: &mut Formatter<'_>, seconds_per_item: f64) -> Result {
868        let items_per_second = 1.0 / seconds_per_item;
869
870        let time_unit_timing = self
871            .time_unit
872            .unwrap_or_else(|| TimeUnit::nice_time_unit(seconds_per_item));
873
874        let time_unit_speed = self
875            .time_unit
876            .unwrap_or_else(|| TimeUnit::nice_speed_unit(seconds_per_item));
877
878        f.write_fmt(format_args!(
879            "{:.2} {}/{}, {:.2} {}/{}",
880            items_per_second * time_unit_speed.as_seconds(),
881            self.items_name,
882            time_unit_speed.label(),
883            seconds_per_item / time_unit_timing.as_seconds(),
884            time_unit_timing.label(),
885            self.item_name
886        ))?;
887
888        Ok(())
889    }
890}
891
892impl ProgressLog for ProgressLogger {
893    type Concurrent = ConcurrentWrapper<Self>;
894
895    fn log(&mut self, now: Instant) {
896        self.refresh();
897        log!(target: &self.log_target, self.log_level, "{}", self);
898        self.last_count = self.count;
899        self.last_log_time = now;
900        self.next_log_time = now + self.log_interval;
901    }
902
903    fn log_if(&mut self, now: Instant) {
904        if self.next_log_time <= now {
905            self.log(now);
906        }
907    }
908
909    fn add_to_count(&mut self, count: usize) {
910        self.count += count;
911    }
912
913    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
914        match (display_memory, &self.system) {
915            (true, None) => {
916                self.system = Some(System::new_with_specifics(
917                    RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()),
918                ));
919            }
920            (false, Some(_)) => {
921                self.system = None;
922            }
923            _ => (),
924        }
925        self
926    }
927
928    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
929        self.item_name = item_name.as_ref().into();
930        self.items_name = pluralize(item_name.as_ref(), 2, false);
931        self
932    }
933
934    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
935        self.log_interval = log_interval;
936        self
937    }
938
939    fn expected_updates(&mut self, expected_updates: impl Into<Option<usize>>) -> &mut Self {
940        self.expected_updates = expected_updates.into();
941        self
942    }
943
944    fn time_unit(&mut self, time_unit: impl Into<Option<TimeUnit>>) -> &mut Self {
945        self.time_unit = time_unit.into();
946        self
947    }
948
949    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
950        self.local_speed = local_speed;
951        self
952    }
953
954    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
955        self.log_target = target.as_ref().into();
956        self
957    }
958
959    fn log_level(&mut self, log_level: Level) -> &mut Self {
960        self.log_level = log_level;
961        self
962    }
963
964    fn start(&mut self, msg: impl AsRef<str>) {
965        let now = Instant::now();
966        self.start_time = Some(now);
967        self.stop_time = None;
968        self.count = 0;
969        self.last_count = 0;
970        self.last_log_time = now;
971        self.next_log_time = now + self.log_interval;
972        if !msg.as_ref().is_empty() {
973            log!(target: &self.log_target, self.log_level, "{}", msg.as_ref());
974        }
975    }
976
977    fn refresh(&mut self) {
978        if let Some(system) = &mut self.system {
979            system.refresh_processes_specifics(
980                ProcessesToUpdate::Some(&[self.pid]),
981                false,
982                ProcessRefreshKind::nothing().with_memory(),
983            );
984        }
985    }
986
987    fn update(&mut self) {
988        self.count += 1;
989        self.log_if(Instant::now());
990    }
991
992    fn update_with_count_and_time(&mut self, count: usize, now: Instant) {
993        self.count += count;
994        self.log_if(now);
995    }
996
997    /// Increases the count and, once every
998    /// [`LIGHT_UPDATE_MASK`](#fields.LIGHT_UPDATE_MASK) + 1 calls, check
999    /// whether it is time to log.
1000    #[inline(always)]
1001    fn light_update(&mut self) {
1002        self.count += 1;
1003        if (self.count & Self::LIGHT_UPDATE_MASK) == 0 {
1004            self.log_if(Instant::now());
1005        }
1006    }
1007
1008    fn update_and_display(&mut self) {
1009        self.count += 1;
1010        self.log(Instant::now());
1011    }
1012
1013    fn stop(&mut self) {
1014        self.stop_time = Some(Instant::now());
1015        // just to avoid wrong reuses
1016        self.expected_updates = None;
1017    }
1018
1019    fn done(&mut self) {
1020        self.stop();
1021        log!(target: &self.log_target, self.log_level, "Completed.");
1022        self.refresh();
1023        log!(target: &self.log_target, self.log_level, "{}", self);
1024    }
1025
1026    fn done_with_count(&mut self, count: usize) {
1027        self.count = count;
1028        self.done();
1029    }
1030
1031    fn elapsed(&self) -> Option<Duration> {
1032        let start_time = self.start_time?;
1033        Some(self.stop_time.unwrap_or_else(Instant::now) - start_time)
1034    }
1035
1036    fn count(&self) -> usize {
1037        self.count
1038    }
1039
1040    fn trace(&self, args: Arguments<'_>) {
1041        trace!(target: &self.log_target, "{}", std::fmt::format(args));
1042    }
1043
1044    fn debug(&self, args: Arguments<'_>) {
1045        debug!(target: &self.log_target, "{}", std::fmt::format(args));
1046    }
1047
1048    fn info(&self, args: Arguments<'_>) {
1049        info!(target: &self.log_target, "{}", std::fmt::format(args));
1050    }
1051
1052    fn warn(&self, args: Arguments<'_>) {
1053        warn!(target: &self.log_target, "{}", std::fmt::format(args));
1054    }
1055
1056    fn error(&self, args: Arguments<'_>) {
1057        error!(target: &self.log_target, "{}", std::fmt::format(args));
1058    }
1059
1060    fn concurrent(&self) -> Self::Concurrent {
1061        ConcurrentWrapper::wrap(self.clone())
1062    }
1063}
1064
1065impl Display for ProgressLogger {
1066    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1067        if let Some(start_time) = self.start_time {
1068            let count_fmtd = if self.time_unit.is_none() {
1069                self.count.to_formatted_string(&Locale::en)
1070            } else {
1071                self.count.to_string()
1072            };
1073
1074            if let Some(stop_time) = self.stop_time {
1075                let elapsed = stop_time - start_time;
1076                let seconds_per_item = elapsed.as_secs_f64() / self.count as f64;
1077
1078                f.write_fmt(format_args!(
1079                    "Elapsed: {}",
1080                    TimeUnit::pretty_print(elapsed.as_millis())
1081                ))?;
1082
1083                if self.count != 0 {
1084                    f.write_fmt(format_args!(
1085                        " [{} {}, ",
1086                        count_fmtd,
1087                        if self.count == 1 {
1088                            &self.item_name
1089                        } else {
1090                            &self.items_name
1091                        }
1092                    ))?;
1093                    self.fmt_timing_speed(f, seconds_per_item)?;
1094                    f.write_fmt(format_args!("]"))?
1095                }
1096            } else {
1097                let now = Instant::now();
1098
1099                let elapsed = now - start_time;
1100
1101                f.write_fmt(format_args!(
1102                    "{} {}, {}, ",
1103                    count_fmtd,
1104                    if self.count == 1 {
1105                        &self.item_name
1106                    } else {
1107                        &self.items_name
1108                    },
1109                    TimeUnit::pretty_print(elapsed.as_millis()),
1110                ))?;
1111
1112                let seconds_per_item = elapsed.as_secs_f64() / self.count as f64;
1113                self.fmt_timing_speed(f, seconds_per_item)?;
1114
1115                if let Some(expected_updates) = self.expected_updates {
1116                    let millis_to_end: u128 = (expected_updates.saturating_sub(self.count) as u128
1117                        * elapsed.as_millis())
1118                        / (self.count as u128 + 1);
1119                    f.write_fmt(format_args!(
1120                        "; {:.2}% done, {} to end",
1121                        100.0 * self.count as f64 / expected_updates as f64,
1122                        TimeUnit::pretty_print(millis_to_end)
1123                    ))?;
1124                }
1125
1126                if self.local_speed && self.stop_time.is_none() {
1127                    f.write_fmt(format_args!(" ["))?;
1128
1129                    let elapsed = now - self.last_log_time;
1130                    let seconds_per_item =
1131                        elapsed.as_secs_f64() / (self.count - self.last_count) as f64;
1132                    self.fmt_timing_speed(f, seconds_per_item)?;
1133
1134                    f.write_fmt(format_args!("]"))?;
1135                }
1136            }
1137
1138            // It would be ideal to refresh self.system here, but this operation
1139            // would require an &mut self reference.
1140            if let Some(system) = &self.system {
1141                f.write_fmt(format_args!(
1142                    "; res/vir/avail/free/total mem {}/{}/{}B/{}B/{}B",
1143                    system
1144                        .process(self.pid)
1145                        .map(|process| humanize(process.memory() as _) + "B")
1146                        .unwrap_or("N/A".to_string()),
1147                    system
1148                        .process(self.pid)
1149                        .map(|process| humanize(process.virtual_memory() as _) + "B")
1150                        .unwrap_or("N/A".to_string()),
1151                    humanize(system.available_memory() as _),
1152                    humanize(system.free_memory() as _),
1153                    humanize(system.total_memory() as _)
1154                ))?;
1155            }
1156
1157            Ok(())
1158        } else {
1159            write!(f, "ProgressLogger not started")
1160        }
1161    }
1162}
1163
1164/// A [`ConcurrentProgressLog`] implementation that wraps a [`ProgressLog`] in
1165/// an [`Arc`]/[`Mutex`].
1166///
1167/// The methods [`update`](ProgressLog::update) and
1168/// [`update_with_count`](ProgressLog::update_with_count) buffer the increment
1169/// and add it to the underlying logger only when the buffer reaches a
1170/// threshold; this prevents locking the underlying logger too often. The
1171/// threshold is set at creation using the methods
1172/// [`with_threshold`](Self::with_threshold) and
1173/// [`wrap_with_threshold`](Self::wrap_with_threshold), or by calling the method
1174/// [`threshold`](Self::threshold).
1175///
1176/// The method [`light_update`](ProgressLog::light_update), as in the case of
1177/// [`ProgressLogger`], further delays updates using an even faster check.
1178///
1179/// # Examples
1180///
1181/// In this example, we manually spawn processes:
1182///
1183/// ```rust
1184/// use dsi_progress_logger::prelude::*;
1185/// use std::thread;
1186///
1187/// let mut cpl = concurrent_progress_logger![item_name = "pumpkin"];
1188/// cpl.start("Smashing pumpkins (using many threads)...");
1189///
1190/// std::thread::scope(|s| {
1191///     for i in 0..100 {
1192///         let mut pl = cpl.clone();
1193///         s.spawn(move || {
1194///             for _ in 0..100000 {
1195///                 // do something on each pumpkin
1196///                 pl.update();
1197///             }
1198///         });
1199///     }
1200/// });
1201///
1202/// cpl.done();
1203/// ```
1204///
1205/// You can obtain the same behavior with
1206/// [`rayon`](https://crates.io/crates/rayon) using methods such as
1207/// [`for_each_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.for_each_with)
1208/// and
1209/// [`map_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.map_with):
1210///
1211/// ```rust
1212/// use dsi_progress_logger::prelude::*;
1213/// use rayon::prelude::*;
1214///
1215/// let mut cpl = concurrent_progress_logger![item_name = "pumpkin"];
1216/// cpl.start("Smashing pumpkins (using many threads)...");
1217///
1218/// (0..1000000).into_par_iter().
1219///     with_min_len(1000). // optional, might reduce the amount of cloning
1220///     for_each_with(cpl.clone(), |pl, i| {
1221///         // do something on each pumpkin
1222///         pl.update();
1223///     }
1224/// );
1225///
1226/// cpl.done();
1227/// ```
1228///
1229/// Note that you have to pass `cpl.clone()` to avoid a move that would make the
1230/// call to [`done`](ProgressLog::done) impossible. Also, since
1231/// [`for_each_with`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.for_each_with)
1232/// might perform excessive cloning if jobs are too short, you can use
1233/// [`with_min_len`](https://docs.rs/rayon/latest/rayon/iter/trait.ParallelIterator.html#method.with_min_len)
1234/// to reduce the amount of cloning.
1235pub struct ConcurrentWrapper<P: ProgressLog = ProgressLogger> {
1236    /// Underlying logger
1237    inner: Arc<Mutex<P>>,
1238    /// The number of items processed by the current thread.
1239    local_count: u32,
1240    /// The threshold for updating the underlying logger.
1241    threshold: u32,
1242}
1243
1244impl Default for ConcurrentWrapper {
1245    /// Create a new [`ConcurrentWrapper`] based on a default
1246    /// [`ProgressLogger`], with a threshold of
1247    /// [`DEFAULT_THRESHOLD`](Self::DEFAULT_THRESHOLD).
1248    fn default() -> Self {
1249        Self {
1250            inner: Arc::new(Mutex::new(ProgressLogger::default())),
1251            local_count: 0,
1252            threshold: Self::DEFAULT_THRESHOLD,
1253        }
1254    }
1255}
1256
1257impl<P: ProgressLog + Clone> Clone for ConcurrentWrapper<P> {
1258    /// Clone the concurrent wrapper, obtaining a new one with the same
1259    /// threshold, with a local count of zero, and with the same inner
1260    /// [`ProgressLog`].
1261    fn clone(&self) -> Self {
1262        Self {
1263            inner: self.inner.clone(),
1264            local_count: 0,
1265            threshold: self.threshold,
1266        }
1267    }
1268}
1269
1270/// Macro to create a [`ConcurrentWrapper`] based on a
1271/// [`ProgressLogger`], with default log target set to [`std::module_path!`],
1272/// and key-value pairs instead of setters.
1273///
1274/// # Examples
1275///
1276/// ```rust
1277/// use dsi_progress_logger::prelude::*;
1278///
1279/// let mut pl = concurrent_progress_logger![item_name="pumpkin", display_memory=true];
1280/// ```
1281#[macro_export]
1282macro_rules! concurrent_progress_logger {
1283    ($($method:ident = $arg:expr),* $(,)?) => {
1284        {
1285            let mut cpl = $crate::ConcurrentWrapper::default();
1286            $crate::ProgressLog::log_target(&mut cpl, ::std::module_path!());
1287            $(
1288                $crate::ProgressLog::$method(&mut cpl, $arg);
1289            )*
1290            cpl
1291        }
1292    }
1293}
1294
1295impl ConcurrentWrapper {
1296    /// Create a new [`ConcurrentWrapper`] based on a default
1297    /// [`ProgressLogger`], using the [default
1298    /// threshold](Self::DEFAULT_THRESHOLD).
1299    pub fn new() -> Self {
1300        Self::with_threshold(Self::DEFAULT_THRESHOLD)
1301    }
1302
1303    /// Create a new [`ConcurrentWrapper`] wrapping a default
1304    /// [`ProgressLogger`], using the given threshold.
1305    pub fn with_threshold(threshold: u32) -> Self {
1306        Self {
1307            inner: Arc::new(Mutex::new(ProgressLogger::default())),
1308            local_count: 0,
1309            threshold,
1310        }
1311    }
1312}
1313
1314impl<P: ProgressLog> ConcurrentWrapper<P> {
1315    /// The default threshold for updating the underlying logger.
1316    pub const DEFAULT_THRESHOLD: u32 = 1 << 15;
1317
1318    /// Calls to [`light_update`](ProgressLog::light_update) will cause a call
1319    /// to [`update_with_count`](ProgressLog::update_with_count) only if the
1320    /// current local count is a multiple of this mask plus one.
1321    ///
1322    /// Note that this constant is significantly smaller than the one used in
1323    /// [`ProgressLogger`], as updates will be further delayed by the threshold
1324    /// mechanism.
1325    pub const LIGHT_UPDATE_MASK: u32 = (1 << 10) - 1;
1326
1327    /// Set the threshold for updating the underlying logger.
1328    ///
1329    /// Note that concurrent loggers with the same underlying logger
1330    /// have independent thresholds.
1331    pub fn threshold(&mut self, threshold: u32) -> &mut Self {
1332        self.threshold = threshold;
1333        self
1334    }
1335
1336    /// Wrap a given [`ProgressLog`] in a [`ConcurrentWrapper`]
1337    /// using the [default threshold](Self::DEFAULT_THRESHOLD).
1338    pub fn wrap(inner: P) -> Self {
1339        Self {
1340            inner: Arc::new(Mutex::new(inner)),
1341            local_count: 0,
1342            threshold: Self::DEFAULT_THRESHOLD,
1343        }
1344    }
1345
1346    /// Wrap a given [`ProgressLog`] in a [`ConcurrentWrapper`] using a
1347    /// given threshold.
1348    pub fn wrap_with_threshold(inner: P, threshold: u32) -> Self {
1349        Self {
1350            inner: Arc::new(Mutex::new(inner)),
1351            local_count: 0,
1352            threshold,
1353        }
1354    }
1355
1356    /// Force an update of the underlying logger with the current local count.
1357    pub fn flush(&mut self) {
1358        self.inner
1359            .lock()
1360            .unwrap()
1361            .update_with_count(self.local_count as _);
1362        self.local_count = 0;
1363    }
1364}
1365
1366impl<P: ProgressLog + Clone> ConcurrentWrapper<P> {
1367    /// Duplicates the concurrent wrapper, obtaining a new one with the same
1368    /// threshold, with a local count of zero, and with an inner
1369    /// [`ProgressLog`] that is a clone of the original one.
1370    pub fn dup(&self) -> Self {
1371        Self {
1372            inner: Arc::new(Mutex::new(self.inner.lock().unwrap().clone())),
1373            local_count: 0,
1374            threshold: self.threshold,
1375        }
1376    }
1377}
1378
1379impl<P: ProgressLog + Clone + Send> ConcurrentProgressLog for ConcurrentWrapper<P> {
1380    type Duplicated = ConcurrentWrapper<P>;
1381    fn dup(&self) -> Self {
1382        Self {
1383            inner: Arc::new(Mutex::new(self.inner.lock().unwrap().clone())),
1384            local_count: 0,
1385            threshold: self.threshold,
1386        }
1387    }
1388}
1389
1390impl<P: ProgressLog + Clone + Send> ProgressLog for ConcurrentWrapper<P> {
1391    type Concurrent = Self;
1392
1393    fn log(&mut self, now: Instant) {
1394        self.inner.lock().unwrap().log(now);
1395    }
1396
1397    fn log_if(&mut self, now: Instant) {
1398        self.inner.lock().unwrap().log_if(now);
1399    }
1400
1401    fn add_to_count(&mut self, count: usize) {
1402        self.inner.lock().unwrap().add_to_count(count);
1403    }
1404
1405    fn display_memory(&mut self, display_memory: bool) -> &mut Self {
1406        self.inner.lock().unwrap().display_memory(display_memory);
1407        self
1408    }
1409
1410    fn item_name(&mut self, item_name: impl AsRef<str>) -> &mut Self {
1411        self.inner.lock().unwrap().item_name(item_name);
1412        self
1413    }
1414
1415    fn log_interval(&mut self, log_interval: Duration) -> &mut Self {
1416        self.inner.lock().unwrap().log_interval(log_interval);
1417        self
1418    }
1419
1420    fn expected_updates(&mut self, expected_updates: impl Into<Option<usize>>) -> &mut Self {
1421        self.inner
1422            .lock()
1423            .unwrap()
1424            .expected_updates(expected_updates.into());
1425        self
1426    }
1427
1428    fn time_unit(&mut self, time_unit: impl Into<Option<TimeUnit>>) -> &mut Self {
1429        self.inner.lock().unwrap().time_unit(time_unit.into());
1430        self
1431    }
1432
1433    fn local_speed(&mut self, local_speed: bool) -> &mut Self {
1434        self.inner.lock().unwrap().local_speed(local_speed);
1435        self
1436    }
1437
1438    fn log_target(&mut self, target: impl AsRef<str>) -> &mut Self {
1439        self.inner.lock().unwrap().log_target(target);
1440        self
1441    }
1442
1443    fn log_level(&mut self, log_level: Level) -> &mut Self {
1444        self.inner.lock().unwrap().log_level(log_level);
1445        self
1446    }
1447
1448    fn start(&mut self, msg: impl AsRef<str>) {
1449        self.inner.lock().unwrap().start(msg);
1450        self.local_count = 0;
1451    }
1452
1453    #[inline]
1454    fn update(&mut self) {
1455        self.update_with_count(1)
1456    }
1457
1458    #[inline]
1459    fn update_with_count_and_time(&mut self, count: usize, _now: Instant) {
1460        self.update_with_count(count);
1461    }
1462
1463    #[inline]
1464    fn update_with_count(&mut self, count: usize) {
1465        match (self.local_count as usize).checked_add(count) {
1466            None => {
1467                // Sum overflows, update in two steps
1468                {
1469                    let now = Instant::now();
1470                    let mut inner = self.inner.lock().unwrap();
1471                    inner.update_with_count_and_time(self.local_count as _, now);
1472                    inner.update_with_count_and_time(count, now);
1473                }
1474                self.local_count = 0;
1475            }
1476            Some(total_count) => {
1477                if total_count >= self.threshold as usize {
1478                    self.local_count = 0;
1479                    // Threshold reached, time to flush to the inner ProgressLog
1480                    let now = Instant::now();
1481                    self.inner
1482                        .lock()
1483                        .unwrap()
1484                        .update_with_count_and_time(total_count, now);
1485                } else {
1486                    // total_count is lower than self.threshold, which is a u32;
1487                    // so total_count fits in u32.
1488                    self.local_count = total_count as u32;
1489                }
1490            }
1491        }
1492    }
1493
1494    #[inline]
1495    fn light_update(&mut self) {
1496        self.local_count += 1;
1497        if (self.local_count & Self::LIGHT_UPDATE_MASK) == 0 && self.local_count >= self.threshold {
1498            // Threshold reached, time to flush to the inner ProgressLog
1499            let local_count = self.local_count as usize;
1500            self.local_count = 0;
1501            let now = Instant::now();
1502            self.inner
1503                .lock()
1504                .unwrap()
1505                .update_with_count_and_time(local_count, now);
1506        }
1507    }
1508
1509    fn update_and_display(&mut self) {
1510        {
1511            let mut inner = self.inner.lock().unwrap();
1512            inner.add_to_count(self.local_count as _);
1513            inner.update_and_display();
1514        }
1515        self.local_count = 0;
1516    }
1517
1518    fn stop(&mut self) {
1519        self.inner.lock().unwrap().stop();
1520    }
1521
1522    fn done(&mut self) {
1523        self.inner.lock().unwrap().done();
1524    }
1525
1526    fn done_with_count(&mut self, count: usize) {
1527        self.inner.lock().unwrap().done_with_count(count);
1528    }
1529
1530    fn elapsed(&self) -> Option<Duration> {
1531        self.inner.lock().unwrap().elapsed()
1532    }
1533
1534    fn count(&self) -> usize {
1535        self.inner.lock().unwrap().count()
1536    }
1537
1538    fn refresh(&mut self) {
1539        self.inner.lock().unwrap().refresh();
1540    }
1541
1542    fn trace(&self, args: Arguments<'_>) {
1543        self.inner.lock().unwrap().trace(args);
1544    }
1545
1546    fn debug(&self, args: Arguments<'_>) {
1547        self.inner.lock().unwrap().debug(args);
1548    }
1549
1550    fn info(&self, args: Arguments<'_>) {
1551        self.inner.lock().unwrap().info(args);
1552    }
1553
1554    fn warn(&self, args: Arguments<'_>) {
1555        self.inner.lock().unwrap().warn(args);
1556    }
1557
1558    fn error(&self, args: Arguments<'_>) {
1559        self.inner.lock().unwrap().error(args);
1560    }
1561
1562    fn concurrent(&self) -> Self::Concurrent {
1563        self.dup()
1564    }
1565}
1566
1567/// This implementation just calls [`flush`](ConcurrentWrapper::flush),
1568/// to guarantee that all updates are correctly passed to the underlying logger.
1569impl<P: ProgressLog> Drop for ConcurrentWrapper<P> {
1570    fn drop(&mut self) {
1571        self.flush();
1572    }
1573}
1574
1575impl Display for ConcurrentWrapper {
1576    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
1577        self.inner.lock().unwrap().fmt(f)
1578    }
1579}
1580
1581/// Convenience macro specifying that no (concurrent) logging should be
1582/// performed.
1583#[macro_export]
1584macro_rules! no_logging {
1585    () => {
1586        &mut Option::<$crate::ConcurrentWrapper<$crate::ProgressLogger>>::None
1587    };
1588}
1589
1590pub mod prelude {
1591    pub use log::Level;
1592
1593    pub use super::{
1594        ConcurrentProgressLog, ConcurrentWrapper, ProgressLog, ProgressLogger, TimeUnit,
1595        concurrent_progress_logger, no_logging, progress_logger,
1596    };
1597}