diff --git a/tracing/providers/jaeger/pom.xml b/tracing/providers/jaeger/pom.xml
index 116887b6fd5..d02677b8a7e 100644
--- a/tracing/providers/jaeger/pom.xml
+++ b/tracing/providers/jaeger/pom.xml
@@ -154,12 +154,12 @@
org.apache.maven.plugins
maven-surefire-plugin
-
+
default-test
- **/JaegerBaggagePropagationTest.java
+ **/JaegerBaggagePropagationTest.java,**/JaegerTracerBuilderTest.java
@@ -176,6 +176,20 @@
**/JaegerBaggagePropagationTest.java
+
+ tracer-builder-tests
+
+ test
+
+
+
+
+ junit.jupiter.extensions.autodetection.enabled = true
+
+
+ **/JaegerTracerBuilderTest.java
+
+
diff --git a/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilder.java b/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilder.java
index 4f13506fd13..5bf59f6672f 100644
--- a/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilder.java
+++ b/tracing/providers/jaeger/src/main/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilder.java
@@ -18,6 +18,7 @@
import java.lang.System.Logger.Level;
import java.time.Duration;
+import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
@@ -37,6 +38,7 @@
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter;
@@ -46,6 +48,7 @@
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
+import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
@@ -179,6 +182,7 @@ public class JaegerTracerBuilder implements TracerBuilder {
private int maxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
private int maxExportBatchSize = DEFAULT_MAX_EXPORT_BATCH_SIZE;
private SpanProcessorType spanProcessorType = SpanProcessorType.BATCH;
+ private final List adHocExporters = new ArrayList<>(); // primarily for testing
/**
@@ -512,17 +516,25 @@ public Tracer build() {
: Sampler.alwaysOff();
};
- Resource serviceName = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, this.serviceName));
+ AttributesBuilder attributesBuilder = Attributes.builder()
+ .put(ResourceAttributes.SERVICE_NAME, serviceName);
+ tags.forEach(attributesBuilder::put);
+ Resource otelResource = Resource.create(attributesBuilder.build());
+
+ SdkTracerProviderBuilder sdkTracerProviderBuilder = SdkTracerProvider.builder()
+ .setSampler(sampler)
+ .setResource(otelResource)
+ .addSpanProcessor(spanProcessor(exporter));
+ adHocExporters.stream()
+ .map(this::spanProcessor)
+ .forEach(sdkTracerProviderBuilder::addSpanProcessor);
+
OpenTelemetry ot = OpenTelemetrySdk.builder()
- .setTracerProvider(SdkTracerProvider.builder()
- .addSpanProcessor(spanProcessor(exporter))
- .setSampler(sampler)
- .setResource(serviceName)
- .build())
+ .setTracerProvider(sdkTracerProviderBuilder.build())
.setPropagators(ContextPropagators.create(TextMapPropagator.composite(createPropagators())))
.build();
- result = HelidonOpenTelemetry.create(ot, ot.getTracer(this.serviceName), tags);
+ result = HelidonOpenTelemetry.create(ot, ot.getTracer(this.serviceName), Map.of());
if (global) {
GlobalOpenTelemetry.set(ot);
@@ -604,6 +616,12 @@ List createPropagators() {
.toList();
}
+ // Primarily for testing
+ JaegerTracerBuilder exporter(SpanExporter spanExporter) {
+ adHocExporters.add(spanExporter);
+ return this;
+ }
+
private SpanProcessor spanProcessor(SpanExporter exporter) {
return switch (spanProcessorType) {
case BATCH -> BatchSpanProcessor.builder(exporter)
diff --git a/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilderTest.java b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilderTest.java
index c2d14b93b0e..d97ddf28157 100644
--- a/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilderTest.java
+++ b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/JaegerTracerBuilderTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019, 2023 Oracle and/or its affiliates.
+ * Copyright (c) 2019, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,13 +21,18 @@
import java.util.Map;
import io.helidon.config.Config;
+import io.helidon.tracing.Span;
import io.helidon.tracing.Tracer;
import io.helidon.tracing.TracerBuilder;
+import io.helidon.tracing.providers.opentelemetry.HelidonOpenTelemetry;
import io.opentelemetry.api.baggage.propagation.W3CBaggagePropagator;
+import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.extension.trace.propagation.B3Propagator;
import io.opentelemetry.extension.trace.propagation.JaegerPropagator;
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import io.opentelemetry.sdk.trace.data.SpanData;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@@ -126,4 +131,27 @@ void testFullHttp() {
assertThat(propagators.get(1), instanceOf(JaegerPropagator.class));
assertThat(propagators.get(2), instanceOf(W3CBaggagePropagator.class));
}
+
+ @Test
+ void testProcessTagHandling() {
+ TracerBuilder> builder = TracerBuilder.create(config.get("jaeger-defaults"));
+ builder.addTracerTag("tracerLevelTag", "val-1");
+ JaegerTracerBuilder jaegerTracerBuilder = builder.unwrap(JaegerTracerBuilder.class);
+ jaegerTracerBuilder.scheduleDelay(Duration.ofMillis(100)); // for faster test runs
+ jaegerTracerBuilder.exporter(new TestSpanExporterProvider().createExporter(null));
+ Tracer tracer = builder.build();
+
+ Span span = tracer.spanBuilder("testSpan").tag("spanLevelTag", "val-2").start();
+ span.end();
+
+ try (TestSpanExporter exporter = TestSpanExporterProvider.exporter()) {
+ List spanData = exporter.spanData(1);
+ assertThat("Expect span count", spanData.size(), is(1));
+ SpanData spanDataItem = spanData.get(0);
+ assertThat("Trace-level attribute tracerLevelTag",
+ spanDataItem.getResource().getAttributes().get(AttributeKey.stringKey("tracerLevelTag")),
+ is("val-1"));
+ }
+
+ }
}
\ No newline at end of file
diff --git a/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporter.java b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporter.java
new file mode 100644
index 00000000000..bd64892307b
--- /dev/null
+++ b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporter.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * 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.helidon.tracing.providers.jaeger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import io.helidon.common.testing.junit5.MatcherWithRetry;
+
+import io.opentelemetry.sdk.common.CompletableResultCode;
+import io.opentelemetry.sdk.trace.data.SpanData;
+import io.opentelemetry.sdk.trace.export.SpanExporter;
+
+import static org.hamcrest.Matchers.iterableWithSize;
+
+// Partially inspired by the MP Telemetry TCK InMemorySpanExporter.
+public class TestSpanExporter implements SpanExporter {
+
+ private final List spanData = new CopyOnWriteArrayList<>();
+ private final System.Logger LOGGER = System.getLogger(TestSpanExporter.class.getName());
+
+ private final int RETRY_COUNT = Integer.getInteger(TestSpanExporter.class.getName() + ".test.retryCount", 120);
+ private final int RETRY_DELAY_MS = Integer.getInteger(TestSpanExporter.class.getName() + ".test.retryDelayMs", 500);
+
+
+ private enum State {READY, STOPPED}
+
+ private State state = State.READY;
+
+ @Override
+ public CompletableResultCode export(Collection collection) {
+ if (state == State.STOPPED) {
+ return CompletableResultCode.ofFailure();
+ }
+ spanData.addAll(collection);
+ return CompletableResultCode.ofSuccess();
+ }
+
+ @Override
+ public CompletableResultCode flush() {
+ return CompletableResultCode.ofSuccess();
+ }
+
+ @Override
+ public CompletableResultCode shutdown() {
+ state = State.STOPPED;
+ spanData.clear();
+ return CompletableResultCode.ofSuccess();
+ }
+
+ List spanData(int expectedCount) {
+ long startTime = 0;
+ if (LOGGER.isLoggable(System.Logger.Level.DEBUG)) {
+ startTime = System.currentTimeMillis();
+ }
+ var result = MatcherWithRetry.assertThatWithRetry("Expected span count",
+ () -> new ArrayList<>(spanData),
+ iterableWithSize(expectedCount),
+ RETRY_COUNT,
+ RETRY_DELAY_MS);
+ if (LOGGER.isLoggable(System.Logger.Level.DEBUG)) {
+ LOGGER.log(System.Logger.Level.DEBUG, "spanData waited "
+ + (System.currentTimeMillis() - startTime)
+ + " ms for expected spans to arrive.");
+ }
+ return result;
+ }
+
+ void clear() {
+ spanData.clear();
+ }
+}
diff --git a/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporterProvider.java b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporterProvider.java
new file mode 100644
index 00000000000..022159d2800
--- /dev/null
+++ b/tracing/providers/jaeger/src/test/java/io/helidon/tracing/providers/jaeger/TestSpanExporterProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2024 Oracle and/or its affiliates.
+ *
+ * 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.helidon.tracing.providers.jaeger;
+
+import io.helidon.common.LazyValue;
+
+import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
+import io.opentelemetry.sdk.trace.export.SpanExporter;
+
+public class TestSpanExporterProvider implements ConfigurableSpanExporterProvider {
+
+ private static final LazyValue SPAN_EXPORTER = LazyValue.create(TestSpanExporter::new);
+
+ public TestSpanExporterProvider() {
+ System.err.println("provider ctor");
+ }
+
+ @Override
+ public SpanExporter createExporter(ConfigProperties configProperties) {
+ return SPAN_EXPORTER.get();
+ }
+
+ @Override
+ public String getName() {
+ return "in-memory";
+ }
+
+ static TestSpanExporter exporter() {
+ if (SPAN_EXPORTER.isLoaded()) {
+ return SPAN_EXPORTER.get();
+ }
+ throw new IllegalStateException("Attempt to retrieve TestSpanExporter before it has been created");
+ }
+}