Introduction: Why Logging is Critically Important in Rust
Logging is not just about recording events to a file. In high-load and safety-critical systems where Rust finds its primary application (web servers, CLI utilities, embedded systems), logs are the single source of truth during debugging and monitoring. Rust offers an ecosystem where choosing the right approach to logging can significantly impact performance and code maintainability.
In this article, we will cover the basic principles of logging in Rust, starting with the foundational log crate and ending with advanced tools like tracing. You will learn how to avoid common mistakes, configure log levels, and structure output for analysis.
Basics: The log Crate and Facade Architecture
In Rust, the de facto standard is the log crate. It provides a facade (abstraction) that does not depend on a specific backend. You write code using macros like log::info!, log::error!, while the choice of implementation (where to write: to a file, console, syslog) is left to the library user.
Installation and Basic Setup
Add to Cargo.toml:
[dependencies]log = "0.4"env_logger = "0.11"env_logger is the most popular backend for outputting to stderr. It is configured via the RUST_LOG environment variable.
Simple Logging Example
use log::{info, warn, error};
fn main() { env_logger::init();
info!("Application started"); warn!("This is a warning"); error!("Critical error: {}", 42);}Run with a level: RUST_LOG=info cargo run. You will see only info and above. If the variable is not set, logs will not be displayed.
Log Levels
In log, there are 5 levels (in ascending order of priority):
trace— extremely detailed debugging information (e.g., function entry/exit).debug— useful for developers during debugging.info— important application events (startup, shutdown, successful operations).warn— potentially dangerous situations that do not break functionality.error— unrecoverable errors requiring intervention.
Tip: In production, use the info or warn level. trace and debug are best left for development.
Best Practices: How to Write Logs Correctly
Simply inserting info!("Something") is not enough. To make logs useful, follow these rules.
1. Structured Logs Instead of Strings
Avoid manual string concatenation. Use named fields. In log, this is done through formatting:
let user_id = 123;let action = "login";info!("User {} performed {}", user_id, action);For advanced structuring (JSON), use the slog or tracing crate.
2. Context and Identifiers
Always add context: request_id, user_id, session_id. This allows linking logs in distributed systems.
warn!("Validation error", user_id = user_id, field = "email", reason = "invalid format");3. Avoid Logging in Hot Loops
Logging is an expensive operation (I/O). Do not put trace! inside loops with millions of iterations. Use conditional logging or metric counters.
4. Use RUST_LOG for Flexible Configuration
You can filter logs by modules and crates:
RUST_LOG="warn,my_crate=debug,othe