Use fern to build a flexible logging pipeline by chaining configuration methods that define output destinations, formatting, and filtering levels, then initialize it once at the start of your application. It works by creating a Dispatch object that can be cloned and passed to the log crate, allowing you to route logs to stdout, files, or both with custom formatters.
Here is a practical example of setting up a logger that writes to both the console and a file, with different formatting for each:
use fern::{colors::{Color, ColoredLevelConfig}, Dispatch};
use log::{info, LevelFilter};
use std::io;
fn main() -> Result<(), fern::InitError> {
// Define color mapping for log levels
let level_config = ColoredLevelConfig::new()
.error(Color::Red)
.warn(Color::Yellow)
.info(Color::BrightBlack)
.debug(Color::White);
// Configure the logger
let dispatch = Dispatch::new()
// Console output with colors
.chain(
fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{} [{}] {}",
chrono::Local::now().format("[%Y-%m-%d %H:%M:%S]"),
record.level(),
message
))
})
.level(LevelFilter::Info)
.level_map(level_config)
.chain(std::io::stdout())
)
// File output (plain text, no colors)
.chain(
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{} [{}] {}",
chrono::Local::now().format("[%Y-%m-%d %H:%M:%S]"),
record.level(),
message
))
})
.level(LevelFilter::Debug)
.chain(std::fs::File::create("app.log").expect("Failed to create log file"))
)
.level(LevelFilter::Debug);
dispatch.apply()?;
info!("Application started");
log::debug!("Debugging details here");
Ok(())
}
To use this, add fern, log, and chrono to your Cargo.toml. The key is that fern acts as a bridge; you must still call log::info!, log::error!, etc., in your code. The Dispatch object handles the routing. You can also chain multiple file outputs or use fern::log_file() for a quick setup if you don't need complex formatting logic. Remember to call dispatch.apply() before any logging macros are invoked, or the logs will be lost.