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