@@ -8,9 +8,10 @@ use eyre::WrapErr;
88use  http:: { header:: CONTENT_TYPE ,  HeaderValue ,  Response } ; 
99use  metrics:: describe_gauge; 
1010use  metrics_process:: Collector ; 
11+ use  reqwest:: Client ; 
1112use  reth_metrics:: metrics:: Unit ; 
1213use  reth_tasks:: TaskExecutor ; 
13- use  std:: { convert:: Infallible ,  net:: SocketAddr ,  sync:: Arc } ; 
14+ use  std:: { convert:: Infallible ,  net:: SocketAddr ,  sync:: Arc ,  time :: Duration } ; 
1415
1516/// Configuration for the [`MetricServer`] 
1617#[ derive( Debug ) ]  
@@ -20,6 +21,8 @@ pub struct MetricServerConfig {
2021    chain_spec_info :  ChainSpecInfo , 
2122    task_executor :  TaskExecutor , 
2223    hooks :  Hooks , 
24+     push_gateway_url :  Option < String > , 
25+     push_gateway_interval :  Duration , 
2326} 
2427
2528impl  MetricServerConfig  { 
@@ -31,7 +34,22 @@ impl MetricServerConfig {
3134        task_executor :  TaskExecutor , 
3235        hooks :  Hooks , 
3336    )  -> Self  { 
34-         Self  {  listen_addr,  hooks,  task_executor,  version_info,  chain_spec_info } 
37+         Self  { 
38+             listen_addr, 
39+             hooks, 
40+             task_executor, 
41+             version_info, 
42+             chain_spec_info, 
43+             push_gateway_url :  None , 
44+             push_gateway_interval :  Duration :: from_secs ( 5 ) , 
45+         } 
46+     } 
47+ 
48+     /// Set the gateway URL and interval for pushing metrics 
49+ pub  fn  with_push_gateway ( mut  self ,  url :  Option < String > ,  interval :  Duration )  -> Self  { 
50+         self . push_gateway_url  = url; 
51+         self . push_gateway_interval  = interval; 
52+         self 
3553    } 
3654} 
3755
@@ -49,18 +67,35 @@ impl MetricServer {
4967
5068    /// Spawns the metrics server 
5169pub  async  fn  serve ( & self )  -> eyre:: Result < ( ) >  { 
52-         let  MetricServerConfig  {  listen_addr,  hooks,  task_executor,  version_info,  chain_spec_info }  =
53-             & self . config ; 
70+         let  MetricServerConfig  { 
71+             listen_addr, 
72+             hooks, 
73+             task_executor, 
74+             version_info, 
75+             chain_spec_info, 
76+             push_gateway_url, 
77+             push_gateway_interval, 
78+         }  = & self . config ; 
5479
55-         let  hooks  = hooks. clone ( ) ; 
80+         let  hooks_for_endpoint  = hooks. clone ( ) ; 
5681        self . start_endpoint ( 
5782            * listen_addr, 
58-             Arc :: new ( move  || hooks . iter ( ) . for_each ( |hook| hook ( ) ) ) , 
83+             Arc :: new ( move  || hooks_for_endpoint . iter ( ) . for_each ( |hook| hook ( ) ) ) , 
5984            task_executor. clone ( ) , 
6085        ) 
6186        . await 
6287        . wrap_err_with ( || format ! ( "Could not start Prometheus endpoint at {listen_addr}" ) ) ?; 
6388
89+         // Start push-gateway task if configured 
90+         if  let  Some ( url)  = push_gateway_url { 
91+             self . start_push_gateway_task ( 
92+                 url. clone ( ) , 
93+                 * push_gateway_interval, 
94+                 hooks. clone ( ) , 
95+                 task_executor. clone ( ) , 
96+             ) ?; 
97+         } 
98+ 
6499        // Describe metrics after recorder installation 
65100        describe_db_metrics ( ) ; 
66101        describe_static_file_metrics ( ) ; 
@@ -128,6 +163,51 @@ impl MetricServer {
128163
129164        Ok ( ( ) ) 
130165    } 
166+ 
167+     /// Starts a background task to push metrics to a metrics gateway 
168+ fn  start_push_gateway_task ( 
169+         & self , 
170+         url :  String , 
171+         interval :  Duration , 
172+         hooks :  Hooks , 
173+         task_executor :  TaskExecutor , 
174+     )  -> eyre:: Result < ( ) >  { 
175+         let  client = Client :: builder ( ) 
176+             . build ( ) 
177+             . wrap_err ( "Could not create HTTP client to push metrics to gateway" ) ?; 
178+         task_executor. spawn_with_graceful_shutdown_signal ( move  |mut  signal| { 
179+             Box :: pin ( async  move  { 
180+                 tracing:: info!( url = %url,  interval = ?interval,  "Starting task to push metrics to gateway" ) ; 
181+                 let  handle = install_prometheus_recorder ( ) ; 
182+                 loop  { 
183+                     tokio:: select! { 
184+                         _ = & mut  signal => { 
185+                             tracing:: info!( "Shutting down task to push metrics to gateway" ) ; 
186+                             break ; 
187+                         } 
188+                         _ = tokio:: time:: sleep( interval)  => { 
189+                             hooks. iter( ) . for_each( |hook| hook( ) ) ; 
190+                             let  metrics = handle. handle( ) . render( ) ; 
191+                             match  client. put( & url) . header( "Content-Type" ,  "text/plain" ) . body( metrics) . send( ) . await  { 
192+                                 Ok ( response)  => { 
193+                                     if  !response. status( ) . is_success( )  { 
194+                                         tracing:: warn!( 
195+                                             status = %response. status( ) , 
196+                                             "Failed to push metrics to gateway" 
197+                                         ) ; 
198+                                     } 
199+                                 } 
200+                                 Err ( err)  => { 
201+                                     tracing:: warn!( %err,  "Failed to push metrics to gateway" ) ; 
202+                                 } 
203+                             } 
204+                         } 
205+                     } 
206+                 } 
207+             } ) 
208+         } ) ; 
209+         Ok ( ( ) ) 
210+     } 
131211} 
132212
133213fn  describe_db_metrics ( )  { 
0 commit comments