| Crates.io | winston |
| lib.rs | winston |
| version | 0.5.1 |
| created_at | 2024-08-23 04:47:52.185901+00 |
| updated_at | 2025-09-04 10:04:46.189815+00 |
| description | winston for rust |
| homepage | |
| repository | https://github.com/ifeanyi-ugwu/winston_rs |
| max_upload_size | |
| id | 1348660 |
| size | 121,704 |
A fast, flexible logging library for Rust inspired by Winston.js.
Winston provides structured logging with composable transports, formats, and levels. Built on async foundations with intelligent backpressure handling, it's designed for both development convenience and production performance.
use winston::{log, configure, transports::stdout, LoggerOptions};
fn main() {
configure(Some(
LoggerOptions::new()
.level("info")
.add_transport(stdout())
));
log!(info, "Application started");
log!(warn, "Low disk space", usage = 92);
}
use winston::{Logger, format::{timestamp, json, chain}, transports::{stdout, File}};
fn main() {
let logger = Logger::builder()
.level("debug")
.format(timestamp().chain(json()))
.add_transport(stdout())
.add_transport(File::builder().filename("app.log").build())
.build();
log!(logger, info, "Logging to console and file");
}
Every log message is represented by a LogInfo struct containing level, message, and metadata:
// Simple log
let info = LogInfo::new("info", "User authenticated");
// With metadata
let info = info.with_meta("user_id", 12345)
.with_meta("session_id", "abc123");
Transports define output destinations. Each implements the Transport trait:
pub trait Transport: Send + Sync {
fn log(&self, info: LogInfo);
fn flush(&self) -> Result<(), String> { Ok(()) }
fn query(&self, options: &LogQuery) -> Result<Vec<LogInfo>, String> { Ok(Vec::new()) }
}
Built-in transports:
stdout() / stderr() - Console outputFile - File logging with querying supportWriterTransport - Generic writer for custom destinationsMultiple transports example:
let logger = Logger::builder()
.add_transport(stdout().with_level("info")) // Console: info+
.add_transport(File::builder() // File: debug+
.filename("app.log")
.level("debug")
.build())
.build();
Winston uses RFC 5424 severity levels (lower = more critical):
levels: {
error: 0, // System errors
warn: 1, // Warnings
info: 2, // General info
debug: 3, // Debug details
trace: 4 // Verbose tracing
}
Set minimum level to control verbosity:
let logger = Logger::builder()
.level("info") // Logs info, warn, error (filters out debug, trace)
.build();
Winston uses the powerful logform library for message formatting through composable format chaining:
use winston::format::{timestamp, json, colorize, chain};
// Using the chain method
let logger = Logger::builder()
.format(
timestamp()
.with_format("%Y-%m-%d %H:%M:%S")
.chain(colorize())
.chain(json())
)
.build();
// Using the chain! macro for cleaner syntax
let logger = Logger::builder()
.format(chain!(
timestamp().with_format("%Y-%m-%d %H:%M:%S"),
colorize(),
json()
))
.build();
Per-transport formatting:
let logger = Logger::builder()
.add_transport(
stdout().with_format(
timestamp()
.with_format("%H:%M:%S")
.chain(colorize())
)
) // Colorized console with timestamps
.add_transport(File::builder()
.filename("app.log")
.format(
timestamp()
.with_format("%Y-%m-%d %H:%M:%S")
.chain(json())
) // Structured file logs with full timestamps
.build())
.build();
Define domain-specific severity levels:
use std::collections::HashMap;
let custom_levels = HashMap::from([
("critical", 0),
("high", 1),
("medium", 2),
("low", 3)
]);
let logger = Logger::builder()
.levels(custom_levels)
.build();
Create custom logging methods and macros:
winston::create_log_methods!(critical, high, medium, low);
winston::create_level_macros!(critical, high, medium, low);
// Now you can use:
logger.critical("System failure", None);
high!(logger, "Priority task failed", retries = 3);
Control behavior when the log buffer fills up:
use winston::BackpressureStrategy;
let logger = Logger::builder()
.channel_capacity(1000)
.backpressure_strategy(BackpressureStrategy::DropOldest) // or Block, DropCurrent
.build();
Strategy recommendations:
Block - Best for critical logs where no messages should be lostDropOldest - Good for high-volume applications where recent logs matter mostDropCurrent - Suitable when preserving historical context is more importantRetrieve historical logs from queryable transports:
use winston::LogQuery;
let query = LogQuery::new()
.from("2 hours ago")
.until("now")
.levels(vec!["error", "warn"])
.search_term("database")
.limit(50);
let results = logger.query(query)?;
Query options:
from / until - Time range (supports natural language via parse_datetime)levels - Filter by severitysearch_term - Text search in messageslimit / start - Paginationorder - asc or descfields - Projection (which fields to return)Change logger settings dynamically:
logger.configure(
LoggerOptions::new()
.level("debug")
.add_transport(File::builder().filename("debug.log").build())
);
Implement the Transport trait for custom destinations:
use winston::{Transport, LogInfo};
struct DatabaseTransport {
connection: DatabaseConnection,
}
impl Transport for DatabaseTransport {
fn log(&self, info: LogInfo) {
// Insert log into database
self.connection.execute("INSERT INTO logs ...", &info);
}
fn query(&self, options: &LogQuery) -> Result<Vec<LogInfo>, String> {
// Query logs from database
self.connection.query_logs(options)
}
}
Convenient for application-wide logging:
use winston::{configure, log, flush};
configure(Some(LoggerOptions::new().add_transport(stdout())));
log!(info, "Using global logger");
flush(); // Important: flush before app exit
Better for libraries or multi-tenant applications:
let logger = Logger::builder()
.add_transport(stdout())
.build();
log!(logger, info, "Using specific logger instance");
// Automatic cleanup on drop
channel_capacity based on log volumelog CrateWinston can also act as a backend for the widely used log facade.
This means that existing libraries and crates which emit logs via log will automatically route their output through Winston’s transports and formatting system.
Enable the feature in Cargo.toml:
[dependencies]
winston = { version = "0.5", features = ["log-backend"] }
Then initialize Winston as the global logger:
use winston::Logger;
fn main() {
Logger::default().init_as_global().unwrap();
log::info!("Hello from the log crate!");
log::warn!("This also goes through Winston transports");
}
Notes:
Key–value metadata support from log is available with the log-backend-kv feature.
Winston’s transports, levels, formats, and backpressure strategies apply seamlessly.
Useful when integrating Winston into projects that already rely on the log ecosystem.
Add to your Cargo.toml:
[dependencies]
winston = "0.5"
Or use cargo:
cargo add winston
Contributions welcome! Please submit issues and pull requests on GitHub.
MIT License
Inspired by the excellent Winston.js logging library.