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