function_timer/
lib.rs

1//! This crate allow to put a `time` attribut macro on any function
2//! or `impl` block.
3//! It will time the execution of functions and emit a histogram
4//! metric using [metrics](https://crates.io/crates/metrics) crate.
5//!
6//! In case the annotation is on an `impl` block :
7//! * all method will be timed
8//! * there will be a tag `struct` with the struct name.
9//! * all `time` annotations on any method will override the one on `impl` block.
10//! * it's possible to disable specific methods using `#[time(disable)]`.
11//!
12//! Note that `#[time(disable)]` can't be on an `impl` block.
13//!
14//! # Example
15//!
16//! * On functions and methods :
17//!
18//! ```rust
19//! use std::error::Error;
20//! use metrics_exporter_prometheus::PrometheusBuilder;
21//! use function_timer::time;
22//!
23//! struct Test {}
24//!
25//! impl Test {
26//!     #[time("my_metric")]
27//!     pub fn impl_function(&self) {
28//!         println!("This another test");
29//!     }
30//!
31//!     #[time("another_metric")]
32//!     pub fn impl_fail_function(&self, text:&str) -> Result<(), Box<dyn Error>>{
33//!         let number:usize = text.parse()?;
34//!         println!("{number}");
35//!
36//!         Ok(())
37//!     }
38//!
39//!     #[time("my_metric")]
40//!     pub fn static_function() {
41//!         println!("This another test");
42//!     }
43//! }
44//!
45//! #[time("my_metric")]
46//! pub fn free_function() {
47//!     println!("This a test");
48//! }
49//!
50//! fn main() -> Result<(), Box<dyn Error>> {
51//!     let builder = PrometheusBuilder::new();
52//!     let handle = builder.install_recorder()?;
53//!
54//!     free_function();
55//!
56//!     Test::static_function();
57//!
58//!     let t = Test {};
59//!     t.impl_function();
60//!
61//!     let result = t.impl_fail_function("azerty");
62//!     assert!(result.is_err());
63//!     let result = t.impl_fail_function("1");
64//!     assert!(result.is_ok());
65//!
66//!
67//!     println!("{}", handle.render());
68//!
69//!     Ok(())
70//! }
71//! ```
72//!
73//! * on `impl` block :
74//!
75//! ```rust
76//! use std::error::Error;
77//! use metrics_exporter_prometheus::PrometheusBuilder;
78//! use function_timer::time;
79//!
80//! struct Test {}
81//!
82//! #[time("my_metric")]
83//! impl Test {
84//!     #[time("override_my_metric")]
85//!     pub fn impl_function(&self) {
86//!         println!("This another test");
87//!     }
88//!
89//!     pub fn impl_fail_function(&self, text:&str) -> Result<(), Box<dyn Error>>{
90//!         let number:usize = text.parse()?;
91//!         println!("{number}");
92//!
93//!         Ok(())
94//!     }
95//!
96//!     pub fn static_function() {
97//!         println!("This another test");
98//!     }
99//! }
100//!
101//! fn main() -> Result<(), Box<dyn Error>> {
102//!     let builder = PrometheusBuilder::new();
103//!     let handle = builder.install_recorder()?;
104//!
105//!     Test::static_function();
106//!
107//!     let t = Test {};
108//!     t.impl_function();
109//!
110//!     let result = t.impl_fail_function("azerty");
111//!     assert!(result.is_err());
112//!     let result = t.impl_fail_function("1");
113//!     assert!(result.is_ok());
114//!
115//!
116//!     println!("{}", handle.render());
117//!
118//!     Ok(())
119//! }
120//! ```
121use std::time::Instant;
122
123pub use function_timer_macro::time;
124use metrics::histogram;
125
126/// Timer.
127pub struct FunctionTimer {
128    metric_name: &'static str,
129    struct_name: Option<&'static str>,
130    function: &'static str,
131    chrono: Instant,
132}
133
134impl FunctionTimer {
135    /// Create a new [FunctionTimer].
136    ///
137    /// # Parameters
138    ///
139    /// * `metric_name` : name of the metric.
140    /// * `struct_name` : name of the struct.
141    /// * `function` : name of the function that have the annotation. It is used to generate
142    ///   the tag `function`.
143    pub fn new(
144        metric_name: &'static str,
145        struct_name: Option<&'static str>,
146        function: &'static str,
147    ) -> Self {
148        Self {
149            metric_name,
150            struct_name,
151            function,
152            chrono: Instant::now(),
153        }
154    }
155}
156
157impl Drop for FunctionTimer {
158    /// Get execution time and call [`histogram!`](histogram).
159    fn drop(&mut self) {
160        let d = self.chrono.elapsed();
161        let histogram = if let Some(struct_name) = self.struct_name {
162            histogram!(self.metric_name,  "struct" => struct_name, "function" => self.function)
163        } else {
164            histogram!(self.metric_name,  "function" => self.function)
165        };
166        histogram.record(d);
167    }
168}