0x192/universal-android-debloater · error

setup logging

Error message

setup logging

What it means

This panic comes from `.expect("setup logging")` on the `Result<(), fern::InitError>` returned by `setup_logger()`. The fern crate returns `InitError` (typically `SetLoggerError`) when `fern::Dispatch::apply()` finds that a global logger has already been installed — the `log` crate only allows one logger per process. `setup_uad_dir` in the SOURCE could also fail indirectly: if the config/cache directory can't be resolved or created, `static` initialization panics before `setup_logger` even runs.

Source

Thrown at src/main.rs:25

    colors::{Color, ColoredLevelConfig},
    FormatCallback,
};
use log::Record;
use static_init::dynamic;
use std::path::PathBuf;
use std::{fmt::Arguments, fs::OpenOptions};

mod core;
mod gui;

#[dynamic]
static CONFIG_DIR: PathBuf = setup_uad_dir(dirs::config_dir());

#[dynamic]
static CACHE_DIR: PathBuf = setup_uad_dir(dirs::cache_dir());

fn main() -> iced::Result {
    setup_logger().expect("setup logging");
    gui::UadGui::start()
}

pub fn setup_logger() -> Result<(), fern::InitError> {
    let colors = ColoredLevelConfig::new().info(Color::Green);

    let make_formatter = |use_colors: bool| {
        move |out: FormatCallback, message: &Arguments, record: &Record| {
            out.finish(format_args!(
                "{} {} [{}:{}] {}",
                chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                if use_colors {
                    format!("{:5}", colors.color(record.level()))
                } else {
                    format!("{:5}", record.level().to_string())
                },
                record.file().unwrap_or("?"),
                record.line().map(|l| l.to_string()).unwrap_or_default(),

View on GitHub (pinned to 11f27c671c)

Solutions

  1. Ensure `setup_logger()` is called exactly once per process, early in `main` before any other crate initializes a logger
  2. Guard the call: check `log::max_level()` is `Off` / use a `OnceLock`/`std::sync::Once` so repeat invocations are no-ops
  3. Check no dependency or module calls `log::set_logger` / installs env_logger or a tracing-log bridge before this point
  4. In tests, don't call `setup_logger()` — or wrap it so it tolerates `SetLoggerError` and continues
  5. If the failure is the static `CONFIG_DIR` init (dirs returning None), fix XDG_CONFIG_HOME/XDG_CACHE_HOME or HOME env vars and directory permissions so `dirs::config_dir()/cache_dir()` return Some

Example fix

// before
fn main() -> iced::Result {
    setup_logger().expect("setup logging");
    gui::UadGui::start()
}
// after
fn main() -> iced::Result {
    static INIT: std::sync::Once = std::sync::Once::new();
    INIT.call_once(|| {
        if let Err(e) = setup_logger() {
            eprintln!("logging init failed: {e}"); // continue without logging
        }
    });
    gui::UadGui::start()
}
Defensive patterns

Strategy: fallback

Validate before calling

fn can_init_logger() -> bool {
    // log crate: a logger is only settable once; probe cheap state first
    std::env::var("NO_LOG").is_err()
}
if can_init_logger() { let _ = setup_logger(); }

Type guard

fn logger_initialized() -> bool {
    use log::LevelFilter;
    log::max_level() != LevelFilter::Off
}

Try / catch

match setup_logger() {
    Ok(()) => {},
    Err(fern::InitError::SetLoggerError(_)) => { /* logger already set; proceed */ },
    Err(e) => eprintln!("failed to init logging: {e}"),
}

Prevention

When it happens

Trigger: Calling `setup_logger()` twice (e.g. once in main, once in a library/test init, or when the app is relaunched inside the same process); another logger (env_logger, tracing's log bridge, test harness) already registered a logger via `log::set_logger` before this call; in tests, Rust's libtest installs its own logger, making `apply()` fail.

Common situations: Running the GUI binary under a test harness or embedding UAD as a library where another crate already initialized logging; duplicate logger setup added accidentally in a refactor; calling `main`'s setup path again during in-process restarts.

Related errors


AI-assisted analysis of 0x192/universal-android-debloater@11f27c671c (2026-09-02). Data as JSON: /api/errors/7ab01f0ad3a2b7de. Report an issue: GitHub.