Skip to content

Commit 0da8e3f

Browse files
bz2hawkw
andauthored
Improve README for first time users (#825)
## Motivation The documentation nearly put me off trying to use `tracing` at all. It looks hard to set up and use, and the two problems I first ran into (needing to output to stderr, and instrumenting a function with a non-formattable arg) were not easy to find the right solutions for. ## Solution This change rearranges the README a little to emphasise solutions, rather than problems and extra details a first time user probably doesn't need to care about yet. * Start with the simple way of setting up a subscriber rather than diving into the complex ways. * Show the preferred method of instrumenting async code before explaining why it is more complex and how it's implemented. * Remove yak credit. Does anyone care? * Further tweaks to README contents Address @hawkw review comments. Opted for clearer wording in passages joining examples together rather than extra reordering for now. Co-authored-by: Eliza Weisman <[email protected]>
1 parent c0964d9 commit 0da8e3f

File tree

1 file changed

+41
-44
lines changed

1 file changed

+41
-44
lines changed

README.md

+41-44
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ Tokio project, but does _not_ require the `tokio` runtime to be used.
3333

3434
## Usage
3535

36-
(The examples below are borrowed from the `log` crate's yak-shaving
37-
[example](https://docs.rs/log/0.4.10/log/index.html#examples), modified to
38-
idiomatic `tracing`.)
39-
4036
### In Applications
4137

4238
In order to record trace events, executables have to use a `Subscriber`
@@ -55,23 +51,15 @@ tracing = "0.1"
5551
tracing-subscriber = "0.2"
5652
```
5753

58-
To set a global subscriber for the entire program, use the `set_global_default` function:
54+
Then create and install a `Subscriber`, for example using [`init()`]:
5955

6056
```rust
61-
use tracing::{info, Level};
57+
use tracing::info;
6258
use tracing_subscriber;
6359

6460
fn main() {
65-
// a builder for `FmtSubscriber`.
66-
let subscriber = tracing_subscriber::fmt()
67-
// all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.)
68-
// will be written to stdout.
69-
.with_max_level(Level::TRACE)
70-
// completes the builder
71-
.finish();
72-
// and sets the constructed `Subscriber` as the default.
73-
tracing::subscriber::set_global_default(subscriber)
74-
.expect("no global subscriber has been set")
61+
// install global subscriber configured based on RUST_LOG envvar.
62+
tracing_subscriber::init()
7563

7664
let number_of_yaks = 3;
7765
// this creates a new event, outside of any spans.
@@ -85,25 +73,27 @@ fn main() {
8573
}
8674
```
8775

76+
Using `init()` calls [`set_global_default()`] so this subscriber will be used
77+
as the default in all threads for the remainder of the duration of the
78+
program, similar to how loggers work in the `log` crate.
79+
8880
[tracing-subscriber-docs]: https://docs.rs/tracing-subscriber/
8981
[fmt]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/index.html
9082
[`set_global_default`]: https://docs.rs/tracing/latest/tracing/subscriber/fn.set_global_default.html
9183

92-
This subscriber will be used as the default in all threads for the remainder of the duration
93-
of the program, similar to how loggers work in the `log` crate.
9484

95-
In addition, you can locally override the default subscriber. For example:
85+
For more control, a subscriber can be built in stages and not set globally,
86+
but instead used to locally override the default subscriber. For example:
9687

9788
```rust
9889
use tracing::{info, Level};
9990
use tracing_subscriber;
10091

10192
fn main() {
10293
let subscriber = tracing_subscriber::fmt()
103-
// all spans/events with a level higher than TRACE (e.g, debug, info, warn, etc.)
104-
// will be written to stdout.
94+
// filter spans/events with level TRACE or higher.
10595
.with_max_level(Level::TRACE)
106-
// builds the subscriber.
96+
// build but do not install the subscriber.
10797
.finish();
10898

10999
tracing::subscriber::with_default(subscriber, || {
@@ -122,6 +112,11 @@ currently executing thread; other threads will not see the change from with_defa
122112
Once a subscriber has been set, instrumentation points may be added to the
123113
executable using the `tracing` crate's macros.
124114

115+
[`tracing-subscriber`]: https://docs.rs/tracing-subscriber/
116+
[fmt]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/index.html
117+
[`init()`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/fn.init.html
118+
[`set_global_default()`]: https://docs.rs/tracing/latest/tracing/subscriber/fn.set_global_default.html
119+
125120
### In Libraries
126121

127122
Libraries should only rely on the `tracing` crate and use the provided macros
@@ -191,14 +186,32 @@ pub fn shave_all(yaks: usize) -> usize {
191186
tracing = "0.1"
192187
```
193188

194-
Note: Libraries should *NOT* call `set_global_default()`, as this will cause
195-
conflicts when executables try to set the default later.
189+
Note: Libraries should *NOT* install a subscriber by using a method than calls
190+
[`set_global_default()`], as this will cause conflicts when executables try to
191+
set the default later.
196192

197193
### In Asynchronous Code
198194

199-
If you are instrumenting code that make use of
200-
[`std::future::Future`][std-future] or async/await, be sure to use the
201-
[tracing-futures][tracing-futures-docs] crate. This is needed because the
195+
To trace `async fn`s, the preferred method is using the [`#[instrument]`] attribute:
196+
197+
```rust
198+
use tracing::{info, instrument};
199+
use tokio::{io::AsyncWriteExt, net::TcpStream};
200+
use std::io;
201+
202+
#[instrument]
203+
async fn write(stream: &mut TcpStream) -> io::Result<usize> {
204+
let result = stream.write(b"hello world\n").await;
205+
info!("wrote to stream; success={:?}", result.is_ok());
206+
result
207+
}
208+
```
209+
210+
The [`tracing-futures`] crate must be specified as a dependency to enable
211+
`async` support.
212+
213+
Special handling is needed for the general case of code using
214+
[`std::future::Future`][std-future] or blocks with `async`/`await`, as the
202215
following example _will not_ work:
203216

204217
```rust
@@ -214,8 +227,7 @@ the span remains entered for as long as the future exists, rather than being ent
214227
it is polled, leading to very confusing and incorrect output.
215228
For more details, see [the documentation on closing spans][closing].
216229

217-
There are two ways to instrument asynchronous code. The first is through the
218-
[`Future::instrument`] combinator:
230+
This problem can be solved using the [`Future::instrument`] combinator:
219231

220232
```rust
221233
use tracing_futures::Instrument;
@@ -232,21 +244,6 @@ my_future
232244
`Future::instrument` attaches a span to the future, ensuring that the span's lifetime
233245
is as long as the future's.
234246

235-
The second, and preferred, option is through the [`#[instrument]`][instrument] attribute:
236-
237-
```rust
238-
use tracing::{info, instrument};
239-
use tokio::{io::AsyncWriteExt, net::TcpStream};
240-
use std::io;
241-
242-
#[instrument]
243-
async fn write(stream: &mut TcpStream) -> io::Result<usize> {
244-
let result = stream.write(b"hello world\n").await;
245-
info!("wrote to stream; success={:?}", result.is_ok());
246-
result
247-
}
248-
```
249-
250247
Under the hood, the `#[instrument]` macro performs same the explicit span
251248
attachment that `Future::instrument` does.
252249

0 commit comments

Comments
 (0)