From d23bc116c0bb6e9bfff2850ca8cafd0bb432a03e Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Mon, 17 Jun 2024 21:46:02 +0200 Subject: [PATCH] add config support for BaggageSpanProcessor (#1330) Co-authored-by: Mike Goldsmith --- .github/component_owners.yml | 1 + baggage-processor/README.md | 10 ++ baggage-processor/build.gradle.kts | 5 + .../processor/BaggageSpanProcessor.java | 12 +- .../BaggageSpanProcessorCustomizer.java | 42 +++++ ...re.spi.AutoConfigurationCustomizerProvider | 1 + .../BaggageSpanProcessorCustomizerTest.java | 148 ++++++++++++++++++ .../processor/BaggageSpanProcessorTest.java | 3 +- 8 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 baggage-processor/src/main/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorCustomizer.java create mode 100644 baggage-processor/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider create mode 100644 baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorCustomizerTest.java diff --git a/.github/component_owners.yml b/.github/component_owners.yml index b052ee74..e89c0069 100644 --- a/.github/component_owners.yml +++ b/.github/component_owners.yml @@ -21,6 +21,7 @@ components: - srprash baggage-procesor: - mikegoldsmith + - zeitlinger compressors: - jack-berg consistent-sampling: diff --git a/baggage-processor/README.md b/baggage-processor/README.md index 76f50ccc..0f0f9917 100644 --- a/baggage-processor/README.md +++ b/baggage-processor/README.md @@ -42,8 +42,18 @@ Pattern pattern = Pattern.compile("^key.+"); new BaggageSpanProcessor(baggageKey -> pattern.matcher(baggageKey).matches()) ``` +## Usage with SDK auto-configuration + +If you are using the OpenTelemetry SDK auto-configuration, you can add the span processor this +library to configure the span processor. + +| Property | Description | +|------------------------------------------------------------------|---------------------------------------------------------------------------------------------------| +| otel.java.experimental.span-attributes.copy-from-baggage.include | Add baggage entries as span attributes, e.g. `key1,key2` or `*` to add all baggage items as keys. | + ## Component owners - [Mike Golsmith](https://github.com/MikeGoldsmith), Honeycomb +- [Gregor Zeitlinger](https://github.com/zeitlinger), Grafana Learn more about component owners in [component_owners.yml](../.github/component_owners.yml). diff --git a/baggage-processor/build.gradle.kts b/baggage-processor/build.gradle.kts index 5d65a515..01715839 100644 --- a/baggage-processor/build.gradle.kts +++ b/baggage-processor/build.gradle.kts @@ -10,6 +10,11 @@ otelJava.moduleName.set("io.opentelemetry.contrib.baggage.processor") dependencies { api("io.opentelemetry:opentelemetry-api") api("io.opentelemetry:opentelemetry-sdk") + implementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + testImplementation("io.opentelemetry:opentelemetry-sdk-testing") + testImplementation("org.mockito:mockito-inline") + testImplementation("com.google.guava:guava") + testImplementation("org.awaitility:awaitility") } diff --git a/baggage-processor/src/main/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessor.java b/baggage-processor/src/main/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessor.java index 941ac56b..dbc1bd92 100644 --- a/baggage-processor/src/main/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessor.java +++ b/baggage-processor/src/main/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessor.java @@ -19,8 +19,8 @@ import java.util.function.Predicate; public class BaggageSpanProcessor implements SpanProcessor { private final Predicate baggageKeyPredicate; - /** A {@link Predicate} that returns true for all baggage keys. */ - public static final Predicate allowAllBaggageKeys = baggageKey -> true; + /** use {@link #allowAllBaggageKeys()} instead */ + @Deprecated public static final Predicate allowAllBaggageKeys = baggageKey -> true; /** * Creates a new {@link BaggageSpanProcessor} that copies only baggage entries with keys that pass @@ -30,6 +30,14 @@ public class BaggageSpanProcessor implements SpanProcessor { this.baggageKeyPredicate = baggageKeyPredicate; } + /** + * Creates a new {@link BaggageSpanProcessor} that copies all baggage entries into the newly + * created {@link io.opentelemetry.api.trace.Span}. + */ + public static BaggageSpanProcessor allowAllBaggageKeys() { + return new BaggageSpanProcessor(baggageKey -> true); + } + @Override public void onStart(Context parentContext, ReadWriteSpan span) { Baggage.fromContext(parentContext) diff --git a/baggage-processor/src/main/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorCustomizer.java b/baggage-processor/src/main/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorCustomizer.java new file mode 100644 index 00000000..59de7f94 --- /dev/null +++ b/baggage-processor/src/main/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorCustomizer.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.baggage.processor; + +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizer; +import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder; +import java.util.List; + +public class BaggageSpanProcessorCustomizer implements AutoConfigurationCustomizerProvider { + @Override + public void customize(AutoConfigurationCustomizer autoConfigurationCustomizer) { + autoConfigurationCustomizer.addTracerProviderCustomizer( + (sdkTracerProviderBuilder, config) -> { + addSpanProcessor(sdkTracerProviderBuilder, config); + return sdkTracerProviderBuilder; + }); + } + + private static void addSpanProcessor( + SdkTracerProviderBuilder sdkTracerProviderBuilder, ConfigProperties config) { + List keys = + config.getList("otel.java.experimental.span-attributes.copy-from-baggage.include"); + + if (keys.isEmpty()) { + return; + } + + sdkTracerProviderBuilder.addSpanProcessor(createProcessor(keys)); + } + + static BaggageSpanProcessor createProcessor(List keys) { + if (keys.size() == 1 && keys.get(0).equals("*")) { + return BaggageSpanProcessor.allowAllBaggageKeys(); + } + return new BaggageSpanProcessor(keys::contains); + } +} diff --git a/baggage-processor/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider b/baggage-processor/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider new file mode 100644 index 00000000..6cd88e91 --- /dev/null +++ b/baggage-processor/src/main/resources/META-INF/services/io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider @@ -0,0 +1 @@ +io.opentelemetry.contrib.baggage.processor.BaggageSpanProcessorCustomizer diff --git a/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorCustomizerTest.java b/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorCustomizerTest.java new file mode 100644 index 00000000..c6e7b13c --- /dev/null +++ b/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorCustomizerTest.java @@ -0,0 +1,148 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.baggage.processor; + +import static org.awaitility.Awaitility.await; +import static org.mockito.Mockito.verify; + +import com.google.common.collect.ImmutableMap; +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdkBuilder; +import io.opentelemetry.sdk.autoconfigure.internal.AutoConfigureUtil; +import io.opentelemetry.sdk.autoconfigure.internal.ComponentLoader; +import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper; +import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; +import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.testing.assertj.TracesAssert; +import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter; +import io.opentelemetry.sdk.trace.ReadWriteSpan; +import io.opentelemetry.sdk.trace.export.SpanExporter; +import java.time.Duration; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class BaggageSpanProcessorCustomizerTest { + + private static final String MEMORY_EXPORTER = "memory"; + + @Test + void test_customizer() { + assertCustomizer(Collections.emptyMap(), span -> span.hasTotalAttributeCount(0)); + assertCustomizer( + Collections.singletonMap( + "otel.java.experimental.span-attributes.copy-from-baggage.include", "key"), + span -> span.hasAttribute(AttributeKey.stringKey("key"), "value")); + } + + private static void assertCustomizer( + Map properties, Consumer spanDataAssertConsumer) { + + InMemorySpanExporter spanExporter = InMemorySpanExporter.create(); + + OpenTelemetrySdk sdk = getOpenTelemetrySdk(properties, spanExporter); + try (Scope ignore = Baggage.current().toBuilder().put("key", "value").build().makeCurrent()) { + sdk.getTracer("test").spanBuilder("test").startSpan().end(); + } + await() + .atMost(Duration.ofSeconds(1)) + .untilAsserted( + () -> + TracesAssert.assertThat(spanExporter.getFinishedSpanItems()) + .hasTracesSatisfyingExactly( + trace -> trace.hasSpansSatisfyingExactly(spanDataAssertConsumer))); + } + + private static OpenTelemetrySdk getOpenTelemetrySdk( + Map properties, InMemorySpanExporter spanExporter) { + SpiHelper spiHelper = + SpiHelper.create(BaggageSpanProcessorCustomizerTest.class.getClassLoader()); + + AutoConfiguredOpenTelemetrySdkBuilder sdkBuilder = + AutoConfiguredOpenTelemetrySdk.builder() + .addPropertiesSupplier( + () -> + ImmutableMap.of( + // We set the export interval of the spans to 100 ms. The default value is 5 + // seconds. + "otel.bsp.schedule.delay", + "10", + "otel.traces.exporter", + MEMORY_EXPORTER, + "otel.metrics.exporter", + "none", + "otel.logs.exporter", + "none")) + .addPropertiesSupplier(() -> properties); + AutoConfigureUtil.setComponentLoader( + sdkBuilder, + new ComponentLoader() { + @SuppressWarnings("unchecked") + @Override + public List load(Class spiClass) { + if (spiClass == ConfigurableSpanExporterProvider.class) { + return Collections.singletonList( + (T) + new ConfigurableSpanExporterProvider() { + @Override + public SpanExporter createExporter(ConfigProperties configProperties) { + return spanExporter; + } + + @Override + public String getName() { + return MEMORY_EXPORTER; + } + }); + } + return spiHelper.load(spiClass); + } + }); + return sdkBuilder.build().getOpenTelemetrySdk(); + } + + @Test + public void test_baggageSpanProcessor_adds_attributes_to_spans(@Mock ReadWriteSpan span) { + try (BaggageSpanProcessor processor = + BaggageSpanProcessorCustomizer.createProcessor(Collections.singletonList("*"))) { + try (Scope ignore = Baggage.current().toBuilder().put("key", "value").build().makeCurrent()) { + processor.onStart(Context.current(), span); + verify(span).setAttribute("key", "value"); + } + } + } + + @Test + public void test_baggageSpanProcessor_adds_attributes_to_spans_when_key_filter_matches( + @Mock ReadWriteSpan span) { + try (BaggageSpanProcessor processor = + BaggageSpanProcessorCustomizer.createProcessor(Collections.singletonList("key"))) { + try (Scope ignore = + Baggage.current().toBuilder() + .put("key", "value") + .put("other", "value") + .build() + .makeCurrent()) { + processor.onStart(Context.current(), span); + verify(span).setAttribute("key", "value"); + verify(span, Mockito.never()).setAttribute("other", "value"); + } + } + } +} diff --git a/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorTest.java b/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorTest.java index 6bcbd0e1..ca1180dc 100644 --- a/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorTest.java +++ b/baggage-processor/src/test/java/io/opentelemetry/contrib/baggage/processor/BaggageSpanProcessorTest.java @@ -21,8 +21,7 @@ public class BaggageSpanProcessorTest { @Test public void test_baggageSpanProcessor_adds_attributes_to_spans(@Mock ReadWriteSpan span) { - try (BaggageSpanProcessor processor = - new BaggageSpanProcessor(BaggageSpanProcessor.allowAllBaggageKeys)) { + try (BaggageSpanProcessor processor = BaggageSpanProcessor.allowAllBaggageKeys()) { try (Scope ignore = Baggage.current().toBuilder().put("key", "value").build().makeCurrent()) { processor.onStart(Context.current(), span); Mockito.verify(span).setAttribute("key", "value");