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/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/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/trace/export/BatchSpansProcessor.java b/sdk/src/main/java/io/opentelemetry/sdk/trace/export/BatchSpansProcessor.java index 2b68eb7414a..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,6 +16,9 @@ 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; @@ -27,7 +30,10 @@ 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; @@ -36,7 +42,10 @@ 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; /** * Implementation of the {@link SpanProcessor} that batches spans exported by the SDK then pushes @@ -82,6 +91,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.isExportOnlySampled(), + config.getScheduleDelayMillis(), + config.getMaxQueueSize(), + config.getMaxExportBatchSize(), + config.getExporterTimeoutMillis()); + } + @Override public void onStart(ReadableSpan span) {} @@ -394,4 +430,310 @@ public void run() { } } } + + @Immutable + @AutoValue + public abstract static class Config { + + 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 abstract boolean isExportOnlySampled(); + + public abstract long getScheduleDelayMillis(); + + public abstract int getMaxQueueSize(); + + public abstract int getMaxExportBatchSize(); + + 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 newBuilder().build(); + } + + /** + * 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(); + } + + /** + * 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); + } + + @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("_", "."); + } + }; + + public abstract String normalize(@Nonnull String key); + + 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 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. + */ + @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 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. + */ + public Builder readProperties(Properties properties) { + return fromConfigMap(Maps.fromProperties(properties), NamingConvention.DOT); + } + + /** + * 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. + *
+ * + * @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 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 abstract Builder setExporterTimeoutMillis(int exporterTimeoutMillis); + + /** + * 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}. + * + * @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 abstract Builder setMaxQueueSize(int maxQueueSize); + + /** + * Sets the maximum batch size for every export. This must be smaller or equal to {@code + * maxQueuedSpans}. + * + *

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 abstract Builder setMaxExportBatchSize(int maxExportBatchSize); + + abstract Config autoBuild(); // not public + + /** + * Builds the {@link Config} object. + * + * @return the {@link Config} object. + */ + public Config build() { + 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 d3f5331dcda..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) { @@ -91,8 +293,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 +302,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 +317,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 +346,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 +372,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 +394,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 +461,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 +498,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 +519,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 +575,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()