Skip to content

Commit da41016

Browse files
committed
async: Log every esplora request
Log every request, so we know 1) which endpoints we're calling, 2) how many requests we're making, and 3) which services these requests are sent to. TODO(max): Eventually this should be done with metrics, not logs. License: PolyForm Noncommercial License 1.0.0
1 parent 09e03b5 commit da41016

File tree

1 file changed

+67
-0
lines changed

1 file changed

+67
-0
lines changed

src/async.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ impl<S: Sleeper> AsyncClient<S> {
263263
let url = format!("{}{}", self.url, path);
264264
let body = serialize::<T>(&body).to_lower_hex_string();
265265

266+
self.log_request(&url, "POST");
266267
let response = self.client.post(url).body(body).send().await?;
267268

268269
if !response.status().is_success() {
@@ -466,6 +467,7 @@ impl<S: Sleeper> AsyncClient<S> {
466467
let mut attempts = 0;
467468

468469
loop {
470+
self.log_request(url, "GET");
469471
match self.client.get(url).send().await? {
470472
resp if attempts < self.max_retries && is_status_retryable(resp.status()) => {
471473
S::sleep(delay).await;
@@ -476,6 +478,40 @@ impl<S: Sleeper> AsyncClient<S> {
476478
}
477479
}
478480
}
481+
482+
/// Lexe patch: Log every request, so we know
483+
///
484+
/// 1) which endpoints we're calling,
485+
/// 2) how many requests we're making, and
486+
/// 3) which services these requests are sent to.
487+
///
488+
/// TODO(max): Eventually this should be done with metrics, not logs.
489+
fn log_request(&self, url: &str, method: &str) {
490+
let base_url = &self.url;
491+
492+
// For user privacy, log only the part of the path up to the first `/`.
493+
let path_prefix = path_prefix(&self.url, url);
494+
495+
// "Esplora request: GET https://blockstream.info/api/address"
496+
// "Esplora request: POST https://mempool.space/api/tx"
497+
debug!("Esplora request: {method} {base_url}/{path_prefix}");
498+
}
499+
}
500+
501+
/// `base_url`: "https://mempool.space/api"
502+
/// `url`: "/tx/foo"
503+
/// => `path_prefix`: "/tx"
504+
fn path_prefix<'a>(base_url: &str, url: &'a str) -> &'a str {
505+
if url.len() <= base_url.len() + 1 {
506+
return url;
507+
}
508+
509+
let path = &url[base_url.len() + 1..];
510+
if let Some(slash_idx) = path.find('/') {
511+
&url[..base_url.len() + 1 + slash_idx]
512+
} else {
513+
url
514+
}
479515
}
480516

481517
fn is_status_retryable(status: reqwest::StatusCode) -> bool {
@@ -498,3 +534,34 @@ impl Sleeper for DefaultSleeper {
498534
tokio::time::sleep(dur)
499535
}
500536
}
537+
538+
#[cfg(test)]
539+
mod test {
540+
use super::*;
541+
542+
/// $ cargo test test_sanitize -- --show-output
543+
#[test]
544+
fn test_path_prefix() {
545+
let base_url = "https://mempool.space/api";
546+
let test_cases = [
547+
["https://mempool.space", "https://mempool.space"],
548+
["https://mempool.space/api/", "https://mempool.space/api/"],
549+
[
550+
"https://mempool.space/api/tx",
551+
"https://mempool.space/api/tx",
552+
],
553+
[
554+
"https://mempool.space/api/tx/foo",
555+
"https://mempool.space/api/tx",
556+
],
557+
];
558+
559+
for [url, expected] in test_cases {
560+
let sanitized = path_prefix(base_url, url);
561+
assert_eq!(sanitized, expected);
562+
println!("Original URL: {url}");
563+
println!("Sanitized: {sanitized}");
564+
println!("---");
565+
}
566+
}
567+
}

0 commit comments

Comments
 (0)