miden_note_transport_node/
metrics.rs1use opentelemetry::KeyValue;
2use opentelemetry::metrics::{Counter, Histogram, Meter};
3
4#[derive(Debug, Clone)]
8pub struct Metrics {
9 pub grpc: MetricsGrpc,
11 pub db: MetricsDatabase,
13}
14
15#[derive(Debug, Clone)]
17pub struct MetricsGrpc {
18 send_note_count: Counter<u64>,
21 send_note_duration: Histogram<f64>,
22 send_note_note_size: Histogram<u64>,
23 fetch_notes_count: Counter<u64>,
25 fetch_notes_duration: Histogram<f64>,
26 fetch_notes_replied_notes_number: Histogram<u64>,
27 fetch_notes_replied_notes_size: Histogram<u64>,
28}
29
30#[derive(Debug, Clone)]
32pub struct MetricsDatabase {
33 store_note_count: Counter<u64>,
36 store_note_duration: Histogram<f64>,
37 fetch_notes_count: Counter<u64>,
39 fetch_notes_duration: Histogram<f64>,
40 fetch_notes_legacy_cursor_reset_count: Counter<u64>,
42 maintenance_cleanup_notes_count: Counter<u64>,
44 maintenance_cleanup_notes_duration: Histogram<f64>,
45}
46
47impl Metrics {
48 pub fn new(meter: &Meter) -> Self {
50 let grpc = MetricsGrpc::new(meter);
51 let db = MetricsDatabase::new(meter);
52 Self { grpc, db }
53 }
54}
55
56impl MetricsGrpc {
57 pub fn new(meter: &Meter) -> Self {
59 let send_note_count = meter
60 .u64_counter("grpc_send_note_count")
61 .with_description("Total number of gRPC send_note() requests")
62 .build();
63
64 let send_note_duration = meter
65 .f64_histogram("grpc_send_note_duration")
66 .with_description("Duration of gRPC send_note() requests in seconds")
67 .with_unit("s")
68 .build();
69
70 let send_note_note_size = meter
71 .u64_histogram("grpc_send_note_note_size")
72 .with_description("Size of incoming note in send_note() requests in bytes")
73 .with_unit("B")
74 .build();
75
76 let fetch_notes_count = meter
77 .u64_counter("grpc_fetch_notes_count")
78 .with_description("Total number of gRPC fetch_notes() requests")
79 .build();
80
81 let fetch_notes_duration = meter
82 .f64_histogram("grpc_fetch_notes_duration")
83 .with_description("Duration of gRPC fetch_notes() requests in seconds")
84 .with_unit("s")
85 .build();
86
87 let fetch_notes_replied_notes_number = meter
88 .u64_histogram("grpc_fetch_notes_replied_notes_number")
89 .with_description("Number of replied notes per gRPC fetch_notes() request")
90 .build();
91
92 let fetch_notes_replied_notes_size = meter
93 .u64_histogram("grpc_fetch_notes_replied_notes_size")
94 .with_description("Total size of replied notes per gRPC fetch_notes() request in bytes")
95 .with_unit("B")
96 .build();
97
98 Self {
99 send_note_count,
100 send_note_duration,
101 send_note_note_size,
102 fetch_notes_count,
103 fetch_notes_duration,
104 fetch_notes_replied_notes_number,
105 fetch_notes_replied_notes_size,
106 }
107 }
108
109 pub fn grpc_send_note_request(&self, size_b: u64) -> RequestTimer<'_> {
113 let operation = "grpc.send_note.request";
114
115 self.send_note_note_size
116 .record(size_b, &[KeyValue::new("operation", operation.to_string())]);
117
118 let counter = &self.send_note_count;
119 let histogram = &self.send_note_duration;
120 request_count_measure(operation, counter, histogram)
121 }
122
123 pub fn grpc_fetch_notes_request(&self) -> RequestTimer<'_> {
127 let operation = "grpc.fetch_notes";
128 let counter = &self.fetch_notes_count;
129 let histogram = &self.fetch_notes_duration;
130
131 request_count_measure(operation, counter, histogram)
132 }
133
134 pub fn grpc_fetch_notes_response(&self, number: u64, size_b: u64) {
138 let operation = "grpc.fetch_notes.response";
139
140 self.fetch_notes_replied_notes_number
141 .record(number, &[KeyValue::new("operation", operation.to_string())]);
142 self.fetch_notes_replied_notes_size
143 .record(size_b, &[KeyValue::new("operation", operation.to_string())]);
144 }
145}
146
147impl MetricsDatabase {
148 pub fn new(meter: &Meter) -> Self {
150 let store_note_count = meter
151 .u64_counter("db_store_note_count")
152 .with_description("Total number of DB store_note() requests")
153 .build();
154
155 let store_note_duration = meter
156 .f64_histogram("db_store_note_duration")
157 .with_description("Duration of DB store_note() requests in seconds")
158 .with_unit("s")
159 .build();
160
161 let fetch_notes_count = meter
162 .u64_counter("db_fetch_notes_count")
163 .with_description("Total number of DB fetch_notes() requests")
164 .build();
165
166 let fetch_notes_duration = meter
167 .f64_histogram("db_fetch_notes_duration")
168 .with_description("Duration of dB fetch_notes() requests in seconds")
169 .with_unit("s")
170 .build();
171
172 let fetch_notes_legacy_cursor_reset_count = meter
173 .u64_counter("db_fetch_notes_legacy_cursor_reset_count")
174 .with_description(
175 "Number of fetch_notes() requests where the client's cursor was \
176 above the legacy-cursor threshold and reset to 0 (pre-seq-migration \
177 clients)",
178 )
179 .build();
180
181 let maintenance_cleanup_notes_count = meter
182 .u64_counter("db_maintenance_cleanup_notes_count")
183 .with_description("Total number of DB maintenance cleanup_old_notes() requests")
184 .build();
185
186 let maintenance_cleanup_notes_duration = meter
187 .f64_histogram("db_maintenance_cleanup_notes_duration")
188 .with_description("Duration of DB maintenance cleanup_old_notes() requests in seconds")
189 .with_unit("s")
190 .build();
191
192 Self {
193 store_note_count,
194 store_note_duration,
195 fetch_notes_count,
196 fetch_notes_duration,
197 fetch_notes_legacy_cursor_reset_count,
198 maintenance_cleanup_notes_count,
199 maintenance_cleanup_notes_duration,
200 }
201 }
202
203 pub fn db_store_note(&self) -> RequestTimer<'_> {
207 let operation = "db.store_note";
208 let counter = &self.store_note_count;
209 let histogram = &self.store_note_duration;
210
211 request_count_measure(operation, counter, histogram)
212 }
213
214 pub fn db_fetch_notes(&self) -> RequestTimer<'_> {
218 let operation = "db.fetch_notes";
219 let counter = &self.fetch_notes_count;
220 let histogram = &self.fetch_notes_duration;
221
222 request_count_measure(operation, counter, histogram)
223 }
224
225 pub fn db_fetch_notes_legacy_cursor_reset(&self) {
227 self.fetch_notes_legacy_cursor_reset_count.add(1, &[]);
228 }
229
230 pub fn db_maintenance_cleanup_notes(&self) -> RequestTimer<'_> {
234 let operation = "db.maintenance.cleanup_old_notes";
235 let counter = &self.maintenance_cleanup_notes_count;
236 let histogram = &self.maintenance_cleanup_notes_duration;
237
238 request_count_measure(operation, counter, histogram)
239 }
240}
241
242fn request_count_measure<'a>(
246 operation: &str,
247 counter: &Counter<u64>,
248 histogram: &'a Histogram<f64>,
249) -> RequestTimer<'a> {
250 let start = std::time::Instant::now();
251
252 counter.add(1, &[KeyValue::new("operation", operation.to_string())]);
254
255 RequestTimer {
256 operation: operation.to_string(),
257 start,
258 histogram,
259 }
260}
261
262impl Default for Metrics {
263 fn default() -> Self {
264 let meter = opentelemetry::global::meter("miden-note-transport-node");
265 Self::new(&meter)
266 }
267}
268
269pub struct RequestTimer<'a> {
271 operation: String,
272 start: std::time::Instant,
273 histogram: &'a Histogram<f64>,
274}
275
276impl RequestTimer<'_> {
277 pub fn finish(&self, status: &str) {
279 let duration = self.start.elapsed();
280 let duration_s = duration.as_secs_f64();
281
282 self.histogram.record(
284 duration_s,
285 &[
286 KeyValue::new("operation", self.operation.clone()),
287 KeyValue::new("status", status.to_string()),
288 ],
289 );
290 }
291}
292
293impl Drop for RequestTimer<'_> {
294 fn drop(&mut self) {
295 self.finish("dropped");
296 }
297}