How to Initialize tui-logger with Crossterm or Termion: A Complete Guide

Enable exactly one of the crossterm or termion Cargo features in your Cargo.toml, then call init_terminal() to create a ratatui::Terminal bound to your selected backend.

The tui-logger crate provides a ratatui-compatible logging widget for Rust terminal applications. While the logger itself is backend-agnostic, the demo and helper utilities abstract over two popular terminal drivers—crossterm and termion—using conditional compilation to ensure your application compiles with exactly one active backend.

Select Your Terminal Backend via Cargo Features

tui-logger uses optional Cargo features to determine which terminal backend to compile. You must enable exactly one feature; enabling both or neither triggers a compile-time error.

Add the dependency to your Cargo.toml with your chosen backend:

[dependencies]

# Option 1: Crossterm (recommended for cross-platform support)

tui-logger = { version = "0.18", features = ["crossterm"] }

# Option 2: Termion (Unix-like systems with raw stdin)

tui-logger = { version = "0.18", features = ["termion"] }

The crate enforces this constraint through compile guards in examples/demo.rs (lines 8-17). If you forget to select a backend, the compiler emits: "One of the features 'crossterm' or 'termion' must be enabled."

Initialize the Logger and Terminal

Regardless of which backend you choose, initialization follows the same three-step pattern using functions re-exported from the backend modules:

Backend-Independent Logger Setup

First, initialize the log-compatible hot logger to capture log macros. This step is identical for both backends.

use tui_logger::{init_logger, LevelFilter};

fn main() -> anyhow::Result<()> {
    // Capture log macros at Trace level
    init_logger(LevelFilter::Trace)?;
    // ...
}

Terminal Initialization

Next, create the terminal instance. The init_terminal() function resolves to the correct backend implementation based on your Cargo feature flag.

use tui_logger::{init_terminal, restore_terminal};

let mut terminal = init_terminal()?;  // Returns Terminal<impl Backend>
terminal.clear()?;
terminal.hide_cursor()?;

Terminal Cleanup

Before your application exits, restore the terminal to its original state and clear residual UI artifacts.

// ... run your TUI code ...

restore_terminal()?;  // Disables raw mode, leaves alternate screen
terminal.clear()?;

Implementation Examples by Backend

The concrete implementations live in examples/demo.rs within the crossterm_backend module (lines 53-71) and termion_backend module (lines 88-106). Both expose identical public APIs so your application remains agnostic to the underlying driver.

Crossterm Example

Crossterm is the default recommendation for cross-platform support (Windows, macOS, Linux).

[dependencies]
tui-logger = { version = "0.18", features = ["crossterm"] }
use log::LevelFilter;
use tui_logger::{init_logger, init_terminal, restore_terminal};

fn main() -> anyhow::Result<()> {
    init_logger(LevelFilter::Trace)?;
    
    let mut terminal = init_terminal()?;  // Crossterm-specific initialization
    terminal.clear()?;
    terminal.hide_cursor()?;

    // Your TUI application logic here
    // App::new().start(&mut terminal)?;

    restore_terminal()?;  // Crossterm cleanup: disable raw mode, restore screen
    terminal.clear()?;
    Ok(())
}

Termion Example

Termion is suitable for Unix-like systems where you need direct control over raw stdin.

[dependencies]
tui-logger = { version = "0.18", features = ["termion"] }
use log::LevelFilter;
use tui_logger::{init_logger, init_terminal, restore_terminal};

fn main() -> anyhow::Result<()> {
    init_logger(LevelFilter::Trace)?;
    
    let mut terminal = init_terminal()?;  // Termion-specific initialization
    terminal.clear()?;
    terminal.hide_cursor()?;

    // Your TUI application logic here
    // App::new().start(&mut terminal)?;

    restore_terminal()?;  // Termion cleanup (currently a no-op in the demo)
    terminal.clear()?;
    Ok(())
}

How the Backend Abstraction Works

The crate uses #[cfg(feature = "...")] attributes to conditionally expose the correct module. In examples/demo.rs, the code imports from either crossterm_backend or termion_backend based on which feature is active:

#[cfg(all(feature = "crossterm", not(feature = "termion")))]
use self::crossterm_backend::*;

#[cfg(all(feature = "termion", not(feature = "crossterm")))]
use self::termion_backend::*;

Both modules implement init_terminal() and restore_terminal() with backend-specific logic (such as entering raw mode and alternate screen buffers), allowing the rest of your code to call these functions without knowing which driver is active underneath.

Summary

  • Enable one feature: Add either features = ["crossterm"] or features = ["termion"] to your tui-logger dependency, never both.
  • Three initialization steps: Call init_logger() for logging setup, init_terminal() for the TUI backend, and restore_terminal() on exit.
  • Source locations: Backend-specific code resides in examples/demo.rs inside mod crossterm_backend (lines 53-71) and mod termion_backend (lines 88-106).
  • Compile-time safety: The crate uses compile_error! macros in examples/demo.rs to guarantee exactly one backend is selected at build time.

Frequently Asked Questions

Can I use tui-logger without enabling either crossterm or termion?

No. According to the source code in examples/demo.rs, the crate requires exactly one backend feature to be enabled. If you enable neither, the compiler stops with the error: "One of the features 'crossterm' or 'termion' must be enabled."

How do I switch from crossterm to termion in an existing project?

Change your Cargo.toml dependency from features = ["crossterm"] to features = ["termion"] (or vice versa). No changes to your Rust source code are required because init_terminal() and restore_terminal() share the same signature in both backend modules.

What is the difference between init_logger and init_terminal?

init_logger initializes the internal logging system that captures log crate macros and is completely backend-independent. init_terminal creates the actual ratatui::Terminal instance bound to your chosen crossterm or termion driver.

Where does restore_terminal come from?

restore_terminal is re-exported from the active backend module (either crossterm_backend or termion_backend in examples/demo.rs). It handles cleanup specific to that backend, such as disabling raw mode and leaving the alternate screen buffer.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →