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

> Learn how to initialize tui-logger with Crossterm or Termion backends. This guide shows you how to select your terminal and set up tui-logger for your application.

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

---

**Enable exactly one of the `crossterm` or `termion` Cargo features in your [`Cargo.toml`](https://github.com/gin66/tui-logger/blob/main/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`](https://github.com/gin66/tui-logger/blob/main/Cargo.toml) with your chosen backend:

```toml
[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`](https://github.com/gin66/tui-logger/blob/main/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.

```rust
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.

```rust
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.

```rust
// ... 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`](https://github.com/gin66/tui-logger/blob/main/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).

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

```

```rust
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.

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

```

```rust
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`](https://github.com/gin66/tui-logger/blob/main/examples/demo.rs), the code imports from either `crossterm_backend` or `termion_backend` based on which feature is active:

```rust
#[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`](https://github.com/gin66/tui-logger/blob/main/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`](https://github.com/gin66/tui-logger/blob/main/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`](https://github.com/gin66/tui-logger/blob/main/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`](https://github.com/gin66/tui-logger/blob/main/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`](https://github.com/gin66/tui-logger/blob/main/examples/demo.rs)). It handles cleanup specific to that backend, such as disabling raw mode and leaving the alternate screen buffer.