Tracing for Rust: A Complete Guide with Examples

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

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 module
  • field = 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<

Recommendations