1// Copyright 2023 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
1415use ruma::time::Instant;
16use tracing::{callsite::DefaultCallsite, Callsite as _};
1718/// A named RAII that will show on Drop how long its covered section took to
19/// execute.
20pub struct TracingTimer {
21 id: String,
22 callsite: &'static DefaultCallsite,
23 start: Instant,
24 level: tracing::Level,
25}
2627#[cfg(not(tarpaulin_include))]
28impl std::fmt::Debug for TracingTimer {
29fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 f.debug_struct("TracingTimer").field("id", &self.id).field("start", &self.start).finish()
31 }
32}
3334impl Drop for TracingTimer {
35fn drop(&mut self) {
36let message = format!("{} finished in {}ms", self.id, self.start.elapsed().as_millis());
3738let enabled = tracing::level_enabled!(self.level) && {
39let interest = self.callsite.interest();
40 !interest.is_never()
41 && tracing::__macro_support::__is_enabled(self.callsite.metadata(), interest)
42 };
4344if !enabled {
45return;
46 }
4748let metadata = self.callsite.metadata();
49let fields = metadata.fields();
50let message_field = fields.field("message").unwrap();
51#[allow(trivial_casts)] // The compiler is lying, it can't infer this cast
52let values = [(&message_field, Some(&message as &dyn tracing::Value))];
5354// This function is hidden from docs, but we have to use it
55 // because there is no other way of obtaining a `ValueSet`.
56 // It's not entirely clear why it is private. See this issue:
57 // https://github.com/tokio-rs/tracing/issues/2363
58let values = fields.value_set(&values);
5960 tracing::Event::dispatch(metadata, &values);
61 }
62}
6364impl TracingTimer {
65/// Create a new `TracingTimer` at the `debug` log level.
66pub fn new_debug(
67 callsite: &'static DefaultCallsite,
68 id: String,
69 level: tracing::Level,
70 ) -> Self {
71Self { id, callsite, start: Instant::now(), level }
72 }
73}
7475/// Macro to create a RAII timer that will log a `tracing` event once it's
76/// dropped.
77///
78/// The tracing level can be specified as a first argument, but it's optional.
79/// If it's missing, this will use the debug level.
80#[macro_export]
81macro_rules! timer {
82 ($level:expr, $string:expr) => {{
83static __CALLSITE: tracing::callsite::DefaultCallsite = tracing::callsite2! {
84 name: tracing::__macro_support::concat!(
85"event ",
86file!(),
87":",
88line!(),
89 ),
90 kind: tracing::metadata::Kind::EVENT,
91 target: module_path!(),
92 level: $level,
93 fields: []
94 };
9596$crate::tracing_timer::TracingTimer::new_debug(&__CALLSITE, $string.into(), $level)
97 }};
9899 ($string:expr) => {
100$crate::timer!(tracing::Level::DEBUG, $string)
101 };
102}
103104#[cfg(test)]
105mod tests {
106#[cfg(not(target_arch = "wasm32"))]
107 #[matrix_sdk_test_macros::async_test]
108async fn test_timer_name() {
109use tracing::{span, Level};
110111tracing::warn!("Starting test...");
112113mod time123 {
114pub async fn run() {
115let _timer_guard = timer!(tracing::Level::DEBUG, "test");
116 tokio::time::sleep(ruma::time::Duration::from_millis(123)).await;
117// Displays: 2023-08-25T15:18:31.169498Z DEBUG
118 // matrix_sdk_common::tracing_timer::tests: test finished in
119 // 124ms
120}
121 }
122123 time123::run().await;
124125let span = span!(Level::DEBUG, "le 256ms span");
126let _guard = span.enter();
127128let _timer_guard = timer!("in span");
129 tokio::time::sleep(ruma::time::Duration::from_millis(256)).await;
130131tracing::warn!("Test about to finish.");
132// Displays: 2023-08-25T15:18:31.427070Z DEBUG le 256ms span:
133 // matrix_sdk_common::tracing_timer::tests: in span finished in 257ms
134}
135}