How to Use TuiTracingSubscriberLayer to Collect Tracing Events in a TUI App
Register TuiTracingSubscriberLayer on a tracing_subscriber::registry after calling tui_logger::init_logger to forward tracing events into the crate's circular buffer for real-time terminal display.
The tui-logger crate provides a tracing-subscriber compatible layer that bridges the tracing ecosystem with its internal log-based renderer. By enabling the tracing-support feature and registering TuiTracingSubscriberLayer, any tracing::info!, tracing::debug!, or other instrumentation events automatically appear in your Ratatui widgets without additional handlers.
Enable the Tracing Support Feature
To access the integration, add the tracing-support feature to your Cargo.toml dependency:
[dependencies]
tui-logger = { version = "*", features = ["tracing-support"] }
tracing = "0.1"
tracing-subscriber = "0.3"
This feature flag exposes TuiTracingSubscriberLayer, which is re-exported from src/lib.rs for convenient access.
Initialize the Logger Before the Registry
The setup requires strict initialization order. You must call tui_logger::init_logger before any tracing events are emitted or the registry is initialized, as noted in the documentation at src/tracing_subscriber.rs#L74-L78.
use tracing_subscriber::prelude::*;
use tui_logger::TuiTracingSubscriberLayer;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Must initialize first to create the circular buffer and background thread
tui_logger::init_logger(tui_logger::LevelFilter::Trace)?;
// Then register the layer on the registry
tracing_subscriber::registry()
.with(TuiTracingSubscriberLayer)
.init();
tracing::info!("Application started");
Ok(())
}
The init_logger function, located in src/logger/api.rs, spawns the background thread that moves events from the hot buffer into the circular buffer that feeds the UI.
How the Layer Bridges Events to the UI
TuiTracingSubscriberLayer implements tracing_subscriber::Layer and acts as a converter between the two logging systems. When a tracing::Event is emitted, the layer's on_event method (defined in src/tracing_subscriber.rs#L16-L48) performs three operations:
- Extracts metadata including the target, level, file, line, and module path.
- Visits structured fields using
ToStringVisitor(src/tracing_subscriber.rs#L25-L64) to convert key-value pairs into a formatted string. - Creates a
log::Recordand forwards it to the globalTUI_LOGGERsingleton viaTUI_LOGGER.log(...)(src/tracing_subscriber.rs#L37-L46).
The TUI_LOGGER instance stores the record in a hot buffer, which the background thread drains into a lock-free circular buffer. The TuiLoggerWidget (implemented in src/widget/standard.rs) reads from this buffer during each render cycle to display the messages.
Complete Integration Example
The following example demonstrates the full setup including structured fields and custom targets:
use tracing_subscriber::prelude::*;
use tui_logger::TuiTracingSubscriberLayer;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 1. Initialize the logger (creates TUI_LOGGER global)
tui_logger::init_logger(tui_logger::LevelFilter::Debug)?;
// 2. Register the tracing layer
tracing_subscriber::registry()
.with(TuiTracingSubscriberLayer)
.init();
// 3. Emit events that automatically appear in the TUI
tracing::info!(target: "app_startup", "Server initializing");
tracing::debug!(target: "network", bytes_sent = 1024, "Packet transmitted");
// 4. Run your terminal interface with tui_logger widgets
// tui_logger::demo::run()?;
Ok(())
}
Key Source Files and Components
Understanding the repository structure helps debug integration issues:
src/tracing_subscriber.rs– ContainsTuiTracingSubscriberLayerwith theon_eventhandler that performs thetracing::Eventtolog::Recordconversion.src/lib.rs– Re-exports the layer at lines 17-22, making it available astui_logger::TuiTracingSubscriberLayer.src/logger/api.rs– Definesinit_loggerand the globalTUI_LOGGERsingleton that receives the bridged events.src/widget/standard.rs– Implements the default widget that renders buffered log lines from the circular buffer.
Summary
- Enable the feature: Add
tracing-supportto yourtui-loggerdependency to access the layer. - Initialize first: Always call
tui_logger::init_loggerbefore creating thetracing_subscriberregistry to ensure the circular buffer exists. - Register the layer: Use
.with(TuiTracingSubscriberLayer)on your registry to activate the bridge. - Emit normally: Standard
tracingmacros automatically populate the TUI widget through the internallogtranslation layer.
Frequently Asked Questions
What happens if I initialize the tracing registry before calling init_logger?
The application will panic or silently drop events. The TUI_LOGGER global instance must exist before TuiTracingSubscriberLayer attempts to forward log::Record objects to it, as the layer directly calls TUI_LOGGER.log() inside its on_event implementation.
Does TuiTracingSubscriberLayer support structured fields and spans?
Yes. The layer uses a ToStringVisitor to serialize structured fields from tracing events into the message string. While it captures span context via on_new_span, the primary output format is a log::Record compatible string suitable for the widget's display buffer.
Can I use TuiTracingSubscriberLayer alongside other tracing layers?
Yes. The layer is composable with other tracing_subscriber::Layer implementations. You can chain it with formatting layers or filtering layers using the registry's .with() method, allowing you to write to both the TUI widget and stdout simultaneously.
Do I need the log crate in my dependencies?
No. While TuiTracingSubscriberLayer creates log::Record structs internally to interface with tui-logger's buffer, the log crate is a dependency of tui-logger itself. You only need tracing and tracing-subscriber in your application code to emit events.
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 →