# How to Use tui-logger with the log Crate and env_logger as Formatter

> Learn to use tui-logger with the Rust log crate and env_logger formatter. Integrate effortlessly by wrapping env_logger's formatting logic into tui-logger's circular buffers for enhanced logging.

- Repository: [gin66/tui-logger](https://github.com/gin66/tui-logger)
- Tags: how-to-guide
- Published: 2026-03-02

---

**Yes, you can use tui-logger with the log crate while delegating formatting to env_logger by creating a `Drain` wrapper that forwards records from env_logger's formatter closure into tui-logger's circular buffers.**

The `tui-logger` crate from `gin66/tui-logger` provides terminal user interface (TUI) widgets for viewing logs in Rust applications built with `ratatui`. While it can serve as a standalone logger implementation, you may want to combine it with `env_logger` to leverage its flexible formatting capabilities. This guide explains how to configure this integration so that `env_logger` controls output formatting while `tui-logger` handles UI storage and rendering.

## Architecture Overview

When combining these crates, log records flow through a specific pipeline that separates formatting from UI storage:

```

log macros → env_logger (formatter) → TuiLogger::Drain → TUI_LOGGER (hot buffer → main buffer) → TuiLoggerWidget

```

- The `log` crate macros (`info!`, `warn!`, etc.) produce `log::Record` instances.
- `env_logger` receives these records and formats them according to your configuration.
- Inside the formatter closure, you forward the record to `tui_logger::Drain::log`, which pushes the event into the hot circular buffer defined in [`src/logger/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/inner.rs).
- A background thread spawned by `init_logger` (located in [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs)) periodically moves events from the hot buffer to the main buffer.
- The UI widgets (`TuiLoggerWidget` in [`src/widget/standard.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/standard.rs) or `TuiLoggerSmartWidget` in [`src/widget/smart.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/smart.rs)) read from the main buffer to render log lines.

Because the `log` crate permits only one global logger, you must choose between installing `tui-logger` as the global logger or using `env_logger` as the global logger with `tui-logger` as a drain.

## Step-by-Step Implementation

To use `env_logger` as the formatter while capturing output in `tui-logger`, follow this pattern demonstrated in [`examples/demo.rs`](https://github.com/gin66/tui-logger/blob/main/examples/demo.rs).

### Initialize the Background Thread

First, call `tui_logger::init_logger` to spawn the background worker thread that moves events between buffers. In this configuration, it does not register `tui-logger` as the global logger.

```rust
use log::LevelFilter;
use tui_logger::init_logger;

// Start the background mover thread (move_events loop in src/logger/api.rs)
init_logger(LevelFilter::Trace)?;

```

This initializes the hot and main circular buffers in [`src/logger/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/inner.rs) and starts the thread that periodically flushes `HotLog` events into the main buffer for UI consumption.

### Create the Drain Bridge

Instantiate a `Drain` struct, which acts as a lightweight wrapper exposing a `log` method to forward records into `tui-logger`'s storage without going through the global logger interface.

```rust
use tui_logger::Drain;

let tui_drain = Drain::new();

```

The `Drain` implementation (lines 37–50 in [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs)) simply forwards calls to `TUI_LOGGER.raw_log`, injecting records directly into the hot buffer.

### Configure env_logger with Custom Formatting

Build `env_logger` with a custom formatter that performs your desired formatting and then passes the record to the drain. Finally, call `init()` to install `env_logger` as the global logger.

```rust
use std::io::Write;

env_logger::Builder::default()
    .format(move |buf, record| {
        // Apply env_logger formatting (timestamps, levels, etc.)
        let ts = buf.timestamp();
        writeln!(buf, "[{}] {} - {}", ts, record.level(), record.args())?;
        
        // Forward the record to tui-logger for UI capture
        tui_drain.log(record);
        Ok(())
    })
    .filter_level(LevelFilter::Trace)
    .init(); // Installs env_logger as the global logger

```

After this setup, calls to `log::info!`, `log::warn!`, and other macros will route through `env_logger`, appear in your formatted console output, and simultaneously populate the `tui-logger` buffers for display in your `ratatui` interface.

### Render the Widget

Create and configure the logger widget to display the captured logs. You can use either the standard widget or the smart widget with target selection.

```rust
use tui_logger::TuiLoggerWidget;
use ratatui::widgets::Block;

let logger_widget = TuiLoggerWidget::default()
    .block(Block::default().title("Application Logs"))
    .state(&tui_logger::TuiWidgetState::new());

```

Insert this widget into your terminal layout and call `terminal.draw()` each frame to render the latest logs from the main buffer.

## Alternative: Using tui-logger as the Global Logger

If you do not require `env_logger`'s formatting features, you can install `tui-logger` directly as the global logger. This approach simplifies configuration but requires you to set formatting options via the widget's `LogFormatter` if you want to customize output appearance.

```rust
use log::{info, LevelFilter};
use tui_logger::{init_logger, set_default_level, TuiLoggerWidget};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Register tui-logger as the global logger and start the background thread
    init_logger(LevelFilter::Trace)?;
    set_default_level(LevelFilter::Trace);

    let widget = TuiLoggerWidget::default()
        .block(ratatui::widgets::Block::default().title("Logs"));

    info!("Application started with tui-logger as the global logger");
    Ok(())
}

```

In this mode, `init_logger` calls `log::set_logger` internally (as implemented in [`src/logger/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/inner.rs)), making `tui-logger` the sole recipient of all log records.

## Key Components and Source Locations

Understanding the source layout helps diagnose issues or extend functionality:

- **[`src/logger/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/inner.rs)** – Contains `TuiLoggerInner`, `HotLog`, and `HotSelect` structures implementing the lock‑free circular buffers and the `log::Log` trait for the global logger implementation.
- **[`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs)** – Provides `init_logger`, `set_hot_buffer_depth`, and the `Drain` struct (lines 37–50) that wraps calls to `raw_log`.
- **[`src/widget/standard.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/standard.rs)** – Implements `TuiLoggerWidget` for basic log display.
- **[`src/widget/smart.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/smart.rs)** – Implements `TuiLoggerSmartWidget` with target filtering and selection capabilities.
- **[`examples/demo.rs`](https://github.com/gin66/tui-logger/blob/main/examples/demo.rs)** – Demonstrates both the env_logger integration pattern and standalone usage.
- **[`src/lib.rs`](https://github.com/gin66/tui-logger/blob/main/src/lib.rs)** – Public API entry point re‑exporting all types including `Drain`, `TuiLoggerWidget`, and `LogFormatter`.

## Summary

- **Use `tui_logger::init_logger`** to spawn the background thread that moves records from the hot buffer to the main buffer; when using `env_logger`, this does not register the global logger.
- **Create a `Drain` instance** from [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs) to bridge records from `env_logger` into `tui-logger`’s storage system.
- **Configure `env_logger::Builder`** with a custom format closure that calls `drain.log(record)` to ensure every formatted event appears in both the console and the TUI widget.
- **Maintain standard macro usage** (`info!`, `warn!`, etc.) which dispatch to the globally installed `env_logger` while the drain captures copies for the UI.
- **Reference [`src/logger/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/inner.rs)** for buffer implementations and [`src/widget/standard.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/standard.rs) for rendering logic when customizing log display behavior.

## Frequently Asked Questions

### Can I use tui-logger without installing it as the global logger?

Yes. Call `tui_logger::init_logger` to start the background event mover thread, then create a `Drain` instance and call `drain.log(record)` inside another logger's formatter (such as `env_logger`). This allows `env_logger` to remain the global logger while `tui-logger` captures records for the UI via the drain.

### Where does tui-logger store incoming log records?

Records are first written to a hot circular buffer (`HotLog` in [`src/logger/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/inner.rs)) to minimize latency on the logging thread. A background thread (spawned in [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs)) periodically moves these events to the main buffer, from which `TuiLoggerWidget` and `TuiLoggerSmartWidget` read for rendering.

### Does using env_logger as the formatter affect the standard log macros?

No. The `log!`, `info!`, `warn!`, and other macros continue to function exactly as documented in the `log` crate. They dispatch to whichever logger is globally installed (`env_logger` in this setup), which formats the record before your closure forwards it to the `tui-logger` drain.

### Can I customize how logs appear inside the TUI widget?

Yes. When using the `env_logger` approach, formatting is determined by the closure passed to `env_logger::Builder::format`. If you use `tui-logger` as the standalone global logger instead, you can provide a custom `LogFormatter` implementation to the `TuiLoggerWidget` to control timestamp display, color coding, and field ordering within the UI.