use std::{ collections::HashMap, sync::{Arc, Mutex}, }; use tracing_subscriber::{EnvFilter, reload}; use crate::{Result, error}; /// We need to store a reload::Handle value, but can't name it's type explicitly /// because the S type parameter depends on the subscriber's previous layers. In /// our case, this includes unnameable 'impl Trait' types. /// /// This is fixed[1] in the unreleased tracing-subscriber from the master /// branch, which removes the S parameter. Unfortunately can't use it without /// pulling in a version of tracing that's incompatible with the rest of our /// deps. /// /// To work around this, we define an trait without the S paramter that forwards /// to the reload::Handle::reload method, and then store the handle as a trait /// object. /// /// [1]: pub trait ReloadHandle { fn current(&self) -> Option; fn reload(&self, new_value: L) -> Result<(), reload::Error>; } impl ReloadHandle for reload::Handle { fn current(&self) -> Option { Self::clone_current(self) } fn reload(&self, new_value: L) -> Result<(), reload::Error> { Self::reload(self, new_value) } } #[derive(Clone)] pub struct LogLevelReloadHandles { handles: Arc>, } type HandleMap = HashMap; type Handle = Box + Send + Sync>; impl LogLevelReloadHandles { pub fn add(&self, name: &str, handle: Handle) { self.handles .lock() .expect("locked") .insert(name.into(), handle); } pub fn reload(&self, new_value: &EnvFilter, names: Option<&[&str]>) -> Result<()> { self.handles .lock() .expect("locked") .iter() .filter(|(name, _)| names.is_some_and(|names| names.contains(&name.as_str()))) .for_each(|(_, handle)| { _ = handle.reload(new_value.clone()).or_else(error::else_log); }); Ok(()) } #[must_use] pub fn current(&self, name: &str) -> Option { self.handles .lock() .expect("locked") .get(name) .map(|handle| handle.current())? } } impl Default for LogLevelReloadHandles { fn default() -> Self { Self { handles: Arc::new(HandleMap::new().into()), } } }