{"record":{"id":"7ab01f0ad3a2b7de","repo":"0x192/universal-android-debloater","slug":"setup-logging","errorCode":null,"errorMessage":"setup logging","messagePattern":"setup logging","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/main.rs","lineNumber":25,"sourceCode":"    colors::{Color, ColoredLevelConfig},\n    FormatCallback,\n};\nuse log::Record;\nuse static_init::dynamic;\nuse std::path::PathBuf;\nuse std::{fmt::Arguments, fs::OpenOptions};\n\nmod core;\nmod gui;\n\n#[dynamic]\nstatic CONFIG_DIR: PathBuf = setup_uad_dir(dirs::config_dir());\n\n#[dynamic]\nstatic CACHE_DIR: PathBuf = setup_uad_dir(dirs::cache_dir());\n\nfn main() -> iced::Result {\n    setup_logger().expect(\"setup logging\");\n    gui::UadGui::start()\n}\n\npub fn setup_logger() -> Result<(), fern::InitError> {\n    let colors = ColoredLevelConfig::new().info(Color::Green);\n\n    let make_formatter = |use_colors: bool| {\n        move |out: FormatCallback, message: &Arguments, record: &Record| {\n            out.finish(format_args!(\n                \"{} {} [{}:{}] {}\",\n                chrono::Local::now().format(\"%Y-%m-%d %H:%M:%S\"),\n                if use_colors {\n                    format!(\"{:5}\", colors.color(record.level()))\n                } else {\n                    format!(\"{:5}\", record.level().to_string())\n                },\n                record.file().unwrap_or(\"?\"),\n                record.line().map(|l| l.to_string()).unwrap_or_default(),","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/0x192/universal-android-debloater/blob/11f27c671cba278d71296cdef4c5a5dba06add5e/src/main.rs#L7-L43","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure `setup_logger()` is called exactly once per process, early in `main` before any other crate initializes a logger","Guard the call: check `log::max_level()` is `Off` / use a `OnceLock`/`std::sync::Once` so repeat invocations are no-ops","Check no dependency or module calls `log::set_logger` / installs env_logger or a tracing-log bridge before this point","In tests, don't call `setup_logger()` — or wrap it so it tolerates `SetLoggerError` and continues","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"],"exampleFix":"// before\nfn main() -> iced::Result {\n    setup_logger().expect(\"setup logging\");\n    gui::UadGui::start()\n}\n// after\nfn main() -> iced::Result {\n    static INIT: std::sync::Once = std::sync::Once::new();\n    INIT.call_once(|| {\n        if let Err(e) = setup_logger() {\n            eprintln!(\"logging init failed: {e}\"); // continue without logging\n        }\n    });\n    gui::UadGui::start()\n}","handlingStrategy":"fallback","validationCode":"fn can_init_logger() -> bool {\n    // log crate: a logger is only settable once; probe cheap state first\n    std::env::var(\"NO_LOG\").is_err()\n}\nif can_init_logger() { let _ = setup_logger(); }","typeGuard":"fn logger_initialized() -> bool {\n    use log::LevelFilter;\n    log::max_level() != LevelFilter::Off\n}","tryCatchPattern":"match setup_logger() {\n    Ok(()) => {},\n    Err(fern::InitError::SetLoggerError(_)) => { /* logger already set; proceed */ },\n    Err(e) => eprintln!(\"failed to init logging: {e}\"),\n}","preventionTips":["Call logger setup only once, at the top of `main`, guarded by `std::sync::Once` or `OnceLock`","Prefer `if let Err(e) = setup_logger()` or a `.unwrap_or_else` with eprintln over `.expect(...)` so a duplicate-logger error never aborts the GUI","Don't install loggers in library code or unit tests; keep them in the binary entry point","Keep `setup_uad_dir` for statics free of `.expect()`-heavy work, and validate XDG/HOME env vars in deployment scripts"],"tags":["rust","logging","fern","panic","duplicate-logger"],"backgroundTag":"global-logger-already-set","analyzedSha":"11f27c671cba278d71296cdef4c5a5dba06add5e","analyzedAt":"2026-09-02T16:12:43.433Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T21:17:11.164Z"}