What is Tracing and why is it needed in Rust?
The Tracing library is a powerful framework for structured logging and instrumentation (tracing) of applications in the Rust language. Unlike simple loggers, Tracing provides the concept of spans and events, which allow you to track code execution in the context of asynchronous operations, distributed systems, and complex nested calls.
The main goal of Tracing is to give the developer a complete picture of what is happening inside the program: which functions are called, how long they take to execute, what errors occur, and how they are related to each other. It is an indispensable tool for debugging, profiling, and monitoring production systems.
The library is part of the Tokio ecosystem and is actively used in projects such as actix-web, axum, hyper, and many others.
Installing Tracing
To add Tracing to your Rust project, open the Cargo.toml file and add the following dependencies:
[dependencies]tracing = "0.1"tracing-subscriber = "0.3" # For outputting logs to the consoletracing-appender = "0.2" # For writing logs to a file (optional)After that, run the command cargo build to download and compile the libraries.
Quick Start: First Example
Let's write a simple program that logs a message using Tracing:
use tracing::{info, Level};use tracing_subscriber::FmtSubscriber;
fn main() { // Initialize a subscriber to output logs to stdout let subscriber = FmtSubscriber::builder() .with_max_level(Level::INFO) .finish(); tracing::subscriber::set_global_default(subscriber) .expect("Failed to set global subscriber");
// Log a simple message info!("Application started!");
// Call a function with tracing process_data("example.txt");}
fn process_data(filename: &str) { // Create a span to track the function let span = tracing::span!(Level::INFO, "process_data", filename = filename); let _guard = span.enter(); // Enter the span
info!("Starting file processing"); // ... some work ... std::thread::sleep(std::time::Duration::from_millis(100)); info!("Processing completed");}When you run it, you will see structured output with timestamps, levels, and span names.
Main Methods and Functions of Tracing
Below are 10 key functions and macros of the Tracing library with detailed descriptions and examples.
1. tracing::span! — Creating a Span
What it does: Creates a new span — a logical unit of work that can be nested. A span has a name, a level, and a set of fields.
Parameters:
Level— the logging level (ERROR, WARN, INFO, DEBUG, TRACE)target(optional) — a string identifying the modulefield = value— arbitrary fields for context
Example:
use tracing::Level;
fn handle_request(id: u64) { let span = tracing::span!(Level::INFO, "handle_request", request_id = id); let _guard = span.enter(); // Code inside the span tracing::info!("Processing request");}2. tracing::info! — Logging at the INFO Level
What it does: Records an event at the INFO level. An event is a single message that can be associated with the current span.
Parameters:
- A format string (like in
format!) - Arguments for formatting
- Fields via
field = value<
Also in library
Monitoring C++ Applications: metrics Library for Collecting Metrics
Working with Dates in JavaScript (Node.js): Best Libraries
Working with Apache Kafka in C++: librdkafka Library and Practical Examples
Sending Email in Java: JavaMail and Simple Java Mail Libraries
Generating PDF Reports in Swift: PDFKit and UIPrintPageRenderer