1#![cfg_attr(docsrs, feature(doc_cfg))]
85#![warn(clippy::pedantic)]
86#![warn(
87 clippy::allow_attributes,
88 clippy::allow_attributes_without_reason,
89 clippy::as_conversions,
90 clippy::expect_used,
91 clippy::future_not_send,
92 clippy::indexing_slicing,
93 clippy::panic,
94 clippy::panic_in_result_fn,
95 clippy::string_slice,
96 clippy::todo,
97 clippy::unreachable,
98 clippy::unwrap_used
99)]
100#![expect(
101 clippy::missing_panics_doc,
102 clippy::missing_errors_doc,
103 reason = "These need some more cleanup and documenting."
104)]
105
106use std::{
107 cell::Cell,
108 collections::HashMap,
109 marker::PhantomData,
110 num::NonZeroUsize,
111 pin::Pin,
112 sync::{Arc, Mutex, OnceLock, RwLock, Weak, atomic::AtomicBool},
113};
114
115use subscriber_cache::SubscriberCache;
116use tracing::{Subscriber, dispatcher::WeakDispatch};
117use tracing_core::{callsite, span};
118
119macro_rules! try_lock {
120 ($lock:expr) => {
121 try_lock!($lock, else return)
122 };
123 ($lock:expr, else $els:expr) => {
124 if let ::core::result::Result::Ok(l) = $lock {
125 l
126 } else if std::thread::panicking() {
127 $els
128 } else {
129 panic!("lock poisoned")
130 }
131 };
132}
133
134mod error;
135mod layer;
136mod subscriber;
137mod subscriber_cache;
138
139pub use error::Error;
140pub use subscriber::ReloadSubscriber;
141use tracing_subscriber::{
142 filter::FilterId,
143 layer::{Context, SubscriberExt},
144 registry::LookupSpan,
145};
146
147const MAX_FILTER_COUNT: NonZeroUsize = NonZeroUsize::new(16).unwrap();
148
149#[derive(Debug, Clone, Copy)]
151pub enum ReloadRouting {
152 NewSpanToLatestLayer,
154
155 NewSpanToParentLayer,
157}
158
159#[derive(Debug)]
166pub struct Layer<L, S> {
167 inner: Arc<Shared<L>>,
168 span_routing: ReloadRouting,
169 subscriber_cache: Mutex<SubscriberCache>,
170 _s: PhantomData<fn(S)>,
171}
172
173#[derive(Debug)]
175pub struct Handle<L, S> {
176 inner: Weak<Shared<L>>,
177 _s: PhantomData<fn(S)>,
178}
179
180#[derive(Debug)]
182struct Shared<L> {
183 dispatch: OnceLock<WeakDispatch>,
184 dispatch_leaked: AtomicBool,
185 layers: RwLock<Vec<Pin<Box<L>>>>,
186 span_map: RwLock<HashMap<span::Id, usize>>,
187 filters: Mutex<ReloadableFilters>,
188}
189
190#[derive(Debug, Clone)]
191struct ReloadableFilters {
192 used: Vec<FilterId>,
193 free: Vec<FilterId>,
194 backup: FilterId,
195}
196impl ReloadableFilters {
197 fn new() -> Self {
198 let backup = tracing_subscriber::registry().register_filter();
200 Self {
201 used: Vec::new(),
202 free: Vec::new(),
203 backup,
204 }
205 }
206}
207
208impl<L, S> Layer<L, S> {
211 pub fn new(inner: L) -> (Self, Handle<L, S>) {
214 Self::new_with_routing(inner, ReloadRouting::NewSpanToLatestLayer)
215 }
216
217 pub fn new_with_routing(inner: L, span_routing: ReloadRouting) -> (Self, Handle<L, S>) {
220 let this = Self {
221 inner: Arc::new(Shared {
222 dispatch: OnceLock::new(),
223 dispatch_leaked: AtomicBool::new(false),
224 layers: RwLock::new(vec![Box::pin(inner)]),
225 span_map: RwLock::new(HashMap::new()),
226 filters: Mutex::new(ReloadableFilters::new()),
227 }),
228 span_routing,
229 subscriber_cache: Mutex::new(SubscriberCache::default()),
230 _s: PhantomData,
231 };
232 let handle = this.handle();
233 (this, handle)
234 }
235
236 pub fn handle(&self) -> Handle<L, S> {
238 Handle {
239 inner: Arc::downgrade(&self.inner),
240 _s: PhantomData,
241 }
242 }
243
244 fn with_span<T>(&self, span: &span::Id, mapper: impl FnOnce(&L) -> T) -> Option<T> {
245 let index = *try_lock!(self.inner.span_map.read(), else return None).get(span)?;
246 let layers = try_lock!(self.inner.layers.read(), else return None);
247 let layer = layers.get(index)?;
248 Some(mapper(layer))
249 }
250
251 fn with_ctx<F, T>(&self, f: F) -> Option<T>
252 where
253 S: Subscriber,
254 F: FnOnce(Context<'_, subscriber::ReloadSubscriber<S>>) -> T,
255 T: 'static,
256 {
257 type DynFnOnce<'a, S, T> =
258 dyn FnOnce(Context<'_, subscriber::ReloadSubscriber<S>>) -> T + 'a;
259 struct ContextStealer<S: 'static, T>(
260 Cell<Option<Box<DynFnOnce<'static, S, T>>>>,
261 Cell<Option<T>>,
262 );
263
264 impl<S: Subscriber, T> tracing_subscriber::Layer<subscriber::ReloadSubscriber<S>>
265 for ContextStealer<S, T>
266 where
267 T: 'static,
268 {
269 fn on_follows_from(
272 &self,
273 _span: &span::Id,
274 _follows: &span::Id,
275 ctx: Context<'_, subscriber::ReloadSubscriber<S>>,
276 ) {
277 #[expect(
278 clippy::unwrap_used,
279 reason = "We set `self.0` before calling this in this function."
280 )]
281 self.1.set(Some(self.0.replace(None).unwrap()(ctx)));
282 }
283 }
284
285 self.inner.with_subscriber(|subscriber| {
286 let inner_subscriber = unsafe { std::mem::transmute::<&'_ S, &'static S>(subscriber) };
290 let subscriber = subscriber::ReloadSubscriber::new(
291 inner_subscriber,
292 try_lock!(self.inner.filters.lock(), else return None).clone(),
293 );
294
295 let boxed: Box<DynFnOnce<'_, S, T>> = Box::new(f);
297
298 let transmuted = unsafe {
304 std::mem::transmute::<Box<DynFnOnce<'_, S, T>>, Box<DynFnOnce<'static, S, T>>>(
305 boxed,
306 )
307 };
308
309 let layered = subscriber.with(ContextStealer::<S, T>(
310 Cell::new(Some(transmuted)),
311 Cell::new(None),
312 ));
313 layered
317 .record_follows_from(&span::Id::from_u64(u64::MAX), &span::Id::from_u64(u64::MAX));
318
319 #[expect(
320 clippy::unwrap_used,
321 reason = "We registered tis layer and its second field is set unconditionally in \
322 the `on_follows_from` implementation."
323 )]
324 let result = layered
325 .downcast_ref::<ContextStealer<S, T>>()
326 .unwrap()
327 .1
328 .replace(None)
329 .unwrap();
330
331 drop(layered);
334
335 Some(result)
336 })?
337 }
338}
339
340impl<L, S> Handle<L, S> {
343 pub fn reload_with(&self, f: impl FnOnce(&L) -> L) -> Result<(), Error>
348 where
349 L: tracing_subscriber::Layer<ReloadSubscriber<S>>,
350 S: Subscriber,
351 {
352 let inner = self.inner.upgrade().ok_or_else(Error::subscriber_gone)?;
353
354 inner
355 .with_subscriber(|subscriber| {
356 let inner_subscriber =
360 unsafe { std::mem::transmute::<&'_ S, &'static S>(subscriber) };
361
362 let mut layers =
363 try_lock!(inner.layers.write(), else return Err(Error::poisoned()));
364 #[expect(clippy::unwrap_used, reason = "There is always at least one layer.")]
365 let mut next = f(layers.last().unwrap());
366
367 let mut filters =
368 try_lock!(inner.filters.lock(), else return Err(Error::poisoned()));
369
370 let mut subscriber = ReloadSubscriber::new(inner_subscriber, filters.clone());
371
372 next.on_layer(&mut subscriber);
373 next.on_register_dispatch(
374 &inner
375 .dispatch
376 .get()
377 .ok_or(Error::subscriber_not_initialized())?
378 .upgrade()
379 .ok_or(Error::subscriber_gone())?,
380 );
381
382 *filters = subscriber.into_filters();
383
384 layers.push(Box::pin(next));
385 drop(layers);
386
387 callsite::rebuild_interest_cache();
388
389 #[cfg(feature = "tracing-log")]
390 tracing_log::log::set_max_level(tracing_log::AsLog::as_log(
391 &tracing_subscriber::filter::LevelFilter::current(),
392 ));
393
394 Ok(())
395 })
396 .ok_or(Error::subscriber_not_initialized())?
397 }
398
399 pub fn reload(&self, layer: impl Into<L>) -> Result<(), Error>
403 where
404 L: tracing_subscriber::Layer<ReloadSubscriber<S>>,
405 S: Subscriber,
406 {
407 self.reload_with(|_| layer.into())
408 }
409
410 pub fn with_current<T>(&self, f: impl FnOnce(&L) -> T) -> Result<T, Error> {
413 let inner = self.inner.upgrade().ok_or_else(Error::subscriber_gone)?;
414 let layers = try_lock!(inner.layers.read(), else return Err(Error::poisoned()));
415 #[expect(clippy::unwrap_used, reason = "There is always at least one layer.")]
416 Ok(f(layers.last().unwrap()))
417 }
418}
419
420impl<L, S> Clone for Handle<L, S> {
421 fn clone(&self) -> Self {
422 Handle {
423 inner: self.inner.clone(),
424 _s: PhantomData,
425 }
426 }
427}
428
429impl<L> Shared<L> {
430 fn with_subscriber<S, T, F>(&self, fun: F) -> Option<T>
431 where
432 F: FnOnce(&S) -> T,
433 S: 'static,
434 {
435 let dispatch = self.dispatch.get()?.upgrade()?;
436
437 let subscriber = dispatch.downcast_ref::<S>()?;
438 let result = fun(subscriber);
439
440 Some(result)
441 }
442}
443
444#[cfg(test)]
445mod tests {
446 use tracing::Level;
447 use tracing_mock::{expect, layer as mock_layer};
448 use tracing_subscriber::{filter::LevelFilter, registry::Registry};
449
450 use super::*;
451
452 #[test]
453 fn modify_while_subscriber_active() {
454 fn emit_event(level: tracing::Level, id: u8) {
455 match level {
456 tracing::Level::ERROR => tracing::error!("event {}", id),
457 tracing::Level::WARN => tracing::warn!("event {}", id),
458 tracing::Level::INFO => tracing::info!("event {}", id),
459 tracing::Level::DEBUG => tracing::debug!("event {}", id),
460 tracing::Level::TRACE => tracing::trace!("event {}", id),
461 }
462 }
463
464 let (mock, handle) = mock_layer::mock()
465 .event(
466 expect::event()
467 .at_level(Level::ERROR)
468 .with_fields(expect::msg("event 0")),
469 )
470 .event(
471 expect::event()
472 .at_level(Level::WARN)
473 .with_fields(expect::msg("event 1")),
474 )
475 .event(
476 expect::event()
477 .at_level(Level::ERROR)
478 .with_fields(expect::msg("event 3")),
479 )
480 .only()
481 .run_with_handle();
482
483 let (reload, reload_handle) = Layer::new(LevelFilter::TRACE);
484 let subscriber = Registry::default().with(reload).with(mock);
485
486 tracing::subscriber::with_default(subscriber, || {
487 emit_event(tracing::Level::ERROR, 0);
488 emit_event(tracing::Level::WARN, 1);
489
490 reload_handle.reload_with(|_| LevelFilter::ERROR).unwrap();
491
492 emit_event(tracing::Level::INFO, 2);
493 emit_event(tracing::Level::ERROR, 3);
494 });
495
496 handle.assert_finished();
497 }
498
499 #[test]
500 fn with_current_borrows() {
501 let (reload, handle) = Layer::new(LevelFilter::INFO);
502 let subscriber = Registry::default().with(reload);
503 tracing::subscriber::with_default(subscriber, || {
504 let val = handle
505 .with_current(|layer| layer.into_level().unwrap())
506 .unwrap();
507 assert_eq!(val, Level::INFO);
508
509 handle.reload(LevelFilter::ERROR).unwrap();
510
511 let val = handle
512 .with_current(|layer| layer.into_level().unwrap())
513 .unwrap();
514 assert_eq!(val, Level::ERROR);
515 });
516 }
517
518 #[test]
519 fn handle_errors_after_subscriber_dropped() {
520 let (reload, handle) = Layer::new(LevelFilter::OFF);
521 let subscriber = Registry::default().with(reload);
522 tracing::subscriber::with_default(subscriber, || {});
523
524 assert!(handle.with_current(|_| ()).is_err());
525 assert!(handle.reload_with(|_current| LevelFilter::OFF).is_err());
526 }
527
528 #[test]
529 fn downcast_survives_reload_with_snapshot_semantics() {
530 let (layer, handle) = Layer::new(LevelFilter::TRACE);
531
532 let dispatch = tracing::Dispatch::new(tracing_subscriber::registry());
534 <Layer<LevelFilter, _> as tracing_subscriber::Layer<Registry>>::on_register_dispatch(
535 &layer, &dispatch,
536 );
537
538 let before = unsafe {
539 <Layer<LevelFilter, _> as tracing_subscriber::Layer<Registry>>::downcast_raw(
540 &layer,
541 core::any::TypeId::of::<LevelFilter>(),
542 )
543 }
544 .unwrap()
545 .cast::<LevelFilter>();
546 assert_eq!(unsafe { *before }, LevelFilter::TRACE);
547
548 handle.reload(LevelFilter::DEBUG).unwrap();
549
550 assert_eq!(unsafe { *before }, LevelFilter::TRACE);
551
552 let after = unsafe {
553 <Layer<LevelFilter, _> as tracing_subscriber::Layer<Registry>>::downcast_raw(
554 &layer,
555 core::any::TypeId::of::<LevelFilter>(),
556 )
557 }
558 .unwrap()
559 .cast::<LevelFilter>();
560 assert_eq!(unsafe { *after }, LevelFilter::DEBUG);
561
562 assert_eq!(unsafe { *before }, LevelFilter::TRACE);
564 }
565
566 #[test]
567 fn parent_layer_routing() {
568 let parent = expect::span()
569 .named("parent")
570 .at_level(tracing::Level::INFO);
571 let child = expect::span().named("child").at_level(tracing::Level::INFO);
572
573 let (mock1, handle1) = mock_layer::mock()
574 .new_span(parent.clone())
575 .enter(&parent)
576 .new_span(child.clone())
577 .enter(&child)
578 .exit(&child)
579 .exit(&parent)
580 .only()
581 .run_with_handle();
582
583 let (mock2, handle2) = mock_layer::mock().only().run_with_handle();
584
585 let (reload, reload_handle) =
586 Layer::new_with_routing(mock1, ReloadRouting::NewSpanToParentLayer);
587 let subscriber = Registry::default().with(reload);
588
589 tracing::subscriber::with_default(subscriber, || {
590 let _parent = tracing::info_span!("parent").entered();
591
592 reload_handle.reload(mock2).unwrap();
593
594 _ = tracing::info_span!("child").entered();
595 });
596
597 handle2.assert_finished();
598 handle1.assert_finished();
599 }
600
601 #[test]
602 fn current_layer_routing() {
603 let parent = expect::span()
604 .named("parent")
605 .at_level(tracing::Level::INFO);
606 let child = expect::span().named("child").at_level(tracing::Level::INFO);
607
608 let (mock1, handle1) = mock_layer::mock()
609 .new_span(parent.clone())
610 .enter(&parent)
611 .exit(&parent)
612 .only()
613 .run_with_handle();
614
615 let (mock2, handle2) = mock_layer::mock()
616 .new_span(child.clone())
617 .enter(&child)
618 .exit(&child)
619 .only()
620 .run_with_handle();
621
622 let (reload, reload_handle) =
623 Layer::new_with_routing(mock1, ReloadRouting::NewSpanToLatestLayer);
624 let subscriber = Registry::default().with(reload);
625
626 tracing::subscriber::with_default(subscriber, || {
627 let _parent = tracing::info_span!("parent").entered();
628
629 reload_handle.reload(mock2).unwrap();
630
631 _ = tracing::info_span!("child").entered();
632 });
633
634 handle2.assert_finished();
635 handle1.assert_finished();
636 }
637}