| Crates.io | twyg |
| lib.rs | twyg |
| version | 0.6.2 |
| created_at | 2020-02-09 04:33:54.72025+00 |
| updated_at | 2026-01-23 00:54:59.212546+00 |
| description | A tiny logging setup for Rust applications |
| homepage | |
| repository | https://github.com/oxur/twyg |
| max_upload_size | |
| id | 206556 |
| size | 261,039 |
A tiny logging setup for Rust applications
I got used to logging my apps in with:
so here's something similar for Rust ;-)
Add twyg to your Cargo.toml:
[dependencies]
twyg = "0.6"
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.
| 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 |
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"
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}
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.
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");
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
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
}
ColorAttribute provides these options:
Standard colors:
Black, Red, Green, Yellow, Blue, Magenta, Cyan, WhiteBright/high-intensity colors:
HiBlack, HiRed, HiGreen, HiYellow, HiBlue, HiMagenta, HiCyan, HiWhiteSpecial:
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
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();
See examples/fine-grained-colors.rs for a complete working example with custom colors, padding, and formatting options.
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
twyg works seamlessly with configuration libraries thanks to serde support.
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)?;
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.
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.
v0.6 adds fine-grained color configuration and new formatting options. All changes are backward compatible:
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();
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()))
v0.5 introduces type-safe enums and a builder pattern for better API ergonomics. Here's how to migrate:
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();
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();
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();
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 */ },
}
The old stringly-typed functions are still available but deprecated:
level::debug(), level::info(), etc. → Use LogLevel::Debug, LogLevel::Infoout::stdout(), out::stderr() → Use Output::Stdout, Output::StderrCopyright © 2020-2026, Oxur Group
Apache License, Version 2.0