add config support for BaggageSpanProcessor (#1330)

Co-authored-by: Mike Goldsmith <goldsmith.mike@gmail.com>
This commit is contained in:
Gregor Zeitlinger 2024-06-17 21:46:02 +02:00 committed by GitHub
parent 6f3cfa3c39
commit d23bc116c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 218 additions and 4 deletions

View File

@ -21,6 +21,7 @@ components:
- srprash
baggage-procesor:
- mikegoldsmith
- zeitlinger
compressors:
- jack-berg
consistent-sampling:

View File

@ -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).

View File

@ -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")
}

View File

@ -19,8 +19,8 @@ import java.util.function.Predicate;
public class BaggageSpanProcessor implements SpanProcessor {
private final Predicate<String> baggageKeyPredicate;
/** A {@link Predicate} that returns true for all baggage keys. */
public static final Predicate<String> allowAllBaggageKeys = baggageKey -> true;
/** use {@link #allowAllBaggageKeys()} instead */
@Deprecated public static final Predicate<String> 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)

View File

@ -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<String> keys =
config.getList("otel.java.experimental.span-attributes.copy-from-baggage.include");
if (keys.isEmpty()) {
return;
}
sdkTracerProviderBuilder.addSpanProcessor(createProcessor(keys));
}
static BaggageSpanProcessor createProcessor(List<String> keys) {
if (keys.size() == 1 && keys.get(0).equals("*")) {
return BaggageSpanProcessor.allowAllBaggageKeys();
}
return new BaggageSpanProcessor(keys::contains);
}
}

View File

@ -0,0 +1 @@
io.opentelemetry.contrib.baggage.processor.BaggageSpanProcessorCustomizer

View File

@ -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<String, String> properties, Consumer<SpanDataAssert> 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<String, String> 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 <T> List<T> load(Class<T> 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");
}
}
}
}

View File

@ -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");