Skip to content

Commit b4ed532

Browse files
Propagate serialization IOException instead of rethrowing as runtime (#6082)
Co-authored-by: Ricardo Mestre <[email protected]>
1 parent 63fe708 commit b4ed532

File tree

13 files changed

+94
-50
lines changed

13 files changed

+94
-50
lines changed

exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporter.java

+3-20
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
import io.opentelemetry.sdk.common.CompletableResultCode;
1313
import io.opentelemetry.sdk.internal.ThrottlingLogger;
1414
import java.io.IOException;
15-
import java.io.OutputStream;
1615
import java.util.concurrent.atomic.AtomicBoolean;
17-
import java.util.function.Consumer;
1816
import java.util.function.Supplier;
1917
import java.util.logging.Level;
2018
import java.util.logging.Logger;
@@ -37,7 +35,6 @@ public final class HttpExporter<T extends Marshaler> {
3735
private final String type;
3836
private final HttpSender httpSender;
3937
private final ExporterMetrics exporterMetrics;
40-
private final boolean exportAsJson;
4138

4239
public HttpExporter(
4340
String exporterName,
@@ -51,7 +48,6 @@ public HttpExporter(
5148
exportAsJson
5249
? ExporterMetrics.createHttpJson(exporterName, type, meterProviderSupplier)
5350
: ExporterMetrics.createHttpProtobuf(exporterName, type, meterProviderSupplier);
54-
this.exportAsJson = exportAsJson;
5551
}
5652

5753
public CompletableResultCode export(T exportRequest, int numItems) {
@@ -63,21 +59,8 @@ public CompletableResultCode export(T exportRequest, int numItems) {
6359

6460
CompletableResultCode result = new CompletableResultCode();
6561

66-
Consumer<OutputStream> marshaler =
67-
os -> {
68-
try {
69-
if (exportAsJson) {
70-
exportRequest.writeJsonTo(os);
71-
} else {
72-
exportRequest.writeBinaryTo(os);
73-
}
74-
} catch (IOException e) {
75-
throw new IllegalStateException(e);
76-
}
77-
};
78-
7962
httpSender.send(
80-
marshaler,
63+
exportRequest,
8164
exportRequest.getBinarySerializedSize(),
8265
httpResponse -> {
8366
int statusCode = httpResponse.statusCode();
@@ -90,11 +73,11 @@ public CompletableResultCode export(T exportRequest, int numItems) {
9073

9174
exporterMetrics.addFailed(numItems);
9275

93-
byte[] body;
76+
byte[] body = null;
9477
try {
9578
body = httpResponse.responseBody();
9679
} catch (IOException ex) {
97-
throw new IllegalStateException(ex);
80+
logger.log(Level.FINE, "Unable to obtain response body", ex);
9881
}
9982

10083
String status = extractErrorStatus(httpResponse.statusMessage(), body);

exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java

+1
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ public HttpExporter<T> build() {
182182
httpSenderProvider.createSender(
183183
endpoint,
184184
compressor,
185+
exportAsJson,
185186
exportAsJson ? "application/json" : "application/x-protobuf",
186187
timeoutNanos,
187188
connectTimeoutNanos,

exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpSender.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55

66
package io.opentelemetry.exporter.internal.http;
77

8+
import io.opentelemetry.exporter.internal.marshal.Marshaler;
89
import io.opentelemetry.sdk.common.CompletableResultCode;
910
import java.io.IOException;
10-
import java.io.OutputStream;
1111
import java.util.function.Consumer;
1212

1313
/**
@@ -33,7 +33,7 @@ public interface HttpSender {
3333
* @param onError the callback to invoke when the HTTP request could not be executed
3434
*/
3535
void send(
36-
Consumer<OutputStream> marshaler,
36+
Marshaler marshaler,
3737
int contentLength,
3838
Consumer<Response> onResponse,
3939
Consumer<Throwable> onError);

exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpSenderProvider.java

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public interface HttpSenderProvider {
2929
HttpSender createSender(
3030
String endpoint,
3131
@Nullable Compressor compressor,
32+
boolean exportAsJson,
3233
String contentType,
3334
long timeoutNanos,
3435
long connectTimeout,

exporters/sender/jdk/build.gradle.kts

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ otelJava.moduleName.set("io.opentelemetry.exporter.sender.jdk.internal")
99
dependencies {
1010
implementation(project(":exporters:common"))
1111
implementation(project(":sdk:common"))
12+
13+
compileOnly("com.fasterxml.jackson.core:jackson-core")
1214
}
1315

1416
tasks {

exporters/sender/jdk/src/main/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSender.java

+20-5
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77

88
import io.opentelemetry.exporter.internal.compression.Compressor;
99
import io.opentelemetry.exporter.internal.http.HttpSender;
10+
import io.opentelemetry.exporter.internal.marshal.Marshaler;
1011
import io.opentelemetry.sdk.common.CompletableResultCode;
1112
import io.opentelemetry.sdk.common.export.RetryPolicy;
1213
import java.io.ByteArrayOutputStream;
1314
import java.io.IOException;
1415
import java.io.OutputStream;
16+
import java.io.UncheckedIOException;
1517
import java.net.URI;
1618
import java.net.URISyntaxException;
1719
import java.net.http.HttpClient;
@@ -53,6 +55,7 @@ public final class JdkHttpSender implements HttpSender {
5355
private final HttpClient client;
5456
private final URI uri;
5557
@Nullable private final Compressor compressor;
58+
private final boolean exportAsJson;
5659
private final String contentType;
5760
private final long timeoutNanos;
5861
private final Supplier<Map<String, List<String>>> headerSupplier;
@@ -63,6 +66,7 @@ public final class JdkHttpSender implements HttpSender {
6366
HttpClient client,
6467
String endpoint,
6568
@Nullable Compressor compressor,
69+
boolean exportAsJson,
6670
String contentType,
6771
long timeoutNanos,
6872
Supplier<Map<String, List<String>>> headerSupplier,
@@ -74,6 +78,7 @@ public final class JdkHttpSender implements HttpSender {
7478
throw new IllegalArgumentException(e);
7579
}
7680
this.compressor = compressor;
81+
this.exportAsJson = exportAsJson;
7782
this.contentType = contentType;
7883
this.timeoutNanos = timeoutNanos;
7984
this.headerSupplier = headerSupplier;
@@ -83,6 +88,7 @@ public final class JdkHttpSender implements HttpSender {
8388
JdkHttpSender(
8489
String endpoint,
8590
@Nullable Compressor compressor,
91+
boolean exportAsJson,
8692
String contentType,
8793
long timeoutNanos,
8894
long connectTimeoutNanos,
@@ -93,6 +99,7 @@ public final class JdkHttpSender implements HttpSender {
9399
configureClient(sslContext, connectTimeoutNanos),
94100
endpoint,
95101
compressor,
102+
exportAsJson,
96103
contentType,
97104
timeoutNanos,
98105
headerSupplier,
@@ -111,7 +118,7 @@ private static HttpClient configureClient(
111118

112119
@Override
113120
public void send(
114-
Consumer<OutputStream> marshaler,
121+
Marshaler marshaler,
115122
int contentLength,
116123
Consumer<Response> onResponse,
117124
Consumer<Throwable> onError) {
@@ -121,7 +128,7 @@ public void send(
121128
try {
122129
return sendInternal(marshaler);
123130
} catch (IOException e) {
124-
throw new IllegalStateException(e);
131+
throw new UncheckedIOException(e);
125132
}
126133
},
127134
executorService)
@@ -136,7 +143,7 @@ public void send(
136143
}
137144

138145
// Visible for testing
139-
HttpResponse<byte[]> sendInternal(Consumer<OutputStream> marshaler) throws IOException {
146+
HttpResponse<byte[]> sendInternal(Marshaler marshaler) throws IOException {
140147
long startTimeNanos = System.nanoTime();
141148
HttpRequest.Builder requestBuilder =
142149
HttpRequest.newBuilder().uri(uri).timeout(Duration.ofNanos(timeoutNanos));
@@ -151,12 +158,12 @@ HttpResponse<byte[]> sendInternal(Consumer<OutputStream> marshaler) throws IOExc
151158
if (compressor != null) {
152159
requestBuilder.header("Content-Encoding", compressor.getEncoding());
153160
try (OutputStream compressed = compressor.compress(os)) {
154-
marshaler.accept(compressed);
161+
write(marshaler, compressed);
155162
} catch (IOException e) {
156163
throw new IllegalStateException(e);
157164
}
158165
} else {
159-
marshaler.accept(os);
166+
write(marshaler, os);
160167
}
161168

162169
ByteBufferPool byteBufferPool = threadLocalByteBufPool.get();
@@ -211,6 +218,14 @@ HttpResponse<byte[]> sendInternal(Consumer<OutputStream> marshaler) throws IOExc
211218
throw exception;
212219
}
213220

221+
private void write(Marshaler marshaler, OutputStream os) throws IOException {
222+
if (exportAsJson) {
223+
marshaler.writeJsonTo(os);
224+
} else {
225+
marshaler.writeBinaryTo(os);
226+
}
227+
}
228+
214229
private HttpResponse<byte[]> sendRequest(
215230
HttpRequest.Builder requestBuilder, ByteBufferPool byteBufferPool) throws IOException {
216231
try {

exporters/sender/jdk/src/main/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSenderProvider.java

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public final class JdkHttpSenderProvider implements HttpSenderProvider {
2929
public HttpSender createSender(
3030
String endpoint,
3131
@Nullable Compressor compressor,
32+
boolean exportAsJson,
3233
String contentType,
3334
long timeoutNanos,
3435
long connectTimeout,
@@ -40,6 +41,7 @@ public HttpSender createSender(
4041
return new JdkHttpSender(
4142
endpoint,
4243
compressor,
44+
exportAsJson,
4345
contentType,
4446
timeoutNanos,
4547
connectTimeout,

exporters/sender/jdk/src/test/java/io/opentelemetry/exporter/sender/jdk/internal/JdkHttpSenderTest.java

+18-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import static org.mockito.Mockito.verify;
1515
import static org.mockito.Mockito.when;
1616

17+
import io.opentelemetry.exporter.internal.marshal.Marshaler;
18+
import io.opentelemetry.exporter.internal.marshal.Serializer;
1719
import io.opentelemetry.sdk.common.export.RetryPolicy;
1820
import java.io.IOException;
1921
import java.net.http.HttpClient;
@@ -54,6 +56,7 @@ void setup() throws IOException, InterruptedException {
5456
"http://10.255.255.1", // Connecting to a non-routable IP address to trigger connection
5557
// timeout
5658
null,
59+
false,
5760
"text/plain",
5861
Duration.ofSeconds(10).toNanos(),
5962
Collections::emptyMap,
@@ -65,7 +68,7 @@ void setup() throws IOException, InterruptedException {
6568

6669
@Test
6770
void sendInternal_RetryableConnectTimeoutException() throws IOException, InterruptedException {
68-
assertThatThrownBy(() -> sender.sendInternal(marshaler -> {}))
71+
assertThatThrownBy(() -> sender.sendInternal(new NoOpMarshaler()))
6972
.isInstanceOf(HttpConnectTimeoutException.class);
7073

7174
verify(mockHttpClient, times(2)).send(any(), any());
@@ -75,7 +78,7 @@ void sendInternal_RetryableConnectTimeoutException() throws IOException, Interru
7578
void sendInternal_RetryableIoException() throws IOException, InterruptedException {
7679
doThrow(new IOException("error!")).when(mockHttpClient).send(any(), any());
7780

78-
assertThatThrownBy(() -> sender.sendInternal(marshaler -> {}))
81+
assertThatThrownBy(() -> sender.sendInternal(new NoOpMarshaler()))
7982
.isInstanceOf(IOException.class)
8083
.hasMessage("error!");
8184

@@ -86,7 +89,7 @@ void sendInternal_RetryableIoException() throws IOException, InterruptedExceptio
8689
void sendInternal_NonRetryableException() throws IOException, InterruptedException {
8790
doThrow(new SSLException("unknown error")).when(mockHttpClient).send(any(), any());
8891

89-
assertThatThrownBy(() -> sender.sendInternal(marshaler -> {}))
92+
assertThatThrownBy(() -> sender.sendInternal(new NoOpMarshaler()))
9093
.isInstanceOf(IOException.class)
9194
.hasMessage("unknown error");
9295

@@ -99,6 +102,7 @@ void connectTimeout() {
99102
new JdkHttpSender(
100103
"http://localhost",
101104
null,
105+
false,
102106
"text/plain",
103107
1,
104108
TimeUnit.SECONDS.toNanos(10),
@@ -112,4 +116,15 @@ void connectTimeout() {
112116
httpClient ->
113117
assertThat(httpClient.connectTimeout().get()).isEqualTo(Duration.ofSeconds(10)));
114118
}
119+
120+
private static class NoOpMarshaler extends Marshaler {
121+
122+
@Override
123+
public int getBinarySerializedSize() {
124+
return 0;
125+
}
126+
127+
@Override
128+
protected void writeTo(Serializer output) {}
129+
}
115130
}

exporters/sender/okhttp/build.gradle.kts

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ dependencies {
1515
implementation("com.squareup.okhttp3:okhttp")
1616

1717
compileOnly("io.grpc:grpc-stub")
18+
compileOnly("com.fasterxml.jackson.core:jackson-core")
1819

1920
testImplementation("com.linecorp.armeria:armeria-junit5")
2021
}

exporters/sender/okhttp/src/main/java/io/opentelemetry/exporter/sender/okhttp/internal/OkHttpHttpSender.java

+16-7
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
import io.opentelemetry.exporter.internal.auth.Authenticator;
1111
import io.opentelemetry.exporter.internal.compression.Compressor;
1212
import io.opentelemetry.exporter.internal.http.HttpSender;
13+
import io.opentelemetry.exporter.internal.marshal.Marshaler;
1314
import io.opentelemetry.sdk.common.CompletableResultCode;
1415
import io.opentelemetry.sdk.common.export.RetryPolicy;
1516
import java.io.IOException;
16-
import java.io.OutputStream;
1717
import java.time.Duration;
1818
import java.util.List;
1919
import java.util.Map;
@@ -44,6 +44,7 @@ public final class OkHttpHttpSender implements HttpSender {
4444
private final OkHttpClient client;
4545
private final HttpUrl url;
4646
@Nullable private final Compressor compressor;
47+
private final boolean exportAsJson;
4748
private final Supplier<Map<String, List<String>>> headerSupplier;
4849
private final MediaType mediaType;
4950

@@ -52,6 +53,7 @@ public final class OkHttpHttpSender implements HttpSender {
5253
public OkHttpHttpSender(
5354
String endpoint,
5455
@Nullable Compressor compressor,
56+
boolean exportAsJson,
5557
String contentType,
5658
long timeoutNanos,
5759
long connectionTimeoutNanos,
@@ -86,13 +88,14 @@ public OkHttpHttpSender(
8688
this.client = builder.build();
8789
this.url = HttpUrl.get(endpoint);
8890
this.compressor = compressor;
91+
this.exportAsJson = exportAsJson;
8992
this.mediaType = MediaType.parse(contentType);
9093
this.headerSupplier = headerSupplier;
9194
}
9295

9396
@Override
9497
public void send(
95-
Consumer<OutputStream> marshaler,
98+
Marshaler marshaler,
9699
int contentLength,
97100
Consumer<Response> onResponse,
98101
Consumer<Throwable> onError) {
@@ -103,7 +106,7 @@ public void send(
103106
headers.forEach(
104107
(key, values) -> values.forEach(value -> requestBuilder.addHeader(key, value)));
105108
}
106-
RequestBody body = new RawRequestBody(marshaler, contentLength, mediaType);
109+
RequestBody body = new RawRequestBody(marshaler, exportAsJson, contentLength, mediaType);
107110
if (compressor != null) {
108111
requestBuilder.addHeader("Content-Encoding", compressor.getEncoding());
109112
requestBuilder.post(new CompressedRequestBody(compressor, body));
@@ -161,13 +164,15 @@ static boolean isRetryable(okhttp3.Response response) {
161164

162165
private static class RawRequestBody extends RequestBody {
163166

164-
private final Consumer<OutputStream> marshaler;
167+
private final Marshaler marshaler;
168+
private final boolean exportAsJson;
165169
private final int contentLength;
166170
private final MediaType mediaType;
167171

168172
private RawRequestBody(
169-
Consumer<OutputStream> marshaler, int contentLength, MediaType mediaType) {
173+
Marshaler marshaler, boolean exportAsJson, int contentLength, MediaType mediaType) {
170174
this.marshaler = marshaler;
175+
this.exportAsJson = exportAsJson;
171176
this.contentLength = contentLength;
172177
this.mediaType = mediaType;
173178
}
@@ -183,8 +188,12 @@ public MediaType contentType() {
183188
}
184189

185190
@Override
186-
public void writeTo(BufferedSink bufferedSink) {
187-
marshaler.accept(bufferedSink.outputStream());
191+
public void writeTo(BufferedSink bufferedSink) throws IOException {
192+
if (exportAsJson) {
193+
marshaler.writeJsonTo(bufferedSink.outputStream());
194+
} else {
195+
marshaler.writeBinaryTo(bufferedSink.outputStream());
196+
}
188197
}
189198
}
190199

0 commit comments

Comments
 (0)