Skip to content

Commit 4d4f6a8

Browse files
committed
store placeholder span in root context
1 parent 205ad8a commit 4d4f6a8

5 files changed

Lines changed: 101 additions & 4 deletions

File tree

sentry-opentelemetry/sentry-opentelemetry-bootstrap/api/sentry-opentelemetry-bootstrap.api

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,20 @@ public final class io/sentry/opentelemetry/SentryContextWrapper : io/opentelemet
171171
public static fun wrap (Lio/opentelemetry/context/Context;)Lio/sentry/opentelemetry/SentryContextWrapper;
172172
}
173173

174+
public final class io/sentry/opentelemetry/SentryOtelGlobalHubModeSpan : io/opentelemetry/api/trace/Span {
175+
public fun <init> ()V
176+
public fun addEvent (Ljava/lang/String;Lio/opentelemetry/api/common/Attributes;)Lio/opentelemetry/api/trace/Span;
177+
public fun addEvent (Ljava/lang/String;Lio/opentelemetry/api/common/Attributes;JLjava/util/concurrent/TimeUnit;)Lio/opentelemetry/api/trace/Span;
178+
public fun end ()V
179+
public fun end (JLjava/util/concurrent/TimeUnit;)V
180+
public fun getSpanContext ()Lio/opentelemetry/api/trace/SpanContext;
181+
public fun isRecording ()Z
182+
public fun recordException (Ljava/lang/Throwable;Lio/opentelemetry/api/common/Attributes;)Lio/opentelemetry/api/trace/Span;
183+
public fun setAttribute (Lio/opentelemetry/api/common/AttributeKey;Ljava/lang/Object;)Lio/opentelemetry/api/trace/Span;
184+
public fun setStatus (Lio/opentelemetry/api/trace/StatusCode;Ljava/lang/String;)Lio/opentelemetry/api/trace/Span;
185+
public fun updateName (Ljava/lang/String;)Lio/opentelemetry/api/trace/Span;
186+
}
187+
174188
public final class io/sentry/opentelemetry/SentryOtelKeys {
175189
public static final field SENTRY_BAGGAGE_KEY Lio/opentelemetry/context/ContextKey;
176190
public static final field SENTRY_SCOPES_KEY Lio/opentelemetry/context/ContextKey;

sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextStorage.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ public Context current() {
4242

4343
@Override
4444
public Context root() {
45-
final @NotNull Context originalRoot = ContextStorage.super.root();
45+
final @NotNull Context originalRoot = contextStorage.root();
4646

4747
if (Sentry.isGlobalHubMode()) {
48-
return SentryContextWrapper.wrap(originalRoot);
48+
return new SentryOtelGlobalHubModeSpan()
49+
.storeInContext(SentryContextWrapper.wrap(originalRoot));
4950
}
5051

5152
return originalRoot;

sentry-opentelemetry/sentry-opentelemetry-bootstrap/src/main/java/io/sentry/opentelemetry/SentryContextWrapper.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ private <V> boolean shouldReturnRootSpanInstead(
4040
if (result == null) {
4141
return true;
4242
}
43+
if (result instanceof SentryOtelGlobalHubModeSpan) {
44+
return true;
45+
}
4346
return result instanceof Span && !((Span) result).getSpanContext().isValid();
4447
}
4548

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package io.sentry.opentelemetry;
2+
3+
import io.opentelemetry.api.common.AttributeKey;
4+
import io.opentelemetry.api.common.Attributes;
5+
import io.opentelemetry.api.trace.Span;
6+
import io.opentelemetry.api.trace.SpanContext;
7+
import io.opentelemetry.api.trace.StatusCode;
8+
import java.util.concurrent.TimeUnit;
9+
import org.jetbrains.annotations.ApiStatus;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
12+
13+
@ApiStatus.Experimental
14+
public final class SentryOtelGlobalHubModeSpan implements Span {
15+
16+
private @NotNull Span getOtelSpan() {
17+
final @Nullable IOtelSpanWrapper lastKnownUnfinishedRootSpan =
18+
SentryWeakSpanStorage.getInstance().getLastKnownUnfinishedRootSpan();
19+
if (lastKnownUnfinishedRootSpan != null) {
20+
final @Nullable Span openTelemetrySpan = lastKnownUnfinishedRootSpan.getOpenTelemetrySpan();
21+
if (openTelemetrySpan != null) {
22+
return openTelemetrySpan;
23+
}
24+
}
25+
26+
return Span.getInvalid();
27+
}
28+
29+
@Override
30+
public <T> Span setAttribute(AttributeKey<T> key, T value) {
31+
return getOtelSpan().setAttribute(key, value);
32+
}
33+
34+
@Override
35+
public Span addEvent(String name, Attributes attributes) {
36+
return getOtelSpan().addEvent(name, attributes);
37+
}
38+
39+
@Override
40+
public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) {
41+
return getOtelSpan().addEvent(name, attributes, timestamp, unit);
42+
}
43+
44+
@Override
45+
public Span setStatus(StatusCode statusCode, String description) {
46+
return getOtelSpan().setStatus(statusCode, description);
47+
}
48+
49+
@Override
50+
public Span recordException(Throwable exception, Attributes additionalAttributes) {
51+
return getOtelSpan().recordException(exception, additionalAttributes);
52+
}
53+
54+
@Override
55+
public Span updateName(String name) {
56+
return getOtelSpan().updateName(name);
57+
}
58+
59+
@Override
60+
public void end() {
61+
getOtelSpan().end();
62+
}
63+
64+
@Override
65+
public void end(long timestamp, TimeUnit unit) {
66+
getOtelSpan().end(timestamp, unit);
67+
}
68+
69+
@Override
70+
public SpanContext getSpanContext() {
71+
return getOtelSpan().getSpanContext();
72+
}
73+
74+
@Override
75+
public boolean isRecording() {
76+
return getOtelSpan().isRecording();
77+
}
78+
}

sentry-opentelemetry/sentry-opentelemetry-core/src/test/kotlin/SentryContextWrapperTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import kotlin.test.BeforeTest
1818
import kotlin.test.Test
1919
import kotlin.test.assertNull
2020
import kotlin.test.assertSame
21+
import kotlin.test.assertTrue
2122

2223
class SentryContextWrapperTest {
2324

@@ -35,15 +36,15 @@ class SentryContextWrapperTest {
3536
}
3637

3738
@Test
38-
fun `returns null if no transaction is available`() {
39+
fun `returns global hub span if no transaction is available`() {
3940
Sentry.init {
4041
it.dsn = "https://key@sentry.io/proj"
4142
it.isGlobalHubMode = true
4243
}
4344

4445
val c = SentryContextWrapper.wrap(Context.root())
4546
val returnedSpan = Span.fromContextOrNull(c)
46-
assertNull(returnedSpan)
47+
assertTrue(returnedSpan is SentryOtelGlobalHubModeSpan)
4748
}
4849

4950
@Test

0 commit comments

Comments
 (0)