Skip to content

Commit e7dae5b

Browse files
committed
Handle IO_ERROR for Apache HTTP client with observation API
See #3312 (review)
1 parent 484ad98 commit e7dae5b

File tree

3 files changed

+25
-5
lines changed

3 files changed

+25
-5
lines changed

micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/DefaultApacheHttpClientObservationConvention.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717

1818
import io.micrometer.common.KeyValues;
1919
import io.micrometer.common.lang.Nullable;
20+
import org.apache.http.HttpException;
2021
import org.apache.http.HttpRequest;
2122
import org.apache.http.HttpResponse;
2223

24+
import java.io.IOException;
25+
2326
/**
2427
* Default implementation of {@link ApacheHttpClientObservationConvention}.
2528
*
@@ -56,14 +59,18 @@ public KeyValues getLowCardinalityKeyValues(ApacheHttpClientContext context) {
5659
ApacheHttpClientDocumentedObservation.ApacheHttpClientKeyNames.URI
5760
.withValue(context.getUriMapper().apply(context.getCarrier())),
5861
ApacheHttpClientDocumentedObservation.ApacheHttpClientKeyNames.STATUS
59-
.withValue(getStatusValue(context.getResponse())));
62+
.withValue(getStatusValue(context.getResponse(), context.getError().orElse(null))));
6063
if (context.shouldExportTagsForRoute()) {
6164
keyValues = keyValues.and(HttpContextUtils.generateTagStringsForRoute(context.getApacheHttpContext()));
6265
}
6366
return keyValues;
6467
}
6568

66-
String getStatusValue(@Nullable HttpResponse response) {
69+
String getStatusValue(@Nullable HttpResponse response, Throwable error) {
70+
if (error instanceof IOException || error instanceof HttpException || error instanceof RuntimeException) {
71+
return "IO_ERROR";
72+
}
73+
6774
return response != null ? Integer.toString(response.getStatusLine().getStatusCode()) : "CLIENT_ERROR";
6875
}
6976

micrometer-core/src/main/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,12 @@ public HttpResponse execute(HttpRequest request, HttpClientConnection conn, Http
117117
try {
118118
HttpResponse response = super.execute(request, conn, context);
119119
sample.setResponse(response);
120-
statusCodeOrError = DefaultApacheHttpClientObservationConvention.INSTANCE.getStatusValue(response);
120+
statusCodeOrError = DefaultApacheHttpClientObservationConvention.INSTANCE.getStatusValue(response, null);
121121
return response;
122122
}
123123
catch (IOException | HttpException | RuntimeException e) {
124124
statusCodeOrError = "IO_ERROR";
125+
sample.setThrowable(e);
125126
throw e;
126127
}
127128
finally {

micrometer-core/src/test/java/io/micrometer/core/instrument/binder/httpcomponents/MicrometerHttpRequestExecutorTest.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import io.micrometer.observation.GlobalObservationConvention;
2727
import io.micrometer.observation.tck.TestObservationRegistry;
2828
import io.micrometer.observation.tck.TestObservationRegistryAssert;
29+
import org.apache.http.client.ClientProtocolException;
2930
import org.apache.http.client.HttpClient;
3031
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
3132
import org.apache.http.client.methods.HttpGet;
@@ -45,7 +46,7 @@
4546
import java.util.stream.Collectors;
4647

4748
import static com.github.tomakehurst.wiremock.client.WireMock.*;
48-
import static org.assertj.core.api.Assertions.assertThat;
49+
import static org.assertj.core.api.Assertions.*;
4950
import static org.junit.jupiter.api.Assertions.assertThrows;
5051

5152
/**
@@ -298,7 +299,18 @@ public String getMethod() {
298299

299300
}
300301

301-
// TODO add test for status = IO_ERROR case.
302+
@ParameterizedTest
303+
@ValueSource(booleans = { false, true })
304+
void httpStatusCodeIsTaggedWithIoError(boolean configureObservationRegistry,
305+
@WiremockResolver.Wiremock WireMockServer server) {
306+
server.stubFor(any(urlEqualTo("/error")).willReturn(aResponse().withStatus(1)));
307+
HttpClient client = client(executor(false, configureObservationRegistry));
308+
assertThatThrownBy(
309+
() -> EntityUtils.consume(client.execute(new HttpGet(server.baseUrl() + "/error")).getEntity()))
310+
.isInstanceOf(ClientProtocolException.class);
311+
assertThat(registry.get(EXPECTED_METER_NAME).tags("method", "GET", "status", "IO_ERROR").timer().count())
312+
.isEqualTo(1L);
313+
}
302314

303315
static class CustomGlobalApacheHttpConvention extends DefaultApacheHttpClientObservationConvention
304316
implements GlobalObservationConvention<ApacheHttpClientContext> {

0 commit comments

Comments
 (0)