@@ -133,6 +133,47 @@ impl Metrics {
133133/// Global metrics instance
134134static METRICS : once_cell:: sync:: Lazy < Metrics > = once_cell:: sync:: Lazy :: new ( || Metrics :: default ( ) ) ;
135135
136+ /// Detect network name from RPC URL
137+ ///
138+ /// Analyzes the RPC URL to determine the network name for Prometheus metrics.
139+ /// This function recognizes common Solana network patterns and returns appropriate labels.
140+ ///
141+ /// # Arguments
142+ /// * `rpc_url` - The RPC endpoint URL
143+ ///
144+ /// # Returns
145+ /// * `&'static str` - Network name for metrics (e.g., "mainnet", "devnet", "testnet", "localhost", "custom")
146+ ///
147+ /// # Examples
148+ /// ```
149+ /// use solana_mcp_server::logging::detect_network_from_url;
150+ ///
151+ /// assert_eq!(detect_network_from_url("https://api.mainnet-beta.solana.com"), "mainnet");
152+ /// assert_eq!(detect_network_from_url("https://api.devnet.solana.com"), "devnet");
153+ /// assert_eq!(detect_network_from_url("https://api.testnet.solana.com"), "testnet");
154+ /// assert_eq!(detect_network_from_url("http://localhost:8899"), "localhost");
155+ /// assert_eq!(detect_network_from_url("https://api.opensvm.com"), "custom");
156+ /// ```
157+ pub fn detect_network_from_url ( rpc_url : & str ) -> & ' static str {
158+ let url_lower = rpc_url. to_lowercase ( ) ;
159+
160+ // Check for common Solana network patterns
161+ if url_lower. contains ( "mainnet-beta" ) || url_lower. contains ( "mainnet.solana.com" ) {
162+ "mainnet"
163+ } else if url_lower. contains ( "devnet" ) {
164+ "devnet"
165+ } else if url_lower. contains ( "testnet" ) {
166+ "testnet"
167+ } else if url_lower. contains ( "localhost" ) || url_lower. contains ( "127.0.0.1" ) {
168+ "localhost"
169+ } else if url_lower. contains ( "opensvm.com" ) {
170+ "opensvm"
171+ } else {
172+ // For any other URL (custom networks, etc.)
173+ "custom"
174+ }
175+ }
176+
136177/// Initialize structured logging with tracing
137178///
138179/// Sets up JSON-formatted structured logging with the tracing crate.
@@ -202,7 +243,7 @@ pub fn log_rpc_request_start(
202243 info ! ( "RPC request started" ) ;
203244}
204245
205- /// Log RPC request success with context
246+ /// Log RPC request success with context and URL
206247#[ instrument( skip_all, fields(
207248 request_id = %request_id,
208249 method = %method,
@@ -214,12 +255,16 @@ pub fn log_rpc_request_success(
214255 method : & str ,
215256 duration_ms : u64 ,
216257 result_summary : Option < & str > ,
258+ rpc_url : Option < & str > ,
217259) {
218260 METRICS . increment_successful_calls ( duration_ms) ;
219261
220- // Also record in Prometheus metrics
262+ // Also record in Prometheus metrics with dynamic network detection
221263 let duration_seconds = duration_ms as f64 / 1000.0 ;
222- crate :: metrics:: PROMETHEUS_METRICS . record_success ( method, "mainnet" , duration_seconds) ;
264+ let network = rpc_url
265+ . map ( detect_network_from_url)
266+ . unwrap_or ( "unknown" ) ;
267+ crate :: metrics:: PROMETHEUS_METRICS . record_success ( method, network, duration_seconds) ;
223268
224269 let span = Span :: current ( ) ;
225270 span. record ( "duration_ms" , duration_ms) ;
@@ -231,7 +276,7 @@ pub fn log_rpc_request_success(
231276 info ! ( "RPC request completed successfully" ) ;
232277}
233278
234- /// Log RPC request failure with context
279+ /// Log RPC request failure with context and URL
235280#[ instrument( skip_all, fields(
236281 request_id = %request_id,
237282 method = %method,
@@ -245,12 +290,16 @@ pub fn log_rpc_request_failure(
245290 error_type : & str ,
246291 duration_ms : u64 ,
247292 error_details : Option < & Value > ,
293+ rpc_url : Option < & str > ,
248294) {
249295 METRICS . increment_failed_calls ( error_type, Some ( method) , duration_ms) ;
250296
251- // Also record in Prometheus metrics
297+ // Also record in Prometheus metrics with dynamic network detection
252298 let duration_seconds = duration_ms as f64 / 1000.0 ;
253- crate :: metrics:: PROMETHEUS_METRICS . record_failure ( method, "mainnet" , error_type, duration_seconds) ;
299+ let network = rpc_url
300+ . map ( detect_network_from_url)
301+ . unwrap_or ( "unknown" ) ;
302+ crate :: metrics:: PROMETHEUS_METRICS . record_failure ( method, network, error_type, duration_seconds) ;
254303
255304 let span = Span :: current ( ) ;
256305 span. record ( "duration_ms" , duration_ms) ;
@@ -509,6 +558,7 @@ macro_rules! log_rpc_call {
509558 $method,
510559 duration,
511560 Some ( "request completed" ) ,
561+ Some ( & $client. url( ) ) ,
512562 ) ;
513563
514564 Ok ( result)
@@ -526,6 +576,7 @@ macro_rules! log_rpc_call {
526576 & error. error_type( ) ,
527577 duration,
528578 Some ( & error. to_log_value( ) ) ,
579+ Some ( & $client. url( ) ) ,
529580 ) ;
530581
531582 Err ( error)
@@ -552,6 +603,7 @@ macro_rules! log_rpc_call {
552603 $method,
553604 duration,
554605 Some ( "request completed" ) ,
606+ Some ( & $client. url( ) ) ,
555607 ) ;
556608
557609 Ok ( result)
@@ -569,6 +621,7 @@ macro_rules! log_rpc_call {
569621 & error. error_type( ) ,
570622 duration,
571623 Some ( & error. to_log_value( ) ) ,
624+ Some ( & $client. url( ) ) ,
572625 ) ;
573626
574627 Err ( error)
@@ -653,7 +706,8 @@ mod tests {
653706 request_id,
654707 "getBalance" ,
655708 150 ,
656- Some ( "balance returned" )
709+ Some ( "balance returned" ) ,
710+ None ,
657711 ) ;
658712
659713 log_validation_error (
@@ -736,6 +790,25 @@ mod tests {
736790 assert ! ( Account :: LEN > 0 ) ;
737791 assert ! ( Mint :: LEN > 0 ) ;
738792 }
793+
794+ #[ test]
795+ fn test_network_detection_from_url ( ) {
796+ // Test common Solana network patterns
797+ assert_eq ! ( detect_network_from_url( "https://api.mainnet-beta.solana.com" ) , "mainnet" ) ;
798+ assert_eq ! ( detect_network_from_url( "https://api.devnet.solana.com" ) , "devnet" ) ;
799+ assert_eq ! ( detect_network_from_url( "https://api.testnet.solana.com" ) , "testnet" ) ;
800+ assert_eq ! ( detect_network_from_url( "http://localhost:8899" ) , "localhost" ) ;
801+ assert_eq ! ( detect_network_from_url( "http://127.0.0.1:8899" ) , "localhost" ) ;
802+ assert_eq ! ( detect_network_from_url( "https://api.opensvm.com" ) , "opensvm" ) ;
803+
804+ // Test case insensitive matching
805+ assert_eq ! ( detect_network_from_url( "https://API.MAINNET-BETA.SOLANA.COM" ) , "mainnet" ) ;
806+ assert_eq ! ( detect_network_from_url( "HTTPS://API.DEVNET.SOLANA.COM" ) , "devnet" ) ;
807+
808+ // Test custom/unknown networks
809+ assert_eq ! ( detect_network_from_url( "https://custom-rpc.example.com" ) , "custom" ) ;
810+ assert_eq ! ( detect_network_from_url( "https://my-solana-node.com" ) , "custom" ) ;
811+ }
739812
740813 #[ test]
741814 fn test_solana_dependency_compatibility ( ) {
0 commit comments