diff --git a/exporters/sender/jdk/src/main/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSender.java b/exporters/sender/jdk/src/main/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSender.java index 1c9e7b5d669..069e223a634 100644 --- a/exporters/sender/jdk/src/main/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSender.java +++ b/exporters/sender/jdk/src/main/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSender.java @@ -32,6 +32,7 @@ import java.util.OptionalLong; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.RejectedExecutionException; @@ -185,7 +186,7 @@ public void send( .whenComplete( (httpResponse, throwable) -> { if (throwable != null) { - onError.accept(throwable); + onError.accept(unwrap(throwable)); return; } onResponse.accept(httpResponse); @@ -196,6 +197,19 @@ public void send( } } + // supplyAsync wraps failures in CompletionException, and IOExceptions are additionally wrapped in + // UncheckedIOException above. Pass the original exception to onError, as OkHttpHttpSender does. + private static Throwable unwrap(Throwable throwable) { + Throwable cause = throwable; + if (cause instanceof CompletionException && cause.getCause() != null) { + cause = cause.getCause(); + } + if (cause instanceof UncheckedIOException && cause.getCause() != null) { + cause = cause.getCause(); + } + return cause; + } + // Visible for testing HttpResponse sendInternal(MessageWriter requestBodyWriter) throws IOException { long startTimeNanos = System.nanoTime(); diff --git a/exporters/sender/jdk/src/test/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSenderTest.java b/exporters/sender/jdk/src/test/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSenderTest.java index 6b75403c06a..3fee137d9b2 100644 --- a/exporters/sender/jdk/src/test/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSenderTest.java +++ b/exporters/sender/jdk/src/test/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSenderTest.java @@ -327,7 +327,8 @@ void send_successfulResponse_callsOnResponse() throws Exception { @Test void send_ioException_callsOnError() throws Exception { - doThrow(new IOException("send failed")).when(mockHttpClient).send(any(), any()); + IOException exception = new IOException("send failed"); + doThrow(exception).when(mockHttpClient).send(any(), any()); JdkHttpSender testSender = new JdkHttpSender( @@ -358,9 +359,7 @@ void send_ioException_callsOnError() throws Exception { }); assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(errorRef.get()).isNotNull(); - assertThat(errorRef.get()).hasRootCauseInstanceOf(IOException.class); - assertThat(errorRef.get()).hasRootCauseMessage("send failed"); + assertThat(errorRef.get()).isSameAs(exception); assertThat(responseRef.get()).isNull(); } finally { testSender.shutdown();