Skip to content

Commit 7aec3d6

Browse files
Copilot0xrinegade
andcommitted
Fix hardcoded mainnet network labels with dynamic network detection
Co-authored-by: 0xrinegade <[email protected]>
1 parent 5fa7b04 commit 7aec3d6

File tree

4 files changed

+114
-7
lines changed

4 files changed

+114
-7
lines changed

src/logging.rs

Lines changed: 80 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,47 @@ impl Metrics {
133133
/// Global metrics instance
134134
static 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() {

src/rpc/accounts.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub async fn get_balance(client: &RpcClient, pubkey: &Pubkey) -> McpResult<Value
3636
method,
3737
duration,
3838
Some("balance retrieved"),
39+
None,
3940
);
4041

4142
Ok(result)
@@ -53,6 +54,7 @@ pub async fn get_balance(client: &RpcClient, pubkey: &Pubkey) -> McpResult<Value
5354
error.error_type(),
5455
duration,
5556
Some(&error.to_log_value()),
57+
None,
5658
);
5759

5860
Err(error)
@@ -83,6 +85,7 @@ pub async fn get_account_info(client: &RpcClient, pubkey: &Pubkey) -> McpResult<
8385
method,
8486
duration,
8587
Some("account info retrieved"),
88+
None,
8689
);
8790

8891
Ok(result)
@@ -100,6 +103,7 @@ pub async fn get_account_info(client: &RpcClient, pubkey: &Pubkey) -> McpResult<
100103
error.error_type(),
101104
duration,
102105
Some(&error.to_log_value()),
106+
None,
103107
);
104108

105109
Err(error)
@@ -142,6 +146,7 @@ pub async fn get_account_info_with_config(
142146
method,
143147
duration,
144148
Some("account info with config retrieved"),
149+
None,
145150
);
146151

147152
Ok(result)
@@ -159,6 +164,7 @@ pub async fn get_account_info_with_config(
159164
error.error_type(),
160165
duration,
161166
Some(&error.to_log_value()),
167+
None,
162168
);
163169

164170
Err(error)
@@ -198,6 +204,7 @@ pub async fn get_multiple_accounts(client: &RpcClient, pubkeys: &[Pubkey]) -> Mc
198204
method,
199205
duration,
200206
Some(&format!("{} accounts retrieved", pubkeys.len())),
207+
Some(&client.url()),
201208
);
202209

203210
Ok(result)
@@ -215,6 +222,7 @@ pub async fn get_multiple_accounts(client: &RpcClient, pubkeys: &[Pubkey]) -> Mc
215222
error.error_type(),
216223
duration,
217224
Some(&error.to_log_value()),
225+
None,
218226
);
219227

220228
Err(error)
@@ -283,6 +291,7 @@ pub async fn get_multiple_accounts_with_config(
283291
error.error_type(),
284292
duration,
285293
Some(&error.to_log_value()),
294+
None,
286295
);
287296

288297
Err(error)
@@ -330,6 +339,7 @@ pub async fn get_program_accounts(client: &RpcClient, program_id: &Pubkey) -> Mc
330339
error.error_type(),
331340
duration,
332341
Some(&error.to_log_value()),
342+
None,
333343
);
334344

335345
Err(error)
@@ -395,6 +405,7 @@ pub async fn get_program_accounts_with_config(
395405
error.error_type(),
396406
duration,
397407
Some(&error.to_log_value()),
408+
None,
398409
);
399410

400411
Err(error)
@@ -434,6 +445,7 @@ pub async fn get_largest_accounts(
434445
method,
435446
duration,
436447
Some("largest accounts retrieved"),
448+
None,
437449
);
438450

439451
Ok(result)
@@ -451,6 +463,7 @@ pub async fn get_largest_accounts(
451463
error.error_type(),
452464
duration,
453465
Some(&error.to_log_value()),
466+
None,
454467
);
455468

456469
Err(error)
@@ -484,6 +497,7 @@ pub async fn get_minimum_balance_for_rent_exemption(
484497
method,
485498
duration,
486499
Some("minimum balance calculated"),
500+
None,
487501
);
488502

489503
Ok(result)
@@ -501,6 +515,7 @@ pub async fn get_minimum_balance_for_rent_exemption(
501515
error.error_type(),
502516
duration,
503517
Some(&error.to_log_value()),
518+
None,
504519
);
505520

506521
Err(error)

0 commit comments

Comments
 (0)