Skip to content

Commit

Permalink
Add baggage span processor component (#1290)
Browse files Browse the repository at this point in the history
  • Loading branch information
MikeGoldsmith authored May 28, 2024
1 parent 71e5417 commit 72d7976
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .github/component_owners.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ components:
aws-xray-propagator:
- wangzlei
- srprash
baggage-procesor:
- mikegoldsmith
compressors:
- jack-berg
consistent-sampling:
Expand Down
49 changes: 49 additions & 0 deletions baggage-processor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# OpenTelemetry Baggage Span Processor

The BaggageSpanProcessor reads entries stored in Baggage from the parent context
and adds the baggage keys and values to the `Span` as attributes on start.

Add this span processor to a tracer provider.

Warning!

To repeat: a consequence of adding data to Baggage is that the keys and values
will appear in all outgoing trace context headers from the application.

Do not put sensitive information in Baggage.

## Usage

Add the span processor when configuring the tracer provider.

To configure the span processor to copy all baggage entries during configuration:

```java
import io.opentelemetry.contrib.baggage.processor;
// ...

Tracer tracer = SdkTracerProvider.builder()
.addSpanProcessor(new BaggageSpanProcessor(BaggageSpanProcessor.allowAllBaggageKeys))
.build()
```

Alternatively, you can provide a custom baggage key predicate to select which baggage keys you want to copy.

For example, to only copy baggage entries that start with `my-key`:

```java
new BaggageSpanProcessor(baggageKey -> baggageKey.startsWith("my-key"))
```

For example, to only copy baggage entries that match the regex `^key.+`:

```java
Pattern pattern = Pattern.compile("^key.+");
new BaggageSpanProcessor(baggageKey -> pattern.matcher(baggageKey).matches())
```

## Component owners

- [Mike Golsmith](https://github.com/MikeGoldsmith), Honeycomb

Learn more about component owners in [component_owners.yml](../.github/component_owners.yml).
15 changes: 15 additions & 0 deletions baggage-processor/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
plugins {
id("otel.java-conventions")

id("otel.publish-conventions")
}

description = "OpenTelemetry Baggage Span Processor"
otelJava.moduleName.set("io.opentelemetry.contrib.baggage.processor")

dependencies {
api("io.opentelemetry:opentelemetry-api")
api("io.opentelemetry:opentelemetry-sdk")

testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
}
2 changes: 2 additions & 0 deletions baggage-processor/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# TODO: uncomment when ready to mark as stable
# otel.stable=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.baggage.processor;

import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;
import java.util.function.Predicate;

/**
* This span processor copies attributes stored in {@link Baggage} into each newly created {@link
* io.opentelemetry.api.trace.Span}.
*/
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;

/**
* Creates a new {@link BaggageSpanProcessor} that copies only baggage entries with keys that pass
* the provided filter into the newly created {@link io.opentelemetry.api.trace.Span}.
*/
public BaggageSpanProcessor(Predicate<String> baggageKeyPredicate) {
this.baggageKeyPredicate = baggageKeyPredicate;
}

@Override
public void onStart(Context parentContext, ReadWriteSpan span) {
Baggage.fromContext(parentContext)
.forEach(
(s, baggageEntry) -> {
if (baggageKeyPredicate.test(s)) {
span.setAttribute(s, baggageEntry.getValue());
}
});
}

@Override
public boolean isStartRequired() {
return true;
}

@Override
public void onEnd(ReadableSpan span) {}

@Override
public boolean isEndRequired() {
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.baggage.processor;

import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import java.util.regex.Pattern;
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)
public class BaggageSpanProcessorTest {

@Test
public void test_baggageSpanProcessor_adds_attributes_to_spans(@Mock ReadWriteSpan span) {
try (BaggageSpanProcessor processor =
new BaggageSpanProcessor(BaggageSpanProcessor.allowAllBaggageKeys)) {
try (Scope ignore = Baggage.current().toBuilder().put("key", "value").build().makeCurrent()) {
processor.onStart(Context.current(), span);
Mockito.verify(span).setAttribute("key", "value");
}
}
}

@Test
public void test_baggageSpanProcessor_adds_attributes_to_spans_when_key_filter_matches(
@Mock ReadWriteSpan span) {
try (BaggageSpanProcessor processor = new BaggageSpanProcessor(key -> key.startsWith("k"))) {
try (Scope ignore =
Baggage.current().toBuilder()
.put("key", "value")
.put("other", "value")
.build()
.makeCurrent()) {
processor.onStart(Context.current(), span);
Mockito.verify(span).setAttribute("key", "value");
Mockito.verify(span, Mockito.never()).setAttribute("other", "value");
}
}
}

@Test
public void test_baggageSpanProcessor_adds_attributes_to_spans_when_key_filter_matches_regex(
@Mock ReadWriteSpan span) {
Pattern pattern = Pattern.compile("k.*");
try (BaggageSpanProcessor processor =
new BaggageSpanProcessor(key -> pattern.matcher(key).matches())) {
try (Scope ignore =
Baggage.current().toBuilder()
.put("key", "value")
.put("other", "value")
.build()
.makeCurrent()) {
processor.onStart(Context.current(), span);
Mockito.verify(span).setAttribute("key", "value");
Mockito.verify(span, Mockito.never()).setAttribute("other", "value");
}
}
}
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ include(":all")
include(":aws-resources")
include(":aws-xray")
include(":aws-xray-propagator")
include(":baggage-processor")
include(":compressors:compressor-zstd")
include(":consistent-sampling")
include(":dependencyManagement")
Expand Down

0 comments on commit 72d7976

Please sign in to comment.