From 5671f56450ed0ef71548c0b57f6a1bf43f6ee191 Mon Sep 17 00:00:00 2001 From: Giovanni Liva Date: Mon, 6 Apr 2020 08:35:18 +0200 Subject: [PATCH 1/3] Add config class for BatchSpanProcessor --- .../jaeger/JaegerGrpcSpanExporter.java | 2 +- .../sdk/common/ConfigBuilder.java | 52 +++++ .../sdk/trace/export/BatchSpansProcessor.java | 210 ++++++++++++++++++ .../trace/export/BatchSpansProcessorTest.java | 82 ++++--- .../export/SimpleSpansProcessorTest.java | 8 +- 5 files changed, 319 insertions(+), 35 deletions(-) create mode 100644 sdk/src/main/java/io/opentelemetry/sdk/common/ConfigBuilder.java diff --git a/exporters/jaeger/src/main/java/io/opentelemetry/exporters/jaeger/JaegerGrpcSpanExporter.java b/exporters/jaeger/src/main/java/io/opentelemetry/exporters/jaeger/JaegerGrpcSpanExporter.java index 8beba33ba1f..0e7f9e1eb4b 100644 --- a/exporters/jaeger/src/main/java/io/opentelemetry/exporters/jaeger/JaegerGrpcSpanExporter.java +++ b/exporters/jaeger/src/main/java/io/opentelemetry/exporters/jaeger/JaegerGrpcSpanExporter.java @@ -232,7 +232,7 @@ public JaegerGrpcSpanExporter build() { * @param tracerSdkProvider tracer SDK provider */ public void install(TracerSdkProvider tracerSdkProvider) { - BatchSpansProcessor spansProcessor = BatchSpansProcessor.newBuilder(this.build()).build(); + BatchSpansProcessor spansProcessor = BatchSpansProcessor.create(this.build()); tracerSdkProvider.addSpanProcessor(spansProcessor); } diff --git a/sdk/src/main/java/io/opentelemetry/sdk/common/ConfigBuilder.java b/sdk/src/main/java/io/opentelemetry/sdk/common/ConfigBuilder.java new file mode 100644 index 00000000000..2a67d981a02 --- /dev/null +++ b/sdk/src/main/java/io/opentelemetry/sdk/common/ConfigBuilder.java @@ -0,0 +1,52 @@ +/* + * Copyright 2020, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.sdk.common; + +import java.util.Map; + +public abstract class ConfigBuilder { + + protected ConfigBuilder() {} + + public abstract ConfigBuilder fromConfigMap(Map configMap); + + public abstract ConfigBuilder fromEnv(); + + protected boolean getProperty(String name, Map map, boolean defaultValue) { + try { + return Boolean.parseBoolean(System.getProperty(name, map.get(name))); + } catch (NumberFormatException ex) { + return defaultValue; + } + } + + protected int getProperty(String name, Map map, int defaultValue) { + try { + return Integer.parseInt(System.getProperty(name, map.get(name))); + } catch (NumberFormatException ex) { + return defaultValue; + } + } + + protected long getProperty(String name, Map map, long defaultValue) { + try { + return Long.parseLong(System.getProperty(name, map.get(name))); + } catch (NumberFormatException ex) { + return defaultValue; + } + } +} diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java index 2b68eb7414a..5f501258e70 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java @@ -22,12 +22,14 @@ import io.opentelemetry.metrics.LongCounter.BoundLongCounter; import io.opentelemetry.metrics.Meter; import io.opentelemetry.sdk.common.DaemonThreadFactory; +import io.opentelemetry.sdk.common.ConfigBuilder; import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.data.SpanData; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -37,6 +39,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.Immutable; /** * Implementation of the {@link SpanProcessor} that batches spans exported by the SDK then pushes @@ -82,6 +85,33 @@ private BatchSpansProcessor( this.sampled = sampled; } + /** + * Creates a {@code BatchSpansProcessor} instance using the default configuration. + * + * @param spanExporter The {@link SpanExporter} to use + * @return a {@code BatchSpansProcessor} instance. + */ + public static BatchSpansProcessor create(SpanExporter spanExporter) { + return create(spanExporter, Config.getDefault()); + } + + /** + * Creates a {@code BatchSpansProcessor} instance. + * + * @param spanExporter The {@link SpanExporter} to use + * @param config The {@link Config} to use + * @return a {@code BatchSpansProcessor} instance. + */ + public static BatchSpansProcessor create(SpanExporter spanExporter, Config config) { + return new BatchSpansProcessor( + spanExporter, + config.sampled, + config.scheduleDelayMillis, + config.maxQueueSize, + config.maxExportBatchSize, + config.exporterTimeoutMillis); + } + @Override public void onStart(ReadableSpan span) {} @@ -394,4 +424,184 @@ public void run() { } } } + + @Immutable + public static final class Config { + private final long scheduleDelayMillis; + private final int maxQueueSize; + private final int maxExportBatchSize; + private final int exporterTimeoutMillis; + private final boolean sampled; + + private Config( + boolean sampled, + long scheduleDelayMillis, + int maxQueueSize, + int maxExportBatchSize, + int exporterTimeoutMillis) { + this.sampled = sampled; + this.scheduleDelayMillis = scheduleDelayMillis; + this.maxQueueSize = maxQueueSize; + this.maxExportBatchSize = maxExportBatchSize; + this.exporterTimeoutMillis = exporterTimeoutMillis; + } + + public long getScheduleDelayMillis() { + return scheduleDelayMillis; + } + + public int getMaxQueueSize() { + return maxQueueSize; + } + + public int getMaxExportBatchSize() { + return maxExportBatchSize; + } + + public int getExporterTimeoutMillis() { + return exporterTimeoutMillis; + } + + public boolean isReportOnlySampled() { + return sampled; + } + + public static Config getDefault() { + return new Builder().build(); + } + + public static Builder newBuilder() { + return new Builder(); + } + + public static class Builder extends ConfigBuilder { + + private static final String KEY_SCHEDULE_DELAY_MILLIS = "OTEL_BSP_SCHEDULE_DELAY"; + private static final String KEY_MAX_QUEUE_SIZE = "OTEL_BSP_MAX_QUEUE"; + private static final String KEY_MAX_EXPORT_BATCH_SIZE = "OTEL_BSP_MAX_EXPORT_BATCH"; + private static final String KEY_EXPORT_TIMEOUT_MILLIS = "OTEL_BSP_EXPORT_TIMEOUT"; + private static final String KEY_SAMPLED = "OTEL_BSP_REPORT_SAMPLED"; + + private static final long SCHEDULE_DELAY_MILLIS = 5000; + private static final int MAX_QUEUE_SIZE = 2048; + private static final int MAX_EXPORT_BATCH_SIZE = 512; + private static final int EXPORT_TIMEOUT_MILLIS = 30_000; + private static final boolean REPORT_ONLY_SAMPLED = true; + + private long scheduleDelayMillis = SCHEDULE_DELAY_MILLIS; + private int maxQueueSize = MAX_QUEUE_SIZE; + private int maxExportBatchSize = MAX_EXPORT_BATCH_SIZE; + private int exporterTimeoutMillis = EXPORT_TIMEOUT_MILLIS; + private boolean sampled = REPORT_ONLY_SAMPLED; + + /** + * Sets the configuration values from the given configuration map. + * + * @param configMap {@link Map} holding the configuration values. + * @return this. + */ + @Override + public Builder fromConfigMap(Map configMap) { + this.setScheduleDelayMillis( + getProperty(KEY_SCHEDULE_DELAY_MILLIS, configMap, SCHEDULE_DELAY_MILLIS)); + this.setMaxQueueSize(getProperty(KEY_MAX_QUEUE_SIZE, configMap, MAX_QUEUE_SIZE)); + this.setMaxExportBatchSize( + getProperty(KEY_MAX_EXPORT_BATCH_SIZE, configMap, MAX_EXPORT_BATCH_SIZE)); + this.setExporterTimeoutMillis( + getProperty(KEY_EXPORT_TIMEOUT_MILLIS, configMap, EXPORT_TIMEOUT_MILLIS)); + this.reportOnlySampled(getProperty(KEY_SAMPLED, configMap, REPORT_ONLY_SAMPLED)); + return this; + } + + /** + * Sets the configuration values from environment variables. + * + * @return this. + */ + @Override + public Builder fromEnv() { + return fromConfigMap(System.getenv()); + } + + /** + * Set whether only sampled spans should be reported. + * + * @param sampled report only sampled spans. + * @return this. + */ + public Builder reportOnlySampled(boolean sampled) { + this.sampled = sampled; + return this; + } + + /** + * Sets the delay interval between two consecutive exports. The actual interval may be shorter + * if the batch size is getting larger than {@code maxQueuedSpans / 2}. + * + *

Default value is {@code 5000}ms. + * + * @param scheduleDelayMillis the delay interval between two consecutive exports. + * @return this. + */ + public Builder setScheduleDelayMillis(long scheduleDelayMillis) { + Utils.checkArgument( + scheduleDelayMillis >= 0, "scheduleDelayMillis must greater than or equal 0."); + this.scheduleDelayMillis = scheduleDelayMillis; + return this; + } + + /** + * Sets the maximum time an exporter will be allowed to run before being cancelled. + * + *

Default value is {@code 30000}ms + * + * @param exporterTimeoutMillis the timeout for exports in milliseconds. + * @return this + */ + public Builder setExporterTimeoutMillis(int exporterTimeoutMillis) { + Utils.checkArgument( + exporterTimeoutMillis >= 0, "exporterTimeoutMillis must greater than or equal 0."); + this.exporterTimeoutMillis = exporterTimeoutMillis; + return this; + } + + /** + * Sets the maximum number of Spans that are kept in the queue before start dropping. + * + *

See the BatchSampledSpansProcessor class description for a high-level design description + * of this class. + * + *

Default value is {@code 2048}. + * + * @param maxQueueSize the maximum number of Spans that are kept in the queue before start + * dropping. + * @return this. + */ + public Builder setMaxQueueSize(int maxQueueSize) { + Utils.checkArgument(maxQueueSize > 0, "maxQueueSize must be positive."); + this.maxQueueSize = maxQueueSize; + return this; + } + + /** + * Sets the maximum batch size for every export. This must be smaller or equal to {@code + * maxQueuedSpans}. + * + *

Default value is {@code 512}. + * + * @param maxExportBatchSize the maximum batch size for every export. + * @return this. + */ + public Builder setMaxExportBatchSize(int maxExportBatchSize) { + Utils.checkArgument(maxExportBatchSize > 0, "maxExportBatchSize must be positive."); + this.maxExportBatchSize = maxExportBatchSize; + return this; + } + + public Config build() { + return new Config( + sampled, scheduleDelayMillis, maxQueueSize, maxExportBatchSize, exporterTimeoutMillis); + } + } + } } diff --git a/sdk/src/test/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessorTest.java b/sdk/src/test/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessorTest.java index d3f5331dcda..67ac7340577 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessorTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessorTest.java @@ -91,8 +91,7 @@ private void createNotSampledEndedSpan(String spanName) { @Test public void startEndRequirements() { - BatchSpansProcessor spansProcessor = - BatchSpansProcessor.newBuilder(new WaitingSpanExporter(0)).build(); + BatchSpansProcessor spansProcessor = BatchSpansProcessor.create(new WaitingSpanExporter(0)); assertThat(spansProcessor.isStartRequired()).isFalse(); assertThat(spansProcessor.isEndRequired()).isTrue(); } @@ -101,9 +100,11 @@ public void startEndRequirements() { public void exportDifferentSampledSpans() { WaitingSpanExporter waitingSpanExporter = new WaitingSpanExporter(2); tracerSdkFactory.addSpanProcessor( - BatchSpansProcessor.newBuilder(waitingSpanExporter) - .setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS) - .build()); + BatchSpansProcessor.create( + waitingSpanExporter, + BatchSpansProcessor.Config.newBuilder() + .setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS) + .build())); ReadableSpan span1 = createSampledEndedSpan(SPAN_NAME_1); ReadableSpan span2 = createSampledEndedSpan(SPAN_NAME_2); @@ -114,12 +115,14 @@ public void exportDifferentSampledSpans() { @Test public void exportMoreSpansThanTheBufferSize() { WaitingSpanExporter waitingSpanExporter = new WaitingSpanExporter(6); - tracerSdkFactory.addSpanProcessor( - BatchSpansProcessor.newBuilder(waitingSpanExporter) + BatchSpansProcessor.Config config = + BatchSpansProcessor.Config.newBuilder() .setMaxQueueSize(6) .setMaxExportBatchSize(2) .setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS) - .build()); + .build(); + + tracerSdkFactory.addSpanProcessor(BatchSpansProcessor.create(waitingSpanExporter, config)); ReadableSpan span1 = createSampledEndedSpan(SPAN_NAME_1); ReadableSpan span2 = createSampledEndedSpan(SPAN_NAME_1); @@ -141,12 +144,15 @@ public void exportMoreSpansThanTheBufferSize() { @Test public void forceExport() { WaitingSpanExporter waitingSpanExporter = new WaitingSpanExporter(1, 1); - BatchSpansProcessor batchSpansProcessor = - BatchSpansProcessor.newBuilder(waitingSpanExporter) + BatchSpansProcessor.Config config = + BatchSpansProcessor.Config.newBuilder() .setMaxQueueSize(10_000) .setMaxExportBatchSize(2_000) .setScheduleDelayMillis(10_000) // 10s .build(); + BatchSpansProcessor batchSpansProcessor = + BatchSpansProcessor.create(waitingSpanExporter, config); + tracerSdkFactory.addSpanProcessor(batchSpansProcessor); for (int i = 0; i < 100; i++) { createSampledEndedSpan("notExported"); @@ -164,12 +170,15 @@ public void forceExport() { public void exportSpansToMultipleServices() { WaitingSpanExporter waitingSpanExporter = new WaitingSpanExporter(2); WaitingSpanExporter waitingSpanExporter2 = new WaitingSpanExporter(2); - tracerSdkFactory.addSpanProcessor( - BatchSpansProcessor.newBuilder( - MultiSpanExporter.create( - Arrays.asList(waitingSpanExporter, waitingSpanExporter2))) + BatchSpansProcessor.Config config = + BatchSpansProcessor.Config.newBuilder() .setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS) - .build()); + .build(); + tracerSdkFactory.addSpanProcessor( + BatchSpansProcessor.create( + MultiSpanExporter.create( + Arrays.asList(waitingSpanExporter, waitingSpanExporter2)), + config)); ReadableSpan span1 = createSampledEndedSpan(SPAN_NAME_1); ReadableSpan span2 = createSampledEndedSpan(SPAN_NAME_2); @@ -183,13 +192,16 @@ public void exportSpansToMultipleServices() { public void exportMoreSpansThanTheMaximumLimit() { final int maxQueuedSpans = 8; WaitingSpanExporter waitingSpanExporter = new WaitingSpanExporter(maxQueuedSpans); - tracerSdkFactory.addSpanProcessor( - BatchSpansProcessor.newBuilder( - MultiSpanExporter.create(Arrays.asList(blockingSpanExporter, waitingSpanExporter))) + BatchSpansProcessor.Config config = + BatchSpansProcessor.Config.newBuilder() .setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS) .setMaxQueueSize(maxQueuedSpans) .setMaxExportBatchSize(maxQueuedSpans / 2) - .build()); + .build(); + tracerSdkFactory.addSpanProcessor( + BatchSpansProcessor.create( + MultiSpanExporter.create(Arrays.asList(blockingSpanExporter, waitingSpanExporter)), + config)); List spansToExport = new ArrayList<>(maxQueuedSpans + 1); // Wait to block the worker thread in the BatchSampledSpansProcessor. This ensures that no items @@ -247,11 +259,14 @@ public void serviceHandlerThrowsException() { .when(mockServiceHandler) .export(ArgumentMatchers.anyList()); - tracerSdkFactory.addSpanProcessor( - BatchSpansProcessor.newBuilder( - MultiSpanExporter.create(Arrays.asList(mockServiceHandler, waitingSpanExporter))) + BatchSpansProcessor.Config config = + BatchSpansProcessor.Config.newBuilder() .setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS) - .build()); + .build(); + tracerSdkFactory.addSpanProcessor( + BatchSpansProcessor.create( + MultiSpanExporter.create(Arrays.asList(mockServiceHandler, waitingSpanExporter)), + config)); ReadableSpan span1 = createSampledEndedSpan(SPAN_NAME_1); List exported = waitingSpanExporter.waitForExport(); assertThat(exported).containsExactly(span1.toSpanData()); @@ -281,12 +296,14 @@ public ResultCode export(Collection spans) { }; int exporterTimeoutMillis = 100; - tracerSdkFactory.addSpanProcessor( - BatchSpansProcessor.newBuilder(waitingSpanExporter) + BatchSpansProcessor.Config config = + BatchSpansProcessor.Config.newBuilder() .setExporterTimeoutMillis(exporterTimeoutMillis) .setScheduleDelayMillis(1) .setMaxQueueSize(1) - .build()); + .build(); + + tracerSdkFactory.addSpanProcessor(BatchSpansProcessor.create(waitingSpanExporter, config)); ReadableSpan span = createSampledEndedSpan(SPAN_NAME_1); List exported = waitingSpanExporter.waitForExport(); @@ -300,10 +317,11 @@ public ResultCode export(Collection spans) { @Test public void exportNotSampledSpans() { WaitingSpanExporter waitingSpanExporter = new WaitingSpanExporter(1); - tracerSdkFactory.addSpanProcessor( - BatchSpansProcessor.newBuilder(waitingSpanExporter) + BatchSpansProcessor.Config config = + BatchSpansProcessor.Config.newBuilder() .setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS) - .build()); + .build(); + tracerSdkFactory.addSpanProcessor(BatchSpansProcessor.create(waitingSpanExporter, config)); createNotSampledEndedSpan(SPAN_NAME_1); createNotSampledEndedSpan(SPAN_NAME_2); @@ -355,8 +373,10 @@ public void exportNotSampledSpans_reportOnlySampled() { public void shutdownFlushes() { WaitingSpanExporter waitingSpanExporter = new WaitingSpanExporter(1); // Set the export delay to zero, for no timeout, in order to confirm the #flush() below works - tracerSdkFactory.addSpanProcessor( - BatchSpansProcessor.newBuilder(waitingSpanExporter).setScheduleDelayMillis(0).build()); + BatchSpansProcessor.Config config = + BatchSpansProcessor.Config.newBuilder().setScheduleDelayMillis(0).build(); + + tracerSdkFactory.addSpanProcessor(BatchSpansProcessor.create(waitingSpanExporter, config)); ReadableSpan span2 = createSampledEndedSpan(SPAN_NAME_2); diff --git a/sdk/src/test/java/io/opentelemetry/sdk/trace/export/SimpleSpansProcessorTest.java b/sdk/src/test/java/io/opentelemetry/sdk/trace/export/SimpleSpansProcessorTest.java index bbf84256606..f815a047045 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/trace/export/SimpleSpansProcessorTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/trace/export/SimpleSpansProcessorTest.java @@ -117,10 +117,12 @@ public void onEndSync_OnlySampled_SampledSpan() { @Test public void tracerSdk_NotSampled_Span() { WaitingSpanExporter waitingSpanExporter = new WaitingSpanExporter(1); - tracerSdkFactory.addSpanProcessor( - BatchSpansProcessor.newBuilder(waitingSpanExporter) + + BatchSpansProcessor.Config config = + BatchSpansProcessor.Config.newBuilder() .setScheduleDelayMillis(MAX_SCHEDULE_DELAY_MILLIS) - .build()); + .build(); + tracerSdkFactory.addSpanProcessor(BatchSpansProcessor.create(waitingSpanExporter, config)); TestUtils.startSpanWithSampler(tracerSdkFactory, tracer, SPAN_NAME, Samplers.alwaysOff()) .startSpan() From 9982c8f9cc71bf12ace644e57238569ea84c4014 Mon Sep 17 00:00:00 2001 From: Giovanni Liva Date: Tue, 14 Apr 2020 07:43:37 +0200 Subject: [PATCH 2/3] Implement Configuration for BatchSpanProcessor --- build.gradle | 1 + sdk/build.gradle | 2 + .../sdk/common/ConfigBuilder.java | 52 --- .../sdk/trace/export/BatchSpansProcessor.java | 337 ++++++++++++------ .../sdk/trace/export/package-info.java | 65 ++++ .../trace/export/BatchSpansProcessorTest.java | 202 +++++++++++ 6 files changed, 505 insertions(+), 154 deletions(-) delete mode 100644 sdk/src/main/java/io/opentelemetry/sdk/common/ConfigBuilder.java create mode 100644 sdk/src/main/java/io/opentelemetry/sdk/trace/export/package-info.java diff --git a/build.gradle b/build.gradle index 8246c4ff1ea..b57cc9feffc 100644 --- a/build.gradle +++ b/build.gradle @@ -126,6 +126,7 @@ subprojects { junit : 'junit:junit:4.12', mockito : 'org.mockito:mockito-core:2.28.2', truth : 'com.google.truth:truth:1.0.1', + system_rules : 'com.github.stefanbirkner:system-rules:1.19.0', // env and system properties slf4jsimple : 'org.slf4j:slf4j-simple:1.7.25', // Compatibility layer awaitility : 'org.awaitility:awaitility:3.0.0', // Compatibility layer testcontainers : 'org.testcontainers:testcontainers:1.13.0', diff --git a/sdk/build.gradle b/sdk/build.gradle index dc4ad78ec96..1264025a76f 100644 --- a/sdk/build.gradle +++ b/sdk/build.gradle @@ -18,6 +18,8 @@ dependencies { testAnnotationProcessor libraries.auto_value + testCompile libraries.system_rules + signature "org.codehaus.mojo.signature:java17:1.0@signature" signature "net.sf.androidscents.signature:android-api-level-24:7.0_r2@signature" } diff --git a/sdk/src/main/java/io/opentelemetry/sdk/common/ConfigBuilder.java b/sdk/src/main/java/io/opentelemetry/sdk/common/ConfigBuilder.java deleted file mode 100644 index 2a67d981a02..00000000000 --- a/sdk/src/main/java/io/opentelemetry/sdk/common/ConfigBuilder.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2020, OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.sdk.common; - -import java.util.Map; - -public abstract class ConfigBuilder { - - protected ConfigBuilder() {} - - public abstract ConfigBuilder fromConfigMap(Map configMap); - - public abstract ConfigBuilder fromEnv(); - - protected boolean getProperty(String name, Map map, boolean defaultValue) { - try { - return Boolean.parseBoolean(System.getProperty(name, map.get(name))); - } catch (NumberFormatException ex) { - return defaultValue; - } - } - - protected int getProperty(String name, Map map, int defaultValue) { - try { - return Integer.parseInt(System.getProperty(name, map.get(name))); - } catch (NumberFormatException ex) { - return defaultValue; - } - } - - protected long getProperty(String name, Map map, long defaultValue) { - try { - return Long.parseLong(System.getProperty(name, map.get(name))); - } catch (NumberFormatException ex) { - return defaultValue; - } - } -} diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java index 5f501258e70..2ec61f52f75 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java @@ -16,20 +16,25 @@ package io.opentelemetry.sdk.trace.export; + +import com.google.auto.value.AutoValue; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Maps; import io.opentelemetry.OpenTelemetry; import io.opentelemetry.internal.Utils; import io.opentelemetry.metrics.LongCounter; import io.opentelemetry.metrics.LongCounter.BoundLongCounter; import io.opentelemetry.metrics.Meter; import io.opentelemetry.sdk.common.DaemonThreadFactory; -import io.opentelemetry.sdk.common.ConfigBuilder; import io.opentelemetry.sdk.trace.ReadableSpan; import io.opentelemetry.sdk.trace.SpanProcessor; import io.opentelemetry.sdk.trace.data.SpanData; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -38,6 +43,8 @@ import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.Immutable; @@ -105,11 +112,11 @@ public static BatchSpansProcessor create(SpanExporter spanExporter) { public static BatchSpansProcessor create(SpanExporter spanExporter, Config config) { return new BatchSpansProcessor( spanExporter, - config.sampled, - config.scheduleDelayMillis, - config.maxQueueSize, - config.maxExportBatchSize, - config.exporterTimeoutMillis); + config.isExportOnlySampled(), + config.getScheduleDelayMillis(), + config.getMaxQueueSize(), + config.getMaxExportBatchSize(), + config.getExporterTimeoutMillis()); } @Override @@ -426,144 +433,234 @@ public void run() { } @Immutable - public static final class Config { - private final long scheduleDelayMillis; - private final int maxQueueSize; - private final int maxExportBatchSize; - private final int exporterTimeoutMillis; - private final boolean sampled; + @AutoValue + public abstract static class Config { - private Config( - boolean sampled, - long scheduleDelayMillis, - int maxQueueSize, - int maxExportBatchSize, - int exporterTimeoutMillis) { - this.sampled = sampled; - this.scheduleDelayMillis = scheduleDelayMillis; - this.maxQueueSize = maxQueueSize; - this.maxExportBatchSize = maxExportBatchSize; - this.exporterTimeoutMillis = exporterTimeoutMillis; - } + private static final long DEFAULT_SCHEDULE_DELAY_MILLIS = 5000; + private static final int DEFAULT_MAX_QUEUE_SIZE = 2048; + private static final int DEFAULT_MAX_EXPORT_BATCH_SIZE = 512; + private static final int DEFAULT_EXPORT_TIMEOUT_MILLIS = 30_000; + private static final boolean DEFAULT_EXPORT_ONLY_SAMPLED = true; - public long getScheduleDelayMillis() { - return scheduleDelayMillis; - } + public abstract boolean isExportOnlySampled(); - public int getMaxQueueSize() { - return maxQueueSize; - } + public abstract long getScheduleDelayMillis(); - public int getMaxExportBatchSize() { - return maxExportBatchSize; - } + public abstract int getMaxQueueSize(); - public int getExporterTimeoutMillis() { - return exporterTimeoutMillis; - } + public abstract int getMaxExportBatchSize(); - public boolean isReportOnlySampled() { - return sampled; - } + public abstract int getExporterTimeoutMillis(); + /** + * Creates a {@link Config} object using the default configuration. + * + * @return The {@link Config} object. + * @since 0.4.0 + */ public static Config getDefault() { - return new Builder().build(); + return newBuilder().build(); } - public static Builder newBuilder() { - return new Builder(); + /** + * Creates a {@link Config} object reading the configuration values from the environment and + * from system properties. System properties override values defined in the environment. If a + * configuration value is missing, it uses the default value. + * + * @return The {@link Config} object. + * @since 0.4.0 + */ + public static Config loadFromDefaultSources() { + return newBuilder().readEnvironment().readSystemProperties().build(); } - public static class Builder extends ConfigBuilder { + /** + * Returns a new {@link Builder} with default options. + * + * @return a new {@code Builder} with default options. + * @since 0.4.0 + */ + public static Builder newBuilder() { + return new AutoValue_BatchSpansProcessor_Config.Builder() + .setScheduleDelayMillis(DEFAULT_SCHEDULE_DELAY_MILLIS) + .setMaxQueueSize(DEFAULT_MAX_QUEUE_SIZE) + .setMaxExportBatchSize(DEFAULT_MAX_EXPORT_BATCH_SIZE) + .setExporterTimeoutMillis(DEFAULT_EXPORT_TIMEOUT_MILLIS) + .setExportOnlySampled(DEFAULT_EXPORT_ONLY_SAMPLED); + } - private static final String KEY_SCHEDULE_DELAY_MILLIS = "OTEL_BSP_SCHEDULE_DELAY"; - private static final String KEY_MAX_QUEUE_SIZE = "OTEL_BSP_MAX_QUEUE"; - private static final String KEY_MAX_EXPORT_BATCH_SIZE = "OTEL_BSP_MAX_EXPORT_BATCH"; - private static final String KEY_EXPORT_TIMEOUT_MILLIS = "OTEL_BSP_EXPORT_TIMEOUT"; - private static final String KEY_SAMPLED = "OTEL_BSP_REPORT_SAMPLED"; + @AutoValue.Builder + public abstract static class Builder { + + private static final String KEY_SCHEDULE_DELAY_MILLIS = "otel.bsp.schedule.delay"; + private static final String KEY_MAX_QUEUE_SIZE = "otel.bsp.max.queue"; + private static final String KEY_MAX_EXPORT_BATCH_SIZE = "otel.bsp.max.export.batch"; + private static final String KEY_EXPORT_TIMEOUT_MILLIS = "otel.bsp.export.timeout"; + private static final String KEY_SAMPLED = "otel.bsp.export.sampled"; + + @VisibleForTesting + protected enum NamingConvention { + DOT { + @Override + public String normalize(@Nonnull String key) { + return key.toLowerCase(); + } + }, + ENV_VAR { + @Override + public String normalize(@Nonnull String key) { + return key.toLowerCase().replace("_", "."); + } + }; - private static final long SCHEDULE_DELAY_MILLIS = 5000; - private static final int MAX_QUEUE_SIZE = 2048; - private static final int MAX_EXPORT_BATCH_SIZE = 512; - private static final int EXPORT_TIMEOUT_MILLIS = 30_000; - private static final boolean REPORT_ONLY_SAMPLED = true; + public abstract String normalize(@Nonnull String key); - private long scheduleDelayMillis = SCHEDULE_DELAY_MILLIS; - private int maxQueueSize = MAX_QUEUE_SIZE; - private int maxExportBatchSize = MAX_EXPORT_BATCH_SIZE; - private int exporterTimeoutMillis = EXPORT_TIMEOUT_MILLIS; - private boolean sampled = REPORT_ONLY_SAMPLED; + public Map normalize(@Nonnull Map map) { + Map properties = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + properties.put(normalize(entry.getKey()), entry.getValue()); + } + return Collections.unmodifiableMap(properties); + } + } /** - * Sets the configuration values from the given configuration map. + * Sets the configuration values from the given configuration map for only the available keys. + * This method looks for the following keys: + * + *

    + *
  • {@code otel.bsp.schedule.delay}: to set the delay interval between two consecutive + * exports. + *
  • {@code otel.bsp.max.queue}: to set the maximum queue size. + *
  • {@code otel.bsp.max.export.batch}: to set the maximum batch size. + *
  • {@code otel.bsp.export.timeout}: to set the maximum allowed time to export data. + *
  • {@code otel.bsp.export.sampled}: to set whether only sampled spans should be + * exported. + *
* * @param configMap {@link Map} holding the configuration values. * @return this. */ - @Override - public Builder fromConfigMap(Map configMap) { - this.setScheduleDelayMillis( - getProperty(KEY_SCHEDULE_DELAY_MILLIS, configMap, SCHEDULE_DELAY_MILLIS)); - this.setMaxQueueSize(getProperty(KEY_MAX_QUEUE_SIZE, configMap, MAX_QUEUE_SIZE)); - this.setMaxExportBatchSize( - getProperty(KEY_MAX_EXPORT_BATCH_SIZE, configMap, MAX_EXPORT_BATCH_SIZE)); - this.setExporterTimeoutMillis( - getProperty(KEY_EXPORT_TIMEOUT_MILLIS, configMap, EXPORT_TIMEOUT_MILLIS)); - this.reportOnlySampled(getProperty(KEY_SAMPLED, configMap, REPORT_ONLY_SAMPLED)); + @VisibleForTesting + Builder fromConfigMap(Map configMap, NamingConvention namingConvention) { + configMap = namingConvention.normalize(configMap); + Long longValue = getLongProperty(KEY_SCHEDULE_DELAY_MILLIS, configMap); + if (longValue != null) { + this.setScheduleDelayMillis(longValue); + } + Integer intValue = getIntProperty(KEY_MAX_QUEUE_SIZE, configMap); + if (intValue != null) { + this.setMaxQueueSize(intValue); + } + intValue = getIntProperty(KEY_MAX_EXPORT_BATCH_SIZE, configMap); + if (intValue != null) { + this.setMaxExportBatchSize(intValue); + } + intValue = getIntProperty(KEY_EXPORT_TIMEOUT_MILLIS, configMap); + if (intValue != null) { + this.setExporterTimeoutMillis(intValue); + } + Boolean boolValue = getBooleanProperty(KEY_SAMPLED, configMap); + if (boolValue != null) { + this.setExportOnlySampled(boolValue); + } return this; } /** - * Sets the configuration values from environment variables. + * Sets the configuration values from the given properties object for only the available keys. + * This method looks for the following keys: * + *
    + *
  • {@code otel.bsp.schedule.delay}: to set the delay interval between two consecutive + * exports. + *
  • {@code otel.bsp.max.queue}: to set the maximum queue size. + *
  • {@code otel.bsp.max.export.batch}: to set the maximum batch size. + *
  • {@code otel.bsp.export.timeout}: to set the maximum allowed time to export data. + *
  • {@code otel.bsp.export.sampled}: to set whether only sampled spans should be + * exported. + *
+ * + * @param properties {@link Properties} holding the configuration values. * @return this. */ - @Override - public Builder fromEnv() { - return fromConfigMap(System.getenv()); + public Builder readProperties(Properties properties) { + return fromConfigMap(Maps.fromProperties(properties), NamingConvention.DOT); } /** - * Set whether only sampled spans should be reported. + * Sets the configuration values from environment variables for only the available keys. This + * method looks for the following keys: + * + *
    + *
  • {@code OTEL_BSP_SCHEDULE_DELAY}: to set the delay interval between two consecutive + * exports. + *
  • {@code OTEL_BSP_MAX_QUEUE}: to set the maximum queue size. + *
  • {@code OTEL_BSP_MAX_EXPORT_BATCH}: to set the maximum batch size. + *
  • {@code OTEL_BSP_EXPORT_TIMEOUT}: to set the maximum allowed time to export data. + *
  • {@code OTEL_BSP_EXPORT_SAMPLED}: to set whether only sampled spans should be + * exported. + *
* - * @param sampled report only sampled spans. * @return this. */ - public Builder reportOnlySampled(boolean sampled) { - this.sampled = sampled; - return this; + public Builder readEnvironment() { + return fromConfigMap(System.getenv(), NamingConvention.ENV_VAR); + } + + /** + * Sets the configuration values from system properties for only the available keys. This + * method looks for the following keys: + * + *
    + *
  • {@code otel.bsp.schedule.delay}: to set the delay interval between two consecutive + * exports. + *
  • {@code otel.bsp.max.queue}: to set the maximum queue size. + *
  • {@code otel.bsp.max.export.batch}: to set the maximum batch size. + *
  • {@code otel.bsp.export.timeout}: to set the maximum allowed time to export data. + *
  • {@code otel.bsp.export.sampled}: to set whether only sampled spans should be + * reported. + *
+ * + * @return this. + */ + public Builder readSystemProperties() { + return readProperties(System.getProperties()); } + /** + * Set whether only sampled spans should be exported. + * + *

Default value is {@code true}. + * + * @see BatchSpansProcessor.Config#DEFAULT_EXPORT_ONLY_SAMPLED + * @param sampled report only sampled spans. + * @return this. + */ + public abstract Builder setExportOnlySampled(boolean sampled); + /** * Sets the delay interval between two consecutive exports. The actual interval may be shorter * if the batch size is getting larger than {@code maxQueuedSpans / 2}. * *

Default value is {@code 5000}ms. * + * @see BatchSpansProcessor.Config#DEFAULT_SCHEDULE_DELAY_MILLIS * @param scheduleDelayMillis the delay interval between two consecutive exports. * @return this. */ - public Builder setScheduleDelayMillis(long scheduleDelayMillis) { - Utils.checkArgument( - scheduleDelayMillis >= 0, "scheduleDelayMillis must greater than or equal 0."); - this.scheduleDelayMillis = scheduleDelayMillis; - return this; - } + public abstract Builder setScheduleDelayMillis(long scheduleDelayMillis); /** * Sets the maximum time an exporter will be allowed to run before being cancelled. * *

Default value is {@code 30000}ms * + * @see BatchSpansProcessor.Config#DEFAULT_EXPORT_TIMEOUT_MILLIS * @param exporterTimeoutMillis the timeout for exports in milliseconds. * @return this */ - public Builder setExporterTimeoutMillis(int exporterTimeoutMillis) { - Utils.checkArgument( - exporterTimeoutMillis >= 0, "exporterTimeoutMillis must greater than or equal 0."); - this.exporterTimeoutMillis = exporterTimeoutMillis; - return this; - } + public abstract Builder setExporterTimeoutMillis(int exporterTimeoutMillis); /** * Sets the maximum number of Spans that are kept in the queue before start dropping. @@ -573,15 +670,12 @@ public Builder setExporterTimeoutMillis(int exporterTimeoutMillis) { * *

Default value is {@code 2048}. * + * @see BatchSpansProcessor.Config#DEFAULT_MAX_QUEUE_SIZE * @param maxQueueSize the maximum number of Spans that are kept in the queue before start * dropping. * @return this. */ - public Builder setMaxQueueSize(int maxQueueSize) { - Utils.checkArgument(maxQueueSize > 0, "maxQueueSize must be positive."); - this.maxQueueSize = maxQueueSize; - return this; - } + public abstract Builder setMaxQueueSize(int maxQueueSize); /** * Sets the maximum batch size for every export. This must be smaller or equal to {@code @@ -589,18 +683,57 @@ public Builder setMaxQueueSize(int maxQueueSize) { * *

Default value is {@code 512}. * + * @see BatchSpansProcessor.Config#DEFAULT_MAX_EXPORT_BATCH_SIZE * @param maxExportBatchSize the maximum batch size for every export. * @return this. */ - public Builder setMaxExportBatchSize(int maxExportBatchSize) { - Utils.checkArgument(maxExportBatchSize > 0, "maxExportBatchSize must be positive."); - this.maxExportBatchSize = maxExportBatchSize; - return this; - } + public abstract Builder setMaxExportBatchSize(int maxExportBatchSize); + abstract Config autoBuild(); // not public + + /** + * Builds the {@link Config} object. + * + * @return the {@link Config} object. + */ public Config build() { - return new Config( - sampled, scheduleDelayMillis, maxQueueSize, maxExportBatchSize, exporterTimeoutMillis); + Config config = autoBuild(); + Utils.checkArgument( + config.getScheduleDelayMillis() >= 0, + "scheduleDelayMillis must greater than or equal 0."); + Utils.checkArgument( + config.getExporterTimeoutMillis() >= 0, + "exporterTimeoutMillis must greater than or equal 0."); + Utils.checkArgument(config.getMaxQueueSize() > 0, "maxQueueSize must be positive."); + Utils.checkArgument( + config.getMaxExportBatchSize() > 0, "maxExportBatchSize must be positive."); + return config; + } + + @Nullable + private static Boolean getBooleanProperty(String name, Map map) { + if (map.containsKey(name)) { + return Boolean.parseBoolean(map.get(name)); + } + return null; + } + + @Nullable + private static Integer getIntProperty(String name, Map map) { + try { + return Integer.parseInt(map.get(name)); + } catch (NumberFormatException ex) { + return null; + } + } + + @Nullable + private static Long getLongProperty(String name, Map map) { + try { + return Long.parseLong(map.get(name)); + } catch (NumberFormatException ex) { + return null; + } } } } diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/export/package-info.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/export/package-info.java new file mode 100644 index 00000000000..dfbd1a28e17 --- /dev/null +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/export/package-info.java @@ -0,0 +1,65 @@ +/* + * Copyright 2019, OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Utilities that allows different tracing services to export recorded data for sampled spans in + * their own format. + * + *

Contents

+ * + *
    + *
  • {@link io.opentelemetry.sdk.trace.export.SpanExporter} + *
  • {@link io.opentelemetry.sdk.trace.export.SimpleSpansProcessor} + *
  • {@link io.opentelemetry.sdk.trace.export.BatchSpansProcessor} + *
  • {@link io.opentelemetry.sdk.trace.export.MultiSpanExporter} + *
+ * + *

Default values for {@link io.opentelemetry.sdk.trace.export.BatchSpansProcessor.Config}

+ * + *
    + *
  • {@code SCHEDULE_DELAY_MILLIS: 5000} + *
  • {@code MAX_QUEUE_SIZE: 2048} + *
  • {@code MAX_EXPORT_BATCH_SIZE: 512} + *
  • {@code EXPORT_TIMEOUT_MILLIS: 30_000} + *
  • {@code REPORT_ONLY_SAMPLED: true} + *
+ * + *

Values for {@link io.opentelemetry.sdk.trace.export.BatchSpansProcessor.Config} can be read + * from system properties, environment variables, or {@link java.util.Properties} objects. + * + *

For System Properties and {@link java.util.Properties} objects, {@link + * io.opentelemetry.sdk.trace.export.BatchSpansProcessor.Config} will look for the following names: + * + *

    + *
  • {@code otel.bsp.schedule.delay}: sets the delay interval between two consecutive exports. + *
  • {@code otel.bsp.max.queue}: sets the maximum queue size. + *
  • {@code otel.bsp.max.export.batch}: sets the maximum batch size. + *
  • {@code otel.bsp.export.timeout}: sets the maximum allowed time to export data. + *
  • {@code otel.bsp.export.sampled}: sets whether only sampled spans should be exported. + *
+ * + *

For Environment Variable, {@link io.opentelemetry.sdk.trace.export.BatchSpansProcessor.Config} + * will look for the following names: + * + *

    + *
  • {@code OTEL_BSP_SCHEDULE_DELAY}: sets the delay interval between two consecutive exports. + *
  • {@code OTEL_BSP_MAX_QUEUE}: sets the maximum queue size. + *
  • {@code OTEL_BSP_MAX_EXPORT_BATCH}: sets the maximum batch size. + *
  • {@code OTEL_BSP_EXPORT_TIMEOUT}: sets the maximum allowed time to export data. + *
  • {@code OTEL_BSP_EXPORT_SAMPLED}: sets whether only sampled spans should be exported. + *
+ */ +package io.opentelemetry.sdk.trace.export; diff --git a/sdk/src/test/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessorTest.java b/sdk/src/test/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessorTest.java index 67ac7340577..b1492d6a849 100644 --- a/sdk/src/test/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessorTest.java +++ b/sdk/src/test/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessorTest.java @@ -28,7 +28,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -36,7 +39,10 @@ import javax.annotation.concurrent.GuardedBy; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; +import org.junit.contrib.java.lang.system.ProvideSystemProperty; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import org.mockito.ArgumentMatchers; @@ -65,6 +71,80 @@ public void cleanup() { tracerSdkFactory.shutdown(); } + @RunWith(JUnit4.class) + public static class ConfigurationSystemPropertiesTest { + @Rule + public final ProvideSystemProperty systemProperties = + new ProvideSystemProperty("otel.bsp.schedule.delay", "5") + .and("otel.bsp.max.queue", "5") + .and("otel.bsp.export.timeout", "5") + .and("otel.bsp.export.sampled", "false"); + + @Test + public void testSystemProperties() { + BatchSpansProcessor.Config config = BatchSpansProcessor.Config.loadFromDefaultSources(); + assertThat(config.getScheduleDelayMillis()).isEqualTo(5); + assertThat(config.getMaxQueueSize()).isEqualTo(5); + // This is not defined and should be equal to the default values + assertThat(config.getMaxExportBatchSize()) + .isEqualTo(BatchSpansProcessor.Config.getDefault().getMaxExportBatchSize()); + assertThat(config.getExporterTimeoutMillis()).isEqualTo(5); + assertThat(config.isExportOnlySampled()).isEqualTo(false); + } + } + + @RunWith(JUnit4.class) + public static class ConfigurationEnvironmentVariablesTest { + @Rule + public final EnvironmentVariables environmentVariables = + new EnvironmentVariables() + .set("OTEL_BSP_MAX_QUEUE", "22") + .set("OTEL_BSP_EXPORT_TIMEOUT", "22") + .set("OTEL_BSP_EXPORT_SAMPLED", "true"); + + @Test + public void testEnvironmentVariables() { + BatchSpansProcessor.Config config = BatchSpansProcessor.Config.loadFromDefaultSources(); + BatchSpansProcessor.Config defaultConf = BatchSpansProcessor.Config.getDefault(); + // This is not defined and should be equal to the default values + assertThat(config.getScheduleDelayMillis()).isEqualTo(defaultConf.getScheduleDelayMillis()); + assertThat(config.getMaxQueueSize()).isEqualTo(22); + // This is not defined and should be equal to the default values + assertThat(config.getMaxExportBatchSize()).isEqualTo(defaultConf.getMaxExportBatchSize()); + assertThat(config.getExporterTimeoutMillis()).isEqualTo(22); + assertThat(config.isExportOnlySampled()).isEqualTo(true); + } + } + + @RunWith(JUnit4.class) + public static class ConfigurationSystemAndEnvironmentTest { + @Rule + public final ProvideSystemProperty systemProperties = + new ProvideSystemProperty("otel.bsp.max.queue", "5") + .and("otel.bsp.export.timeout", "5") + .and("otel.bsp.export.sampled", "false"); + + @Rule + public final EnvironmentVariables environmentVariables = + new EnvironmentVariables() + .set("OTEL_BSP_SCHEDULE_DELAY", "22") + .set("OTEL_BSP_MAX_QUEUE", "22") + .set("OTEL_BSP_EXPORT_TIMEOUT", "22") + .set("OTEL_BSP_EXPORT_SAMPLED", "true"); + + @Test + public void testSystemAndEnv() { + BatchSpansProcessor.Config config = BatchSpansProcessor.Config.loadFromDefaultSources(); + BatchSpansProcessor.Config defaultConf = BatchSpansProcessor.Config.getDefault(); + assertThat(config.getScheduleDelayMillis()).isEqualTo(22); + assertThat(config.getMaxQueueSize()).isEqualTo(5); + // This is not defined and should be equal to the default values + assertThat(config.getMaxExportBatchSize()).isEqualTo(defaultConf.getMaxExportBatchSize()); + assertThat(config.getExporterTimeoutMillis()).isEqualTo(5); + assertThat(config.isExportOnlySampled()).isEqualTo(false); + } + } + private ReadableSpan createSampledEndedSpan(String spanName) { io.opentelemetry.trace.Span span = TestUtils.startSpanWithSampler(tracerSdkFactory, tracer, spanName, Samplers.alwaysOn()) @@ -73,6 +153,128 @@ private ReadableSpan createSampledEndedSpan(String spanName) { return (ReadableSpan) span; } + @Test + public void testConfiguration() { + BatchSpansProcessor.Config config; + BatchSpansProcessor.Config defConfig = BatchSpansProcessor.Config.getDefault(); + + config = BatchSpansProcessor.Config.newBuilder().build(); + // check defaults + assertThat(config.getScheduleDelayMillis()).isEqualTo(defConfig.getScheduleDelayMillis()); + assertThat(config.getMaxQueueSize()).isEqualTo(defConfig.getMaxQueueSize()); + assertThat(config.getMaxExportBatchSize()).isEqualTo(defConfig.getMaxExportBatchSize()); + assertThat(config.getExporterTimeoutMillis()).isEqualTo(defConfig.getExporterTimeoutMillis()); + assertThat(config.isExportOnlySampled()).isEqualTo(defConfig.isExportOnlySampled()); + + // check system configuration + Map configMap = new HashMap<>(); + configMap.put("otel.bsp.schedule.delay", "1"); + configMap.put("otel.bsp.max.queue", "1"); + configMap.put("otel.bsp.max.export.batch", "1"); + configMap.put("otel.bsp.export.timeout", "1"); + configMap.put("otel.bsp.export.sampled", "false"); + config = + BatchSpansProcessor.Config.newBuilder() + .fromConfigMap(configMap, BatchSpansProcessor.Config.Builder.NamingConvention.DOT) + .build(); + assertThat(config.getScheduleDelayMillis()).isEqualTo(1); + assertThat(config.getMaxQueueSize()).isEqualTo(1); + assertThat(config.getMaxExportBatchSize()).isEqualTo(1); + assertThat(config.getExporterTimeoutMillis()).isEqualTo(1); + assertThat(config.isExportOnlySampled()).isEqualTo(false); + + // check properties + Properties properties = new Properties(); + for (Map.Entry entry : configMap.entrySet()) { + properties.put(entry.getKey(), entry.getValue()); + } + config = BatchSpansProcessor.Config.newBuilder().readProperties(properties).build(); + assertThat(config.getScheduleDelayMillis()).isEqualTo(1); + assertThat(config.getMaxQueueSize()).isEqualTo(1); + assertThat(config.getMaxExportBatchSize()).isEqualTo(1); + assertThat(config.getExporterTimeoutMillis()).isEqualTo(1); + assertThat(config.isExportOnlySampled()).isEqualTo(false); + + // Check env vars + configMap.clear(); + configMap.put("OTEL_BSP_SCHEDULE_DELAY", "2"); + configMap.put("OTEL_BSP_MAX_QUEUE", "2"); + configMap.put("OTEL_BSP_MAX_EXPORT_BATCH", "2"); + configMap.put("OTEL_BSP_EXPORT_TIMEOUT", "2"); + configMap.put("OTEL_BSP_EXPORT_SAMPLED", "true"); + config = + BatchSpansProcessor.Config.newBuilder() + .fromConfigMap(configMap, BatchSpansProcessor.Config.Builder.NamingConvention.ENV_VAR) + .build(); + assertThat(config.getScheduleDelayMillis()).isEqualTo(2); + assertThat(config.getMaxQueueSize()).isEqualTo(2); + assertThat(config.getMaxExportBatchSize()).isEqualTo(2); + assertThat(config.getExporterTimeoutMillis()).isEqualTo(2); + assertThat(config.isExportOnlySampled()).isEqualTo(true); + + // Check mixing conventions + configMap.clear(); + configMap.put("OTEL_BSP_schedule_DELAY", "3"); + configMap.put("OTEL.BSP.MAX_QUEUE", "3"); + configMap.put("OTEL_bsp.MAX_EXPORT_BATCH", "3"); + configMap.put("OTEL_BSP_ExPoRt_TIMEOUT", "3"); + configMap.put("OTEL_bSp.EXPORT.SAmpleD", "false"); + config = + BatchSpansProcessor.Config.newBuilder() + .fromConfigMap(configMap, BatchSpansProcessor.Config.Builder.NamingConvention.ENV_VAR) + .build(); + assertThat(config.getScheduleDelayMillis()).isEqualTo(3); + assertThat(config.getMaxQueueSize()).isEqualTo(3); + assertThat(config.getMaxExportBatchSize()).isEqualTo(3); + assertThat(config.getExporterTimeoutMillis()).isEqualTo(3); + assertThat(config.isExportOnlySampled()).isEqualTo(false); + } + + @Test + public void testOnlySetPropertiesOverrideDefaults() { + BatchSpansProcessor.Config config; + Map configMap = new HashMap<>(); + // check only set values are written + configMap.clear(); + configMap.put("OTEL_BSP_SCHEDULE_DELAY", "1"); + config = + BatchSpansProcessor.Config.newBuilder() + .fromConfigMap(configMap, BatchSpansProcessor.Config.Builder.NamingConvention.ENV_VAR) + .build(); + assertThat(config.getScheduleDelayMillis()).isEqualTo(1); + assertThat(config.getMaxQueueSize()).isEqualTo(2048); + assertThat(config.getMaxExportBatchSize()).isEqualTo(512); + assertThat(config.getExporterTimeoutMillis()).isEqualTo(30_000); + assertThat(config.isExportOnlySampled()).isEqualTo(true); + } + + @Test + public void testUserSetPropertiesOverrideDefaults() { + BatchSpansProcessor.Config config; + Map configMap = new HashMap<>(); + // check only set values are written + configMap.clear(); + configMap.put("otel.bsp.schedule.delay", "1"); + configMap.put("otel.bsp.max.queue", "1"); + configMap.put("otel.bsp.max.export.batch", "1"); + configMap.put("otel.bsp.export.timeout", "1"); + configMap.put("otel.bsp.export.sampled", "false"); + config = + BatchSpansProcessor.Config.newBuilder() + .fromConfigMap(configMap, BatchSpansProcessor.Config.Builder.NamingConvention.DOT) + .setScheduleDelayMillis(2) + .setMaxQueueSize(2) + .setMaxExportBatchSize(2) + .setExporterTimeoutMillis(2) + .setExportOnlySampled(true) + .build(); + assertThat(config.getScheduleDelayMillis()).isEqualTo(2); + assertThat(config.getMaxQueueSize()).isEqualTo(2); + assertThat(config.getMaxExportBatchSize()).isEqualTo(2); + assertThat(config.getExporterTimeoutMillis()).isEqualTo(2); + assertThat(config.isExportOnlySampled()).isEqualTo(true); + } + // TODO(bdrutu): Fix this when Sampler return RECORD option. /* private ReadableSpan createNotSampledRecordingEventsEndedSpan(String spanName) { From 67a4741b419a5e57a027f7c5997c506b95c0f0fa Mon Sep 17 00:00:00 2001 From: Giovanni Liva Date: Wed, 22 Apr 2020 09:15:27 +0200 Subject: [PATCH 3/3] goJF after rebase --- .../io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java index 2ec61f52f75..ea4fb16cdb3 100644 --- a/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java +++ b/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java @@ -16,7 +16,6 @@ package io.opentelemetry.sdk.trace.export; - import com.google.auto.value.AutoValue; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Maps;