1
+ use actix_web:: web:: Path ;
1
2
use futures:: lock:: Mutex ;
2
3
use lazy_static:: lazy_static;
3
4
use std:: collections:: HashMap ;
@@ -7,6 +8,7 @@ use url::Url;
7
8
8
9
use ya_service_api:: { CliCtx , MetricsCtx } ;
9
10
use ya_service_api_interfaces:: Provider ;
11
+ use ya_utils_consent:: ConsentScope ;
10
12
11
13
use crate :: metrics:: Metrics ;
12
14
@@ -72,6 +74,15 @@ lazy_static! {
72
74
static ref METRICS : Arc <Mutex <Metrics >> = Metrics :: new( ) ;
73
75
}
74
76
77
+ pub async fn export_metrics_filtered_web ( typ : Path < String > ) -> String {
78
+ let allowed_prefixes = typ. split ( ',' ) . collect :: < Vec < _ > > ( ) ;
79
+ log:: info!( "Allowed prefixes: {:?}" , allowed_prefixes) ;
80
+ let filter = MetricsFilter {
81
+ allowed_prefixes : & allowed_prefixes,
82
+ } ;
83
+ export_metrics_filtered ( Some ( filter) ) . await
84
+ }
85
+
75
86
impl MetricsService {
76
87
pub async fn gsb < C : Provider < Self , CliCtx > > ( context : & C ) -> anyhow:: Result < ( ) > {
77
88
// This should initialize Metrics. We need to do this before all other services will start.
@@ -89,35 +100,86 @@ impl MetricsService {
89
100
pub fn rest < C : Provider < Self , ( ) > > ( _ctx : & C ) -> actix_web:: Scope {
90
101
actix_web:: Scope :: new ( "metrics-api/v1" )
91
102
// TODO:: add wrapper injecting Bearer to avoid hack in auth middleware
92
- . route ( "/expose" , actix_web:: web:: get ( ) . to ( export_metrics ) )
103
+ . route ( "/expose" , actix_web:: web:: get ( ) . to ( export_metrics_local ) )
93
104
. route ( "/sorted" , actix_web:: web:: get ( ) . to ( export_metrics_sorted) )
105
+ . route (
106
+ "/filtered/{typ}" ,
107
+ actix_web:: web:: get ( ) . to ( export_metrics_filtered_web) ,
108
+ )
109
+ . route (
110
+ "/filtered" ,
111
+ actix_web:: web:: get ( ) . to ( export_metrics_for_push) ,
112
+ )
94
113
}
95
114
}
115
+
116
+ pub ( crate ) struct MetricsFilter < ' a > {
117
+ pub allowed_prefixes : & ' a [ & ' a str ] ,
118
+ }
119
+
96
120
//algorith is returning metrics in random order, which is fine for prometheus, but not for human checking metrics
97
- pub fn sort_metrics_txt ( metrics : & str ) -> String {
121
+ pub fn sort_metrics_txt ( metrics : & str , filter : Option < MetricsFilter < ' _ > > ) -> String {
98
122
let Some ( first_line_idx) = metrics. find ( '\n' ) else {
99
123
return metrics. to_string ( ) ;
100
124
} ;
101
125
let ( first_line, metrics_content) = metrics. split_at ( first_line_idx) ;
102
126
103
- let mut entries = metrics_content
127
+ let entries = metrics_content
104
128
. split ( "\n \n " ) //splitting by double new line to get separate metrics
105
129
. map ( |s| {
106
130
let trimmed = s. trim ( ) ;
107
131
let mut lines = trimmed. split ( '\n' ) . collect :: < Vec < _ > > ( ) ;
108
132
lines. sort ( ) ; //sort by properties
109
- lines. join ( "\n " )
133
+ ( lines. get ( 1 ) . unwrap_or ( & "" ) . to_string ( ) , lines . join ( "\n " ) )
110
134
} )
111
- . collect :: < Vec < String > > ( ) ;
112
- entries. sort ( ) ; //sort by metric name
135
+ . collect :: < Vec < ( String , String ) > > ( ) ;
136
+
137
+ let mut final_entries = if let Some ( filter) = filter {
138
+ let mut final_entries = Vec :: with_capacity ( entries. len ( ) ) ;
139
+ for entry in entries {
140
+ for prefix in filter. allowed_prefixes {
141
+ if entry. 0 . starts_with ( prefix) {
142
+ log:: info!( "Adding entry: {}" , entry. 0 ) ;
143
+ final_entries. push ( entry. 1 ) ;
144
+ break ;
145
+ }
146
+ }
147
+ }
148
+ final_entries
149
+ } else {
150
+ entries. into_iter ( ) . map ( |( _, s) | s) . collect ( )
151
+ } ;
113
152
114
- first_line. to_string ( ) + "\n " + entries. join ( "\n \n " ) . as_str ( )
153
+ final_entries. sort ( ) ;
154
+
155
+ first_line. to_string ( ) + "\n " + final_entries. join ( "\n \n " ) . as_str ( ) + "\n "
156
+ }
157
+
158
+ pub async fn export_metrics_filtered ( metrics_filter : Option < MetricsFilter < ' _ > > ) -> String {
159
+ sort_metrics_txt ( & METRICS . lock ( ) . await . export ( ) , metrics_filter)
115
160
}
116
161
117
162
async fn export_metrics_sorted ( ) -> String {
118
- sort_metrics_txt ( & METRICS . lock ( ) . await . export ( ) )
163
+ sort_metrics_txt ( & METRICS . lock ( ) . await . export ( ) , None )
164
+ }
165
+
166
+ pub async fn export_metrics_for_push ( ) -> String {
167
+ //if consent is not set assume we are not allowed to push metrics
168
+ let stats_consent = ya_utils_consent:: have_consent_cached ( ConsentScope :: Stats )
169
+ . consent
170
+ . unwrap_or ( false ) ;
171
+ let filter = if stats_consent {
172
+ log:: info!( "Pushing all metrics, because stats consent is given" ) ;
173
+ None
174
+ } else {
175
+ // !internal_consent && !external_consent
176
+ log:: info!( "Not pushing metrics, because stats consent is not given" ) ;
177
+ return "" . to_string ( ) ;
178
+ } ;
179
+
180
+ export_metrics_filtered ( filter) . await
119
181
}
120
182
121
- pub async fn export_metrics ( ) -> String {
122
- METRICS . lock ( ) . await . export ( )
183
+ pub async fn export_metrics_local ( ) -> String {
184
+ export_metrics_sorted ( ) . await
123
185
}
0 commit comments