{"record":{"id":"74a0c96213035c70","repo":"libnyanpasu/clash-nyanpasu","slug":"no-logger-channel","errorCode":null,"errorMessage":"no logger channel","messagePattern":"no logger channel","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/utils/init/logging.rs","lineNumber":40,"sourceCode":"\npub type ReloadSignal = (Option<config::nyanpasu::LoggingLevel>, Option<usize>);\n\nstruct Channel(Option<Sender<ReloadSignal>>);\nimpl Channel {\n    fn globals() -> &'static Mutex<Channel> {\n        static CHANNEL: OnceLock<Mutex<Channel>> = OnceLock::new();\n        CHANNEL.get_or_init(|| Mutex::new(Channel(None)))\n    }\n}\n\npub fn refresh_logger(signal: ReloadSignal) -> Result<()> {\n    let channel = Channel::globals().lock();\n    match &channel.0 {\n        Some(sender) => {\n            let _ = sender.send(signal);\n            Ok(())\n        }\n        None => bail!(\"no logger channel\"),\n    }\n}\n\nfn get_file_appender(max_files: usize) -> Result<(NonBlocking, WorkerGuard)> {\n    let log_dir = dirs::app_logs_dir().unwrap();\n    let file_appender = tracing_appender::rolling::Builder::new()\n        .filename_prefix(\"clash-nyanpasu\")\n        .filename_suffix(\"app.log\")\n        .rotation(Rotation::DAILY)\n        .max_log_files(max_files)\n        .build(log_dir)?;\n    Ok(tracing_appender::non_blocking(file_appender))\n}\n\n/// initial instance global logger\npub fn init() -> Result<()> {\n    let log_dir = dirs::app_logs_dir().unwrap();\n    if !log_dir.exists() {","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/utils/init/logging.rs#L22-L58","documentation":"refresh_logger forwards a reload signal over the global logger channel, but the channel (stored in Channel::globals()) is None because the tracing non-blocking logging pipeline was never initialized. Sending is impossible, so it bails.","triggerScenarios":"Calling refresh_logger (during init or later log reconfiguration) before the logging subsystem initialized its sender, or after the worker guard was dropped and the channel cleared.","commonSituations":"Calling refresh very early in startup before init ran, test harnesses that skip logger initialization, or a shutdown path that already dropped the logger guard while a refresh is still attempted.","solutions":["Ensure init (logging setup) completes and stores the sender before refresh_logger is called.","Make the channel a required step in the startup sequence; guard refresh with an 'initialized' flag.","If refresh during teardown, ignore the missing channel instead of failing.","In tests, initialize the logger or stub the channel."],"exampleFix":"// before\nrefresh_logger(signal)?;\n// after\nif logging::is_initialized() {\n    refresh_logger(signal)?;\n} else {\n    log::warn!(\"logger not initialized; skipping refresh\");\n}","handlingStrategy":"validation","validationCode":"fn logger_ready() -> bool {\n    Channel::globals().lock().0.is_some()\n}\n// call only when ready\nif logger_ready() { refresh_logger(signal)?; }","typeGuard":null,"tryCatchPattern":"match refresh_logger(signal) {\n    Ok(()) => {},\n    Err(e) if e.to_string() == \"no logger channel\" => {\n        log::warn!(\"logger not initialized; skipping refresh\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Initialize logging first in the startup sequence, before any refresh call","Track initialization with a flag/OnceCell and skip refresh when unset","Ignore missing-channel errors during teardown/shutdown paths"],"tags":["logging","tracing","channel","initialization"],"backgroundTag":"missing-config-value","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}