reth_tracing/
log_handle.rs1use crate::layers::DEFAULT_ENV_FILTER_DIRECTIVES;
8use tracing::level_filters::LevelFilter;
9use tracing_subscriber::{reload, EnvFilter, Registry};
10
11pub type LogFilterReloadHandle = reload::Handle<EnvFilter, Registry>;
13
14#[derive(Debug)]
16pub struct LogFilterHandle {
17 handles: Vec<(LogFilterReloadHandle, String)>,
19}
20
21impl LogFilterHandle {
22 const fn new() -> Self {
24 Self { handles: Vec::new() }
25 }
26
27 fn push(&mut self, handle: LogFilterReloadHandle) {
29 let initial = handle.with_current(|filter| filter.to_string()).unwrap_or_default();
30 self.handles.push((handle, initial));
31 }
32
33 const fn is_available(&self) -> bool {
35 !self.handles.is_empty()
36 }
37
38 fn reload_all(
41 &self,
42 make_filter: impl Fn(&str) -> Result<EnvFilter, String>,
43 ) -> Result<(), String> {
44 for (handle, initial) in &self.handles {
45 let filter = make_filter(initial)?;
46 handle.reload(filter).map_err(|e| e.to_string())?;
47 }
48 Ok(())
49 }
50}
51
52static LOG_HANDLE: std::sync::Mutex<LogFilterHandle> =
54 std::sync::Mutex::new(LogFilterHandle::new());
55
56pub fn install_log_handle(handle: LogFilterReloadHandle) {
60 LOG_HANDLE.lock().expect("log handle poisoned").push(handle);
61}
62
63pub fn log_handle_available() -> bool {
65 LOG_HANDLE.lock().expect("log handle poisoned").is_available()
66}
67
68pub fn set_log_verbosity(level: usize) -> Result<(), String> {
82 let guard = LOG_HANDLE.lock().expect("log handle poisoned");
83
84 if !guard.is_available() {
85 return Err("Log filter reload not available".to_string());
86 }
87
88 let level_filter = match level {
89 0 => LevelFilter::OFF,
90 1 => LevelFilter::ERROR,
91 2 => LevelFilter::WARN,
92 3 => LevelFilter::INFO,
93 4 => LevelFilter::DEBUG,
94 _ => LevelFilter::TRACE,
95 };
96
97 guard.reload_all(|_| filter_with_defaults(&level_filter.to_string()))
98}
99
100fn filter_with_defaults(pattern: &str) -> Result<EnvFilter, String> {
103 EnvFilter::try_new(format!("{},{pattern}", DEFAULT_ENV_FILTER_DIRECTIVES.join(",")))
104 .map_err(|e| format!("Invalid filter pattern: {e}"))
105}
106
107pub fn set_log_vmodule(pattern: &str) -> Result<(), String> {
121 let guard = LOG_HANDLE.lock().expect("log handle poisoned");
122
123 if !guard.is_available() {
124 return Err("Log filter reload not available".to_string());
125 }
126
127 if pattern.trim().is_empty() {
128 guard.reload_all(|initial| EnvFilter::try_new(initial).map_err(|e| e.to_string()))
130 } else {
131 filter_with_defaults(pattern)?;
133 guard.reload_all(|_| filter_with_defaults(pattern))
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140 use std::sync::{Arc, Mutex};
141 use tracing::{Event, Subscriber};
142 use tracing_subscriber::{
143 layer::{Context, SubscriberExt},
144 Layer,
145 };
146
147 struct Targets(Arc<Mutex<Vec<String>>>);
149
150 impl<S: Subscriber> Layer<S> for Targets {
151 fn on_event(&self, event: &Event<'_>, _: Context<'_, S>) {
152 self.0.lock().unwrap().push(event.metadata().target().to_owned());
153 }
154 }
155
156 fn emit() {
158 tracing::debug!(target: "discv5::unit", "suppressed by default");
159 tracing::warn!(target: "discv5::unit", "suppressed by default");
160 tracing::debug!(target: "reload_test", "kept");
161 }
162
163 fn seen_through(filter: EnvFilter) -> Vec<String> {
164 let seen = Arc::new(Mutex::new(Vec::new()));
165 let subscriber = Registry::default().with(Targets(Arc::clone(&seen)).with_filter(filter));
166 tracing::subscriber::with_default(subscriber, emit);
167 Arc::into_inner(seen).expect("the subscriber is dropped").into_inner().unwrap()
168 }
169
170 #[test]
171 fn a_reloaded_filter_keeps_the_default_directives() {
172 assert_eq!(
173 seen_through(filter_with_defaults(&LevelFilter::DEBUG.to_string()).unwrap()),
174 ["reload_test"]
175 );
176 assert_eq!(
177 seen_through(filter_with_defaults("debug,discv5=debug").unwrap()),
178 ["discv5::unit", "discv5::unit", "reload_test"]
179 );
180 assert!(filter_with_defaults("reload_test=nonsense").is_err());
181 }
182
183 #[test]
184 fn an_empty_pattern_restores_the_initial_filter() {
185 let initial = EnvFilter::try_new("warn,reload_test=debug").unwrap();
186 let (filter, handle) = reload::Layer::new(initial);
187 install_log_handle(handle);
188
189 let seen = Arc::new(Mutex::new(Vec::new()));
190 let subscriber = Registry::default().with(Targets(Arc::clone(&seen)).with_filter(filter));
191 tracing::subscriber::with_default(subscriber, || {
192 set_log_vmodule("error").unwrap();
193 emit();
194 set_log_vmodule("").unwrap();
195 emit();
196 });
197
198 assert_eq!(*seen.lock().unwrap(), ["discv5::unit", "reload_test"]);
201 }
202}