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