Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/config/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ use log4rs::{
use serde::{Deserialize, Serialize};
use serde_with::{DisplayFromStr, serde_as};

use crate::config::progress_options::multi_progress;

/// Logging Config
#[serde_as]
#[derive(Default, Debug, Parser, Clone, Deserialize, Serialize, Merge)]
Expand Down Expand Up @@ -77,6 +79,7 @@ impl LoggingOptions {
.target(Target::Stderr)
.encoder(Box::new(PatternEncoder::new("{h([{l}])} {m}{n}")))
.build();
let stdout = PbPauseAppender(stdout);

let mut root_builder = Root::builder().appender("stdout");
let mut config_builder = Config::builder().appender(
Expand Down Expand Up @@ -119,3 +122,22 @@ impl LoggingOptions {
Ok(())
}
}

/// A wrapper around [`ConsoleAppender`] that suspends the progress bar when writing logs.
#[derive(Debug)]
struct PbPauseAppender(ConsoleAppender);

impl log4rs::append::Append for PbPauseAppender {
fn append(&self, record: &log::Record<'_>) -> Result<()> {
multi_progress().suspend(|| self.0.append(record))
}

fn flush(&self) {
// as of log4rs 1.4.0, <ConsoleAppender as Append>::flush does nothing,
// so we do not need to pause the progress bar here. In the future,
// if log4rs changes this behavior, we might need to add a suspend here.
// But that's not necessary right now, so we just call flush directly
// to avoid unnecessary suspends.
self.0.flush();
}
}
19 changes: 16 additions & 3 deletions src/config/progress_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
use std::{fmt::Write, time::Duration};

use std::io::IsTerminal;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex, OnceLock};
use std::time::Instant;

use bytesize::ByteSize;
use indicatif::{HumanDuration, ProgressBar, ProgressState, ProgressStyle};
use indicatif::{HumanDuration, MultiProgress, ProgressBar, ProgressState, ProgressStyle};

use clap::Parser;
use conflate::Merge;
Expand All @@ -19,6 +19,19 @@ use serde_with::{DisplayFromStr, serde_as};

use rustic_core::{Progress, ProgressBars, ProgressType, RusticProgress};

/// Returns the global `MultiProgress` instance used by all interactive progress bars.
///
/// Must be shared with `indicatif_log_bridge::LogWrapper` so that log output
/// suspends progress bars before printing.
pub fn multi_progress() -> &'static MultiProgress {
static MP: OnceLock<MultiProgress> = OnceLock::new();
MP.get_or_init(|| {
let mp = MultiProgress::new();
mp.set_move_cursor(true);
mp
})
}

mod constants {
use std::time::Duration;

Expand Down Expand Up @@ -110,7 +123,7 @@ pub struct InteractiveProgress {
impl InteractiveProgress {
fn new(prefix: &str, kind: ProgressType, tick_interval: Duration) -> Self {
let style = Self::initial_style(kind);
let bar = ProgressBar::new(0).with_style(style);
let bar = multi_progress().add(ProgressBar::new(0).with_style(style));
bar.set_prefix(prefix.to_string());
bar.enable_steady_tick(tick_interval);
Self { bar, kind }
Expand Down
Loading