Skip to main content

tuwunel_core/log/
reload.rs

1use std::{
2	collections::HashMap,
3	sync::{Arc, Mutex},
4};
5
6use tracing_subscriber::{EnvFilter, reload};
7
8use crate::{Result, error};
9
10/// We need to store a reload::Handle value, but can't name it's type explicitly
11/// because the S type parameter depends on the subscriber's previous layers. In
12/// our case, this includes unnameable 'impl Trait' types.
13///
14/// This is fixed[1] in the unreleased tracing-subscriber from the master
15/// branch, which removes the S parameter. Unfortunately can't use it without
16/// pulling in a version of tracing that's incompatible with the rest of our
17/// deps.
18///
19/// To work around this, we define an trait without the S parameter that
20/// forwards to the reload::Handle::reload method, and then store the handle as
21/// a trait object.
22///
23/// [1]: <https://github.com/tokio-rs/tracing/pull/1035/commits/8a87ea52425098d3ef8f56d92358c2f6c144a28f>
24pub trait ReloadHandle<L> {
25	fn current(&self) -> Option<L>;
26
27	fn reload(&self, new_value: L) -> Result<(), reload::Error>;
28}
29
30impl<L: Clone, S> ReloadHandle<L> for reload::Handle<L, S> {
31	fn current(&self) -> Option<L> { Self::clone_current(self) }
32
33	fn reload(&self, new_value: L) -> Result<(), reload::Error> { Self::reload(self, new_value) }
34}
35
36#[derive(Clone)]
37pub struct LogLevelReloadHandles {
38	handles: Arc<Mutex<HandleMap>>,
39}
40
41type HandleMap = HashMap<String, Handle>;
42type Handle = Box<dyn ReloadHandle<EnvFilter> + Send + Sync>;
43
44impl LogLevelReloadHandles {
45	pub fn add(&self, name: &str, handle: Handle) {
46		self.handles
47			.lock()
48			.expect("locked")
49			.insert(name.into(), handle);
50	}
51
52	pub fn reload(&self, new_value: &EnvFilter, names: Option<&[&str]>) -> Result {
53		self.handles
54			.lock()
55			.expect("locked")
56			.iter()
57			.filter(|(name, _)| names.is_some_and(|names| names.contains(&name.as_str())))
58			.for_each(|(_, handle)| {
59				_ = handle
60					.reload(new_value.clone())
61					.or_else(error::else_log);
62			});
63
64		Ok(())
65	}
66
67	#[must_use]
68	pub fn current(&self, name: &str) -> Option<EnvFilter> {
69		self.handles
70			.lock()
71			.expect("locked")
72			.get(name)
73			.map(|handle| handle.current())?
74	}
75}
76
77impl Default for LogLevelReloadHandles {
78	fn default() -> Self {
79		Self {
80			handles: Arc::new(HandleMap::new().into()),
81		}
82	}
83}