Skip to content

Commit aac9284

Browse files
authored
Add a new module for extensions to the tracing APIs. (open-telemetry#2962)
* Add a new module for extensions to the tracing APIs. Includes one simple possible example. * formatting * rename and add javadoc * Clean up & simplify based on feedback * rename the module to just incubator * Move to a package named more like the core api trace package. * repackage to match the module
1 parent 906c0e8 commit aac9284

6 files changed

Lines changed: 199 additions & 1 deletion

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
plugins {
2+
`java-library`
3+
`maven-publish`
4+
5+
id("ru.vyarus.animalsniffer")
6+
}
7+
8+
description = "OpenTelemetry API Incubator"
9+
extra["moduleName"] = "io.opentelemetry.extension.incubator"
10+
11+
dependencies {
12+
api(project(":api:all"))
13+
14+
testImplementation(project(":sdk:testing"))
15+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
otel.release=alpha
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.extension.incubator.trace;
7+
8+
import io.opentelemetry.api.trace.Span;
9+
import io.opentelemetry.api.trace.SpanBuilder;
10+
import io.opentelemetry.api.trace.Tracer;
11+
import io.opentelemetry.context.Scope;
12+
import java.util.concurrent.Callable;
13+
14+
/** Provides easy mechanisms for wrapping standard Java constructs with an OpenTelemetry Span. */
15+
public final class ExtendedTracer implements Tracer {
16+
17+
private final Tracer delegate;
18+
19+
/** Create a new {@link ExtendedTracer} that wraps the provided Tracer. */
20+
public static ExtendedTracer create(Tracer delegate) {
21+
return new ExtendedTracer(delegate);
22+
}
23+
24+
private ExtendedTracer(Tracer delegate) {
25+
this.delegate = delegate;
26+
}
27+
28+
/** Run the provided {@link Runnable} and wrap with a {@link Span} with the provided name. */
29+
public void run(String spanName, Runnable runnable) {
30+
Span span = delegate.spanBuilder(spanName).startSpan();
31+
try (Scope scope = span.makeCurrent()) {
32+
runnable.run();
33+
} catch (Throwable e) {
34+
span.recordException(e);
35+
throw e;
36+
} finally {
37+
span.end();
38+
}
39+
}
40+
41+
/** Call the provided {@link Callable} and wrap with a {@link Span} with the provided name. */
42+
public <T> T call(String spanName, Callable<T> callable) throws Exception {
43+
Span span = delegate.spanBuilder(spanName).startSpan();
44+
try (Scope scope = span.makeCurrent()) {
45+
return callable.call();
46+
} catch (Throwable e) {
47+
span.recordException(e);
48+
throw e;
49+
} finally {
50+
span.end();
51+
}
52+
}
53+
54+
@Override
55+
public SpanBuilder spanBuilder(String spanName) {
56+
return delegate.spanBuilder(spanName);
57+
}
58+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.extension.incubator.trace;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
10+
11+
import io.opentelemetry.api.common.AttributeKey;
12+
import io.opentelemetry.api.common.Attributes;
13+
import io.opentelemetry.api.trace.Span;
14+
import io.opentelemetry.api.trace.Tracer;
15+
import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension;
16+
import org.junit.jupiter.api.Test;
17+
import org.junit.jupiter.api.extension.RegisterExtension;
18+
19+
class ExtendedTracerTest {
20+
@RegisterExtension
21+
static final OpenTelemetryExtension otelTesting = OpenTelemetryExtension.create();
22+
23+
private final Tracer tracer = otelTesting.getOpenTelemetry().getTracer("test");
24+
25+
@Test
26+
void runRunnable() {
27+
ExtendedTracer.create(tracer).run("testSpan", () -> Span.current().setAttribute("one", 1));
28+
29+
otelTesting
30+
.assertTraces()
31+
.hasTracesSatisfyingExactly(
32+
traceAssert ->
33+
traceAssert.hasSpansSatisfyingExactly(
34+
spanDataAssert ->
35+
spanDataAssert
36+
.hasName("testSpan")
37+
.hasAttributes(Attributes.of(AttributeKey.longKey("one"), 1L))));
38+
}
39+
40+
@Test
41+
void runRunnable_throws() {
42+
assertThatThrownBy(
43+
() ->
44+
ExtendedTracer.create(tracer)
45+
.run(
46+
"throwingRunnable",
47+
() -> {
48+
Span.current().setAttribute("one", 1);
49+
throw new RuntimeException("failed");
50+
}))
51+
.isInstanceOf(RuntimeException.class);
52+
53+
otelTesting
54+
.assertTraces()
55+
.hasTracesSatisfyingExactly(
56+
traceAssert ->
57+
traceAssert.hasSpansSatisfyingExactly(
58+
span ->
59+
span.hasName("throwingRunnable")
60+
.hasAttributes(Attributes.of(AttributeKey.longKey("one"), 1L))
61+
.hasEventsSatisfying(
62+
(events) ->
63+
assertThat(events)
64+
.singleElement()
65+
.satisfies(
66+
eventData ->
67+
assertThat(eventData.getName())
68+
.isEqualTo("exception")))));
69+
}
70+
71+
@Test
72+
void callCallable() throws Exception {
73+
assertThat(
74+
ExtendedTracer.create(tracer)
75+
.call(
76+
"spanCallable",
77+
() -> {
78+
Span.current().setAttribute("one", 1);
79+
return "hello";
80+
}))
81+
.isEqualTo("hello");
82+
83+
otelTesting
84+
.assertTraces()
85+
.hasTracesSatisfyingExactly(
86+
traceAssert ->
87+
traceAssert.hasSpansSatisfyingExactly(
88+
spanDataAssert ->
89+
spanDataAssert
90+
.hasName("spanCallable")
91+
.hasAttributes(Attributes.of(AttributeKey.longKey("one"), 1L))));
92+
}
93+
94+
@Test
95+
void callCallable_throws() {
96+
assertThatThrownBy(
97+
() ->
98+
ExtendedTracer.create(tracer)
99+
.call(
100+
"throwingCallable",
101+
() -> {
102+
Span.current().setAttribute("one", 1);
103+
throw new RuntimeException("failed");
104+
}))
105+
.isInstanceOf(RuntimeException.class);
106+
107+
otelTesting
108+
.assertTraces()
109+
.hasTracesSatisfyingExactly(
110+
traceAssert ->
111+
traceAssert.hasSpansSatisfyingExactly(
112+
spanDataAssert ->
113+
spanDataAssert
114+
.hasName("throwingCallable")
115+
.hasAttributes(Attributes.of(AttributeKey.longKey("one"), 1L))));
116+
}
117+
}

sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/SpanDataAssert.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public SpanDataAssert startsAt(Instant timestamp) {
198198
/** Asserts the span has the given attributes. */
199199
public SpanDataAssert hasAttributes(Attributes attributes) {
200200
isNotNull();
201-
if (!actual.getAttributes().equals(attributes)) {
201+
if (!attributesAreEqual(attributes)) {
202202
failWithActualExpectedAndMessage(
203203
actual.getAttributes(),
204204
attributes,
@@ -210,6 +210,12 @@ public SpanDataAssert hasAttributes(Attributes attributes) {
210210
return this;
211211
}
212212

213+
private boolean attributesAreEqual(Attributes attributes) {
214+
// compare as maps, since implementations do not have equals that work correctly across
215+
// implementations.
216+
return actual.getAttributes().asMap().equals(attributes.asMap());
217+
}
218+
213219
/** Asserts the span has attributes satisfying the given condition. */
214220
public SpanDataAssert hasAttributesSatisfying(Consumer<Attributes> attributes) {
215221
isNotNull();

settings.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ include(":bom-alpha")
3838
include(":context")
3939
include(":dependencyManagement")
4040
include(":extensions:annotations")
41+
include(":extensions:incubator")
4142
include(":extensions:aws")
4243
include(":extensions:kotlin")
4344
include(":extensions:trace-propagators")

0 commit comments

Comments
 (0)