Skip to content

Commit 457f525

Browse files
asonixseanmonstar
andauthored
feat(subscriber): unify build, init, and the Layer system (#195)
In reference to a conversation I observed on discord, this is an attempt to make the `build` and `init` methods play nicer with the existing `tracing_subscriber` layer system. Changes: - the `Builder` struct has its own `.spawn()` and `.init()` methods now to produce the layer or install a subscriber directly from a configured builder - `build` was renamed to `spawn` and is now a wrapper around `Builder::spawn` - `init` is now a wrapper around `Builder::init` Closes #183 Closes #196 Co-authored-by: Sean McArthur <[email protected]>
1 parent cd541d3 commit 457f525

File tree

3 files changed

+319
-162
lines changed

3 files changed

+319
-162
lines changed

Diff for: console-subscriber/src/builder.rs

+318
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,17 @@ use super::{Server, TasksLayer};
22
use std::{
33
net::{SocketAddr, ToSocketAddrs},
44
path::PathBuf,
5+
thread,
56
time::Duration,
67
};
8+
use tokio::runtime;
9+
use tracing::Subscriber;
10+
use tracing_subscriber::{
11+
filter::{FilterFn, LevelFilter, Targets},
12+
layer::{Layer, SubscriberExt},
13+
prelude::*,
14+
registry::LookupSpan,
15+
};
716

817
/// Builder for configuring [`TasksLayer`]s.
918
#[derive(Clone, Debug)]
@@ -149,6 +158,315 @@ impl Builder {
149158

150159
self
151160
}
161+
162+
/// Initializes the console [tracing `Subscriber`][sub] and starts the console
163+
/// subscriber [`Server`] on its own background thread.
164+
///
165+
/// This function represents the easiest way to get started using
166+
/// tokio-console.
167+
///
168+
/// In addition to the [`TasksLayer`], which collects instrumentation data
169+
/// consumed by the console, the default [`Subscriber`][sub] initialized by this
170+
/// function also includes a [`tracing_subscriber::fmt`] layer, which logs
171+
/// tracing spans and events to stdout. Which spans and events are logged will
172+
/// be determined by the `RUST_LOG` environment variable.
173+
///
174+
/// **Note**: this function sets the [default `tracing` subscriber][default]
175+
/// for your application. If you need to add additional layers to a subscriber,
176+
/// see [`spawn`].
177+
///
178+
/// ## Panics
179+
///
180+
/// * If the subscriber's background thread could not be spawned.
181+
/// * If the [default `tracing` subscriber][default] has already been set.
182+
///
183+
/// [default]: https://docs.rs/tracing/latest/tracing/dispatcher/index.html#setting-the-default-subscriber
184+
/// [sub]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
185+
/// [`tracing_subscriber::fmt`]: https://docs.rs/tracing-subscriber/latest/tracing-subscriber/fmt/index.html
186+
/// [`Server`]: crate::Server
187+
///
188+
/// ## Configuration
189+
///
190+
/// Tokio console subscriber is configured with sensible defaults for most
191+
/// use cases. If you need to tune these parameters, several environmental
192+
/// configuration variables are available:
193+
///
194+
/// | **Environment Variable** | **Purpose** | **Default Value** |
195+
/// |-------------------------------------|---------------------------------------------------------------------------|-------------------|
196+
/// | `TOKIO_CONSOLE_RETENTION` | The number of seconds to accumulate completed tracing data | 3600s (1h) |
197+
/// | `TOKIO_CONSOLE_BIND` | A HOST:PORT description, such as `localhost:1234` | `127.0.0.1:6669` |
198+
/// | `TOKIO_CONSOLE_PUBLISH_INTERVAL` | The number of milliseconds to wait between sending updates to the console | 1000ms (1s) |
199+
/// | `TOKIO_CONSOLE_RECORD_PATH` | The file path to save a recording | None |
200+
/// | `RUST_LOG` | Configures what events are logged events. See [`Targets`] for details. | "error" |
201+
///
202+
/// ## Further customization
203+
///
204+
/// To add additional layers or replace the format layer, replace
205+
/// `console_subscriber::Builder::init` with:
206+
///
207+
/// ```rust
208+
/// use tracing_subscriber::prelude::*;
209+
///
210+
/// let console_layer = console_subscriber::TasksLayer::builder().spawn();
211+
///
212+
/// tracing_subscriber::registry()
213+
/// .with(console_layer)
214+
/// .with(tracing_subscriber::fmt::layer())
215+
/// // .with(..potential additional layer..)
216+
/// .init();
217+
/// ```
218+
///
219+
/// [`Targets`]: https://docs.rs/tracing-subscriber/latest/tracing-subscriber/filter/struct.Targets.html
220+
pub fn init(self) {
221+
let fmt_filter = std::env::var("RUST_LOG")
222+
.ok()
223+
.and_then(|rust_log| match rust_log.parse::<Targets>() {
224+
Ok(targets) => Some(targets),
225+
Err(e) => {
226+
eprintln!("failed to parse `RUST_LOG={:?}`: {}", rust_log, e);
227+
None
228+
}
229+
})
230+
.unwrap_or_else(|| Targets::default().with_default(LevelFilter::ERROR));
231+
232+
let console_layer = self.spawn();
233+
234+
tracing_subscriber::registry()
235+
.with(console_layer)
236+
.with(tracing_subscriber::fmt::layer().with_filter(fmt_filter))
237+
.init();
238+
}
239+
240+
/// Returns a new `tracing` [`Layer`] consisting of a [`TasksLayer`]
241+
/// and a [filter] that enables the spans and events required by the console.
242+
///
243+
/// This function spawns the console subscriber's [`Server`] in its own Tokio
244+
/// runtime in a background thread.
245+
///
246+
/// Unlike [`init`], this function does not set the default subscriber, allowing
247+
/// additional [`Layer`]s to be added.
248+
///
249+
/// [subscriber]: https://docs.rs/tracing/latest/tracing/subscriber/trait.Subscriber.html
250+
/// [filter]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.Targets.html
251+
/// [`Layer`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html
252+
/// [`Server`]: crate::Server
253+
///
254+
/// ## Panics
255+
///
256+
/// * If the subscriber's background thread could not be spawned.
257+
///
258+
/// ## Configuration
259+
///
260+
/// `console_subscriber::build` supports all of the environmental
261+
/// configuration described at [`console_subscriber::init`].
262+
///
263+
/// ## Differences from `init`
264+
///
265+
/// Unlike [`console_subscriber::init`], this function does *not* add a
266+
/// [`tracing_subscriber::fmt`] layer to the configured `Subscriber`. This means
267+
/// that this function will not log spans and events based on the value of the
268+
/// `RUST_LOG` environment variable. Instead, a user-provided [`fmt::Layer`] can
269+
/// be added in order to customize the log format.
270+
///
271+
/// You must call [`.init()`] on the final subscriber in order to [set the
272+
/// subscriber as the default][default].
273+
///
274+
/// ## Examples
275+
///
276+
/// ```rust
277+
/// use tracing_subscriber::prelude::*;
278+
///
279+
/// let console_layer = console_subscriber::TasksLayer::builder()
280+
/// .with_default_env()
281+
/// .spawn();
282+
///
283+
/// tracing_subscriber::registry()
284+
/// .with(console_layer)
285+
/// .with(tracing_subscriber::fmt::layer())
286+
/// // .with(...)
287+
/// .init();
288+
/// ```
289+
/// [`.init()`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/util/trait.SubscriberInitExt.html
290+
/// [default]: https://docs.rs/tracing/latest/tracing/dispatcher/index.html#setting-the-default-subscriber
291+
/// [sub]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
292+
/// [`tracing_subscriber::fmt`]: https://docs.rs/tracing-subscriber/latest/tracing-subscriber/fmt/index.html
293+
/// [`fmt::Layer`]: https://docs.rs/tracing-subscriber/latest/tracing-subscriber/fmt/struct.Layer.html
294+
/// [`console_subscriber::init`]: crate::init()
295+
#[must_use = "a `Layer` must be added to a `tracing::Subscriber` in order to be used"]
296+
pub fn spawn<S>(self) -> impl Layer<S>
297+
where
298+
S: Subscriber + for<'a> LookupSpan<'a>,
299+
{
300+
fn console_filter(meta: &tracing::Metadata<'_>) -> bool {
301+
// events will have *targets* beginning with "runtime"
302+
if meta.is_event() {
303+
return meta.target().starts_with("runtime") || meta.target().starts_with("tokio");
304+
}
305+
306+
// spans will have *names* beginning with "runtime". for backwards
307+
// compatibility with older Tokio versions, enable anything with the `tokio`
308+
// target as well.
309+
meta.name().starts_with("runtime.") || meta.target().starts_with("tokio")
310+
}
311+
312+
let (layer, server) = self.build();
313+
let filter =
314+
FilterFn::new(console_filter as for<'r, 's> fn(&'r tracing::Metadata<'s>) -> bool);
315+
let layer = layer.with_filter(filter);
316+
317+
thread::Builder::new()
318+
.name("console_subscriber".into())
319+
.spawn(move || {
320+
let runtime = runtime::Builder::new_current_thread()
321+
.enable_io()
322+
.enable_time()
323+
.build()
324+
.expect("console subscriber runtime initialization failed");
325+
326+
runtime.block_on(async move {
327+
server
328+
.serve()
329+
.await
330+
.expect("console subscriber server failed")
331+
});
332+
})
333+
.expect("console subscriber could not spawn thread");
334+
335+
layer
336+
}
337+
}
338+
339+
/// Initializes the console [tracing `Subscriber`][sub] and starts the console
340+
/// subscriber [`Server`] on its own background thread.
341+
///
342+
/// This function represents the easiest way to get started using
343+
/// tokio-console.
344+
///
345+
/// In addition to the [`TasksLayer`], which collects instrumentation data
346+
/// consumed by the console, the default [`Subscriber`][sub] initialized by this
347+
/// function also includes a [`tracing_subscriber::fmt`] layer, which logs
348+
/// tracing spans and events to stdout. Which spans and events are logged will
349+
/// be determined by the `RUST_LOG` environment variable.
350+
///
351+
/// **Note**: this function sets the [default `tracing` subscriber][default]
352+
/// for your application. If you need to add additional layers to a subscriber,
353+
/// see [`spawn`].
354+
///
355+
/// ## Panics
356+
///
357+
/// * If the subscriber's background thread could not be spawned.
358+
/// * If the [default `tracing` subscriber][default] has already been set.
359+
///
360+
/// [default]: https://docs.rs/tracing/latest/tracing/dispatcher/index.html#setting-the-default-subscriber
361+
/// [sub]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
362+
/// [`tracing_subscriber::fmt`]: https://docs.rs/tracing-subscriber/latest/tracing-subscriber/fmt/index.html
363+
/// [`Server`]: crate::Server
364+
///
365+
/// ## Configuration
366+
///
367+
/// Tokio console subscriber is configured with sensible defaults for most
368+
/// use cases. If you need to tune these parameters, several environmental
369+
/// configuration variables are available:
370+
///
371+
/// | **Environment Variable** | **Purpose** | **Default Value** |
372+
/// |-------------------------------------|---------------------------------------------------------------------------|-------------------|
373+
/// | `TOKIO_CONSOLE_RETENTION` | The number of seconds to accumulate completed tracing data | 3600s (1h) |
374+
/// | `TOKIO_CONSOLE_BIND` | A HOST:PORT description, such as `localhost:1234` | `127.0.0.1:6669` |
375+
/// | `TOKIO_CONSOLE_PUBLISH_INTERVAL` | The number of milliseconds to wait between sending updates to the console | 1000ms (1s) |
376+
/// | `TOKIO_CONSOLE_RECORD_PATH` | The file path to save a recording | None |
377+
/// | `RUST_LOG` | Configures what events are logged events. See [`Targets`] for details. | "error" |
378+
///
379+
/// ## Further customization
380+
///
381+
/// To add additional layers or replace the format layer, replace
382+
/// `console_subscriber::init` with:
383+
///
384+
/// ```rust
385+
/// use tracing_subscriber::prelude::*;
386+
///
387+
/// let console_layer = console_subscriber::spawn();
388+
///
389+
/// tracing_subscriber::registry()
390+
/// .with(console_layer)
391+
/// .with(tracing_subscriber::fmt::layer())
392+
/// // .with(..potential additional layer..)
393+
/// .init();
394+
/// ```
395+
///
396+
/// Calling `console_subscriber::init` is equivalent to the following:
397+
/// ```rust
398+
/// use console_subscriber::TasksLayer;
399+
///
400+
/// TasksLayer::builder().with_default_env().init();
401+
/// ```
402+
/// [`Targets`]: https://docs.rs/tracing-subscriber/latest/tracing-subscriber/filter/struct.Targets.html
403+
pub fn init() {
404+
TasksLayer::builder().with_default_env().init();
405+
}
406+
407+
/// Returns a new `tracing_subscriber` [`Layer`] configured with a [`TasksLayer`]
408+
/// and a [filter] that enables the spans and events required by the console.
409+
///
410+
/// This function spawns the console subscriber's [`Server`] in its own Tokio
411+
/// runtime in a background thread.
412+
///
413+
/// Unlike [`init`], this function does not set the default subscriber, allowing
414+
/// additional [`Layer`]s to be added.
415+
///
416+
/// This function is equivalent to the following:
417+
/// ```
418+
/// use console_subscriber::TasksLayer;
419+
///
420+
/// let layer = TasksLayer::builder().with_default_env().spawn();
421+
/// # use tracing_subscriber::prelude::*;
422+
/// # tracing_subscriber::registry().with(layer).init(); // to suppress must_use warnings
423+
/// ```
424+
/// [filter]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.Targets.html
425+
/// [`Layer`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/layer/trait.Layer.html
426+
/// [`Server`]: crate::Server
427+
///
428+
/// ## Panics
429+
///
430+
/// * If the subscriber's background thread could not be spawned.
431+
///
432+
/// ## Configuration
433+
///
434+
/// `console_subscriber::build` supports all of the environmental
435+
/// configuration described at [`console_subscriber::init`].
436+
///
437+
/// ## Differences from `init`
438+
///
439+
/// Unlike [`console_subscriber::init`], this function does *not* add a
440+
/// [`tracing_subscriber::fmt`] layer to the configured `Layer`. This means
441+
/// that this function will not log spans and events based on the value of the
442+
/// `RUST_LOG` environment variable. Instead, a user-provided [`fmt::Layer`] can
443+
/// be added in order to customize the log format.
444+
///
445+
/// You must call [`.init()`] on the final subscriber in order to [set the
446+
/// subscriber as the default][default].
447+
///
448+
/// ## Examples
449+
///
450+
/// ```rust
451+
/// use tracing_subscriber::prelude::*;
452+
/// tracing_subscriber::registry()
453+
/// .with(console_subscriber::spawn())
454+
/// .with(tracing_subscriber::fmt::layer())
455+
/// // .with(...)
456+
/// .init();
457+
/// ```
458+
/// [`.init()`]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/util/trait.SubscriberInitExt.html
459+
/// [default]: https://docs.rs/tracing/latest/tracing/dispatcher/index.html#setting-the-default-subscriber
460+
/// [sub]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html
461+
/// [`tracing_subscriber::fmt`]: https://docs.rs/tracing-subscriber/latest/tracing-subscriber/fmt/index.html
462+
/// [`fmt::Layer`]: https://docs.rs/tracing-subscriber/latest/tracing-subscriber/fmt/struct.Layer.html
463+
/// [`console_subscriber::init`]: crate::init()
464+
#[must_use = "a `Layer` must be added to a `tracing::Subscriber`in order to be used"]
465+
pub fn spawn<S>() -> impl Layer<S>
466+
where
467+
S: Subscriber + for<'a> LookupSpan<'a>,
468+
{
469+
TasksLayer::builder().with_default_env().spawn::<S>()
152470
}
153471

154472
fn duration_from_env(var_name: &str) -> Option<Duration> {

0 commit comments

Comments
 (0)