Skip to main content

diesel/connection/
instrumentation.rs

1use alloc::boxed::Box;
2use core::fmt::{Debug, Display};
3use core::num::NonZeroU32;
4use core::ops::{Deref, DerefMut};
5use downcast_rs::Downcast;
6
7#[cfg(feature = "std")]
8static GLOBAL_INSTRUMENTATION: std::sync::RwLock<fn() -> Option<Box<dyn Instrumentation>>> =
9    std::sync::RwLock::new(|| None);
10
11/// A helper trait for opaque query representations
12/// which allows to get a `Display` and `Debug`
13/// representation of the underlying type without
14/// exposing type specific details
15pub trait DebugQuery: Debug + Display {}
16
17impl<T, DB> DebugQuery for crate::query_builder::DebugQuery<'_, T, DB> where Self: Debug + Display {}
18
19/// A helper type that allows printing out str slices
20///
21/// This type is necessary because it's not possible
22/// to cast from a reference of a unsized type like `&str`
23/// to a reference of a trait object even if that
24/// type implements all necessary traits
25#[doc = " A helper type that allows printing out str slices"]
#[doc = ""]
#[doc = " This type is necessary because it\'s not possible"]
#[doc = " to cast from a reference of a unsized type like `&str`"]
#[doc = " to a reference of a trait object even if that"]
#[doc = " type implements all necessary traits"]
#[non_exhaustive]
pub struct StrQueryHelper<'query> {
    s: &'query str,
}#[diesel_derives::__diesel_public_if(
26    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
27)]
28pub(crate) struct StrQueryHelper<'query> {
29    s: &'query str,
30}
31
32impl<'query> StrQueryHelper<'query> {
33    /// Construct a new `StrQueryHelper`
34    #[diesel_derives::__diesel_public_if(
35        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
36    )]
37    #[cfg(any(
38        feature = "postgres",
39        feature = "__sqlite-shared",
40        feature = "mysql",
41        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
42    ))]
43    pub(crate) fn new(s: &'query str) -> Self {
44        Self { s }
45    }
46}
47
48impl Debug for StrQueryHelper<'_> {
49    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
50        Debug::fmt(self.s, f)
51    }
52}
53
54impl Display for StrQueryHelper<'_> {
55    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
56        Display::fmt(&self.s, f)
57    }
58}
59
60impl DebugQuery for StrQueryHelper<'_> {}
61
62/// This enum describes possible connection events
63/// that can be handled by an [`Instrumentation`] implementation
64///
65/// Some fields might contain sensitive information, like login
66/// details for the database.
67///
68/// Diesel does not guarantee that future versions will
69/// emit the same events in the same order or timing.
70/// In addition the output of the [`Debug`] and [`Display`]
71/// implementation of the enum itself and any of its fields
72/// is not guarantee to be stable.
73//
74// This type is carefully designed
75// to avoid any potential overhead by
76// taking references for all things
77// and by not performing any additional
78// work until required.
79// In addition it's carefully designed
80// not to be dependent on the actual backend
81// type, as that makes it easier to reuse
82// `Instrumentation` implementations in
83// a different context
84#[derive(#[automatically_derived]
impl<'a> ::core::fmt::Debug for InstrumentationEvent<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            InstrumentationEvent::StartEstablishConnection { url: __self_0 }
                =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "StartEstablishConnection", "url", &__self_0),
            InstrumentationEvent::FinishEstablishConnection {
                url: __self_0, error: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "FinishEstablishConnection", "url", __self_0, "error",
                    &__self_1),
            InstrumentationEvent::StartQuery { query: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "StartQuery", "query", &__self_0),
            InstrumentationEvent::CacheQuery { sql: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "CacheQuery", "sql", &__self_0),
            InstrumentationEvent::FinishQuery {
                query: __self_0, error: __self_1 } =>
                ::core::fmt::Formatter::debug_struct_field2_finish(f,
                    "FinishQuery", "query", __self_0, "error", &__self_1),
            InstrumentationEvent::BeginTransaction { depth: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "BeginTransaction", "depth", &__self_0),
            InstrumentationEvent::CommitTransaction { depth: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "CommitTransaction", "depth", &__self_0),
            InstrumentationEvent::RollbackTransaction { depth: __self_0 } =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "RollbackTransaction", "depth", &__self_0),
        }
    }
}Debug)]
85#[non_exhaustive]
86pub enum InstrumentationEvent<'a> {
87    /// An event emitted by before starting
88    /// establishing a new connection
89    #[non_exhaustive]
90    StartEstablishConnection {
91        /// The database url the connection
92        /// tries to connect to
93        ///
94        /// This might contain sensitive information
95        /// like the database password
96        url: &'a str,
97    },
98    /// An event emitted after establishing a
99    /// new connection
100    #[non_exhaustive]
101    FinishEstablishConnection {
102        /// The database url the connection
103        /// tries is connected to
104        ///
105        /// This might contain sensitive information
106        /// like the database password
107        url: &'a str,
108        /// An optional error if the connection failed
109        error: Option<&'a crate::result::ConnectionError>,
110    },
111    /// An event that is emitted before executing
112    /// a query
113    #[non_exhaustive]
114    StartQuery {
115        /// A opaque representation of the query
116        ///
117        /// This type implements [`Debug`] and [`Display`],
118        /// but should be considered otherwise as opaque.
119        ///
120        /// The exact output of the [`Debug`] and [`Display`]
121        /// implementation is not considered as part of the
122        /// stable API.
123        query: &'a dyn DebugQuery,
124    },
125    /// An event that is emitted when a query
126    /// is cached in the connection internal
127    /// prepared statement cache
128    #[non_exhaustive]
129    CacheQuery {
130        /// SQL string of the cached query
131        sql: &'a str,
132    },
133    /// An event that is emitted after executing
134    /// a query
135    #[non_exhaustive]
136    FinishQuery {
137        /// A opaque representation of the query
138        ///
139        /// This type implements [`Debug`] and [`Display`],
140        /// but should be considered otherwise as opaque.
141        ///
142        /// The exact output of the [`Debug`] and [`Display`]
143        /// implementation is not considered as part of the
144        /// stable API.
145        query: &'a dyn DebugQuery,
146        /// An optional error if the connection failed
147        error: Option<&'a crate::result::Error>,
148    },
149    /// An event that is emitted while
150    /// starting a new transaction
151    #[non_exhaustive]
152    BeginTransaction {
153        /// Transaction level of the newly started
154        /// transaction
155        depth: NonZeroU32,
156    },
157    /// An event that is emitted while
158    /// committing a transaction
159    #[non_exhaustive]
160    CommitTransaction {
161        /// Transaction level of the to be committed
162        /// transaction
163        depth: NonZeroU32,
164    },
165    /// An event that is emitted while
166    /// rolling back a transaction
167    #[non_exhaustive]
168    RollbackTransaction {
169        /// Transaction level of the to be rolled
170        /// back transaction
171        depth: NonZeroU32,
172    },
173}
174
175// these constructors exist to
176// keep `#[non_exhaustive]` on all the variants
177// and to gate the constructors on the unstable feature
178#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
179impl<'a> InstrumentationEvent<'a> {
180    /// Create a new `InstrumentationEvent::StartEstablishConnection` event
181    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
182    pub fn start_establish_connection(url: &'a str) -> Self {
183        Self::StartEstablishConnection { url }
184    }
185
186    /// Create a new `InstrumentationEvent::FinishEstablishConnection` event
187    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
188    pub fn finish_establish_connection(
189        url: &'a str,
190        error: Option<&'a crate::result::ConnectionError>,
191    ) -> Self {
192        Self::FinishEstablishConnection { url, error }
193    }
194
195    /// Create a new `InstrumentationEvent::StartQuery` event
196    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
197    pub fn start_query(query: &'a dyn DebugQuery) -> Self {
198        Self::StartQuery { query }
199    }
200
201    /// Create a new `InstrumentationEvent::CacheQuery` event
202    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
203    pub fn cache_query(sql: &'a str) -> Self {
204        Self::CacheQuery { sql }
205    }
206
207    /// Create a new `InstrumentationEvent::FinishQuery` event
208    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
209    pub fn finish_query(
210        query: &'a dyn DebugQuery,
211        error: Option<&'a crate::result::Error>,
212    ) -> Self {
213        Self::FinishQuery { query, error }
214    }
215
216    /// Create a new `InstrumentationEvent::BeginTransaction` event
217    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
218    pub fn begin_transaction(depth: NonZeroU32) -> Self {
219        Self::BeginTransaction { depth }
220    }
221
222    /// Create a new `InstrumentationEvent::RollbackTransaction` event
223    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
224    pub fn rollback_transaction(depth: NonZeroU32) -> Self {
225        Self::RollbackTransaction { depth }
226    }
227
228    /// Create a new `InstrumentationEvent::CommitTransaction` event
229    #[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
230    pub fn commit_transaction(depth: NonZeroU32) -> Self {
231        Self::CommitTransaction { depth }
232    }
233}
234
235/// A type that provides an connection `Instrumentation`
236///
237/// This trait is the basic building block for logging or
238/// otherwise instrumenting diesel connection types. It
239/// acts as callback that receives information about certain
240/// important connection states
241///
242/// For simple usages this trait is implemented for closures
243/// accepting a [`InstrumentationEvent`] as argument.
244///
245/// More complex usages and integrations with frameworks like
246/// `tracing` and `log` are supposed to be part of their own
247/// crates.
248pub trait Instrumentation: Downcast + Send + 'static {
249    /// The function that is invoked for each event
250    fn on_connection_event(&mut self, event: InstrumentationEvent<'_>);
251}
252impl dyn Instrumentation<> {
    /// Returns true if the trait object wraps an object of type `__T`.
    #[inline]
    pub fn is<__T: Instrumentation<>>(&self) -> bool {
        ::downcast_rs::Downcast::as_any(self).is::<__T>()
    }
    /// Returns a boxed object from a boxed trait object if the underlying object is of type
    /// `__T`. Returns the original boxed trait if it isn't.
    #[inline]
    pub fn downcast<__T: Instrumentation<>>(self:
            ::downcast_rs::__alloc::boxed::Box<Self>)
        ->
            ::downcast_rs::__std::result::Result<::downcast_rs::__alloc::boxed::Box<__T>,
            ::downcast_rs::__alloc::boxed::Box<Self>> {
        if self.is::<__T>() {
            Ok(::downcast_rs::Downcast::into_any(self).downcast::<__T>().unwrap())
        } else { Err(self) }
    }
    /// Returns an `Rc`-ed object from an `Rc`-ed trait object if the underlying object is of
    /// type `__T`. Returns the original `Rc`-ed trait if it isn't.
    #[inline]
    pub fn downcast_rc<__T: Instrumentation<>>(self:
            ::downcast_rs::__alloc::rc::Rc<Self>)
        ->
            ::downcast_rs::__std::result::Result<::downcast_rs::__alloc::rc::Rc<__T>,
            ::downcast_rs::__alloc::rc::Rc<Self>> {
        if self.is::<__T>() {
            Ok(::downcast_rs::Downcast::into_any_rc(self).downcast::<__T>().unwrap())
        } else { Err(self) }
    }
    /// Returns a reference to the object within the trait object if it is of type `__T`, or
    /// `None` if it isn't.
    #[inline]
    pub fn downcast_ref<__T: Instrumentation<>>(&self)
        -> ::downcast_rs::__std::option::Option<&__T> {
        ::downcast_rs::Downcast::as_any(self).downcast_ref::<__T>()
    }
    /// Returns a mutable reference to the object within the trait object if it is of type
    /// `__T`, or `None` if it isn't.
    #[inline]
    pub fn downcast_mut<__T: Instrumentation<>>(&mut self)
        -> ::downcast_rs::__std::option::Option<&mut __T> {
        ::downcast_rs::Downcast::as_any_mut(self).downcast_mut::<__T>()
    }
}downcast_rs::impl_downcast!(Instrumentation);
253
254/// Get an instance of the default [`Instrumentation`]
255///
256/// This function is mostly useful for crates implementing
257/// their own connection types
258pub fn get_default_instrumentation() -> Option<Box<dyn Instrumentation>> {
259    #[cfg(feature = "std")]
260    match GLOBAL_INSTRUMENTATION.read() {
261        Ok(f) => (*f)(),
262        Err(_) => None,
263    }
264    #[cfg(not(feature = "std"))]
265    None
266}
267
268/// Set a custom constructor for the default [`Instrumentation`]
269/// used by new connections
270///
271/// ```rust
272/// use diesel::connection::{set_default_instrumentation, Instrumentation, InstrumentationEvent};
273///
274/// // a simple logger that prints all events to stdout
275/// fn simple_logger() -> Option<Box<dyn Instrumentation>> {
276///     // we need the explicit argument type there due
277///     // to bugs in rustc
278///     Some(Box::new(|event: InstrumentationEvent<'_>| {
279///         println!("{event:?}")
280///     }))
281/// }
282///
283/// set_default_instrumentation(simple_logger);
284/// ```
285#[cfg(feature = "std")]
286pub fn set_default_instrumentation(
287    default: fn() -> Option<Box<dyn Instrumentation>>,
288) -> crate::QueryResult<()> {
289    match GLOBAL_INSTRUMENTATION.write() {
290        Ok(mut l) => {
291            *l = default;
292            Ok(())
293        }
294        Err(e) => Err(crate::result::Error::DatabaseError(
295            crate::result::DatabaseErrorKind::Unknown,
296            Box::new(e.to_string()),
297        )),
298    }
299}
300
301impl<F> Instrumentation for F
302where
303    F: FnMut(InstrumentationEvent<'_>) + Send + 'static,
304{
305    fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
306        (self)(event)
307    }
308}
309
310impl Instrumentation for Box<dyn Instrumentation> {
311    fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
312        self.deref_mut().on_connection_event(event)
313    }
314}
315
316impl<T> Instrumentation for Option<T>
317where
318    T: Instrumentation,
319{
320    fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
321        if let Some(i) = self {
322            i.on_connection_event(event)
323        }
324    }
325}
326
327#[doc = " An optional dyn instrumentation."]
#[doc = ""]
#[doc =
" For ease of use, this type implements [`Deref`] and [`DerefMut`] to `&dyn Instrumentation`,"]
#[doc =
" falling back to a no-op implementation if no instrumentation is set."]
#[doc = ""]
#[doc =
" The DynInstrumentation type is useful because without it we actually did tend to return"]
#[doc =
" (accidentally) `&mut Option<Box> as &mut dyn Instrumentation` from `connection.instrumentation()`,"]
#[doc =
" so downcasting would have to be done in these two steps by the user, which is counter-intuitive."]
#[non_exhaustive]
pub struct DynInstrumentation {
    #[doc = " zst"]
    no_instrumentation: NoInstrumentation,
    inner: Option<Box<dyn Instrumentation>>,
}#[diesel_derives::__diesel_public_if(
328    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
329)]
330/// An optional dyn instrumentation.
331///
332/// For ease of use, this type implements [`Deref`] and [`DerefMut`] to `&dyn Instrumentation`,
333/// falling back to a no-op implementation if no instrumentation is set.
334///
335/// The DynInstrumentation type is useful because without it we actually did tend to return
336/// (accidentally) `&mut Option<Box> as &mut dyn Instrumentation` from `connection.instrumentation()`,
337/// so downcasting would have to be done in these two steps by the user, which is counter-intuitive.
338pub(crate) struct DynInstrumentation {
339    /// zst
340    no_instrumentation: NoInstrumentation,
341    inner: Option<Box<dyn Instrumentation>>,
342}
343
344impl Deref for DynInstrumentation {
345    type Target = dyn Instrumentation;
346
347    fn deref(&self) -> &Self::Target {
348        self.inner.as_deref().unwrap_or(&self.no_instrumentation)
349    }
350}
351
352impl DerefMut for DynInstrumentation {
353    fn deref_mut(&mut self) -> &mut Self::Target {
354        self.inner
355            .as_deref_mut()
356            .unwrap_or(&mut self.no_instrumentation)
357    }
358}
359
360impl DynInstrumentation {
361    /// Create a instance of the default instrumentation provider
362    #[diesel_derives::__diesel_public_if(
363        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
364    )]
365    #[cfg(any(
366        feature = "postgres",
367        feature = "__sqlite-shared",
368        feature = "mysql",
369        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
370    ))]
371    pub(crate) fn default_instrumentation() -> Self {
372        Self {
373            inner: get_default_instrumentation(),
374            no_instrumentation: NoInstrumentation,
375        }
376    }
377
378    /// Create a noop instrumentation provider instance
379    #[diesel_derives::__diesel_public_if(
380        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
381    )]
382    #[cfg(any(
383        feature = "postgres",
384        feature = "__sqlite-shared",
385        feature = "mysql",
386        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
387    ))]
388    pub(crate) fn none() -> Self {
389        Self {
390            inner: None,
391            no_instrumentation: NoInstrumentation,
392        }
393    }
394
395    /// register an event with the given instrumentation implementation
396    #[diesel_derives::__diesel_public_if(
397        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
398    )]
399    #[cfg(any(
400        feature = "postgres",
401        feature = "__sqlite-shared",
402        feature = "mysql",
403        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
404    ))]
405    pub(crate) fn on_connection_event(&mut self, event: InstrumentationEvent<'_>) {
406        // This implementation is not necessary to be able to call this method on this object
407        // because of the already existing Deref impl.
408        // However it allows avoiding the dynamic dispatch to the stub value
409        if let Some(inner) = self.inner.as_deref_mut() {
410            inner.on_connection_event(event)
411        }
412    }
413}
414
415impl<I: Instrumentation> From<I> for DynInstrumentation {
416    fn from(instrumentation: I) -> Self {
417        Self {
418            inner: Some(unpack_instrumentation(Box::new(instrumentation))),
419            no_instrumentation: NoInstrumentation,
420        }
421    }
422}
423
424struct NoInstrumentation;
425
426impl Instrumentation for NoInstrumentation {
427    fn on_connection_event(&mut self, _: InstrumentationEvent<'_>) {}
428}
429
430/// Unwrap unnecessary boxing levels
431fn unpack_instrumentation(
432    mut instrumentation: Box<dyn Instrumentation>,
433) -> Box<dyn Instrumentation> {
434    loop {
435        match instrumentation.downcast::<Box<dyn Instrumentation>>() {
436            Ok(extra_boxed_instrumentation) => instrumentation = *extra_boxed_instrumentation,
437            Err(not_extra_boxed_instrumentation) => {
438                break not_extra_boxed_instrumentation;
439            }
440        }
441    }
442}