Skip to main content

tracing_reload/
layer.rs

1#![expect(clippy::unwrap_used, reason = "This file needs a bit more cleanup.")]
2
3use std::{ptr, sync::atomic::Ordering};
4
5use tracing_core::{
6    Event,
7    LevelFilter,
8    Metadata,
9    span,
10    subscriber::{Interest, Subscriber},
11};
12use tracing_subscriber::{layer, registry::LookupSpan};
13
14use crate::{Layer, ReloadRouting, subscriber::ReloadSubscriber};
15
16impl<L, S> tracing_subscriber::Layer<S> for Layer<L, S>
17where
18    S: Subscriber + for<'lookup> LookupSpan<'lookup>,
19    L: tracing_subscriber::Layer<ReloadSubscriber<S>>,
20{
21    fn on_layer(&mut self, subscriber: &mut S) {
22        let mut filters = try_lock!(self.inner.filters.lock());
23        filters.backup = subscriber.register_filter();
24        for _ in 1..crate::MAX_FILTER_COUNT.get() {
25            filters.free.push(subscriber.register_filter());
26        }
27
28        let inner_subscriber = unsafe { std::mem::transmute::<&'_ S, &'static S>(&*subscriber) };
29
30        let mut fake_subscriber = ReloadSubscriber::new(inner_subscriber, filters.clone());
31
32        unsafe {
33            #[expect(clippy::unwrap_used, reason = "There is always at least one layer.")]
34            try_lock!(self.inner.layers.write())
35                .last_mut()
36                .unwrap()
37                .as_mut()
38                .get_unchecked_mut()
39                .on_layer(&mut fake_subscriber);
40        }
41    }
42
43    fn on_register_dispatch(&self, subscriber: &tracing_core::Dispatch) {
44        // This should be called just once. If it somehow isn't, we panic because in `downcast_raw`
45        // we rely on having the correct `Dispatch`. This isn't foolproof, but eliminates some
46        // potential issues.
47        #[expect(
48            clippy::expect_used,
49            reason = "Panicking here might prevent worse bugs. This should only be called during \
50                      startup."
51        )]
52        self.inner
53            .dispatch
54            .set(subscriber.downgrade())
55            .expect("on_register_dispatch should be called only once");
56
57        <L as layer::Layer<ReloadSubscriber<S>>>::on_register_dispatch(
58            #[expect(clippy::unwrap_used, reason = "There is always at least one layer.")]
59            try_lock!(self.inner.layers.read()).last().unwrap(),
60            subscriber,
61        );
62    }
63
64    #[inline]
65    fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
66        <L as layer::Layer<ReloadSubscriber<S>>>::register_callsite(
67            #[expect(clippy::unwrap_used, reason = "There is always at least one layer.")]
68            try_lock!(self.inner.layers.read(), else return Interest::sometimes())
69                .last()
70                .unwrap(),
71            metadata,
72        )
73    }
74
75    #[inline]
76    fn enabled(&self, metadata: &Metadata<'_>, _ctx: layer::Context<'_, S>) -> bool {
77        #[expect(
78            clippy::expect_used,
79            reason = "TODO when can this happen and is sentinel return value better than \
80                      panicking."
81        )]
82        self.with_ctx(move |ctx| {
83            #[expect(clippy::unwrap_used, reason = "There is always at least one layer.")]
84            try_lock!(self.inner.layers.read(), else return false)
85                .last()
86                .unwrap()
87                .enabled(metadata, ctx)
88        })
89        .expect("could not get value for enabled")
90    }
91
92    #[inline]
93    fn event_enabled(&self, event: &Event<'_>, _ctx: layer::Context<'_, S>) -> bool {
94        #[expect(
95            clippy::expect_used,
96            reason = "TODO when can this happen and is sentinel return value better than \
97                      panicking."
98        )]
99        self.with_ctx(move |ctx| {
100            #[expect(clippy::unwrap_used, reason = "There is always at least one layer.")]
101            try_lock!(self.inner.layers.read(), else return false)
102                .last()
103                .unwrap()
104                .event_enabled(event, ctx)
105        })
106        .expect("could not get value for event_enabled")
107    }
108
109    #[inline]
110    fn max_level_hint(&self) -> Option<LevelFilter> {
111        <L as layer::Layer<ReloadSubscriber<S>>>::max_level_hint(
112            #[expect(clippy::unwrap_used, reason = "There is always at least one layer.")]
113            try_lock!(self.inner.layers.read(), else return None)
114                .last()
115                .unwrap(),
116        )
117    }
118
119    #[inline]
120    fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) {
121        let index = {
122            let layers = try_lock!(self.inner.layers.read());
123            let index = match self.span_routing {
124                ReloadRouting::NewSpanToLatestLayer => layers.len() - 1,
125                ReloadRouting::NewSpanToParentLayer => {
126                    let parent_id = attrs.parent().cloned().or_else(|| {
127                        attrs
128                            .is_contextual()
129                            .then_some(ctx.current_span().id().cloned())
130                            .flatten()
131                    });
132                    parent_id
133                        .and_then(|parent_id| {
134                            let span_map = try_lock!(self.inner.span_map.read(), else return None);
135                            span_map.get(&parent_id).copied()
136                        })
137                        .unwrap_or(layers.len() - 1)
138                },
139            };
140            self.with_ctx(move |ctx| {
141                layers.get(index).unwrap().on_new_span(attrs, id, ctx);
142            })
143            .unwrap();
144            index
145        };
146        let mut span_map = try_lock!(self.inner.span_map.write());
147        span_map.insert(id.clone(), index);
148    }
149
150    #[inline]
151    fn on_record(&self, span: &span::Id, values: &span::Record<'_>, _ctx: layer::Context<'_, S>) {
152        self.with_span(span, |layer| {
153            self.with_ctx(move |ctx| layer.on_record(span, values, ctx))
154        });
155    }
156
157    #[inline]
158    fn on_follows_from(&self, span: &span::Id, follows: &span::Id, _ctx: layer::Context<'_, S>) {
159        let idx = {
160            let span_map = try_lock!(self.inner.span_map.read());
161            span_map.get(span).copied()
162        };
163        if let Some(idx) = idx {
164            let current = try_lock!(self.inner.layers.read());
165            if let Some(layer) = current.get(idx) {
166                self.with_ctx(move |ctx| {
167                    layer.on_follows_from(span, follows, ctx);
168                });
169            }
170        }
171    }
172
173    #[inline]
174    fn on_event(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) {
175        let span_id = event
176            .parent()
177            .cloned()
178            .or_else(|| ctx.current_span().id().cloned());
179
180        if let Some(span_id) = span_id {
181            self.with_span(&span_id, |layer| {
182                self.with_ctx(move |ctx| layer.on_event(event, ctx))
183            });
184        } else {
185            self.with_ctx(move |ctx| {
186                #[expect(clippy::unwrap_used, reason = "There is always at least one layer.")]
187                try_lock!(self.inner.layers.read())
188                    .last()
189                    .unwrap()
190                    .on_event(event, ctx);
191            });
192        }
193    }
194
195    #[inline]
196    fn on_enter(&self, id: &span::Id, _ctx: layer::Context<'_, S>) {
197        self.with_span(id, |layer| {
198            self.with_ctx(move |ctx| layer.on_enter(id, ctx))
199        });
200    }
201
202    #[inline]
203    fn on_exit(&self, id: &span::Id, _ctx: layer::Context<'_, S>) {
204        self.with_span(id, |layer| self.with_ctx(move |ctx| layer.on_exit(id, ctx)));
205    }
206
207    #[inline]
208    fn on_close(&self, id: span::Id, _ctx: layer::Context<'_, S>) {
209        let index = {
210            let mut span_map = try_lock!(self.inner.span_map.write());
211            span_map.remove(&id).unwrap()
212        };
213        self.with_ctx(move |ctx| {
214            try_lock!(self.inner.layers.read())
215                .get(index)
216                .unwrap()
217                .on_close(id, ctx);
218        });
219    }
220
221    #[inline]
222    fn on_id_change(&self, old: &span::Id, new: &span::Id, _ctx: layer::Context<'_, S>) {
223        let index = {
224            let mut span_map = try_lock!(self.inner.span_map.write());
225            let index = span_map.remove(old).unwrap();
226            span_map.insert(new.clone(), index);
227            index
228        };
229        self.with_ctx(move |ctx| {
230            try_lock!(self.inner.layers.read())
231                .get(index)
232                .unwrap()
233                .on_id_change(old, new, ctx);
234        });
235    }
236
237    #[doc(hidden)]
238    unsafe fn downcast_raw(&self, id: core::any::TypeId) -> Option<*const ()> {
239        if id == core::any::TypeId::of::<Self>() {
240            return Some(ptr::from_ref(self).cast::<()>());
241        }
242
243        if id == core::any::TypeId::of::<ReloadSubscriber<S>>() {
244            let dispatch = self.inner.dispatch.get()?.upgrade()?;
245
246            // See the SAFETY block after this.
247            if !self.inner.dispatch_leaked.swap(true, Ordering::Relaxed) {
248                std::mem::forget(dispatch.clone());
249            }
250
251            let subscriber = dispatch.downcast_ref::<S>()?;
252
253            // SAFETY The scenario that is most likely happening is that a layer wrapped in
254            // `tracing_reload::Layer` has a `Dispatch` and wants to downcast to
255            // `ReloadSubscriber<S>`. That `Dispatch` downcasts first to `dyn Subscriber` which is
256            // actually `S`, most likely some kind of `Layered`. It doesn't recognize the `TypeId`
257            // so it start calling downcast methods on the wrapped layers. And then it got here.
258            // Now, if we have the same `Dispatch` (and each `Subscriber` is owned by a single
259            // `Dispatch` so that should hold), we should be safe since we can downcast and be sure
260            // that the pointer (and thus the reference) is valid for at least as long as the
261            // original caller holds the `Dispatch`.
262            //
263            // Where this could be unsound is if someone holds a reference to this `Layer`, provides
264            // it with _another_ `Dispatch` through `on_register_dispatch`, then downcasts to
265            // `ReloadSubscriber<S>` and drops their `Dispatch`, dropping also `S` and invalidating
266            // the pointer inside the struct pointed to by the pointer we have given out. The only
267            // way out of this I see is not holding just `WeakDispatch`, but the full one so that it
268            // can't ever be dropped. That's another leak though. So that's what we've done just a
269            // few lines higher.
270            let static_subscriber = unsafe { std::mem::transmute::<&'_ S, &'static S>(subscriber) };
271
272            return Some(
273                try_lock!(self.subscriber_cache.lock(), else return None)
274                    .get(id, static_subscriber),
275            );
276        }
277
278        let current = try_lock!(self.inner.layers.read(), else return None);
279
280        current.last().and_then(|layer| unsafe {
281            <L as layer::Layer<ReloadSubscriber<S>>>::downcast_raw(layer, id)
282        })
283    }
284}