11use {
22 crate :: {
33 api:: { ApiBlockChainState , NetworkId , RestError , StateTag } ,
4- history:: RequestStatus ,
4+ config:: LATENCY_BUCKETS ,
5+ history:: { RequestQueryBuilder , RequestStatus , SearchField } ,
56 } ,
67 axum:: {
78 extract:: { Query , State } ,
89 Json ,
910 } ,
1011 chrono:: { DateTime , Utc } ,
12+ prometheus_client:: {
13+ encoding:: { EncodeLabelSet , EncodeLabelValue } ,
14+ metrics:: { family:: Family , histogram:: Histogram } ,
15+ registry:: Registry ,
16+ } ,
17+ std:: sync:: Arc ,
18+ tokio:: { sync:: RwLock , time:: Instant } ,
1119 utoipa:: IntoParams ,
1220} ;
1321
14- #[ derive( Debug , serde:: Serialize , serde:: Deserialize , IntoParams ) ]
22+ #[ derive( Debug ) ]
23+ pub struct ExplorerMetrics {
24+ results_latency : Family < QueryTags , Histogram > ,
25+ count_latency : Family < QueryTags , Histogram > ,
26+ }
27+
28+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , EncodeLabelSet ) ]
29+ pub struct QueryTags {
30+ search_type : Option < SearchType > ,
31+ has_network_id_filter : bool ,
32+ has_state_filter : bool ,
33+ }
34+
35+ impl < ' a > From < RequestQueryBuilder < ' a > > for QueryTags {
36+ fn from ( builder : RequestQueryBuilder < ' a > ) -> Self {
37+ QueryTags {
38+ search_type : builder. search . map ( |val| match val {
39+ SearchField :: TxHash ( _) => SearchType :: TxHash ,
40+ SearchField :: Sender ( _) => SearchType :: Sender ,
41+ SearchField :: SequenceNumber ( _) => SearchType :: SequenceNumber ,
42+ } ) ,
43+ has_network_id_filter : builder. network_id . is_some ( ) ,
44+ has_state_filter : builder. state . is_some ( ) ,
45+ }
46+ }
47+ }
48+
49+ #[ derive( Debug , Clone , PartialEq , Eq , Hash , EncodeLabelValue ) ]
50+ enum SearchType {
51+ TxHash ,
52+ Sender ,
53+ SequenceNumber ,
54+ }
55+
56+ impl ExplorerMetrics {
57+ pub async fn new ( metrics_registry : Arc < RwLock < Registry > > ) -> Self {
58+ let mut guard = metrics_registry. write ( ) . await ;
59+ let sub_registry = guard. sub_registry_with_prefix ( "explorer" ) ;
60+
61+ let results_latency = Family :: < QueryTags , Histogram > :: new_with_constructor ( || {
62+ Histogram :: new ( LATENCY_BUCKETS . into_iter ( ) )
63+ } ) ;
64+ sub_registry. register (
65+ "results_latency" ,
66+ "The latency of requests to the database to collect the limited results." ,
67+ results_latency. clone ( ) ,
68+ ) ;
69+
70+ let count_latency = Family :: < QueryTags , Histogram > :: new_with_constructor ( || {
71+ Histogram :: new ( LATENCY_BUCKETS . into_iter ( ) )
72+ } ) ;
73+ sub_registry. register (
74+ "count_latency" ,
75+ "The latency of requests to the database to collect the total matching result count." ,
76+ count_latency. clone ( ) ,
77+ ) ;
78+
79+ Self {
80+ results_latency,
81+ count_latency,
82+ }
83+ }
84+ }
85+
86+ #[ derive( Debug , Clone , serde:: Serialize , serde:: Deserialize , IntoParams ) ]
1587#[ into_params( parameter_in=Query ) ]
1688pub struct ExplorerQueryParams {
1789 /// Only return logs that are newer or equal to this timestamp. Timestamp is in ISO 8601 format with UTC timezone.
@@ -96,7 +168,13 @@ pub async fn explorer(
96168 query = query. max_timestamp ( max_timestamp) ;
97169 }
98170
99- let ( requests, total_results) = tokio:: join!( query. execute( ) , query. count_results( ) ) ;
171+ let results_latency = & state. explorer_metrics . results_latency ;
172+ let count_latency = & state. explorer_metrics . count_latency ;
173+ let query_tags = & query. clone ( ) . into ( ) ;
174+ let ( requests, total_results) = tokio:: join!(
175+ measure_latency( results_latency, query_tags, query. execute( ) ) ,
176+ measure_latency( count_latency, query_tags, query. count_results( ) )
177+ ) ;
100178 let requests = requests. map_err ( |_| RestError :: TemporarilyUnavailable ) ?;
101179 let total_results = total_results. map_err ( |_| RestError :: TemporarilyUnavailable ) ?;
102180
@@ -105,3 +183,19 @@ pub async fn explorer(
105183 total_results,
106184 } ) )
107185}
186+
187+ async fn measure_latency < T , F > (
188+ metric : & Family < QueryTags , Histogram > ,
189+ query_tags : & QueryTags ,
190+ function : F ,
191+ ) -> T
192+ where
193+ F : std:: future:: Future < Output = T > ,
194+ {
195+ let start = Instant :: now ( ) ;
196+ let return_value = function. await ;
197+ metric
198+ . get_or_create ( query_tags)
199+ . observe ( start. elapsed ( ) . as_secs_f64 ( ) ) ;
200+ return_value
201+ }
0 commit comments