diff --git a/micrometer-tracing-bridges/micrometer-tracing-bridge-brave/src/test/java/io/micrometer/tracing/brave/bridge/BraveSpanBuilderTests.java b/micrometer-tracing-bridges/micrometer-tracing-bridge-brave/src/test/java/io/micrometer/tracing/brave/bridge/BraveSpanBuilderTests.java index 87249a82..ac82c41f 100644 --- a/micrometer-tracing-bridges/micrometer-tracing-bridge-brave/src/test/java/io/micrometer/tracing/brave/bridge/BraveSpanBuilderTests.java +++ b/micrometer-tracing-bridges/micrometer-tracing-bridge-brave/src/test/java/io/micrometer/tracing/brave/bridge/BraveSpanBuilderTests.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.BDDAssertions.then; class BraveSpanBuilderTests { @@ -70,6 +71,21 @@ void should_set_links() { .containsEntry("links[1].spanId", span1.context().spanId()); } + @Test + void should_set_non_string_tags() { + new BraveSpanBuilder(tracing.tracer()).tag("string", "string") + .tag("double", 2.5) + .tag("long", 2) + .tag("boolean", true) + .start() + .end(); + + assertThat(handler.get(0).tags()).containsEntry("string", "string") + .containsEntry("double", "2.5") + .containsEntry("long", "2") + .containsEntry("boolean", "true"); + } + private Map tags() { Map map = new HashMap<>(); map.put("tag1", "value1"); diff --git a/micrometer-tracing-bridges/micrometer-tracing-bridge-brave/src/test/java/io/micrometer/tracing/brave/bridge/BraveSpanTest.java b/micrometer-tracing-bridges/micrometer-tracing-bridge-brave/src/test/java/io/micrometer/tracing/brave/bridge/BraveSpanTest.java new file mode 100644 index 00000000..a3dbec77 --- /dev/null +++ b/micrometer-tracing-bridges/micrometer-tracing-bridge-brave/src/test/java/io/micrometer/tracing/brave/bridge/BraveSpanTest.java @@ -0,0 +1,45 @@ +/** + * Copyright 2022 the original author or authors. + * + * 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 + * + * https://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.micrometer.tracing.brave.bridge; + +import brave.Tracing; +import brave.test.TestSpanHandler; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class BraveSpanTest { + + TestSpanHandler handler = new TestSpanHandler(); + + Tracing tracing = Tracing.newBuilder().addSpanHandler(handler).build(); + + @Test + void should_set_non_string_tags() { + new BraveSpan(tracing.tracer().nextSpan()).start() + .tag("string", "string") + .tag("double", 2.5) + .tag("long", 2) + .tag("boolean", true) + .end(); + + assertThat(handler.get(0).tags()).containsEntry("string", "string") + .containsEntry("double", "2.5") + .containsEntry("long", "2") + .containsEntry("boolean", "true"); + } + +} diff --git a/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpan.java b/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpan.java index 22236da6..a2ca8b8e 100644 --- a/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpan.java +++ b/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpan.java @@ -107,6 +107,24 @@ public Span tag(String key, String value) { return new OtelSpan(this.delegate); } + @Override + public Span tag(String key, long value) { + this.delegate.setAttribute(key, value); + return new OtelSpan(this.delegate); + } + + @Override + public Span tag(String key, double value) { + this.delegate.setAttribute(key, value); + return new OtelSpan(this.delegate); + } + + @Override + public Span tag(String key, boolean value) { + this.delegate.setAttribute(key, value); + return new OtelSpan(this.delegate); + } + @Override public void end(long time, TimeUnit timeUnit) { this.delegate.end(time, timeUnit); diff --git a/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpanBuilder.java b/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpanBuilder.java index 66597733..9f0fb2fd 100644 --- a/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpanBuilder.java +++ b/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpanBuilder.java @@ -89,6 +89,24 @@ public Span.Builder tag(String key, String value) { return this; } + @Override + public Span.Builder tag(String key, long value) { + this.delegate.setAttribute(key, value); + return this; + } + + @Override + public Span.Builder tag(String key, double value) { + this.delegate.setAttribute(key, value); + return this; + } + + @Override + public Span.Builder tag(String key, boolean value) { + this.delegate.setAttribute(key, value); + return this; + } + @Override public Span.Builder error(Throwable throwable) { this.error = throwable; diff --git a/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpanCustomizer.java b/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpanCustomizer.java index 1dd059ad..e425e318 100644 --- a/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpanCustomizer.java +++ b/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/main/java/io/micrometer/tracing/otel/bridge/OtelSpanCustomizer.java @@ -42,6 +42,24 @@ public SpanCustomizer tag(String key, String value) { return this; } + @Override + public SpanCustomizer tag(String key, long value) { + currentSpan().setAttribute(key, value); + return this; + } + + @Override + public SpanCustomizer tag(String key, double value) { + currentSpan().setAttribute(key, value); + return this; + } + + @Override + public SpanCustomizer tag(String key, boolean value) { + currentSpan().setAttribute(key, value); + return this; + } + @Override public SpanCustomizer event(String value) { currentSpan().addEvent(value); diff --git a/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/OtelSpanBuilderTests.java b/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/OtelSpanBuilderTests.java index 44e0419f..99423b4c 100644 --- a/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/OtelSpanBuilderTests.java +++ b/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/OtelSpanBuilderTests.java @@ -17,6 +17,7 @@ import io.micrometer.tracing.Link; import io.micrometer.tracing.Span; +import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.extension.trace.propagation.B3Propagator; import io.opentelemetry.sdk.OpenTelemetrySdk; @@ -29,6 +30,8 @@ import java.util.stream.Collectors; import static org.assertj.core.api.BDDAssertions.then; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; class OtelSpanBuilderTests { @@ -80,6 +83,22 @@ void should_set_links() { .collect(Collectors.toMap(e -> e.getKey().getKey(), e -> e.getValue().toString()))).isEqualTo(tags()); } + @Test + void should_set_non_string_tags() { + new OtelSpanBuilder(otelTracer.spanBuilder("foo")).tag("string", "string") + .tag("double", 2.5) + .tag("long", 2) + .tag("boolean", true) + .start() + .end(); + + SpanData poll = processor.spans().poll(); + assertEquals("string", poll.getAttributes().get(AttributeKey.stringKey("string"))); + assertEquals(2.5, poll.getAttributes().get(AttributeKey.doubleKey("double"))); + assertEquals(2, poll.getAttributes().get(AttributeKey.longKey("long"))); + assertTrue(poll.getAttributes().get(AttributeKey.booleanKey("boolean"))); + } + private Map tags() { Map map = new HashMap<>(); map.put("tag1", "value1"); diff --git a/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/OtelSpanTests.java b/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/OtelSpanTests.java index 41a52750..2f7d1612 100644 --- a/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/OtelSpanTests.java +++ b/micrometer-tracing-bridges/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/OtelSpanTests.java @@ -28,23 +28,27 @@ class OtelSpanTests { + ArrayListSpanProcessor processor = new ArrayListSpanProcessor(); + + SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() + .setSampler(io.opentelemetry.sdk.trace.samplers.Sampler.alwaysOn()) + .addSpanProcessor(processor) + .build(); + + OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() + .setTracerProvider(sdkTracerProvider) + .setPropagators(ContextPropagators.create(B3Propagator.injectingSingleHeader())) + .build(); + + io.opentelemetry.api.trace.Tracer otelTracer = openTelemetrySdk.getTracer("io.micrometer.micrometer-tracing"); + @Test void should_set_status_to_error_when_recording_exception() { - ArrayListSpanProcessor arrayListSpanProcessor = new ArrayListSpanProcessor(); - SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder() - .setSampler(io.opentelemetry.sdk.trace.samplers.Sampler.alwaysOn()) - .addSpanProcessor(arrayListSpanProcessor) - .build(); - OpenTelemetrySdk openTelemetrySdk = OpenTelemetrySdk.builder() - .setTracerProvider(sdkTracerProvider) - .setPropagators(ContextPropagators.create(B3Propagator.injectingSingleHeader())) - .build(); - io.opentelemetry.api.trace.Tracer otelTracer = openTelemetrySdk.getTracer("io.micrometer.micrometer-tracing"); OtelSpan otelSpan = new OtelSpan(otelTracer.spanBuilder("foo").startSpan()); otelSpan.error(new RuntimeException("boom!")).end(); - SpanData poll = arrayListSpanProcessor.spans().poll(); + SpanData poll = processor.spans().poll(); then(poll.getStatus()).isEqualTo(StatusData.create(StatusCode.ERROR, "boom!")); then(poll.getEvents()).hasSize(1); then(poll.getEvents().get(0).getAttributes().asMap().values()).containsAnyOf("boom!"); diff --git a/micrometer-tracing/src/main/java/io/micrometer/tracing/Span.java b/micrometer-tracing/src/main/java/io/micrometer/tracing/Span.java index 311ab285..f0583d68 100644 --- a/micrometer-tracing/src/main/java/io/micrometer/tracing/Span.java +++ b/micrometer-tracing/src/main/java/io/micrometer/tracing/Span.java @@ -151,6 +151,36 @@ public Span remoteIpAndPort(String ip, int port) { */ Span tag(String key, String value); + /** + * Sets a tag on this span. + * @param key tag key + * @param value tag value + * @return this span + */ + default Span tag(String key, long value) { + return tag(key, String.valueOf(value)); + } + + /** + * Sets a tag on this span. + * @param key tag key + * @param value tag value + * @return this span + */ + default Span tag(String key, double value) { + return tag(key, String.valueOf(value)); + } + + /** + * Sets a tag on this span. + * @param key tag key + * @param value tag value + * @return this span + */ + default Span tag(String key, boolean value) { + return tag(key, String.valueOf(value)); + } + /** * Records an exception for this span. * @param throwable to record @@ -333,6 +363,36 @@ public Span start() { */ Builder tag(String key, String value); + /** + * Sets a tag on the span. + * @param key tag key + * @param value tag value + * @return this + */ + default Builder tag(String key, long value) { + return tag(key, String.valueOf(value)); + } + + /** + * Sets a tag on the span. + * @param key tag key + * @param value tag value + * @return this + */ + default Builder tag(String key, double value) { + return tag(key, String.valueOf(value)); + } + + /** + * Sets a tag on the span. + * @param key tag key + * @param value tag value + * @return this + */ + default Builder tag(String key, boolean value) { + return tag(key, String.valueOf(value)); + } + /** * Sets an error on the span. * @param throwable error to set diff --git a/micrometer-tracing/src/main/java/io/micrometer/tracing/SpanCustomizer.java b/micrometer-tracing/src/main/java/io/micrometer/tracing/SpanCustomizer.java index 5e1f1e21..6ad650be 100644 --- a/micrometer-tracing/src/main/java/io/micrometer/tracing/SpanCustomizer.java +++ b/micrometer-tracing/src/main/java/io/micrometer/tracing/SpanCustomizer.java @@ -59,6 +59,36 @@ public SpanCustomizer event(String value) { */ SpanCustomizer tag(String key, String value); + /** + * Sets a tag on a span. + * @param key tag key + * @param value tag value + * @return this, for chaining + */ + default SpanCustomizer tag(String key, long value) { + return tag(key, String.valueOf(value)); + } + + /** + * Sets a tag on a span. + * @param key tag key + * @param value tag value + * @return this, for chaining + */ + default SpanCustomizer tag(String key, double value) { + return tag(key, String.valueOf(value)); + } + + /** + * Sets a tag on a span. + * @param key tag key + * @param value tag value + * @return this, for chaining + */ + default SpanCustomizer tag(String key, boolean value) { + return tag(key, String.valueOf(value)); + } + /** * Sets an event on a span. * @param value event name diff --git a/micrometer-tracing/src/main/java/io/micrometer/tracing/TypedKeyValue.java b/micrometer-tracing/src/main/java/io/micrometer/tracing/TypedKeyValue.java new file mode 100644 index 00000000..e4bbc213 --- /dev/null +++ b/micrometer-tracing/src/main/java/io/micrometer/tracing/TypedKeyValue.java @@ -0,0 +1,80 @@ +/** + * Copyright 2022 the original author or authors. + * + * 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 + * + * https://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.micrometer.tracing; + +import io.micrometer.common.KeyValue; + +public abstract class TypedKeyValue implements KeyValue { + + protected final String key; + + protected final T value; + + TypedKeyValue(String key, T value) { + this.key = key; + this.value = value; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getValue() { + return String.valueOf(value); + } + + public abstract void setAttribute(Span span); + + public static TypedKeyValue of(String key, long value) { + return new TypedKeyValue(key, value) { + @Override + public void setAttribute(Span span) { + span.tag(getKey(), value); + } + }; + } + + public static TypedKeyValue of(String key, String value) { + return new TypedKeyValue(key, value) { + @Override + public void setAttribute(Span span) { + span.tag(getKey(), value); + } + }; + } + + public static TypedKeyValue of(String key, double value) { + return new TypedKeyValue(key, value) { + @Override + public void setAttribute(Span span) { + span.tag(getKey(), value); + } + }; + + } + + public static TypedKeyValue of(String key, boolean value) { + return new TypedKeyValue(key, value) { + @Override + public void setAttribute(Span span) { + span.tag(getKey(), value); + } + }; + } + +} diff --git a/micrometer-tracing/src/main/java/io/micrometer/tracing/handler/TracingObservationHandler.java b/micrometer-tracing/src/main/java/io/micrometer/tracing/handler/TracingObservationHandler.java index d3c84182..6179785a 100755 --- a/micrometer-tracing/src/main/java/io/micrometer/tracing/handler/TracingObservationHandler.java +++ b/micrometer-tracing/src/main/java/io/micrometer/tracing/handler/TracingObservationHandler.java @@ -23,10 +23,7 @@ import io.micrometer.observation.Observation.Event; import io.micrometer.observation.ObservationHandler; import io.micrometer.observation.ObservationView; -import io.micrometer.tracing.CurrentTraceContext; -import io.micrometer.tracing.Span; -import io.micrometer.tracing.TraceContext; -import io.micrometer.tracing.Tracer; +import io.micrometer.tracing.*; import io.micrometer.tracing.internal.SpanNameUtil; import java.util.Map; @@ -48,11 +45,14 @@ public interface TracingObservationHandler extend */ default void tagSpan(T context, Span span) { for (KeyValue keyValue : context.getAllKeyValues()) { - if (!keyValue.getKey().equalsIgnoreCase("ERROR")) { - span.tag(keyValue.getKey(), keyValue.getValue()); + if (keyValue.getKey().equalsIgnoreCase("ERROR")) { + span.error(new RuntimeException(keyValue.getValue())); + } + else if (keyValue instanceof TypedKeyValue) { + ((TypedKeyValue) keyValue).setAttribute(span); } else { - span.error(new RuntimeException(keyValue.getValue())); + span.tag(keyValue.getKey(), keyValue.getValue()); } } }