How to Integrate tui-logger with slog Using TuiSlogDrain
Enable the slog-support feature in your Cargo.toml, initialize the global tui-logger runtime with init_logger(), then create a TuiSlogDrain via slog_drain() to bridge slog records into the terminal UI widgets.
The gin66/tui-logger crate provides a terminal UI widget for viewing and filtering logs in real time. When you need to integrate it with the structured slog ecosystem, the crate exposes a dedicated TuiSlogDrain type that acts as a bridge. This allows you to retain slog's structured logging and key-value pairs while gaining the interactive display capabilities of tui-logger.
Enable the slog-support Feature
Add tui-logger to your Cargo.toml with the slog-support feature enabled:
tui-logger = { version = "0.18", features = ["slog-support"] }
This feature flag gates the src/slog.rs module, which contains the TuiSlogDrain implementation and the slog_drain() constructor. Without this feature, the slog integration code is excluded from the build.
Initialize the tui-logger Runtime
Before any slog records are emitted, you must start the tui-logger runtime. The simplest approach is to call the initialization functions exposed in src/lib.rs:
tui_logger::init_logger(log::LevelFilter::Trace).expect("Failed to init tui-logger");
tui_logger::set_default_level(log::LevelFilter::Trace);
This creates the global TUI_LOGGER instance and prepares the circular buffer that stores incoming log records. If you skip this step, the TuiSlogDrain will have no destination for its converted records.
Create the TuiSlogDrain and slog Logger
Once the runtime is initialized, create the drain and wire it into a slog logger:
use slog::{o, Drain, Logger};
use tui_logger::slog_drain; // Re-exported from src/lib.rs when feature is enabled
let drain = slog_drain().fuse(); // Creates a TuiSlogDrain
let log = Logger::root(drain, o!()); // Standard slog root logger
The slog_drain() function returns a fresh TuiSlogDrain instance defined in src/slog.rs. This type implements the slog::Drain trait, allowing you to fuse it with other drains or use it in composed logging pipelines. The .fuse() method handles error conversion to ensure the drain never returns an error at runtime.
Emit Log Records and Render the UI
With the logger configured, use standard slog macros to emit structured records:
use slog::{info, error, debug, trace, warn};
info!(log, "Application started");
debug!(log, "Debug details"; "user_id" => 42);
warn!(log, "Potential problem detected");
error!(log, "Something went terribly wrong");
trace!(log, "Trace message with extra kv"; "payload" => ?some_data);
The conversion logic—mapping slog Level to log::Level, extracting the target module, and constructing a log::Record—is implemented in the log method of TuiSlogDrain at lines 88-116 of src/slog.rs. Each record is forwarded to the global TUI_LOGGER instance, which stores it in a circular buffer.
Finally, render the logs using a widget in your terminal UI loop:
use tui_logger::TuiLoggerSmartWidget;
terminal.draw(|f| {
let size = f.size();
let widget = TuiLoggerSmartWidget::default()
.state(&mut tui_logger::TuiWidgetState::new());
f.render_widget(widget, size);
})?;
The TuiLoggerSmartWidget (defined in src/widget/smart.rs) automatically displays the slog records that have been drained into the buffer.
Complete Integration Example
Below is a minimal, runnable program demonstrating the full integration:
// Cargo.toml dependencies:
// tui-logger = { version = "0.18", features = ["slog-support", "termion"] }
// slog = "2.7"
// termion = "2.0"
use slog::{o, Drain, Logger, info, warn, error};
use tui_logger::{slog_drain, init_logger, set_default_level};
use std::io;
use termion::raw::IntoRawMode;
use tui::{backend::TermionBackend, Terminal};
fn main() -> Result<(), io::Error> {
// Initialize the global tui-logger
init_logger(log::LevelFilter::Trace).expect("init_logger failed");
set_default_level(log::LevelFilter::Trace);
// Build a slog logger that drains into tui-logger
let drain = slog_drain().fuse();
let log = Logger::root(drain, o!());
// Emit slog records
info!(log, "Hello from slog");
warn!(log, "A warning"; "code" => 404);
error!(log, "An error occurred");
// Set up a termion UI to display the widget
let stdout = io::stdout().into_raw_mode().unwrap();
let backend = TermionBackend::new(stdout);
let mut terminal = Terminal::new(backend).unwrap();
terminal.draw(|f| {
let size = f.size();
let widget = tui_logger::TuiLoggerSmartWidget::default()
.state(&mut tui_logger::TuiWidgetState::new());
f.render_widget(widget, size);
})?;
Ok(())
}
A more comprehensive demonstration is available in the repository at examples/slog.rs_outdated, which shows advanced usage with the termion backend and real-time UI updates.
Summary
- Enable the
slog-supportfeature inCargo.tomlto accessTuiSlogDraininsrc/slog.rs - Call
init_logger()before creating the slog drain to initialize the global logger state - Use
slog_drain()to obtain a drain implementingslog::Drain, then fuse it and pass it toLogger::root - Emit logs using standard slog macros; the drain converts each record to a
log::Recordand forwards it to the circular buffer - Render the UI with
TuiLoggerSmartWidgetorTuiLoggerWidgetto view the structured log stream
Frequently Asked Questions
Do I need to enable a specific feature to use TuiSlogDrain?
Yes, you must enable the slog-support feature when adding tui-logger to your dependencies. This feature gates the src/slog.rs module, which contains the TuiSlogDrain struct and the slog_drain() function. Without it, these items are not compiled or exported from the crate.
Can I combine TuiSlogDrain with other slog drains?
Absolutely. TuiSlogDrain implements the standard slog::Drain trait, so you can fuse it with other drains or include it in a drain chain. The .fuse() method is standard slog practice for error handling, allowing you to compose the drain with async wrappers, duplicate outputs, or level filters without breaking the pipeline.
How does tui-logger handle slog's structured key-value pairs?
The integration preserves structured data. In src/slog.rs, the log method (lines 88-116) extracts the message, target, and level from the slog record, then constructs a standard log::Record that includes the formatted key-value pairs. These records are stored in the internal circular buffer and displayed by the UI widgets with full formatting.
Where can I find a complete working example of this integration?
The gin66/tui-logger repository includes a full demonstration in examples/slog.rs_outdated. This example illustrates feature-gated imports, proper logger initialization, creating the drain, and wiring everything into a termion-based terminal application with interactive log viewing.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →