Commit 30963d5
authored
# Progress Update on non-interactive runs (#1395)
`feat(cli): add periodic logging for progress in non-interactive
terminals`
## PR Description
### Fixes
#1395
### Problem
When running `rustic` in headless environments (systemd), the progress
bar is hidden, resulting in zero output for long-running operations.
This makes it impossible to distinguish between a hang and a working
backup.
### Solution
This PR modifies `ProgressBars` to detect if `stderr` is a TTY.
* **Interactive:** Retains the existing `indicatif` animated progress
bars.
* **Non-Interactive:** Switches to a `PeriodicLog` mode that prints a
plain text status update to stderr at a fixed interval (user-provided,
otherwise default to **10s**.
### Changes
* Added `PeriodicLog` variant to `ProgressType` enum.
* Implemented `IsTerminal` check in `progress_bytes`.
* Added logic in `inc()` to check the elapsed time and print a log line
if the interval has passed.
* **Default Behavior:** If no `--progress-interval` is provided,
non-interactive mode defaults to **10s** to avoid flooding logs (unlike
the 100ms default for TTYs).
### Testing
* Verified interactive mode still renders progress bars correctly.
* Verified piped mode (`cargo run ... 2>&1 | cat`) produces heartbeat
logs:
```
[INFO] starting to backup /private/tmp/rustic-demo/data ...
[INFO] backing up...: 854.0 MiB
[INFO] backing up...: 1.7 GiB
...
```
### Foods for thought
**How `set_length` works in rustic_core**
During backup operations: rustic_core intentionally runs the size
calculation in a **parallel thread** to avoid blocking the actual backup
process:
This means:
inc() is called before `set_length()` during early progress updates.
`set_length()` is called later once the size scan completes.
self.0.length() returns `None` until `set_length()` is called
**Current Fix (Graceful handling)**:
Handle the case where length isn't known yet by checking if let
Some(len) = self.0.length() instead of using unwrap_or(0), and display
progress without total when unknown.
```rs
if let Some(len) = self.0.length() {
eprintln!("[INFO] {}: {} / {}", prefix, ByteSize(pos), ByteSize(len));
} else {
eprintln!("[INFO] {}: {}", prefix, ByteSize(pos));
}
```
**What needs to be tested**:
Given my machine's HW limits (tested backup on 5GB), I haven't been able
to run backup on files large enough for `set_length` to be called. Can
**someone test on a very large backup task** to see if `set_length` is
run and the total size is printed correctly?
1 parent b79206f commit 30963d5
1 file changed
+287
-82
lines changed
0 commit comments