Skip to content

Commit

Permalink
Add SpanExporterFactory support for zipkin exporter (#5771)
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg committed Aug 28, 2023
1 parent 01503ef commit ce7323e
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
Comparing source compatibility of against
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.exporter.zipkin.ZipkinSpanExporter (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) java.lang.String toString()
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,20 @@ public final class ZipkinSpanExporter implements SpanExporter {

private final ThrottlingLogger logger = new ThrottlingLogger(baseLogger);
private final AtomicBoolean isShutdown = new AtomicBoolean();
private final ZipkinSpanExporterBuilder builder;
private final BytesEncoder<Span> encoder;
private final Sender sender;
private final ExporterMetrics exporterMetrics;

private final OtelToZipkinSpanTransformer transformer;

ZipkinSpanExporter(
ZipkinSpanExporterBuilder builder,
BytesEncoder<Span> encoder,
Sender sender,
Supplier<MeterProvider> meterProviderSupplier,
OtelToZipkinSpanTransformer transformer) {
this.builder = builder;
this.encoder = encoder;
this.sender = sender;
this.exporterMetrics =
Expand Down Expand Up @@ -114,6 +117,11 @@ public CompletableResultCode shutdown() {
return CompletableResultCode.ofSuccess();
}

@Override
public String toString() {
return "ZipkinSpanExporter{" + builder.toString(false) + "}";
}

/**
* Returns a new Builder for {@link ZipkinSpanExporter}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.opentelemetry.api.metrics.MeterProvider;
import java.net.InetAddress;
import java.time.Duration;
import java.util.StringJoiner;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import javax.annotation.Nullable;
Expand Down Expand Up @@ -68,13 +69,14 @@ public ZipkinSpanExporterBuilder setEncoder(BytesEncoder<Span> encoder) {
* implementation uses a Supplier that returns a single unchanging IP address that is captured at
* creation time.
*
* @param supplier - A supplier that returns an InetAddress that may be null.
* @param localIpAddressSupplier - A supplier that returns an InetAddress that may be null.
* @return this
* @since 1.18.0
*/
public ZipkinSpanExporterBuilder setLocalIpAddressSupplier(Supplier<InetAddress> supplier) {
requireNonNull(supplier, "encoder");
this.localIpAddressSupplier = supplier;
public ZipkinSpanExporterBuilder setLocalIpAddressSupplier(
Supplier<InetAddress> localIpAddressSupplier) {
requireNonNull(localIpAddressSupplier, "localIpAddressSupplier");
this.localIpAddressSupplier = localIpAddressSupplier;
return this;
}

Expand Down Expand Up @@ -151,6 +153,21 @@ public ZipkinSpanExporterBuilder setMeterProvider(MeterProvider meterProvider) {
return this;
}

String toString(boolean includePrefixAndSuffix) {
StringJoiner joiner =
includePrefixAndSuffix
? new StringJoiner(", ", "ZipkinSpanExporterBuilder{", "}")
: new StringJoiner(", ");
joiner.add("endpoint=" + endpoint);
joiner.add("compressionEnabled=" + compressionEnabled);
joiner.add("readTimeoutMillis=" + readTimeoutMillis);
// Note: omit sender because we can't log the configuration in any readable way
// Note: omit encoder because we can't log the configuration in any readable way
// Note: omit localIpAddressSupplier because we can't log the configuration in any readable way
// Note: omit meterProviderSupplier because we can't log the configuration in any readable way
return joiner.toString();
}

/**
* Builds a {@link ZipkinSpanExporter}.
*
Expand All @@ -168,6 +185,6 @@ public ZipkinSpanExporter build() {
}
OtelToZipkinSpanTransformer transformer =
OtelToZipkinSpanTransformer.create(localIpAddressSupplier);
return new ZipkinSpanExporter(encoder, sender, meterProviderSupplier, transformer);
return new ZipkinSpanExporter(this, encoder, sender, meterProviderSupplier, transformer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import io.opentelemetry.sdk.testing.trace.TestSpanData;
import java.io.IOException;
import java.net.InetAddress;
import java.time.Duration;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -51,7 +52,12 @@ void testExport() {
TestSpanData testSpanData = spanBuilder().build();

ZipkinSpanExporter zipkinSpanExporter =
new ZipkinSpanExporter(mockEncoder, mockSender, MeterProvider::noop, mockTransformer);
new ZipkinSpanExporter(
new ZipkinSpanExporterBuilder(),
mockEncoder,
mockSender,
MeterProvider::noop,
mockTransformer);

byte[] someBytes = new byte[0];
Span zipkinSpan =
Expand Down Expand Up @@ -82,7 +88,12 @@ void testExport_failed() {
TestSpanData testSpanData = spanBuilder().build();

ZipkinSpanExporter zipkinSpanExporter =
new ZipkinSpanExporter(mockEncoder, mockSender, MeterProvider::noop, mockTransformer);
new ZipkinSpanExporter(
new ZipkinSpanExporterBuilder(),
mockEncoder,
mockSender,
MeterProvider::noop,
mockTransformer);

byte[] someBytes = new byte[0];
Span zipkinSpan =
Expand Down Expand Up @@ -204,4 +215,23 @@ void compressionEnabledAndDisabled() {
exporter.shutdown();
}
}

@Test
void stringRepresentation() {
try (ZipkinSpanExporter exporter = ZipkinSpanExporter.builder().build()) {
assertThat(exporter.toString())
.isEqualTo(
"ZipkinSpanExporter{endpoint=http://localhost:9411/api/v2/spans, compressionEnabled=true, readTimeoutMillis=10000}");
}
try (ZipkinSpanExporter exporter =
ZipkinSpanExporter.builder()
.setEndpoint("http://zipkin:9411/api/v2/spans")
.setReadTimeout(Duration.ofSeconds(15))
.setCompression("none")
.build()) {
assertThat(exporter.toString())
.isEqualTo(
"ZipkinSpanExporter{endpoint=http://zipkin:9411/api/v2/spans, compressionEnabled=false, readTimeoutMillis=15000}");
}
}
}
3 changes: 2 additions & 1 deletion sdk-extensions/incubator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ dependencies {

testImplementation(project(":sdk:testing"))
testImplementation(project(":sdk-extensions:autoconfigure"))
testImplementation(project(":exporters:logging"))
testImplementation(project(":exporters:otlp:all"))
testImplementation(project(":exporters:prometheus"))
testImplementation(project(":exporters:logging"))
testImplementation(project(":exporters:zipkin"))
testImplementation(project(":sdk-extensions:jaeger-remote-sampler"))
testImplementation(project(":extensions:trace-propagators"))
// As a part of the tests we check that we can parse examples without error. The https://github.com/open-telemetry/opentelemetry-configuration/blob/main/examples/kitchen-sink.yam contains a reference to the xray propagator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Otlp;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Zipkin;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.io.Closeable;
import java.util.Collections;
Expand Down Expand Up @@ -51,13 +52,12 @@ public SpanExporter create(
}

if (model.getConsole() != null) {
return FileConfigUtil.addAndReturn(
closeables,
FileConfigUtil.assertNotNull(
spanExporterSpiManager(
DefaultConfigProperties.createForTest(Collections.emptyMap()), spiHelper)
.getByName("logging"),
"logging exporter"));
return FileConfigUtil.addAndReturn(closeables, createConsoleExporter(spiHelper));
}

Zipkin zipkinModel = model.getZipkin();
if (zipkinModel != null) {
return FileConfigUtil.addAndReturn(closeables, createZipkinExporter(zipkinModel, spiHelper));
}

// TODO(jack-berg): add support for generic SPI exporters
Expand Down Expand Up @@ -114,6 +114,33 @@ private static SpanExporter createOtlpExporter(Otlp model, SpiHelper spiHelper)
spanExporterSpiManager(configProperties, spiHelper).getByName("otlp"), "otlp exporter");
}

private static SpanExporter createConsoleExporter(SpiHelper spiHelper) {
return FileConfigUtil.assertNotNull(
spanExporterSpiManager(
DefaultConfigProperties.createForTest(Collections.emptyMap()), spiHelper)
.getByName("logging"),
"logging exporter");
}

private static SpanExporter createZipkinExporter(Zipkin model, SpiHelper spiHelper) {
// Translate from file configuration scheme to environment variable scheme. This is ultimately
// interpreted by ZipkinSpanExporterProvider, but we want to avoid the dependency on
// opentelemetry-exporter-zipkin
Map<String, String> properties = new HashMap<>();
if (model.getEndpoint() != null) {
properties.put("otel.exporter.zipkin.endpoint", model.getEndpoint());
}
if (model.getTimeout() != null) {
properties.put("otel.exporter.zipkin.timeout", Integer.toString(model.getTimeout()));
}

// TODO(jack-berg): add method for creating from map
ConfigProperties configProperties = DefaultConfigProperties.createForTest(properties);

return FileConfigUtil.assertNotNull(
spanExporterSpiManager(configProperties, spiHelper).getByName("zipkin"), "zipkin exporter");
}

private static NamedSpiManager<SpanExporter> spanExporterSpiManager(
ConfigProperties config, SpiHelper spiHelper) {
return spiHelper.loadConfigurable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter;
import io.opentelemetry.internal.testing.CleanupExtension;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
Expand All @@ -26,6 +27,7 @@
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Console;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Headers;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Otlp;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Zipkin;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.io.Closeable;
import java.io.IOException;
Expand Down Expand Up @@ -186,6 +188,74 @@ void create_Console() {
assertThat(exporter.toString()).isEqualTo(expectedExporter.toString());
}

@Test
void create_ZipkinDefaults() {
spiHelper = spy(spiHelper);
List<Closeable> closeables = new ArrayList<>();
ZipkinSpanExporter expectedExporter = ZipkinSpanExporter.builder().build();

cleanup.addCloseable(expectedExporter);

SpanExporter exporter =
SpanExporterFactory.getInstance()
.create(
new io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model
.SpanExporter()
.withZipkin(new Zipkin()),
spiHelper,
closeables);
cleanup.addCloseable(exporter);
cleanup.addCloseables(closeables);

assertThat(exporter.toString()).isEqualTo(expectedExporter.toString());

ArgumentCaptor<ConfigProperties> configCaptor = ArgumentCaptor.forClass(ConfigProperties.class);
verify(spiHelper)
.loadConfigurable(
eq(ConfigurableSpanExporterProvider.class), any(), any(), configCaptor.capture());
ConfigProperties configProperties = configCaptor.getValue();
assertThat(configProperties.getString("otel.exporter.zipkin.endpoint")).isNull();
assertThat(configProperties.getDuration("otel.exporter.zipkin.timeout")).isNull();
}

@Test
void create_ZipkinConfigured() {
spiHelper = spy(spiHelper);
List<Closeable> closeables = new ArrayList<>();
ZipkinSpanExporter expectedExporter =
ZipkinSpanExporter.builder()
.setEndpoint("http://zipkin:9411/v1/v2/spans")
.setReadTimeout(Duration.ofSeconds(15))
.build();
cleanup.addCloseable(expectedExporter);

SpanExporter exporter =
SpanExporterFactory.getInstance()
.create(
new io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model
.SpanExporter()
.withZipkin(
new Zipkin()
.withEndpoint("http://zipkin:9411/v1/v2/spans")
.withTimeout(15_000)),
spiHelper,
closeables);
cleanup.addCloseable(exporter);
cleanup.addCloseables(closeables);

assertThat(exporter.toString()).isEqualTo(expectedExporter.toString());

ArgumentCaptor<ConfigProperties> configCaptor = ArgumentCaptor.forClass(ConfigProperties.class);
verify(spiHelper)
.loadConfigurable(
eq(ConfigurableSpanExporterProvider.class), any(), any(), configCaptor.capture());
ConfigProperties configProperties = configCaptor.getValue();
assertThat(configProperties.getString("otel.exporter.zipkin.endpoint"))
.isEqualTo("http://zipkin:9411/v1/v2/spans");
assertThat(configProperties.getDuration("otel.exporter.zipkin.timeout"))
.isEqualTo(Duration.ofSeconds(15));
}

@Test
void create_SpiExporter() {
List<Closeable> closeables = new ArrayList<>();
Expand Down

0 comments on commit ce7323e

Please sign in to comment.