Logs
Recipe | Crates | Categories |
---|---|---|
Tracing initialization | ||
Basic tracing | ||
Combine layers | ||
Configure a custom event formatter | ||
Events | ||
Spans | ||
Add tracing spans to functions | ||
Related crates |
TODO
Add to Cargo.toml
[dependencies]
tracing = "0.1"
tracing-subscriber = "0.3"
Initialization
Basic tracing
fn main() {
// Filter events at runtime using the value
// of the RUST_LOG environment variable:
// for example, RUST_LOG=debug,my_crate=trace
tracing_subscriber::fmt::init();
}
Combine layers
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
fn main() {
tracing_subscriber::registry()
.with(tracing_subscriber::fmt::layer())
.with(tracing_subscriber::filter::EnvFilter::new(
std::env::var("RUST_LOG").unwrap_or_else(|_| {
"myproj=debug,axum=debug,tower_http=debug,mongodb=debug".into()
}),
))
.init();
}
Or with a custom formatting layer
use tracing_subscriber::filter::EnvFilter;
use tracing_subscriber::fmt;
use tracing_subscriber::prelude::*;
fn main() {
let fmt_layer = fmt::layer().with_target(false);
let filter_layer = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new("info"))
.unwrap();
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
}
Configure a custom event formatter
use tracing_subscriber::fmt;
fn main() {
// Configure a custom event formatter
let format = fmt::format()
.with_level(false) // Don't include levels in formatted output
.with_target(false) // Don't include targets
.with_thread_ids(true) // Include the thread ID of the current thread
.with_thread_names(true) // Include the name of the current thread
.compact(); // Use the `Compact` formatting style.
// Create a `fmt` subscriber that uses our custom event format, and
// set it as the default.
tracing_subscriber::fmt().event_format(format).init();
}
Events
use tracing::debug; use tracing::error; use tracing::event; use tracing::info; use tracing::trace; use tracing::warn; use tracing::Level; #[derive(Debug)] struct S; fn main() { event!(Level::INFO, "something happened"); error!("error!"); warn!("warning!"); info!("info!"); debug!("debug info!"); trace!("trace info!"); event!(target: "app_events", Level::TRACE, "something has happened!"); // Records an event with two fields (also works for spans) event!( Level::INFO, answer = 42, question = "life, the universe, and everything" ); // Unlike other fields, `message`'s shorthand initialization is just // the string itself. debug!(excitement = "yay!", "hello!"); // Shorthand for `user = user` let user = "ferris"; event!(Level::TRACE, "login: {}", user); // Note the `?`: `my_struct` will be recorded // using its `fmt::Debug` implementation. let my_struct = S; event!(Level::TRACE, greeting = ?my_struct); }
Spans
use tracing::span; use tracing::Level; fn main() { let span = span!(Level::TRACE, "my_span"); { // current lexical scope. let _guard = span.enter(); // `enter` returns a RAII guard // which, when dropped, exits the // span. // Any trace events that occur here // will occur within the span. } // Dropping the guard exits the span. }
One-liner with .entered()
:
use tracing::span; use tracing::Level; fn main() { let span = span!(Level::TRACE, "some span").entered(); // code here is within the span // optionally, explicitly exit the span, returning it let span = span.exit(); // code here is no longer within the span // enter the span again let _span = span.entered(); }
Holding the drop guard returned by Span::enter
across .await
points will result in incorrect traces. Use tracing::span::Span::in_scope
⮳.
use tracing::debug_span; use tracing::info_span; use tracing::Instrument; async fn my_async_function() { let span = info_span!("my_async_function"); // Instrument synchronous code within an async functiom let _some_value = span.in_scope(|| { // Run some synchronous code inside the span... 42 }); // This is okay! // The span has already been exited before we reach the await point. some_other_async_function().await; // Instrument async code async move { // This is correct! If we yield here, the span will be exited, // and re-entered when we resume. some_other_async_function().await; } .instrument(span) // instrument the async block with the span... .await; // ...and await it. let _some_value = some_other_async_function() .instrument(debug_span!("some_other_async_function")) .await; } async fn some_other_async_function() {} #[tokio::main] async fn main() { my_async_function().await; }
Add tracing spans to functions
use tracing::event; use tracing::instrument; use tracing::Level; #[instrument] fn my_function(my_arg: usize) { // This event will be recorded inside a span named `my_function` // with the field `my_arg`. event!(Level::INFO, "inside my_function!"); // ... } // Used on an async function #[instrument(level = "info")] async fn my_async_function() { // This is correct! If we yield here, the span will be exited, // and re-entered when we resume. some_other_async_function().await; } async fn some_other_async_function() {} #[tokio::main] async fn main() { my_function(42); my_async_function().await; }
Related crates
tracing_journald⮳ provides support for logging tracing
⮳ events natively to journald⮳, preserving any structured information.
TODO: [tracing-next-steps][c-tracing-next-steps]