Skip to content

Commit d816d0a

Browse files
authored
test(o11y): added basic tests for (http) client requests (#3809)
- Added stable "tracing" tests for basic success, parse errors and HTTP server errors. - Fixed handling of URI attributes on HTTP Error path
1 parent 25a62c1 commit d816d0a

File tree

3 files changed

+308
-18
lines changed

3 files changed

+308
-18
lines changed

src/gax-internal/src/http.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ impl ReqwestClient {
182182
let request = builder.build().map_err(map_send_error)?;
183183
#[cfg(google_cloud_unstable_tracing)]
184184
let method = request.method().clone();
185+
#[cfg(google_cloud_unstable_tracing)]
186+
let url = request.url().clone();
185187

186188
#[cfg(google_cloud_unstable_tracing)]
187189
let (reqwest_result, span) = if self._tracing_enabled {
@@ -213,6 +215,7 @@ impl ReqwestClient {
213215
intermediate_result.as_ref(),
214216
_attempt_count,
215217
&method,
218+
&url,
216219
);
217220
}
218221

@@ -589,11 +592,12 @@ mod tests {
589592
} // T4 exit
590593

591594
let response = resp_from_code_content(reqwest::StatusCode::OK, "{}").unwrap();
595+
let url = "https://example.com".parse().unwrap();
592596

593597
// Manually call the enrichment function, mimicking request_attempt
594598
{
595599
let _enter = t3_span.enter();
596-
record_intermediate_client_request(Ok(&response), 1, &Method::GET);
600+
record_intermediate_client_request(Ok(&response), 1, &Method::GET, &url);
597601
}
598602

599603
let _ = super::to_http_response::<wkt::Empty>(response)

src/gax-internal/src/observability/http_tracing.rs

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ pub(crate) fn record_intermediate_client_request(
132132
result: Result<&reqwest::Response, &gax::error::Error>,
133133
prior_attempt_count: u32,
134134
method: &http::Method,
135+
request_url: &reqwest::Url,
135136
) {
136137
let span = Span::current();
137138
if span.is_disabled() {
@@ -149,20 +150,29 @@ pub(crate) fn record_intermediate_client_request(
149150

150151
span.record(otel_trace::HTTP_REQUEST_METHOD, method.as_str());
151152

153+
let final_url = match result {
154+
Ok(response) => response.url(),
155+
Err(_) => request_url,
156+
};
157+
158+
span.record(
159+
otel_trace::SERVER_ADDRESS,
160+
final_url
161+
.host_str()
162+
.map(|h| h.trim_start_matches('[').trim_end_matches(']'))
163+
.unwrap_or(""),
164+
);
165+
span.record(
166+
otel_trace::SERVER_PORT,
167+
final_url
168+
.port_or_known_default()
169+
.map(|p| p as i64)
170+
.unwrap_or(0),
171+
);
172+
span.record(otel_trace::URL_FULL, final_url.as_str());
173+
152174
match result {
153175
Ok(response) => {
154-
let url = response.url();
155-
span.record(
156-
otel_trace::SERVER_ADDRESS,
157-
url.host_str()
158-
.map(|h| h.trim_start_matches('[').trim_end_matches(']'))
159-
.unwrap_or(""),
160-
);
161-
span.record(
162-
otel_trace::SERVER_PORT,
163-
url.port_or_known_default().map(|p| p as i64).unwrap_or(0),
164-
);
165-
span.record(otel_trace::URL_FULL, url.as_str());
166176
span.record(
167177
otel_trace::HTTP_RESPONSE_STATUS_CODE,
168178
response.status().as_u16() as i64,
@@ -172,8 +182,6 @@ pub(crate) fn record_intermediate_client_request(
172182
if let Some(status) = err.http_status_code() {
173183
span.record(otel_trace::HTTP_RESPONSE_STATUS_CODE, status as i64);
174184
}
175-
// For errors, we might not have the final URL if the request failed before sending.
176-
// We rely on the initial URL set on the T3 span.
177185
}
178186
}
179187

@@ -540,12 +548,13 @@ mod tests {
540548
let span = create_client_request_span("test_span", "test_method", &TEST_INFO);
541549
let _enter = span.enter();
542550

551+
let url = "https://example.com/test".parse::<reqwest::Url>().unwrap();
543552
let response = http::Response::builder()
544553
.status(StatusCode::OK)
545554
.body("")
546555
.unwrap();
547556
let reqwest_response: reqwest::Response = response.into();
548-
record_intermediate_client_request(Ok(&reqwest_response), 1, &Method::GET);
557+
record_intermediate_client_request(Ok(&reqwest_response), 1, &Method::GET, &url);
549558

550559
let captured = TestLayer::capture(&guard);
551560
assert_eq!(captured.len(), 1, "captured spans: {:?}", captured);
@@ -576,12 +585,13 @@ mod tests {
576585
);
577586
let _enter = span.enter();
578587

588+
let url = "https://example.com/test".parse::<reqwest::Url>().unwrap();
579589
let response = http::Response::builder()
580590
.status(StatusCode::OK)
581591
.body("")
582592
.unwrap();
583593
let reqwest_response: reqwest::Response = response.into();
584-
record_intermediate_client_request(Ok(&reqwest_response), 1, &Method::GET);
594+
record_intermediate_client_request(Ok(&reqwest_response), 1, &Method::GET, &url);
585595

586596
let captured = TestLayer::capture(&guard);
587597
assert_eq!(captured.len(), 1, "captured spans: {:?}", captured);
@@ -598,9 +608,10 @@ mod tests {
598608
let span = create_client_request_span("test_span", "test_method", &TEST_INFO);
599609
let _enter = span.enter();
600610

611+
let url = "https://example.com/test".parse::<reqwest::Url>().unwrap();
601612
// Simulate a 404 error
602613
let error = gax::error::Error::http(404, http::HeaderMap::new(), bytes::Bytes::new());
603-
record_intermediate_client_request(Err(&error), 1, &Method::POST);
614+
record_intermediate_client_request(Err(&error), 1, &Method::POST, &url);
604615

605616
let captured = TestLayer::capture(&guard);
606617
assert_eq!(captured.len(), 1, "captured spans: {:?}", captured);
@@ -618,5 +629,14 @@ mod tests {
618629
attributes.get(otel_trace::HTTP_REQUEST_METHOD),
619630
Some(&"POST".into())
620631
);
632+
// Verify URL attributes from fallback
633+
assert_eq!(
634+
attributes.get(otel_trace::URL_FULL),
635+
Some(&"https://example.com/test".into())
636+
);
637+
assert_eq!(
638+
attributes.get(otel_trace::SERVER_ADDRESS),
639+
Some(&"example.com".into())
640+
);
621641
}
622642
}

0 commit comments

Comments
 (0)