99import static org .mockito .ArgumentMatchers .any ;
1010import static org .mockito .Mockito .doAnswer ;
1111
12+ import io .github .netmikey .logunit .api .LogCapturer ;
1213import io .opentelemetry .api .common .Attributes ;
14+ import io .opentelemetry .api .metrics .MeterProvider ;
1315import io .opentelemetry .exporter .internal .marshal .Marshaler ;
1416import io .opentelemetry .internal .testing .slf4j .SuppressLogger ;
1517import io .opentelemetry .sdk .common .InternalTelemetryVersion ;
2224import io .opentelemetry .sdk .testing .exporter .InMemoryMetricReader ;
2325import java .io .IOException ;
2426import java .net .URI ;
27+ import java .nio .charset .StandardCharsets ;
28+ import java .util .concurrent .TimeUnit ;
2529import java .util .function .Consumer ;
30+ import javax .annotation .Nullable ;
31+ import org .junit .jupiter .api .Test ;
32+ import org .junit .jupiter .api .extension .RegisterExtension ;
2633import org .junit .jupiter .params .ParameterizedTest ;
2734import org .junit .jupiter .params .provider .EnumSource ;
2835import org .mockito .Mockito ;
2936
3037class HttpExporterTest {
3138
39+ @ RegisterExtension LogCapturer logs = LogCapturer .create ().captureForType (HttpExporter .class );
40+
3241 @ ParameterizedTest
3342 @ EnumSource
3443 @ SuppressLogger (HttpExporter .class )
@@ -197,14 +206,112 @@ void testInternalTelemetry(StandardComponentId.ExporterType exporterType) {
197206 }
198207 }
199208
209+ @ Test
210+ @ SuppressLogger (HttpExporter .class )
211+ void export_httpJsonErrorBodyUsesBodyTextWithoutGrpcParseWarning () {
212+ HttpSender mockSender = Mockito .mock (HttpSender .class );
213+ Marshaler mockMarshaller = Mockito .mock (Marshaler .class );
214+ HttpExporter exporter =
215+ new HttpExporter (
216+ ComponentId .generateLazy (StandardComponentId .ExporterType .OTLP_HTTP_SPAN_EXPORTER ),
217+ mockSender ,
218+ MeterProvider ::noop ,
219+ InternalTelemetryVersion .LATEST ,
220+ URI .create ("http://testing:1234" ),
221+ false );
222+
223+ doAnswer (
224+ invoc -> {
225+ Consumer <HttpResponse > onResponse = invoc .getArgument (1 );
226+ onResponse .accept (
227+ new FakeHttpResponse (
228+ 500 ,
229+ "Internal Server Error" ,
230+ "{\" error\" :\" grpc not supported\" }" .getBytes (StandardCharsets .UTF_8 )));
231+ return null ;
232+ })
233+ .when (mockSender )
234+ .send (any (), any (), any ());
235+
236+ assertThat (exporter .export (mockMarshaller , 1 ).join (10 , TimeUnit .SECONDS ).isSuccess ()).isFalse ();
237+
238+ logs .assertContains ("Response body: {\" error\" :\" grpc not supported\" }" );
239+ logs .assertDoesNotContain ("Unable to parse response body" );
240+ }
241+
242+ @ Test
243+ @ SuppressLogger (HttpExporter .class )
244+ void export_nullErrorBodyUsesMissingBodyMessage () {
245+ HttpSender mockSender = Mockito .mock (HttpSender .class );
246+ Marshaler mockMarshaller = Mockito .mock (Marshaler .class );
247+ HttpExporter exporter =
248+ new HttpExporter (
249+ ComponentId .generateLazy (StandardComponentId .ExporterType .OTLP_HTTP_SPAN_EXPORTER ),
250+ mockSender ,
251+ MeterProvider ::noop ,
252+ InternalTelemetryVersion .LATEST ,
253+ URI .create ("http://testing:1234" ),
254+ false );
255+
256+ doAnswer (
257+ invoc -> {
258+ Consumer <HttpResponse > onResponse = invoc .getArgument (1 );
259+ onResponse .accept (new FakeHttpResponse (500 , "Internal Server Error" , null ));
260+ return null ;
261+ })
262+ .when (mockSender )
263+ .send (any (), any (), any ());
264+
265+ assertThat (exporter .export (mockMarshaller , 1 ).join (10 , TimeUnit .SECONDS ).isSuccess ()).isFalse ();
266+
267+ logs .assertContains ("Response body missing, HTTP status message: Internal Server Error" );
268+ }
269+
270+ @ Test
271+ @ SuppressLogger (HttpExporter .class )
272+ void export_whitespaceErrorBodyFallsBackToStatusMessage () {
273+ HttpSender mockSender = Mockito .mock (HttpSender .class );
274+ Marshaler mockMarshaller = Mockito .mock (Marshaler .class );
275+ HttpExporter exporter =
276+ new HttpExporter (
277+ ComponentId .generateLazy (StandardComponentId .ExporterType .OTLP_HTTP_SPAN_EXPORTER ),
278+ mockSender ,
279+ MeterProvider ::noop ,
280+ InternalTelemetryVersion .LATEST ,
281+ URI .create ("http://testing:1234" ),
282+ false );
283+
284+ doAnswer (
285+ invoc -> {
286+ Consumer <HttpResponse > onResponse = invoc .getArgument (1 );
287+ onResponse .accept (
288+ new FakeHttpResponse (
289+ 500 , "Internal Server Error" , " " .getBytes (StandardCharsets .UTF_8 )));
290+ return null ;
291+ })
292+ .when (mockSender )
293+ .send (any (), any (), any ());
294+
295+ assertThat (exporter .export (mockMarshaller , 1 ).join (10 , TimeUnit .SECONDS ).isSuccess ()).isFalse ();
296+
297+ logs .assertContains ("HTTP status message: Internal Server Error" );
298+ logs .assertDoesNotContain ("Response body:" );
299+ }
300+
200301 private static class FakeHttpResponse implements HttpResponse {
201302
202303 final int statusCode ;
203304 final String statusMessage ;
305+ @ Nullable final byte [] responseBody ;
204306
205307 FakeHttpResponse (int statusCode , String statusMessage ) {
308+ this (statusCode , statusMessage , new byte [0 ]);
309+ }
310+
311+ FakeHttpResponse (int statusCode , String statusMessage , @ Nullable byte [] responseBody ) {
206312 this .statusCode = statusCode ;
207313 this .statusMessage = statusMessage ;
314+ this .responseBody = responseBody ;
208315 }
209316
210317 @ Override
@@ -218,8 +325,9 @@ public String getStatusMessage() {
218325 }
219326
220327 @ Override
328+ @ Nullable
221329 public byte [] getResponseBody () {
222- return new byte [ 0 ] ;
330+ return responseBody ;
223331 }
224332 }
225333}
0 commit comments