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}