From 60ae0af7f683af11588bc540c052d8f3bc40ef74 Mon Sep 17 00:00:00 2001
From: jack-berg <34418638+jack-berg@users.noreply.github.com>
Date: Tue, 2 Jan 2024 15:00:54 -0600
Subject: [PATCH] Add compressor SPI to support additional compression algos
(#5990)
---
.../internal/compression/Compressor.java | 32 +++++++
.../compression/CompressorProvider.java | 18 ++++
.../internal/compression/CompressorUtil.java | 59 ++++++++++++
.../internal/compression/GzipCompressor.java | 37 ++++++++
.../internal/http/HttpExporterBuilder.java | 16 ++--
.../internal/http/HttpSenderProvider.java | 3 +-
.../OtlpHttpLogRecordExporterBuilder.java | 15 ++-
.../OtlpHttpMetricExporterBuilder.java | 15 ++-
.../trace/OtlpHttpSpanExporterBuilder.java | 15 ++-
.../AbstractHttpTelemetryExporterTest.java | 63 +++++++++----
.../internal/compressor/Base64Compressor.java | 35 +++++++
.../compressor/Base64CompressorProvider.java | 17 ++++
...er.internal.compression.CompressorProvider | 1 +
.../sender/jdk/internal/JdkHttpSender.java | 20 ++--
.../jdk/internal/JdkHttpSenderProvider.java | 5 +-
.../jdk/internal/JdkHttpSenderTest.java | 4 +-
.../okhttp/internal/OkHttpHttpSender.java | 27 +++---
.../internal/OkHttpHttpSenderProvider.java | 5 +-
.../internal/HttpExporterBuilderTest.java | 92 -------------------
.../internal/OkHttpHttpSuppressionTest.java | 2 +-
20 files changed, 327 insertions(+), 154 deletions(-)
create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/Compressor.java
create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorProvider.java
create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorUtil.java
create mode 100644 exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/GzipCompressor.java
create mode 100644 exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/compressor/Base64Compressor.java
create mode 100644 exporters/otlp/testing-internal/src/main/java/io/opentelemetry/exporter/otlp/testing/internal/compressor/Base64CompressorProvider.java
create mode 100644 exporters/otlp/testing-internal/src/main/resources/META-INF/services/io.opentelemetry.exporter.internal.compression.CompressorProvider
delete mode 100644 exporters/sender/okhttp/src/test/java/io/opentelemetry/exporter/sender/okhttp/internal/HttpExporterBuilderTest.java
diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/Compressor.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/Compressor.java
new file mode 100644
index 00000000000..71894cc9d4a
--- /dev/null
+++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/Compressor.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.exporter.internal.compression;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * An abstraction for compressing messages. Implementation MUST be thread safe as the same instance
+ * is expected to be used many times and concurrently. Instances are usually singletons.
+ *
+ *
This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ */
+@ThreadSafe
+public interface Compressor {
+
+ /**
+ * The name of the compressor encoding.
+ *
+ *
Used to identify the compressor during configuration and to populate the {@code
+ * Content-Encoding} header.
+ */
+ String getEncoding();
+
+ /** Wrap the {@code outputStream} with a compressing output stream. */
+ OutputStream compress(OutputStream outputStream) throws IOException;
+}
diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorProvider.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorProvider.java
new file mode 100644
index 00000000000..6b4518f1ea0
--- /dev/null
+++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorProvider.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.exporter.internal.compression;
+
+/**
+ * A service provider interface (SPI) for providing {@link Compressor}s.
+ *
+ *
This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ */
+public interface CompressorProvider {
+
+ /** Return the {@link Compressor}. */
+ Compressor getInstance();
+}
diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorUtil.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorUtil.java
new file mode 100644
index 00000000000..6a777f759ba
--- /dev/null
+++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/CompressorUtil.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.exporter.internal.compression;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.Set;
+
+/**
+ * Utilities for resolving SPI {@link Compressor}s.
+ *
+ *
This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ *
+ * @see CompressorProvider
+ */
+public final class CompressorUtil {
+
+ private static final Map compressorRegistry = buildCompressorRegistry();
+
+ private CompressorUtil() {}
+
+ /** Get list of loaded compressors, named according to {@link Compressor#getEncoding()}. */
+ public static Set supportedCompressors() {
+ return Collections.unmodifiableSet(compressorRegistry.keySet());
+ }
+
+ /**
+ * Resolve the {@link Compressor} with the {@link Compressor#getEncoding()} equal to the {@code
+ * encoding}.
+ *
+ * @throws IllegalArgumentException if no match is found
+ */
+ public static Compressor resolveCompressor(String encoding) {
+ Compressor compressor = compressorRegistry.get(encoding);
+ if (compressor == null) {
+ throw new IllegalArgumentException(
+ "Could not resolve compressor for encoding \"" + encoding + "\".");
+ }
+ return compressor;
+ }
+
+ private static Map buildCompressorRegistry() {
+ Map compressors = new HashMap<>();
+ for (CompressorProvider spi :
+ ServiceLoader.load(CompressorProvider.class, CompressorUtil.class.getClassLoader())) {
+ Compressor compressor = spi.getInstance();
+ compressors.put(compressor.getEncoding(), compressor);
+ }
+ // Hardcode gzip compressor
+ compressors.put(GzipCompressor.getInstance().getEncoding(), GzipCompressor.getInstance());
+ return compressors;
+ }
+}
diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/GzipCompressor.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/GzipCompressor.java
new file mode 100644
index 00000000000..7395fdb41b1
--- /dev/null
+++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/compression/GzipCompressor.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.exporter.internal.compression;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * Gzip {@link Compressor}.
+ *
+ * This class is internal and is hence not for public use. Its APIs are unstable and can change
+ * at any time.
+ */
+public final class GzipCompressor implements Compressor {
+
+ private static final GzipCompressor INSTANCE = new GzipCompressor();
+
+ private GzipCompressor() {}
+
+ public static GzipCompressor getInstance() {
+ return INSTANCE;
+ }
+
+ @Override
+ public String getEncoding() {
+ return "gzip";
+ }
+
+ @Override
+ public OutputStream compress(OutputStream outputStream) throws IOException {
+ return new GZIPOutputStream(outputStream);
+ }
+}
diff --git a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java
index ad46956a702..2285cc97729 100644
--- a/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java
+++ b/exporters/common/src/main/java/io/opentelemetry/exporter/internal/http/HttpExporterBuilder.java
@@ -11,6 +11,7 @@
import io.opentelemetry.exporter.internal.ExporterBuilderUtil;
import io.opentelemetry.exporter.internal.TlsConfigHelper;
import io.opentelemetry.exporter.internal.auth.Authenticator;
+import io.opentelemetry.exporter.internal.compression.Compressor;
import io.opentelemetry.exporter.internal.marshal.Marshaler;
import io.opentelemetry.sdk.common.export.RetryPolicy;
import java.net.URI;
@@ -19,6 +20,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.ServiceLoader;
import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;
@@ -48,8 +50,8 @@ public final class HttpExporterBuilder {
private String endpoint;
private long timeoutNanos = TimeUnit.SECONDS.toNanos(DEFAULT_TIMEOUT_SECS);
+ @Nullable private Compressor compressor;
private long connectTimeoutNanos = TimeUnit.SECONDS.toNanos(DEFAULT_CONNECT_TIMEOUT_SECS);
- private boolean compressionEnabled = false;
private boolean exportAsJson = false;
private final Map constantHeaders = new HashMap<>();
private Supplier