Skip to content

Commit

Permalink
Support list values in tags in Span and SpanCustomizer (#817)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jasz authored Sep 11, 2024
1 parent e2627af commit e0f7c41
Show file tree
Hide file tree
Showing 11 changed files with 248 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.micrometer.tracing.brave.bridge;

import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -80,9 +81,14 @@ void should_set_typed_tags() {

Map<String, Object> map = new HashMap<>();
map.put("foo", 2L);
map.put("bar", Arrays.asList("a", "b", "c"));
map.put("baz", Arrays.asList(1, 2, 3));
span.setTypedTags(map);

then(span.getTypedTags().get("foo")).isEqualTo("2");
then(span.getTypedTags()).hasSize(3)
.containsEntry("foo", "2")
.containsEntry("bar", "a,b,c")
.containsEntry("baz", "1,2,3");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package io.micrometer.tracing.brave.bridge;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -85,6 +86,21 @@ void should_set_non_string_tags() {
.containsEntry("boolean", "true");
}

@Test
void should_set_multi_value_tags() {
new BraveSpanBuilder(tracing.tracer()).tagOfStrings("strings", Arrays.asList("s1", "s2", "s3"))
.tagOfDoubles("doubles", Arrays.asList(1.0, 2.5, 3.7))
.tagOfLongs("longs", Arrays.asList(2L, 3L, 4L))
.tagOfBooleans("booleans", Arrays.asList(true, false, false))
.start()
.end();

then(handler.get(0).tags()).containsEntry("strings", "s1,s2,s3")
.containsEntry("doubles", "1.0,2.5,3.7")
.containsEntry("longs", "2,3,4")
.containsEntry("booleans", "true,false,false");
}

private Map<String, Object> tags() {
Map<String, Object> map = new HashMap<>();
map.put("tag1", "value1");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;

class BraveSpanTest {
Expand Down Expand Up @@ -48,4 +50,19 @@ void should_set_non_string_tags() {
.containsEntry("boolean", "true");
}

@Test
void should_set_multi_value_tags() {
new BraveSpan(tracing.tracer().nextSpan()).start()
.tagOfStrings("strings", Arrays.asList("s1", "s2", "s3"))
.tagOfDoubles("doubles", Arrays.asList(1.0, 2.5, 3.7))
.tagOfLongs("longs", Arrays.asList(2L, 3L, 4L))
.tagOfBooleans("booleans", Arrays.asList(true, false, false))
.end();

assertThat(handler.get(0).tags()).containsEntry("strings", "s1,s2,s3")
.containsEntry("doubles", "1.0,2.5,3.7")
.containsEntry("longs", "2,3,4")
.containsEntry("booleans", "true,false,false");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,23 @@ else if (value instanceof Long) {
else if (value instanceof Boolean) {
return AttributeKey.booleanKey(key);
}
else if (value instanceof List) {
List<?> valueAsList = (List<?>) value;
if (valueAsList.isEmpty()) {
return AttributeKey.stringArrayKey(key);
}
Object firstValue = valueAsList.get(0);
if (firstValue instanceof Double) {
return AttributeKey.doubleArrayKey(key);
}
else if (firstValue instanceof Long) {
return AttributeKey.longArrayKey(key);
}
else if (firstValue instanceof Boolean) {
return AttributeKey.doubleArrayKey(key);
}
return AttributeKey.stringArrayKey(key);
}
return AttributeKey.stringKey(key);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
package io.micrometer.tracing.otel.bridge;

import io.micrometer.tracing.Span;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
import io.opentelemetry.semconv.SemanticAttributes;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -123,6 +125,30 @@ public Span tag(String key, boolean value) {
return new OtelSpan(this.delegate);
}

@Override
public Span tagOfStrings(String key, List<String> values) {
this.delegate.setAttribute(AttributeKey.stringArrayKey(key), values);
return new OtelSpan(this.delegate);
}

@Override
public Span tagOfLongs(String key, List<Long> values) {
this.delegate.setAttribute(AttributeKey.longArrayKey(key), values);
return new OtelSpan(this.delegate);
}

@Override
public Span tagOfDoubles(String key, List<Double> values) {
this.delegate.setAttribute(AttributeKey.doubleArrayKey(key), values);
return new OtelSpan(this.delegate);
}

@Override
public Span tagOfBooleans(String key, List<Boolean> values) {
this.delegate.setAttribute(AttributeKey.booleanArrayKey(key), values);
return new OtelSpan(this.delegate);
}

@Override
public void end(long time, TimeUnit timeUnit) {
this.delegate.end(time, timeUnit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,30 @@ public Span.Builder tag(String key, boolean value) {
return this;
}

@Override
public Span.Builder tagOfStrings(String key, List<String> values) {
this.attributes.put(AttributeKey.stringArrayKey(key), values);
return this;
}

@Override
public Span.Builder tagOfLongs(String key, List<Long> values) {
this.attributes.put(AttributeKey.longArrayKey(key), values);
return this;
}

@Override
public Span.Builder tagOfDoubles(String key, List<Double> values) {
this.attributes.put(AttributeKey.doubleArrayKey(key), values);
return this;
}

@Override
public Span.Builder tagOfBooleans(String key, List<Boolean> values) {
this.attributes.put(AttributeKey.booleanArrayKey(key), values);
return this;
}

@Override
public Span.Builder error(Throwable throwable) {
this.error = throwable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,16 @@ void should_set_typed_tags() {

Map<String, Object> map = new HashMap<>();
map.put("foo", 2L);
map.put("bar", Arrays.asList("a", "b", "c"));
map.put("baz", Arrays.asList(1, 2, 3));
map.put("qux", Collections.emptyList());
span.setTypedTags(map);

then(span.getTypedTags().get("foo")).isEqualTo(2L);
then(span.getTypedTags()).hasSize(4)
.containsEntry("foo", 2L)
.containsEntry("bar", Arrays.asList("a", "b", "c"))
.containsEntry("baz", Arrays.asList(1, 2, 3))
.containsEntry("qux", Collections.emptyList());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@
import io.micrometer.tracing.Span;
import io.micrometer.tracing.TraceContext;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.extension.trace.propagation.B3Propagator;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.data.SpanData;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -104,6 +106,24 @@ void should_set_non_string_tags() {
then(poll.getAttributes().get(AttributeKey.booleanKey("boolean"))).isTrue();
}

@Test
void should_set_multi_value_tags() {
new OtelSpanBuilder(otelTracer).name("foo")
.tagOfStrings("strings", Arrays.asList("s1", "s2", "s3"))
.tagOfDoubles("doubles", Arrays.asList(1.0, 2.5, 3.7))
.tagOfLongs("longs", Arrays.asList(2L, 3L, 4L))
.tagOfBooleans("booleans", Arrays.asList(true, false, false))
.start()
.end();

SpanData poll = processor.spans().poll();
Attributes attributes = poll.getAttributes();
then(attributes.get(AttributeKey.stringArrayKey("strings"))).isEqualTo(Arrays.asList("s1", "s2", "s3"));
then(attributes.get(AttributeKey.doubleArrayKey("doubles"))).isEqualTo(Arrays.asList(1.0, 2.5, 3.7));
then(attributes.get(AttributeKey.longArrayKey("longs"))).isEqualTo(Arrays.asList(2L, 3L, 4L));
then(attributes.get(AttributeKey.booleanArrayKey("booleans"))).isEqualTo(Arrays.asList(true, false, false));
}

@Test
void should_honor_parent_context_using_tracecontextbuilder() {
Span foo = tracer.spanBuilder().name("foo").start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package io.micrometer.tracing.otel.bridge;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.propagation.ContextPropagators;
Expand All @@ -25,6 +27,8 @@
import io.opentelemetry.sdk.trace.data.StatusData;
import org.junit.jupiter.api.Test;

import java.util.Arrays;

import static org.assertj.core.api.BDDAssertions.then;

class OtelSpanTests {
Expand Down Expand Up @@ -66,4 +70,22 @@ void should_be_equal_when_two_span_delegates_are_equal() {
then(otelSpanFromSpanContext).isEqualTo(otelSpan);
}

@Test
void should_set_multi_value_tags() {
OtelSpan otelSpan = new OtelSpan(otelTracer.spanBuilder("foo").startSpan());

otelSpan.tagOfStrings("strings", Arrays.asList("s1", "s2", "s3"))
.tagOfDoubles("doubles", Arrays.asList(1.0, 2.5, 3.7))
.tagOfLongs("longs", Arrays.asList(2L, 3L, 4L))
.tagOfBooleans("booleans", Arrays.asList(true, false, false))
.end();

SpanData poll = arrayListSpanProcessor.spans().poll();
Attributes attributes = poll.getAttributes();
then(attributes.get(AttributeKey.stringArrayKey("strings"))).isEqualTo(Arrays.asList("s1", "s2", "s3"));
then(attributes.get(AttributeKey.doubleArrayKey("doubles"))).isEqualTo(Arrays.asList(1.0, 2.5, 3.7));
then(attributes.get(AttributeKey.longArrayKey("longs"))).isEqualTo(Arrays.asList(2L, 3L, 4L));
then(attributes.get(AttributeKey.booleanArrayKey("booleans"))).isEqualTo(Arrays.asList(true, false, false));
}

}
83 changes: 82 additions & 1 deletion micrometer-tracing/src/main/java/io/micrometer/tracing/Span.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
package io.micrometer.tracing;

import io.micrometer.tracing.propagation.Propagator;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* This API was heavily influenced by Brave. Parts of its documentation were taken
Expand Down Expand Up @@ -181,6 +182,46 @@ default Span tag(String key, boolean value) {
return tag(key, String.valueOf(value));
}

/**
* Sets a tag on this span.
* @param key tag key
* @param values tag values
* @return this span
*/
default Span tagOfStrings(String key, List<String> values) {
return tag(key, String.join(",", values));
}

/**
* Sets a tag on this span.
* @param key tag key
* @param values tag values
* @return this span
*/
default Span tagOfLongs(String key, List<Long> values) {
return tag(key, values.stream().map(String::valueOf).collect(Collectors.joining(",")));
}

/**
* Sets a tag on this span.
* @param key tag key
* @param values tag values
* @return this span
*/
default Span tagOfDoubles(String key, List<Double> values) {
return tag(key, values.stream().map(String::valueOf).collect(Collectors.joining(",")));
}

/**
* Sets a tag on this span.
* @param key tag key
* @param values tag values
* @return this span
*/
default Span tagOfBooleans(String key, List<Boolean> values) {
return tag(key, values.stream().map(String::valueOf).collect(Collectors.joining(",")));
}

/**
* Records an exception for this span.
* @param throwable to record
Expand Down Expand Up @@ -393,6 +434,46 @@ default Builder tag(String key, boolean value) {
return tag(key, String.valueOf(value));
}

/**
* Sets a tag on this span.
* @param key tag key
* @param values tag values
* @return this
*/
default Builder tagOfStrings(String key, List<String> values) {
return tag(key, String.join(",", values));
}

/**
* Sets a tag on this span.
* @param key tag key
* @param values tag values
* @return this
*/
default Builder tagOfLongs(String key, List<Long> values) {
return tag(key, values.stream().map(String::valueOf).collect(Collectors.joining(",")));
}

/**
* Sets a tag on this span.
* @param key tag key
* @param values tag values
* @return this
*/
default Builder tagOfDoubles(String key, List<Double> values) {
return tag(key, values.stream().map(String::valueOf).collect(Collectors.joining(",")));
}

/**
* Sets a tag on this span.
* @param key tag key
* @param values tag values
* @return this
*/
default Builder tagOfBooleans(String key, List<Boolean> values) {
return tag(key, values.stream().map(String::valueOf).collect(Collectors.joining(",")));
}

/**
* Sets an error on the span.
* @param throwable error to set
Expand Down
Loading

0 comments on commit e0f7c41

Please sign in to comment.