# How to Programmatically Configure Different Log Levels for Different Targets in tui-logger

> Learn to programmatically configure different log levels for specific targets in tui-logger. Control log capture and UI display with set_level_for_target for efficient debugging.

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

---

**Use `set_level_for_target` to update the global logger state and filtering behavior, or call `TuiWidgetState::set_level_for_target` to adjust display levels in the UI without affecting the underlying log capture.**

The `tui-logger` crate enables runtime adjustment of per-target verbosity through a dual-layer configuration system. To programmatically configure different log levels for different targets, you interact with the **LevelConfig** table—a `HashMap<String, LevelFilter>`—that exists in both the global logger state and widget-local state. This guide examines the implementation in `gin66/tui-logger` to demonstrate how to manipulate these configurations using the public APIs exposed in [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs) and [`src/widget/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/inner.rs).

## The LevelConfig Architecture

At the core of per-target filtering lies the **LevelConfig** structure, which wraps a `HashMap<String, LevelFilter>`. This mapping associates Rust module paths (targets) with their respective verbosity thresholds.

The crate maintains two independent copies of this table:

- **Global logger state** – Stored in `TUI_LOGGER.inner.lock().targets`, this instance controls which log events are actually captured and stored by the logger.
- **Widget-local state** – Held in `TuiWidgetInnerState::config` and exposed through `TuiWidgetState`, this copy influences how events are displayed in the terminal interface.

When the widget renders, it merges the global configuration into its local copy, allowing the UI to reflect the actual logger state while permitting display-only overrides.

## Global vs. Widget-Local Configuration

Understanding the distinction between these two configuration layers is critical for effective log management.

### Global Logger Configuration

Changes to the global **LevelConfig** affect both event capture and UI display. When you modify the global state via `set_level_for_target`, the logger updates its internal hot-select hash table (`hot_select.hashtable`) to ensure subsequent `log!` calls are filtered efficiently according to the new level. This operation also increments a generation counter in `LevelConfig::set` (defined in [`src/config/level_config.rs`](https://github.com/gin66/tui-logger/blob/main/src/config/level_config.rs)), signaling widgets to refresh their cached configuration on the next render.

### Widget-Only Overrides

Widget-local configuration changes impact only the visual representation. This enables scenarios where you hide noisy targets from the display (setting them to `LevelFilter::Off`) while the logger continues recording them in the global buffer. The UI rendering logic in [`src/widget/target.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/target.rs) merges global levels with local overrides by calling `targets.merge(hot_targets)`, prioritizing the widget's private configuration for display purposes.

## API Implementation and Usage

The crate exposes two primary methods for manipulating these configurations, each targeting a specific layer of the system.

### set_level_for_target (Global)

Defined in [[`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs)](https://github.com/gin66/tui-logger/blob/master/src/logger/api.rs#L24-L31), this function writes the level into `TUI_LOGGER.inner.targets` and synchronizes the hot-select hash table used by the logger's fast path:

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

// Set the global level for a specific module path
set_level_for_target("my::crate::network", LevelFilter::Debug);

```

This call immediately affects what events are captured. The implementation in `LevelConfig::set` inserts the entry and bumps the generation counter, ensuring that any widget holding a copy of the configuration can detect the change.

### TuiWidgetState::set_level_for_target (Local)

Located in [[`src/widget/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/inner.rs)](https://github.com/gin66/tui-logger/blob/master/src/widget/inner.rs#L33-L36), this method updates only the widget's private `LevelConfig`:

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

let widget_state = TuiWidgetState::new()
    .set_level_for_target("my::crate::network", LevelFilter::Off);

```

Pass this state to your widget constructor to apply display-only filters. The target remains logged globally but disappears from the UI view.

## Practical Configuration Examples

### Configuring Multiple Targets Globally

Iterate over your target-level pairs to establish comprehensive filtering rules that affect both storage and display:

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

fn configure_logging() {
    let targets = [
        ("net::client", LevelFilter::Debug),
        ("db::queries", LevelFilter::Error),
        ("ui::render", LevelFilter::Trace),
    ];
    
    for (target, level) in targets {
        set_level_for_target(target, level);
    }
}

```

### Initializing with Default Levels

When creating a widget, you can set a baseline display level and override specific targets:

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

let state = TuiWidgetState::new()
    .set_default_display_level(LevelFilter::Info)
    .set_level_for_target("audio::decoder", LevelFilter::Off);

```

This configuration shows only `Info` and higher for most targets while completely hiding the audio decoder output from the terminal view.

### Complete Setup Example

```rust
use log::LevelFilter;
use tui_logger::{set_level_for_target, TuiWidgetState};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Initialize with maximum possible level
    tui_logger::init_logger(LevelFilter::Trace)?;
    
    // Configure global filtering: record Info and above for this module
    set_level_for_target("my::crate::module", LevelFilter::Info);
    
    // Configure widget to display Warn and above for the same module
    // (demonstrating the disconnect between global and UI levels)
    let widget_state = TuiWidgetState::new()
        .set_level_for_target("my::crate::module", LevelFilter::Warn);
    
    // Use widget_state when building your TuiLoggerTargetWidget...
    
    Ok(())
}

```

## Summary

- **LevelConfig** stores per-target mappings as a `HashMap<String, LevelFilter>` in both global and widget-local contexts.
- **Global changes** via `set_level_for_target` in [`src/logger/api.rs`](https://github.com/gin66/tui-logger/blob/main/src/logger/api.rs) affect event capture and update the `hot_select.hashtable` for fast filtering.
- **Widget changes** via `TuiWidgetState::set_level_for_target` in [`src/widget/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/inner.rs) provide display-only overrides that hide targets without stopping logging.
- The **generation counter** in `LevelConfig::set` enables widgets to detect configuration changes and merge updates on the next render cycle.
- Use global APIs to control storage and memory usage; use widget APIs to customize the visual noise level in the terminal.

## Frequently Asked Questions

### What is the difference between global and widget-local log levels?

Global log levels, set via `set_level_for_target`, determine which events the logger actually captures and stores in the ring buffer located in `TUI_LOGGER.inner.targets`. Widget-local levels, set via `TuiWidgetState::set_level_for_target`, only affect which events appear in the terminal UI. A target can be logged at `Trace` level globally but displayed at `Error` level (or hidden entirely) in a specific widget instance.

### How do I hide a target in the UI without affecting the actual logging?

Use the widget-state API with `LevelFilter::Off`. Call `TuiWidgetState::new().set_level_for_target("noisy::module", LevelFilter::Off)` and pass that state to your widget constructor. The module continues logging to the global buffer (subject to its global level), but the widget filters it out during rendering.

### Where does tui-logger store the per-target configuration internally?

The configuration resides in the **LevelConfig** struct defined in [`src/config/level_config.rs`](https://github.com/gin66/tui-logger/blob/main/src/config/level_config.rs). The global instance lives in `TUI_LOGGER.inner.targets`, protected by a mutex. Each `TuiWidgetState` maintains its own `LevelConfig` in the `config` field of `TuiWidgetInnerState`, defined in [`src/widget/inner.rs`](https://github.com/gin66/tui-logger/blob/main/src/widget/inner.rs).

### Does changing a log level require reinitializing the logger?

No. Both `set_level_for_target` and `TuiWidgetState::set_level_for_target` apply changes immediately at runtime. The global API updates the `hot_select.hashtable` directly in the logger's fast path, ensuring that subsequent log calls respect the new level without requiring a restart or reinitialization of the `tui_logger` system.