Skip to content

Commit 8e4d18b

Browse files
committed
Show how to integrate with Tracing
Turns out to be pretty simple! Fixes #6
1 parent ebee48a commit 8e4d18b

File tree

6 files changed

+170
-29
lines changed

6 files changed

+170
-29
lines changed

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "nutmeg"
3-
version = "0.1.3"
3+
version = "0.1.4"
44
edition = "2021"
55
description = "An unopinionated progress bar library"
66
license = "MIT"
@@ -17,3 +17,8 @@ yansi = "0.5"
1717

1818
[dev-dependencies]
1919
rand = "0.8"
20+
tracing = "0.1"
21+
tracing-subscriber = "0.3"
22+
23+
[workspace]
24+
members = ["examples/tracing"]

NEWS.md

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,114 @@
11
# Nutmeg Changelog
22

3+
## 0.1.4
4+
5+
- New: Support and documentation for sending `tracing` updates through a Nutmeg view.
6+
37
## 0.1.3
48

59
Released 2023-05-24
610

7-
* New: [View::clear] temporarily removes the progress view if it is drawn, but
11+
- New: [View::clear] temporarily removes the progress view if it is drawn, but
812
allows it to pop back when the model is next updated.
913

10-
* New: [Options] can be constructed as a `static` value: there's a new
14+
- New: [Options] can be constructed as a `static` value: there's a new
1115
`const fn` [Options::new] constructor and the functions to set fields are also
1216
`const`.
1317

14-
* New: [View::message_bytes] as a convenience for callers who already have
18+
- New: [View::message_bytes] as a convenience for callers who already have
1519
a `[u8]` or `Bytes` or similar.
1620

1721
## 0.1.2
1822

1923
Released 2022-07-27
2024

21-
* API change: Removed `View::new_stderr` and `View::write_to`. Instead, the view
25+
- API change: Removed `View::new_stderr` and `View::write_to`. Instead, the view
2226
can be drawn on stderr or output can be captured using [Options::destination].
2327
This is better aligned with the idea that programs might have a central function
2428
that constructs a [Options], as they will probably want to consistently
2529
write to either stdout or stderr.
2630

27-
* New: Output can be captured for inspection in tests using [Options::destination],
31+
- New: Output can be captured for inspection in tests using [Options::destination],
2832
[Destination::Capture], and [View::captured_output].
2933

30-
* Improved: Nutmeg avoids redrawing if the model renders identical output to what
34+
- Improved: Nutmeg avoids redrawing if the model renders identical output to what
3135
is already displayed, to avoid flicker.
3236

3337
## 0.1.1
3438

3539
Released 2022-03-22
3640

37-
* API change: [View::message] takes the message as an `AsRef<str>`, meaning
41+
- API change: [View::message] takes the message as an `AsRef<str>`, meaning
3842
it may be either a `&str` or `String`. This makes the common case where
3943
the message is the result of `format!` a little easier.
4044

4145
## 0.1.0
4246

4347
Released 2022-03-22
4448

45-
* API change: The "Write" type representing the destination is no longer
49+
- API change: The "Write" type representing the destination is no longer
4650
part of the visible public signature of [View], to hide complexity and
4751
since it is not helpful to most callers.
4852

49-
* API change: Renamed `View::to_stderr` to `View::new_stderr`.
53+
- API change: Renamed `View::to_stderr` to `View::new_stderr`.
5054

51-
* New: [percent_done] and [estimate_remaining] functions to help in rendering progress bars.
55+
- New: [percent_done] and [estimate_remaining] functions to help in rendering progress bars.
5256

53-
* New: The [models] mod provides some generally-useful basic models,
57+
- New: The [models] mod provides some generally-useful basic models,
5458
specifically [models::StringPair], [models::UnboundedModel] and [models::LinearModel].
5559
These build only on the public interface of Nutmeg, so also constitute examples of what can be done in
5660
application-defined models.
5761

58-
* New: [View::finish] removes the progress bar (if painted) and returns the [Model].
62+
- New: [View::finish] removes the progress bar (if painted) and returns the [Model].
5963
[View::abandon] now also returns the model.
6064

61-
* New: [Model::final_message] to let the model render a message to be printed when work
65+
- New: [Model::final_message] to let the model render a message to be printed when work
6266
is complete.
6367

64-
* New: The callback to [View::update] may return a value, and this is passed back to the caller
68+
- New: The callback to [View::update] may return a value, and this is passed back to the caller
6569
of [View::update].
6670

67-
* New: [models::BasicModel] allows simple cases to supply both an initial value
71+
- New: [models::BasicModel] allows simple cases to supply both an initial value
6872
and a render function inline in the [View] constructor call, avoiding any
6973
need to define a [Model] struct.
7074

71-
* New: [View::inspect_model] gives its callback a `&mut` to the model.
75+
- New: [View::inspect_model] gives its callback a `&mut` to the model.
7276

73-
* New: Progress bars constructed by [View::new] and `View::new_stderr` are disabled when
77+
- New: Progress bars constructed by [View::new] and `View::new_stderr` are disabled when
7478
`$TERM=dumb`.
7579

7680
## 0.0.2
7781

7882
Released 2022-03-07
7983

80-
* API change: Renamed `nutmeg::ViewOptions` to just `nutmeg::Options`.
84+
- API change: Renamed `nutmeg::ViewOptions` to just `nutmeg::Options`.
8185

82-
* Fixed: A bug that caused leftover text when multi-line bars shrink in width.
86+
- Fixed: A bug that caused leftover text when multi-line bars shrink in width.
8387

84-
* Fixed: The output from bars created with [View::new] and `View::to_stderr` in
88+
- Fixed: The output from bars created with [View::new] and `View::to_stderr` in
8589
Rust tests is captured with the test output rather than leaking through
8690
to cargo's output.
8791

88-
* New method [View::message] to print a message to the terminal, as an alternative
92+
- New method [View::message] to print a message to the terminal, as an alternative
8993
to using `write!()`.
9094

91-
* New `example/multithreaded.rs` showing how a View and Model can be shared
95+
- New `example/multithreaded.rs` showing how a View and Model can be shared
9296
across threads.
9397

9498
## 0.0.1
9599

96-
* Rate-limit updates to the terminal, controlled by
100+
- Rate-limit updates to the terminal, controlled by
97101
`ViewOptions::update_interval` and `ViewOptions::print_holdoff`.
98102

99-
* Fix a bug where the bar was sometimes not correctly erased
103+
- Fix a bug where the bar was sometimes not correctly erased
100104
by [View::suspend].
101105

102-
* Change to [`parking_lot`](https://docs.rs/parking_lot) mutexes in the implementation.
106+
- Change to [`parking_lot`](https://docs.rs/parking_lot) mutexes in the implementation.
103107

104108
## 0.0.0
105109

106-
* The application has complete control of styling, including coloring etc.
107-
* Draw and erase progress bars.
108-
* Write messages "under" the progress bar with `writeln!(view, ...)`. The
110+
- The application has complete control of styling, including coloring etc.
111+
- Draw and erase progress bars.
112+
- Write messages "under" the progress bar with `writeln!(view, ...)`. The
109113
bar is automatically suspended and restored. If the message has no final
110114
newline, the bar remains suspended until the line is completed.

examples/tracing/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target

examples/tracing/Cargo.toml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "nutmeg-tracing-example"
3+
version = "0.1.0"
4+
edition = "2021"
5+
publish = false
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]
10+
nutmeg = { path = "../..", version = "*" }
11+
tracing = "*"
12+
tracing-fmt = "*"
13+
tracing-subscriber = "*"

examples/tracing/src/main.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//! Show integration of Nutmeg and tracing: tracing console messages
2+
//! are interspersed nicely with the progress bar.
3+
4+
use std::sync::Arc;
5+
use std::time::Instant;
6+
7+
use nutmeg::View;
8+
use tracing::Level;
9+
use tracing_subscriber::prelude::*;
10+
11+
fn main() {
12+
let model = Model {
13+
count: 0,
14+
start: Instant::now(),
15+
};
16+
let view = Arc::new(View::new(model, nutmeg::Options::new()));
17+
let layer = tracing_subscriber::fmt::layer()
18+
.with_ansi(true)
19+
.with_writer(Arc::clone(&view))
20+
.with_target(false)
21+
.with_timer(tracing_subscriber::fmt::time::uptime())
22+
.with_filter(tracing_subscriber::filter::LevelFilter::from_level(
23+
Level::INFO,
24+
));
25+
tracing_subscriber::registry().with(layer).init();
26+
27+
for i in 0..100 {
28+
if i % 10 == 0 {
29+
tracing::info!("count: {}", i);
30+
} else if i % 37 == 0 {
31+
tracing::warn!(i, "spooky!");
32+
}
33+
view.update(|m| m.count += 1);
34+
std::thread::sleep(std::time::Duration::from_millis(100));
35+
}
36+
}
37+
38+
struct Model {
39+
count: usize,
40+
start: Instant,
41+
}
42+
43+
impl nutmeg::Model for Model {
44+
fn render(&mut self, _width: usize) -> String {
45+
let spin = match self.count % 4 {
46+
0 => "|",
47+
1 => "/",
48+
2 => "-",
49+
3 => "\\",
50+
_ => unreachable!(),
51+
};
52+
format!(
53+
"{spin} count: {}, elapsed: {:.1}s",
54+
self.count,
55+
self.start.elapsed().as_secs_f64()
56+
)
57+
}
58+
}

src/lib.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,53 @@ The `examples/bench.rs` sends updates as fast as possible to a model containing
135135
single `u64`, from a single thread. As of 2022-03-22, on a 2019 Core i9 Macbook Pro,
136136
it takes about 500ms to send 10e6 updates, or 50ns/update.
137137
138+
# Integration with `tracing`
139+
140+
Nutmeg can be used to draw progress bars in a terminal interleaved with
141+
[tracing](https://docs.rs/tracing/) messages. The progress bar is automatically
142+
temporarily removed to show messages, and repainted after the next update,
143+
subject to rate limiting and the holdoff time configured in [Options].
144+
145+
`Arc<View<M>>` implicitly implements [`tracing_subscriber::fmt::writer::MakeWriter`](https://docs.rs/tracing-subscriber/0.3.17/tracing_subscriber/fmt/writer/trait.MakeWriter.html)
146+
and so can be passed to `tracing_subscriber::fmt::layer().with_writer()`.
147+
148+
For example:
149+
150+
```rust
151+
use std::sync::Arc;
152+
use tracing::Level;
153+
use tracing_subscriber::prelude::*;
154+
155+
struct Model { count: usize }
156+
impl nutmeg::Model for Model {
157+
fn render(&mut self, _width: usize) -> String { todo!() }
158+
}
159+
160+
let model = Model {
161+
count: 0,
162+
};
163+
let view = Arc::new(nutmeg::View::new(model, nutmeg::Options::new()));
164+
let layer = tracing_subscriber::fmt::layer()
165+
.with_ansi(true)
166+
.with_writer(Arc::clone(&view))
167+
.with_target(false)
168+
.with_timer(tracing_subscriber::fmt::time::uptime())
169+
.with_filter(tracing_subscriber::filter::LevelFilter::from_level(
170+
Level::INFO,
171+
));
172+
tracing_subscriber::registry().with(layer).init();
173+
174+
for i in 0..10 {
175+
if i % 10 == 0 {
176+
tracing::info!(i, "cats adored");
177+
}
178+
view.update(|m| m.count += 1);
179+
std::thread::sleep(std::time::Duration::from_millis(100));
180+
}
181+
```
182+
183+
See `examples/tracing` for a runnable example.
184+
138185
# Project status
139186
140187
Nutmeg is a young library. Although the API will not break gratuitously,
@@ -515,6 +562,19 @@ impl<M: Model> View<M> {
515562
}
516563
}
517564

565+
impl<M: Model> io::Write for &View<M> {
566+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
567+
if buf.is_empty() {
568+
return Ok(0);
569+
}
570+
self.inner.lock().as_mut().unwrap().write(buf)
571+
}
572+
573+
fn flush(&mut self) -> io::Result<()> {
574+
Ok(())
575+
}
576+
}
577+
518578
impl<M: Model> io::Write for View<M> {
519579
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
520580
if buf.is_empty() {

0 commit comments

Comments
 (0)