From fff6013fc399d3f2dd4c0e57c9a526e793d41530 Mon Sep 17 00:00:00 2001 From: ADITYA-CODE-SOURCE Date: Wed, 27 May 2026 19:47:05 +0530 Subject: [PATCH 1/2] Use HTTP error body in HttpExporter warnings --- .../exporter/otlp/internal/HttpExporter.java | 19 +++++- .../otlp/internal/HttpExporterTest.java | 64 ++++++++++++++++++- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporter.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporter.java index eaaad3c8659..390e86aeee8 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporter.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporter.java @@ -7,6 +7,7 @@ import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.internal.FailedExportException; +import io.opentelemetry.exporter.internal.grpc.GrpcExporterUtil; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.internal.metrics.ExporterInstrumentation; import io.opentelemetry.sdk.common.CompletableResultCode; @@ -18,6 +19,7 @@ import io.opentelemetry.sdk.common.internal.ThrottlingLogger; import java.io.IOException; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import java.util.logging.Level; @@ -32,6 +34,8 @@ */ @SuppressWarnings("checkstyle:JavadocMethod") public final class HttpExporter { + // Limit logged response body text to avoid flooding warnings with large payloads. + private static final int MAX_RESPONSE_BODY_LOG_LENGTH = 1024; private static final Logger internalLogger = Logger.getLogger(HttpExporter.class.getName()); @@ -132,10 +136,23 @@ private static String extractErrorStatus(String statusMessage, @Nullable byte[] if (responseBody == null) { return "Response body missing, HTTP status message: " + statusMessage; } + if (responseBody.length == 0) { + return "Response body has 0 length, HTTP status message: " + statusMessage; + } try { return GrpcExporterUtil.getStatusMessage(responseBody); } catch (IOException e) { - return "Unable to parse response body, HTTP status message: " + statusMessage; + return extractResponseBodyMessage(responseBody, statusMessage); + } + } + + private static String extractResponseBodyMessage(byte[] responseBody, String statusMessage) { + int lengthToRead = Math.min(responseBody.length, MAX_RESPONSE_BODY_LOG_LENGTH); + String responseBodyText = + new String(responseBody, 0, lengthToRead, StandardCharsets.UTF_8).trim(); + if (responseBodyText.isEmpty()) { + return "HTTP status message: " + statusMessage; } + return "Response body: " + responseBodyText + ", HTTP status message: " + statusMessage; } } diff --git a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/HttpExporterTest.java b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/HttpExporterTest.java index f44de7f72ce..d8f3652d348 100644 --- a/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/HttpExporterTest.java +++ b/exporters/otlp/all/src/test/java/io/opentelemetry/exporter/otlp/internal/HttpExporterTest.java @@ -6,10 +6,13 @@ package io.opentelemetry.exporter.otlp.internal; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; +import io.github.netmikey.logunit.api.LogCapturer; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.internal.testing.slf4j.SuppressLogger; import io.opentelemetry.sdk.common.InternalTelemetryVersion; @@ -22,13 +25,33 @@ import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader; import java.io.IOException; import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import org.mockito.Mockito; class HttpExporterTest { + @RegisterExtension LogCapturer logs = LogCapturer.create().captureForType(HttpExporter.class); + + @Test + void build_NoHttpSenderProvider() { + assertThatThrownBy( + () -> + new HttpExporterBuilder( + StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER, + "http://localhost") + .build()) + .isInstanceOf(IllegalStateException.class) + .hasMessage( + "No HttpSenderProvider found on classpath. Please add dependency on " + + "opentelemetry-exporter-sender-okhttp or opentelemetry-exporter-sender-jdk"); + } + @ParameterizedTest @EnumSource @SuppressLogger(HttpExporter.class) @@ -197,14 +220,53 @@ void testInternalTelemetry(StandardComponentId.ExporterType exporterType) { } } + @Test + @SuppressLogger(HttpExporter.class) + void export_httpJsonErrorBodyUsesBodyTextWithoutGrpcParseWarning() { + HttpSender mockSender = Mockito.mock(HttpSender.class); + Marshaler mockMarshaller = Mockito.mock(Marshaler.class); + HttpExporter exporter = + new HttpExporter( + ComponentId.generateLazy(StandardComponentId.ExporterType.OTLP_HTTP_SPAN_EXPORTER), + mockSender, + MeterProvider::noop, + InternalTelemetryVersion.LATEST, + URI.create("http://testing:1234"), + false); + + doAnswer( + invoc -> { + Consumer onResponse = invoc.getArgument(1); + onResponse.accept( + new FakeHttpResponse( + 500, + "Internal Server Error", + "{\"error\":\"grpc not supported\"}".getBytes(StandardCharsets.UTF_8))); + return null; + }) + .when(mockSender) + .send(any(), any(), any()); + + assertThat(exporter.export(mockMarshaller, 1).join(10, TimeUnit.SECONDS).isSuccess()).isFalse(); + + logs.assertContains("Response body: {\"error\":\"grpc not supported\"}"); + logs.assertDoesNotContain("Unable to parse response body"); + } + private static class FakeHttpResponse implements HttpResponse { final int statusCode; final String statusMessage; + final byte[] responseBody; FakeHttpResponse(int statusCode, String statusMessage) { + this(statusCode, statusMessage, new byte[0]); + } + + FakeHttpResponse(int statusCode, String statusMessage, byte[] responseBody) { this.statusCode = statusCode; this.statusMessage = statusMessage; + this.responseBody = responseBody; } @Override @@ -219,7 +281,7 @@ public String getStatusMessage() { @Override public byte[] getResponseBody() { - return new byte[0]; + return responseBody; } } } From 47159e2562e86425bb584d06f0c314eb0cb73b69 Mon Sep 17 00:00:00 2001 From: ADITYA-CODE-SOURCE Date: Sat, 30 May 2026 08:28:10 +0530 Subject: [PATCH 2/2] Fix GrpcExporterUtil import after rebase --- .../io/opentelemetry/exporter/otlp/internal/HttpExporter.java | 1 - 1 file changed, 1 deletion(-) diff --git a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporter.java b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporter.java index 390e86aeee8..ea2b108f20e 100644 --- a/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporter.java +++ b/exporters/otlp/all/src/main/java/io/opentelemetry/exporter/otlp/internal/HttpExporter.java @@ -7,7 +7,6 @@ import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.internal.FailedExportException; -import io.opentelemetry.exporter.internal.grpc.GrpcExporterUtil; import io.opentelemetry.exporter.internal.marshal.Marshaler; import io.opentelemetry.exporter.internal.metrics.ExporterInstrumentation; import io.opentelemetry.sdk.common.CompletableResultCode;