@@ -106,13 +106,21 @@ impl DistributedRuntime {
106106 component_registry : component:: Registry :: new ( ) ,
107107 is_static,
108108 instance_sources : Arc :: new ( Mutex :: new ( HashMap :: new ( ) ) ) ,
109- prometheus_registries_by_prefix : Arc :: new ( std:: sync:: Mutex :: new ( HashMap :: <
109+ metrics_registry_by_prefix : Arc :: new ( std:: sync:: Mutex :: new ( HashMap :: <
110110 String ,
111- prometheus :: Registry ,
111+ crate :: MetricsRegistryEntry ,
112112 > :: new ( ) ) ) ,
113113 system_health,
114114 } ;
115115
116+ // Register NATS client metrics after creation
117+ if let Err ( e) = distributed_runtime
118+ . nats_client ( )
119+ . register_metrics ( & distributed_runtime)
120+ {
121+ tracing:: warn!( "Failed to register NATS client metrics: {}" , e) ;
122+ }
123+
116124 // Start metrics server if enabled
117125 if let Some ( cancel_token) = cancel_token {
118126 let host = config. system_host . clone ( ) ;
@@ -226,11 +234,6 @@ impl DistributedRuntime {
226234 self . nats_client . clone ( )
227235 }
228236
229- /// Get metrics server information if available
230- pub fn metrics_server_info ( & self ) -> Option < Arc < crate :: metrics_server:: MetricsServerInfo > > {
231- self . metrics_server . get ( ) . cloned ( )
232- }
233-
234237 // todo(ryan): deprecate this as we move to Discovery traits and Component Identifiers
235238 pub fn etcd_client ( & self ) -> Option < etcd:: Client > {
236239 self . etcd_client . clone ( )
@@ -243,6 +246,97 @@ impl DistributedRuntime {
243246 pub fn instance_sources ( & self ) -> Arc < Mutex < HashMap < Endpoint , Weak < InstanceSource > > > > {
244247 self . instance_sources . clone ( )
245248 }
249+
250+ /// Get metrics server information if available
251+ pub fn metrics_server_info ( & self ) -> Option < Arc < crate :: metrics_server:: MetricsServerInfo > > {
252+ self . metrics_server . get ( ) . cloned ( )
253+ }
254+
255+ /// Add a Prometheus metric to a specific prefix's registry
256+ pub fn add_prometheus_metric (
257+ & self ,
258+ prefix : & str ,
259+ prometheus_metric : Box < dyn prometheus:: core:: Collector > ,
260+ ) -> anyhow:: Result < ( ) > {
261+ let mut registries = self . metrics_registry_by_prefix . lock ( ) . unwrap ( ) ;
262+ let entry = registries. entry ( prefix. to_string ( ) ) . or_default ( ) ;
263+ entry. prometheus_registry . register ( prometheus_metric) ?;
264+ Ok ( ( ) )
265+ }
266+
267+ /// Add a callback function to a metrics registry for the given prefix
268+ pub fn add_metrics_callback < F > ( & self , prefix : & str , callback : F )
269+ where
270+ F : Fn ( & dyn crate :: metrics:: MetricsRegistry ) -> anyhow:: Result < String >
271+ + Send
272+ + Sync
273+ + ' static ,
274+ {
275+ let mut registries = self . metrics_registry_by_prefix . lock ( ) . unwrap ( ) ;
276+ registries
277+ . entry ( prefix. to_string ( ) )
278+ . or_default ( )
279+ . add_callback ( self as & dyn crate :: metrics:: MetricsRegistry , callback) ;
280+ }
281+
282+ /// Execute all callbacks for a given prefix and return their results
283+ pub fn execute_metrics_callbacks ( & self , prefix : & str ) -> Vec < anyhow:: Result < String > > {
284+ let registries = self . metrics_registry_by_prefix . lock ( ) . unwrap ( ) ;
285+ if let Some ( entry) = registries. get ( prefix) {
286+ entry. execute_callbacks ( self as & dyn crate :: metrics:: MetricsRegistry )
287+ } else {
288+ Vec :: new ( )
289+ }
290+ }
291+
292+ /// Get all registered prefixes. Private because it is only used for testing.
293+ fn get_metrics_prefixes ( & self ) -> Vec < String > {
294+ let registries = self . metrics_registry_by_prefix . lock ( ) . unwrap ( ) ;
295+ registries. keys ( ) . cloned ( ) . collect ( )
296+ }
297+ }
298+
299+ #[ cfg( test) ]
300+ mod tests {
301+ use super :: * ;
302+
303+ #[ test]
304+ fn test_metrics_registry_methods ( ) {
305+ let rt = tokio:: runtime:: Runtime :: new ( ) . unwrap ( ) ;
306+ let drt = rt. block_on ( async {
307+ let runtime = Runtime :: single_threaded ( ) . unwrap ( ) ;
308+ DistributedRuntime :: from_settings_without_discovery ( runtime)
309+ . await
310+ . unwrap ( )
311+ } ) ;
312+
313+ // Test adding callbacks
314+ drt. add_metrics_callback ( "test_prefix" , |_| Ok ( "test_callback" . to_string ( ) ) ) ;
315+ drt. add_metrics_callback ( "test_prefix" , |_| Ok ( "test_callback2" . to_string ( ) ) ) ;
316+
317+ // Test executing callbacks
318+ let results = drt. execute_metrics_callbacks ( "test_prefix" ) ;
319+ assert_eq ! ( results. len( ) , 2 ) ;
320+ assert_eq ! ( results[ 0 ] . as_ref( ) . unwrap( ) , "test_callback" ) ;
321+ assert_eq ! ( results[ 1 ] . as_ref( ) . unwrap( ) , "test_callback2" ) ;
322+
323+ // Test getting prefixes
324+ let prefixes = drt. get_metrics_prefixes ( ) ;
325+ assert ! ( prefixes. contains( & "test_prefix" . to_string( ) ) ) ;
326+
327+ // Test non-existent prefix
328+ let empty_results = drt. execute_metrics_callbacks ( "non_existent" ) ;
329+ assert_eq ! ( empty_results. len( ) , 0 ) ;
330+
331+ // Test adding a Prometheus metric
332+ let counter = prometheus:: Counter :: new ( "test_counter" , "A test counter" ) . unwrap ( ) ;
333+ drt. add_prometheus_metric ( "test_prefix" , Box :: new ( counter. clone ( ) ) )
334+ . unwrap ( ) ;
335+
336+ // Verify the metric was added by checking if the prefix exists
337+ let prefixes = drt. get_metrics_prefixes ( ) ;
338+ assert ! ( prefixes. contains( & "test_prefix" . to_string( ) ) ) ;
339+ }
246340}
247341
248342#[ derive( Dissolve ) ]
0 commit comments