Hide implementation of MultiSpanExporter/Processor behind interface. (#2091)

* Hide implementation of MultiSpanExporter behind interface.

* spanprocessor too

* composite

* composite

* Optimize composites based on number of items.

* Spotless

* IntelliJ + spotless race condition

* Fix test
This commit is contained in:
Anuraag Agrawal 2020-11-20 10:54:35 +09:00 committed by GitHub
parent c0b53e9901
commit d940947e53
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 130 additions and 57 deletions

View File

@ -10,7 +10,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.common.export.ConfigBuilder;
import io.opentelemetry.sdk.trace.MultiSpanProcessor;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;
@ -225,7 +224,7 @@ class DisruptorAsyncSpanProcessorTest {
IncrementSpanProcessor incrementSpanProcessor2 = new IncrementSpanProcessor(REQUIRED, REQUIRED);
DisruptorAsyncSpanProcessor disruptorAsyncSpanProcessor =
DisruptorAsyncSpanProcessor.builder(
MultiSpanProcessor.create(
SpanProcessor.composite(
Arrays.asList(incrementSpanProcessor1, incrementSpanProcessor2)))
.build();
disruptorAsyncSpanProcessor.onStart(Context.root(), readWriteSpan);

View File

@ -61,7 +61,7 @@ public class MultiSpanExporterBenchmark {
public final void setup() {
SpanExporter[] exporter = new SpanExporter[exporterCount];
Arrays.fill(exporter, new NoopSpanExporter());
this.exporter = MultiSpanExporter.create(Arrays.asList(exporter));
this.exporter = SpanExporter.composite(Arrays.asList(exporter));
TestSpanData[] spans = new TestSpanData[spanCount];
for (int i = 0; i < spans.length; i++) {

View File

@ -15,7 +15,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
* Implementation of the {@code SpanProcessor} that simply forwards all received events to a list of
* {@code SpanProcessor}s.
*
* @deprecated Use {@link SpanProcessor#composite(SpanProcessor...)}
*/
@Deprecated
public final class MultiSpanProcessor implements SpanProcessor {
private final List<SpanProcessor> spanProcessorsStart;
private final List<SpanProcessor> spanProcessorsEnd;
@ -28,7 +31,9 @@ public final class MultiSpanProcessor implements SpanProcessor {
* @param spanProcessorList the {@code List} of {@code SpanProcessor}s.
* @return a new {@code MultiSpanProcessor}.
* @throws NullPointerException if the {@code spanProcessorList} is {@code null}.
* @deprecated Use {@link SpanProcessor#composite(SpanProcessor...)}
*/
@Deprecated
public static SpanProcessor create(List<SpanProcessor> spanProcessorList) {
return new MultiSpanProcessor(
new ArrayList<>(Objects.requireNonNull(spanProcessorList, "spanProcessorList")));

View File

@ -8,12 +8,43 @@ package io.opentelemetry.sdk.trace;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.common.CompletableResultCode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* SpanProcessor is the interface {@code TracerSdk} uses to allow synchronous hooks for when a
* {@code Span} is started or when a {@code Span} is ended.
*/
public interface SpanProcessor {
/**
* Returns a {@link SpanProcessor} which simply delegates all processing to the {@code processors}
* in order.
*/
static SpanProcessor composite(SpanProcessor... processors) {
return composite(Arrays.asList(processors));
}
/**
* Returns a {@link SpanProcessor} which simply delegates all processing to the {@code processors}
* in order.
*/
@SuppressWarnings("deprecation")
static SpanProcessor composite(Iterable<SpanProcessor> processors) {
List<SpanProcessor> processorsList = new ArrayList<>();
for (SpanProcessor processor : processors) {
processorsList.add(processor);
}
if (processorsList.isEmpty()) {
return NoopSpanProcessor.getInstance();
}
if (processorsList.size() == 1) {
return processorsList.get(0);
}
return MultiSpanProcessor.create(processorsList);
}
/**
* Called when a {@link io.opentelemetry.api.trace.Span} is started, if the {@link
* Span#isRecording()} returns true.

View File

@ -82,7 +82,7 @@ final class TracerSharedState {
void addSpanProcessor(SpanProcessor spanProcessor) {
synchronized (lock) {
registeredSpanProcessors.add(spanProcessor);
activeSpanProcessor = MultiSpanProcessor.create(registeredSpanProcessors);
activeSpanProcessor = SpanProcessor.composite(registeredSpanProcessors);
}
}

View File

@ -19,7 +19,10 @@ import java.util.logging.Logger;
*
* <p>Can be used to export to multiple backends using the same {@code SpanProcessor} like a {@code
* SimpleSampledSpansProcessor} or a {@code BatchSampledSpansProcessor}.
*
* @deprecated Use {@link SpanExporter#composite(SpanExporter...)}
*/
@Deprecated
public final class MultiSpanExporter implements SpanExporter {
private static final Logger logger = Logger.getLogger(MultiSpanExporter.class.getName());
@ -30,9 +33,11 @@ public final class MultiSpanExporter implements SpanExporter {
*
* @param spanExporters the exporters spans should be sent to
* @return the aggregate span exporter
* @deprecated Use {@link SpanExporter#composite(SpanExporter...)}
*/
@Deprecated
public static SpanExporter create(List<SpanExporter> spanExporters) {
return new MultiSpanExporter(spanExporters);
return new MultiSpanExporter(spanExporters.toArray(new SpanExporter[0]));
}
@Override
@ -94,7 +99,7 @@ public final class MultiSpanExporter implements SpanExporter {
return CompletableResultCode.ofAll(results);
}
private MultiSpanExporter(List<SpanExporter> spanExporters) {
this.spanExporters = spanExporters.toArray(new SpanExporter[0]);
private MultiSpanExporter(SpanExporter[] spanExporters) {
this.spanExporters = spanExporters;
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.sdk.trace.export;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.trace.data.SpanData;
import java.util.Collection;
final class NoopSpanExporter implements SpanExporter {
private static final SpanExporter INSTANCE = new NoopSpanExporter();
static SpanExporter getInstance() {
return INSTANCE;
}
@Override
public CompletableResultCode export(Collection<SpanData> spans) {
return CompletableResultCode.ofSuccess();
}
@Override
public CompletableResultCode flush() {
return CompletableResultCode.ofSuccess();
}
@Override
public CompletableResultCode shutdown() {
return CompletableResultCode.ofSuccess();
}
}

View File

@ -8,7 +8,10 @@ package io.opentelemetry.sdk.trace.export;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.trace.TracerSdkManagement;
import io.opentelemetry.sdk.trace.data.SpanData;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* An interface that allows different tracing services to export recorded data for sampled spans in
@ -19,6 +22,39 @@ import java.util.Collection;
*/
public interface SpanExporter {
/**
* Returns a {@link SpanExporter} which simply delegates all exports to the {@code exporters} in
* order.
*
* <p>Can be used to export to multiple backends using the same {@code SpanProcessor} like a
* {@code SimpleSampledSpansProcessor} or a {@code BatchSampledSpansProcessor}.
*/
static SpanExporter composite(SpanExporter... exporters) {
return composite(Arrays.asList(exporters));
}
/**
* Returns a {@link SpanExporter} which simply delegates all exports to the {@code exporters} in
* order.
*
* <p>Can be used to export to multiple backends using the same {@code SpanProcessor} like a
* {@code SimpleSampledSpansProcessor} or a {@code BatchSampledSpansProcessor}.
*/
@SuppressWarnings("deprecation")
static SpanExporter composite(Iterable<SpanExporter> exporters) {
List<SpanExporter> exportersList = new ArrayList<>();
for (SpanExporter exporter : exporters) {
exportersList.add(exporter);
}
if (exportersList.isEmpty()) {
return NoopSpanExporter.getInstance();
}
if (exportersList.size() == 1) {
return exportersList.get(0);
}
return MultiSpanExporter.create(exportersList);
}
/**
* Called to export sampled {@code Span}s. Note that export operations can be performed
* simultaneously depending on the type of span processor being used. However, the {@link

View File

@ -10,7 +10,6 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import io.opentelemetry.context.Context;
@ -43,7 +42,7 @@ class MultiSpanProcessorTest {
@Test
void empty() {
SpanProcessor multiSpanProcessor = MultiSpanProcessor.create(Collections.emptyList());
SpanProcessor multiSpanProcessor = SpanProcessor.composite(Collections.emptyList());
multiSpanProcessor.onStart(Context.root(), readWriteSpan);
multiSpanProcessor.onEnd(readableSpan);
multiSpanProcessor.shutdown();
@ -52,50 +51,14 @@ class MultiSpanProcessorTest {
@Test
void oneSpanProcessor() {
SpanProcessor multiSpanProcessor =
MultiSpanProcessor.create(Collections.singletonList(spanProcessor1));
multiSpanProcessor.onStart(Context.root(), readWriteSpan);
verify(spanProcessor1).onStart(same(Context.root()), same(readWriteSpan));
multiSpanProcessor.onEnd(readableSpan);
verify(spanProcessor1).onEnd(same(readableSpan));
multiSpanProcessor.forceFlush();
verify(spanProcessor1).forceFlush();
multiSpanProcessor.shutdown();
verify(spanProcessor1).shutdown();
}
@Test
void oneSpanProcessor_NoRequirements() {
when(spanProcessor1.isStartRequired()).thenReturn(false);
when(spanProcessor1.isEndRequired()).thenReturn(false);
SpanProcessor multiSpanProcessor =
MultiSpanProcessor.create(Collections.singletonList(spanProcessor1));
verify(spanProcessor1).isStartRequired();
verify(spanProcessor1).isEndRequired();
assertThat(multiSpanProcessor.isStartRequired()).isFalse();
assertThat(multiSpanProcessor.isEndRequired()).isFalse();
multiSpanProcessor.onStart(Context.root(), readWriteSpan);
verifyNoMoreInteractions(spanProcessor1);
multiSpanProcessor.onEnd(readableSpan);
verifyNoMoreInteractions(spanProcessor1);
multiSpanProcessor.forceFlush();
verify(spanProcessor1).forceFlush();
multiSpanProcessor.shutdown();
verify(spanProcessor1).shutdown();
SpanProcessor.composite(Collections.singletonList(spanProcessor1));
assertThat(multiSpanProcessor).isSameAs(spanProcessor1);
}
@Test
void twoSpanProcessor() {
SpanProcessor multiSpanProcessor =
MultiSpanProcessor.create(Arrays.asList(spanProcessor1, spanProcessor2));
SpanProcessor.composite(Arrays.asList(spanProcessor1, spanProcessor2));
multiSpanProcessor.onStart(Context.root(), readWriteSpan);
verify(spanProcessor1).onStart(same(Context.root()), same(readWriteSpan));
verify(spanProcessor2).onStart(same(Context.root()), same(readWriteSpan));
@ -118,7 +81,7 @@ class MultiSpanProcessorTest {
when(spanProcessor1.isEndRequired()).thenReturn(false);
when(spanProcessor2.isStartRequired()).thenReturn(false);
SpanProcessor multiSpanProcessor =
MultiSpanProcessor.create(Arrays.asList(spanProcessor1, spanProcessor2));
SpanProcessor.composite(Arrays.asList(spanProcessor1, spanProcessor2));
assertThat(multiSpanProcessor.isStartRequired()).isTrue();
assertThat(multiSpanProcessor.isEndRequired()).isTrue();

View File

@ -212,7 +212,7 @@ class BatchSpanProcessorTest {
new WaitingSpanExporter(2, CompletableResultCode.ofSuccess());
tracerSdkFactory.addSpanProcessor(
BatchSpanProcessor.builder(
MultiSpanExporter.create(Arrays.asList(waitingSpanExporter, waitingSpanExporter2)))
SpanExporter.composite(Arrays.asList(waitingSpanExporter, waitingSpanExporter2)))
.setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS)
.build());
@ -231,7 +231,7 @@ class BatchSpanProcessorTest {
new WaitingSpanExporter(maxQueuedSpans, CompletableResultCode.ofSuccess());
BatchSpanProcessor batchSpanProcessor =
BatchSpanProcessor.builder(
MultiSpanExporter.create(Arrays.asList(blockingSpanExporter, waitingSpanExporter)))
SpanExporter.composite(Arrays.asList(blockingSpanExporter, waitingSpanExporter)))
.setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS)
.setMaxQueueSize(maxQueuedSpans)
.setMaxExportBatchSize(maxQueuedSpans / 2)
@ -296,7 +296,7 @@ class BatchSpanProcessorTest {
.export(ArgumentMatchers.anyList());
tracerSdkFactory.addSpanProcessor(
BatchSpanProcessor.builder(
MultiSpanExporter.create(Arrays.asList(mockServiceHandler, waitingSpanExporter)))
SpanExporter.composite(Arrays.asList(mockServiceHandler, waitingSpanExporter)))
.setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS)
.build());
ReadableSpan span1 = createSampledEndedSpan(SPAN_NAME_1);

View File

@ -32,7 +32,7 @@ class MultiSpanExporterTest {
@Test
void empty() {
SpanExporter multiSpanExporter = MultiSpanExporter.create(Collections.emptyList());
SpanExporter multiSpanExporter = SpanExporter.composite(Collections.emptyList());
multiSpanExporter.export(SPAN_LIST);
multiSpanExporter.shutdown();
}
@ -40,7 +40,7 @@ class MultiSpanExporterTest {
@Test
void oneSpanExporter() {
SpanExporter multiSpanExporter =
MultiSpanExporter.create(Collections.singletonList(spanExporter1));
SpanExporter.composite(Collections.singletonList(spanExporter1));
when(spanExporter1.export(same(SPAN_LIST))).thenReturn(CompletableResultCode.ofSuccess());
assertThat(multiSpanExporter.export(SPAN_LIST).isSuccess()).isTrue();
@ -58,7 +58,7 @@ class MultiSpanExporterTest {
@Test
void twoSpanExporter() {
SpanExporter multiSpanExporter =
MultiSpanExporter.create(Arrays.asList(spanExporter1, spanExporter2));
SpanExporter.composite(Arrays.asList(spanExporter1, spanExporter2));
when(spanExporter1.export(same(SPAN_LIST))).thenReturn(CompletableResultCode.ofSuccess());
when(spanExporter2.export(same(SPAN_LIST))).thenReturn(CompletableResultCode.ofSuccess());
@ -82,7 +82,7 @@ class MultiSpanExporterTest {
@Test
void twoSpanExporter_OneReturnFailure() {
SpanExporter multiSpanExporter =
MultiSpanExporter.create(Arrays.asList(spanExporter1, spanExporter2));
SpanExporter.composite(Arrays.asList(spanExporter1, spanExporter2));
when(spanExporter1.export(same(SPAN_LIST))).thenReturn(CompletableResultCode.ofSuccess());
when(spanExporter2.export(same(SPAN_LIST))).thenReturn(CompletableResultCode.ofFailure());
@ -106,7 +106,7 @@ class MultiSpanExporterTest {
@Test
void twoSpanExporter_FirstThrows() {
SpanExporter multiSpanExporter =
MultiSpanExporter.create(Arrays.asList(spanExporter1, spanExporter2));
SpanExporter.composite(Arrays.asList(spanExporter1, spanExporter2));
Mockito.doThrow(new IllegalArgumentException("No export for you."))
.when(spanExporter1)