Logging in Rust Applications: Best Practices and Tools

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

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                

Blogs

Book Recommendations