@@ -8,6 +8,10 @@ use anyhow::{anyhow, Context, Error};
88use async_trait:: async_trait;
99use candid:: { Decode , Encode , Principal } ;
1010use ic_canister_client:: Agent ;
11+ use prometheus:: {
12+ register_histogram_vec_with_registry, register_int_counter_vec_with_registry, HistogramOpts ,
13+ HistogramVec , IntCounterVec , Registry ,
14+ } ;
1115use rsa:: {
1216 pkcs1:: { DecodeRsaPublicKey , EncodeRsaPublicKey } ,
1317 rand_core:: CryptoRngCore ,
@@ -20,7 +24,58 @@ use tokio::{sync::Mutex, time::sleep};
2024const SALT_SIZE : usize = 64 ;
2125const RSA_KEY_SIZE : usize = 2048 ;
2226
23- struct WithLogs < T > ( T ) ;
27+ #[ derive( Clone , Debug ) ]
28+ pub struct MetricParams {
29+ pub action : String ,
30+ pub counter : IntCounterVec ,
31+ pub recorder : HistogramVec ,
32+ }
33+
34+ impl MetricParams {
35+ pub fn new ( registry : & Registry , action : & str ) -> Self {
36+ Self :: new_with_opts ( registry, action, & [ "status" ] , None )
37+ }
38+
39+ pub fn new_with_opts (
40+ registry : & Registry ,
41+ action : & str ,
42+ labels : & [ & str ] ,
43+ buckets : Option < & [ f64 ] > ,
44+ ) -> Self {
45+ let mut recorder_opts = HistogramOpts :: new (
46+ format ! ( "{action}_duration_sec" ) , // name
47+ format ! ( "Records the duration of {action} calls in seconds" ) , // description
48+ ) ;
49+
50+ // Set histogram buckets if given
51+ buckets. inspect ( |bs| {
52+ recorder_opts. buckets = bs. to_vec ( ) ;
53+ } ) ;
54+
55+ Self {
56+ action : action. to_string ( ) ,
57+
58+ // Count
59+ counter : register_int_counter_vec_with_registry ! (
60+ format!( "{action}_total" ) , // name
61+ format!( "Counts occurrences of {action} calls" ) , // description
62+ labels, // labels
63+ registry, // registry
64+ )
65+ . expect ( "failed to register counter" ) ,
66+
67+ // Duration
68+ recorder : register_histogram_vec_with_registry ! (
69+ recorder_opts, // options
70+ labels, // labels
71+ registry, // registry
72+ )
73+ . expect ( "failed to register histogram" ) ,
74+ }
75+ }
76+ }
77+
78+ struct WithMetrics < T > ( T , Option < MetricParams > ) ;
2479
2580pub struct ThrottleParams {
2681 pub d : Duration ,
@@ -92,7 +147,7 @@ pub trait Register: Sync + Send {
92147}
93148
94149#[ async_trait]
95- impl < T : Register > Register for WithLogs < T > {
150+ impl < T : Register > Register for WithMetrics < T > {
96151 async fn register ( & self , pubkey : & [ u8 ] ) -> Result < ( ) , RegisterError > {
97152 let start_time = Instant :: now ( ) ;
98153
@@ -108,11 +163,24 @@ impl<T: Register> Register for WithLogs<T> {
108163
109164 let duration = start_time. elapsed ( ) . as_secs_f64 ( ) ;
110165
166+ // Log
111167 println ! (
112168 "action = 'register', status = {status}, duration = {duration}, error = {:?}" ,
113169 out. as_ref( ) . err( )
114170 ) ;
115171
172+ // Metrics
173+ if let Some ( MetricParams {
174+ counter, recorder, ..
175+ } ) = & self . 1
176+ {
177+ // Count
178+ counter. with_label_values ( & [ status] ) . inc ( ) ;
179+
180+ // Latency
181+ recorder. with_label_values ( & [ status] ) . observe ( duration) ;
182+ }
183+
116184 return out;
117185 }
118186}
@@ -181,7 +249,7 @@ pub trait Query: Sync + Send {
181249}
182250
183251#[ async_trait]
184- impl < T : Query > Query for WithLogs < T > {
252+ impl < T : Query > Query for WithMetrics < T > {
185253 async fn query ( & self ) -> Result < Vec < u8 > , QueryError > {
186254 let start_time = Instant :: now ( ) ;
187255
@@ -202,11 +270,24 @@ impl<T: Query> Query for WithLogs<T> {
202270
203271 let duration = start_time. elapsed ( ) . as_secs_f64 ( ) ;
204272
273+ // Log
205274 println ! (
206275 "action = 'query', status = {status}, duration = {duration}, error = {:?}" ,
207276 out. as_ref( ) . err( )
208277 ) ;
209278
279+ // Metrics
280+ if let Some ( MetricParams {
281+ counter, recorder, ..
282+ } ) = & self . 1
283+ {
284+ // Count
285+ counter. with_label_values ( & [ status] ) . inc ( ) ;
286+
287+ // Latency
288+ recorder. with_label_values ( & [ status] ) . observe ( duration) ;
289+ }
290+
210291 return out;
211292 }
212293}
@@ -226,7 +307,7 @@ pub trait Submit: Sync + Send {
226307}
227308
228309#[ async_trait]
229- impl < T : Submit > Submit for WithLogs < T > {
310+ impl < T : Submit > Submit for WithMetrics < T > {
230311 async fn submit ( & self , vs : & [ Pair ] ) -> Result < ( ) , SubmitError > {
231312 let start_time = Instant :: now ( ) ;
232313
@@ -242,11 +323,24 @@ impl<T: Submit> Submit for WithLogs<T> {
242323
243324 let duration = start_time. elapsed ( ) . as_secs_f64 ( ) ;
244325
326+ // Log
245327 println ! (
246328 "action = 'submit', status = {status}, duration = {duration}, error = {:?}" ,
247329 out. as_ref( ) . err( )
248330 ) ;
249331
332+ // Metrics
333+ if let Some ( MetricParams {
334+ counter, recorder, ..
335+ } ) = & self . 1
336+ {
337+ // Count
338+ counter. with_label_values ( & [ status] ) . inc ( ) ;
339+
340+ // Latency
341+ recorder. with_label_values ( & [ status] ) . observe ( duration) ;
342+ }
343+
250344 return out;
251345 }
252346}
@@ -424,23 +518,43 @@ pub struct CanisterMethods {
424518 submit : Arc < dyn Submit > ,
425519}
426520
427- impl From < Canister > for CanisterMethods {
428- fn from ( value : Canister ) -> Self {
521+ pub struct CanisterMethodsBuilder < ' a > {
522+ canister : Canister ,
523+ registry : Option < & ' a Registry > ,
524+ }
525+
526+ impl < ' a > CanisterMethodsBuilder < ' a > {
527+ pub fn new ( c : Canister ) -> Self {
429528 Self {
430- register : Arc :: new ( {
431- let v = value. clone ( ) ;
432- let v = WithLogs ( v) ;
433- WithThrottle ( v, ThrottleParams :: new ( Duration :: from_secs ( 10 ) ) )
434- } ) ,
435- query : Arc :: new ( {
436- let v = value. clone ( ) ;
437- let v = WithLogs ( v) ;
438- WithThrottle ( v, ThrottleParams :: new ( Duration :: from_secs ( 10 ) ) )
439- } ) ,
440- submit : Arc :: new ( {
441- let v = value. clone ( ) ;
442- WithLogs ( v)
443- } ) ,
529+ canister : c,
530+ registry : None ,
531+ }
532+ }
533+
534+ pub fn with_metrics ( mut self , r : & ' a Registry ) -> Self {
535+ self . registry = Some ( r) ;
536+ self
537+ }
538+
539+ pub fn build ( self ) -> CanisterMethods {
540+ CanisterMethods {
541+ register : {
542+ let v = self . canister . clone ( ) ;
543+ let v = WithMetrics ( v, self . registry . map ( |r| MetricParams :: new ( r, "register" ) ) ) ;
544+ let v = WithThrottle ( v, ThrottleParams :: new ( Duration :: from_secs ( 10 ) ) ) ;
545+ Arc :: new ( v)
546+ } ,
547+ query : {
548+ let v = self . canister . clone ( ) ;
549+ let v = WithMetrics ( v, self . registry . map ( |r| MetricParams :: new ( r, "query" ) ) ) ;
550+ let v = WithThrottle ( v, ThrottleParams :: new ( Duration :: from_secs ( 10 ) ) ) ;
551+ Arc :: new ( v)
552+ } ,
553+ submit : {
554+ let v = self . canister . clone ( ) ;
555+ let v = WithMetrics ( v, self . registry . map ( |r| MetricParams :: new ( r, "submit" ) ) ) ;
556+ Arc :: new ( v)
557+ } ,
444558 }
445559 }
446560}
0 commit comments