21 releases
| 0.6.3 | Feb 8, 2026 |
|---|---|
| 0.5.0 | Jan 15, 2026 |
| 0.4.1 | Jan 4, 2024 |
| 0.2.0 | Mar 11, 2023 |
| 0.1.7 | Mar 4, 2020 |
#35 in Value formatting
50 downloads per month
Used in 10 crates
(7 directly)
145KB
3K
SLoC
twyg
A tiny logging setup for Rust applications
I got used to logging my apps in with:
so here's something similar for Rust ;-)
Features
- 🎨 Beautiful colored output with fine-grained color customization
- ⏰ Multiple timestamp formats (RFC3339, Standard, Simple, TimeOnly)
- 📍 Optional caller information (file, line, function)
- 📏 Configurable level padding for perfect alignment
- 🎯 Structured logging with key-value pairs
- ⚙️ Simple configuration via builder pattern or config files
- 🔄 Both foreground and background color support
- 🚀 Zero-overhead when color is disabled
Installation
Add twyg to your Cargo.toml:
[dependencies]
twyg = "0.6"
Quick Start
use twyg::{LogLevel, OptsBuilder};
// Set up with default configuration
let opts = OptsBuilder::new()
.coloured(true)
.level(LogLevel::Debug)
.report_caller(true)
.build()
.unwrap();
twyg::setup(opts).expect("Failed to set up logger");
// Now use standard Rust logging macros
log::info!("Application started");
log::debug!(user = "alice", id = 42; "User logged in");
log::warn!("Configuration file missing, using defaults");
log::error!("Failed to connect to database");
Once the setup function has been called, all subsequent calls to the standard Rust logging macros will use this configuration, providing beautifully formatted output:
The output in the screenshot above (click for a full-sized view) is from
running the demos in the examples directory.
Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
coloured |
bool |
true |
Enable/disable ANSI color output |
output |
Output |
Stdout |
Output destination: Stdout, Stderr, or File(path) |
level |
LogLevel |
Info |
Minimum log level: Trace, Debug, Info, Warn, Error |
report_caller |
bool |
false |
Include file name and line number in output |
timestamp_format |
TSFormat |
Standard |
Timestamp format (see below) |
pad_level |
bool |
false |
Enable padding of log level strings for alignment |
pad_amount |
usize |
5 |
Number of characters to pad level strings to |
pad_side |
PadSide |
Right |
Padding side: Left (right-align) or Right (left-align) |
arrow_char |
String |
"▶" |
Arrow separator between metadata and message |
msg_separator |
String |
": " |
Separator before structured logging attributes |
colors |
Colors |
See below | Fine-grained color control for each component |
Timestamp Formats
twyg supports multiple timestamp format presets:
use twyg::TSFormat;
// RFC3339 format
TSFormat::RFC3339 // "2026-01-15T14:30:52-08:00"
// Standard format (default)
TSFormat::Standard // "2026-01-15 14:30:52"
// Compact format
TSFormat::Simple // "20260115.143052"
// Time only
TSFormat::TimeOnly // "14:30:52"
// Custom chrono format string
TSFormat::Custom("%H:%M:%S%.3f".to_string()) // "14:30:52.123"
Output Format
twyg produces clean, readable log output with optional caller information and structured key-value pairs:
Without caller information:
2026-01-15 14:30:52 INFO [myapp] ▶ Application started
2026-01-15 14:30:52 DEBUG [myapp::auth] ▶ Processing login request
2026-01-15 14:30:52 WARN [myapp::config] ▶ Using default configuration: file={config.yaml}
2026-01-15 14:30:52 ERROR [myapp::db] ▶ Connection failed: host={localhost}, port={5432}
With caller information:
2026-01-15 14:30:52 INFO [main.rs:42 myapp] ▶ Application started
2026-01-15 14:30:52 DEBUG [auth.rs:127 myapp::auth] ▶ User logged in: user={alice}, id={42}
With level padding and custom formatting:
20260115.143052 INFO [main.rs:42 myapp] → Application started
20260115.143052 WARN [config.rs:18 myapp::config] → Missing key | key={api_token}
20260115.143052 ERROR [db.rs:93 myapp::db] → Failed to connect | error={timeout}
Fine-Grained Color Configuration
twyg allows you to customize the foreground and background colors of every formatted element. By default, twyg uses sensible color defaults, but you can override any color you want.
Simple Example - Changing a Few Colors
You don't need to configure every color. Just customize the ones you want to change:
use twyg::{Color, ColorAttribute, Colors, OptsBuilder};
let mut colors = Colors::default();
// Customize just the colors you want to change
colors.level_error = Some(Color::new(
ColorAttribute::HiWhite, // White text
ColorAttribute::Red // Red background
));
colors.message = Some(Color::fg(ColorAttribute::HiCyan));
colors.arrow = Some(Color::fg(ColorAttribute::Magenta));
let opts = OptsBuilder::new()
.coloured(true)
.colors(colors)
.build()
.unwrap();
twyg::setup(opts).unwrap();
log::error!("This error has white text on a red background!");
log::info!("This message is in high-intensity cyan");
Disabling Color for Specific Elements
To disable color for a specific element while keeping others colored, set both foreground and background to Reset:
let mut colors = Colors::default();
colors.timestamp = Some(Color::new(
ColorAttribute::Reset,
ColorAttribute::Reset
));
// Timestamp will now be uncolored, but everything else remains colored
Complete Color Configuration Reference
The Colors struct provides fine-grained control over every colored element:
pub struct Colors {
// Timestamp color (default: Green)
pub timestamp: Option<Color>,
// Log level colors
pub level_trace: Option<Color>, // default: HiBlue
pub level_debug: Option<Color>, // default: Cyan
pub level_info: Option<Color>, // default: HiGreen
pub level_warn: Option<Color>, // default: HiYellow
pub level_error: Option<Color>, // default: Red
// Message text color (default: Green)
pub message: Option<Color>,
// Arrow separator "▶" (default: Cyan)
pub arrow: Option<Color>,
// Caller information colors
pub caller_file: Option<Color>, // default: HiYellow
pub caller_line: Option<Color>, // default: HiYellow
// Target/module name color (default: HiYellow)
pub target: Option<Color>,
// Structured logging attribute colors
pub attr_key: Option<Color>, // default: HiYellow
pub attr_value: Option<Color>, // default: Cyan
}
pub struct Color {
pub fg: ColorAttribute, // Foreground color
pub bg: ColorAttribute, // Background color
}
Available Colors
ColorAttribute provides these options:
Standard colors:
Black,Red,Green,Yellow,Blue,Magenta,Cyan,White
Bright/high-intensity colors:
HiBlack,HiRed,HiGreen,HiYellow,HiBlue,HiMagenta,HiCyan,HiWhite
Special:
Reset- No color (use for both foreground and background to disable coloring for an element)
You can create colors with just foreground:
Color::fg(ColorAttribute::Red)
Or with both foreground and background:
Color::new(ColorAttribute::White, ColorAttribute::Red) // White text on red background
Global Color Disable
The coloured: false option continues to work and will disable ALL colors regardless of individual color settings:
let opts = OptsBuilder::new()
.coloured(false) // Disables all colors globally
.build()
.unwrap();
Complete Example
See examples/fine-grained-colors.rs for a complete working example with custom colors, padding, and formatting options.
Examples
twyg includes several examples demonstrating different features:
# Run all examples
make run-examples
# Or run individual examples
cargo run --example simple
cargo run --example fine-grained-colors
cargo run --example from-confyg-full # Comprehensive TOML config
cargo run --example from-confyg-env # Environment variable config
Configuration Files
twyg works seamlessly with configuration libraries thanks to serde support.
Using with the config crate
Use with the config library:
1. Set up your configuration file (YAML example):
logging:
coloured: true
level: debug
output: stdout
report_caller: true
timestamp_format: Standard # Or: RFC3339, Simple, TimeOnly
pad_level: true
pad_amount: 7
pad_side: Right
arrow_char: "→"
msg_separator: " | "
colors:
timestamp:
fg: HiCyan
bg: Reset
level_info:
fg: HiWhite
bg: Blue
message:
fg: HiWhite
bg: Reset
2. Add twyg to your config struct:
use serde::Deserialize;
use twyg::Opts;
#[derive(Debug, Deserialize)]
pub struct AppConfig {
pub logging: Opts,
}
3. Load and apply configuration:
let cfg: AppConfig = config::Config::builder()
.add_source(config::File::with_name("config.yaml"))
.build()?
.try_deserialize()?;
twyg::setup(cfg.logging)?;
Using with confyg
For TOML configuration with the confyg library:
[logging]
coloured = true
level = "debug"
output = "stdout"
report_caller = true
timestamp_format = "Simple"
pad_level = true
pad_amount = 7
pad_side = "Right"
arrow_char = "→"
msg_separator = " | "
[logging.colors]
timestamp = { fg = "HiBlack", bg = "Reset" }
level_info = { fg = "HiGreen", bg = "Reset" }
level_error = { fg = "White", bg = "Red" }
message = { fg = "Cyan", bg = "Reset" }
See examples/config-full.toml for a comprehensive configuration example with all available options.
Using Environment Variables
Configuration via environment variables with the envy crate:
export MYAPP_LOGGING_COLOURED=true
export MYAPP_LOGGING_OUTPUT=stdout
export MYAPP_LOGGING_LEVEL=debug
export MYAPP_LOGGING_REPORT_CALLER=true
export MYAPP_LOGGING_TIMESTAMP_FORMAT=Simple
export MYAPP_LOGGING_PAD_LEVEL=true
export MYAPP_LOGGING_PAD_AMOUNT=7
export MYAPP_LOGGING_PAD_SIDE=Left
use serde::Deserialize;
use twyg::Opts;
let logging: Opts = envy::prefixed("MYAPP_LOGGING_").from_env()?;
twyg::setup(logging)?;
See examples/.env-example and examples/from-confyg-env.rs for complete examples.
Note: Configuration uses lowercase serialization for enums, so use strings like "debug", "info", "stdout", etc.
Migration Guide
Upgrading from v0.5 to v0.6
v0.6 adds fine-grained color configuration and new formatting options. All changes are backward compatible:
New Features (Optional)
All new fields have sensible defaults, so existing code works without changes:
// v0.5 code continues to work
let opts = OptsBuilder::new()
.coloured(true)
.level(LogLevel::Debug)
.build()
.unwrap();
To use new features:
use twyg::{Color, ColorAttribute, Colors, PadSide, TSFormat};
let mut colors = Colors::default();
colors.level_error = Some(Color::new(ColorAttribute::White, ColorAttribute::Red));
let opts = OptsBuilder::new()
.coloured(true)
.level(LogLevel::Debug)
.timestamp_format(TSFormat::Simple) // NEW
.pad_level(true) // NEW
.pad_amount(7) // NEW
.pad_side(PadSide::Right) // NEW
.arrow_char("→") // NEW
.msg_separator(" | ") // NEW
.colors(colors) // NEW
.build()
.unwrap();
Deprecated Methods
The time_format() method is deprecated in favor of timestamp_format():
// Deprecated (still works)
.time_format("%H:%M:%S")
// Preferred
.timestamp_format(TSFormat::Custom("%H:%M:%S".to_string()))
Upgrading from v0.4 to v0.5
v0.5 introduces type-safe enums and a builder pattern for better API ergonomics. Here's how to migrate:
1. Replace stringly-typed level functions with LogLevel enum
Before (v0.4):
use twyg::{self, level};
let opts = twyg::Opts {
level: level::debug(),
...
};
After (v0.5):
use twyg::{LogLevel, OptsBuilder};
let opts = OptsBuilder::new()
.level(LogLevel::Debug)
.build()
.unwrap();
2. Replace stringly-typed output with Output enum
Before (v0.4):
use twyg::{self, out};
let opts = twyg::Opts {
file: out::stdout(),
...
};
After (v0.5):
use twyg::{Output, OptsBuilder};
let opts = OptsBuilder::new()
.output(Output::Stdout)
.build()
.unwrap();
3. Use OptsBuilder instead of struct literals
Before (v0.4):
let opts = twyg::Opts {
coloured: true,
level: level::debug(),
report_caller: true,
..Default::default()
};
After (v0.5):
let opts = OptsBuilder::new()
.coloured(true)
.level(LogLevel::Debug)
.report_caller(true)
.build()
.unwrap();
4. Error handling now uses custom TwygError
Before (v0.4):
match twyg::setup(&opts) {
Ok(_) => {},
Err(error) => { /* anyhow::Error */ },
}
After (v0.5):
match twyg::setup(opts) {
Ok(_) => {},
Err(error) => { /* twyg::TwygError with specific variants */ },
}
Backwards Compatibility
The old stringly-typed functions are still available but deprecated:
level::debug(),level::info(), etc. → UseLogLevel::Debug,LogLevel::Infoout::stdout(),out::stderr()→ UseOutput::Stdout,Output::Stderr
License
Copyright © 2020-2026, Oxur Group
Apache License, Version 2.0
Dependencies
~1.4–4.5MB
~78K SLoC
