Skip to content

Commit 210c992

Browse files
authored
POTEL 9 - Tracing Fixes and Baggage (#3455)
* replace hub with scopes * Add Scopes * Introduce `IScopes` interface. * Replace `IHub` with `IScopes` in core * Replace `IHub` with `IScopes` in android core * Replace `IHub` with `IScopes` in android integrations * Replace `IHub` with `IScopes` in apollo integrations * Replace `IHub` with `IScopes` in okhttp integration * Replace `IHub` with `IScopes` in graphql integration * Replace `IHub` with `IScopes` in logging integrations * Replace `IHub` with `IScopes` in more integrations * Replace `IHub` with `IScopes` in OTel integration * Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations * Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations * Replace `IHub` with `IScopes` in samples * gitscopes -> github * Replace ThreadLocal with ScopesStorage * Move client and throwable to span map to scope * Add global scope * use global scope in Scopes * Implement pushScope popScope and withScope for Scopes * Add pushIsolationScope; add fork methods to ISCope * Use separate scopes for current, isolation and global scope; rename mainScopes to rootScopes * Allow controlling which scope configureScope uses * Combine scopes * Use new API for CRONS integrations * Add lifecycle helper * Change spring integrations to use new API * Use new API in servlet integrations * Use new API for kotlin coroutines and wrapers for Supplier/Callable * Discussion TODOs * Fix breadcrumb ordering * Mark TODOS with [HSM] * Add getGlobalScope and forkedRootScopes to IScopes * Fix EventProcessor ordering on scopes * Reuse code in Scopes * No longer replace global scope * Replace hub occurrences in comments, var names etc. * Implement ScopesTest * Implement CombinedScopeViewTest * Fix combined contexts * Use combined scopes for cross platform * Changes according to reviews of previous PRs * more * even more * isEnabled checks client instead of having a property on Scopes * Use SentryOptions.empty * Remove Hub * Use OpenTelemetry for Performance and Scopes propagation * Promote certain span attributes * Use OTel in Sentry API * Deduplicate SpanInfo extraction * Forward Sentry API to Sentry through OTel * Use OTel status for Sentry span API * POTel Tracing * fix root span detection (remote flag), and scope closing * Inherit OTel span IDs when sending to sentry * Fix tracing; parse incoming baggage; add baggage to outgoing
1 parent aa70b16 commit 210c992

File tree

15 files changed

+142
-56
lines changed

15 files changed

+142
-56
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
public final class io/sentry/opentelemetry/InternalSemanticAttributes {
2+
public static final field BAGGAGE Lio/opentelemetry/api/common/AttributeKey;
3+
public static final field BAGGAGE_MUTABLE Lio/opentelemetry/api/common/AttributeKey;
24
public static final field IS_REMOTE_PARENT Lio/opentelemetry/api/common/AttributeKey;
35
public static final field PARENT_SAMPLED Lio/opentelemetry/api/common/AttributeKey;
46
public static final field PROFILE_SAMPLED Lio/opentelemetry/api/common/AttributeKey;
@@ -16,7 +18,7 @@ public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/
1618
}
1719

1820
public final class io/sentry/opentelemetry/OtelSpanContext : io/sentry/SpanContext {
19-
public fun <init> (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;)V
21+
public fun <init> (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;Lio/sentry/Baggage;)V
2022
public fun getStatus ()Lio/sentry/SpanStatus;
2123
public fun setStatus (Lio/sentry/SpanStatus;)V
2224
}
@@ -30,7 +32,7 @@ public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFact
3032
}
3133

3234
public final class io/sentry/opentelemetry/OtelSpanWrapper : io/sentry/ISpan {
33-
public fun <init> (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;)V
35+
public fun <init> (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/sentry/IScopes;Lio/sentry/SentryDate;Lio/sentry/TracesSamplingDecision;Lio/sentry/opentelemetry/OtelSpanWrapper;Lio/sentry/Baggage;)V
3436
public fun finish ()V
3537
public fun finish (Lio/sentry/SpanStatus;)V
3638
public fun finish (Lio/sentry/SpanStatus;Lio/sentry/SentryDate;)V

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ public final class InternalSemanticAttributes {
1818
AttributeKey.doubleKey("sentry.profile_sample_rate");
1919
public static final AttributeKey<Boolean> IS_REMOTE_PARENT =
2020
AttributeKey.booleanKey("sentry.is_remote_parent");
21+
public static final AttributeKey<String> BAGGAGE = AttributeKey.stringKey("sentry.baggage");
22+
public static final AttributeKey<Boolean> BAGGAGE_MUTABLE =
23+
AttributeKey.booleanKey("sentry.baggage_mutable");
2124
// public static final AttributeKey<String> BREADCRUMB_TYPE =
2225
// AttributeKey.stringKey("sentry.breadcrumb.type");
2326
// public static final AttributeKey<SentryLevel> BREADCRUMB_TYPE =

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.opentelemetry.api.trace.StatusCode;
55
import io.opentelemetry.sdk.trace.ReadWriteSpan;
66
import io.opentelemetry.sdk.trace.data.StatusData;
7+
import io.sentry.Baggage;
78
import io.sentry.SpanContext;
89
import io.sentry.SpanId;
910
import io.sentry.SpanStatus;
@@ -26,7 +27,8 @@ public final class OtelSpanContext extends SpanContext {
2627
public OtelSpanContext(
2728
final @NotNull ReadWriteSpan span,
2829
final @Nullable TracesSamplingDecision samplingDecision,
29-
final @Nullable OtelSpanWrapper parentSpan) {
30+
final @Nullable OtelSpanWrapper parentSpan,
31+
final @Nullable Baggage baggage) {
3032
super(
3133
new SentryId(span.getSpanContext().getTraceId()),
3234
new SpanId(span.getSpanContext().getSpanId()),
@@ -39,6 +41,7 @@ public OtelSpanContext(
3941
null,
4042
null);
4143
this.span = new WeakReference<>(span);
44+
this.baggage = baggage;
4245
}
4346

4447
@Override

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io.opentelemetry.api.trace.TraceState;
88
import io.opentelemetry.api.trace.Tracer;
99
import io.opentelemetry.context.Context;
10+
import io.sentry.Baggage;
1011
import io.sentry.IScope;
1112
import io.sentry.IScopes;
1213
import io.sentry.ISpan;
@@ -104,7 +105,12 @@ public final class OtelSpanFactory implements ISpanFactory {
104105
}
105106
}
106107

107-
// TODO [POTEL] send baggage in (note: won't go through propagators)
108+
// note: won't go through propagators
109+
final @Nullable Baggage baggage = spanContext.getBaggage();
110+
if (baggage != null) {
111+
spanBuilder.setAttribute(InternalSemanticAttributes.BAGGAGE_MUTABLE, baggage.isMutable());
112+
spanBuilder.setAttribute(InternalSemanticAttributes.BAGGAGE, baggage.toHeaderString(null));
113+
}
108114

109115
final @Nullable SentryDate startTimestampFromOptions = spanOptions.getStartTimestamp();
110116
final @NotNull SentryDate startTimestamp =

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

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.opentelemetry.api.trace.Span;
44
import io.opentelemetry.context.Scope;
55
import io.opentelemetry.sdk.trace.ReadWriteSpan;
6+
import io.sentry.Baggage;
67
import io.sentry.BaggageHeader;
78
import io.sentry.IScopes;
89
import io.sentry.ISentryLifecycleToken;
@@ -25,6 +26,7 @@
2526
import io.sentry.protocol.MeasurementValue;
2627
import io.sentry.protocol.SentryId;
2728
import io.sentry.protocol.TransactionNameSource;
29+
import io.sentry.protocol.User;
2830
import io.sentry.util.LazyEvaluator;
2931
import io.sentry.util.Objects;
3032
import java.lang.ref.WeakReference;
@@ -34,6 +36,7 @@
3436
import java.util.Map;
3537
import java.util.concurrent.ConcurrentHashMap;
3638
import java.util.concurrent.TimeUnit;
39+
import java.util.concurrent.atomic.AtomicReference;
3740
import org.jetbrains.annotations.ApiStatus;
3841
import org.jetbrains.annotations.NotNull;
3942
import org.jetbrains.annotations.Nullable;
@@ -62,6 +65,7 @@ public final class OtelSpanWrapper implements ISpan {
6265
private final @NotNull Contexts contexts = new Contexts();
6366
private @Nullable String transactionName;
6467
private @Nullable TransactionNameSource transactionNameSource;
68+
private final @Nullable Baggage baggage;
6569

6670
// TODO [POTEL]
6771
// private @Nullable SpanFinishedCallback spanFinishedCallback;
@@ -78,16 +82,28 @@ public final class OtelSpanWrapper implements ISpan {
7882

7983
private @NotNull Deque<ISentryLifecycleToken> tokensToCleanup = new ArrayDeque<>(1);
8084

85+
// TODO [POTEL] reference root span? for getting root baggage
8186
public OtelSpanWrapper(
8287
final @NotNull ReadWriteSpan span,
8388
final @NotNull IScopes scopes,
8489
final @NotNull SentryDate startTimestamp,
8590
final @Nullable TracesSamplingDecision samplingDecision,
86-
final @Nullable OtelSpanWrapper parentSpan) {
91+
final @Nullable OtelSpanWrapper parentSpan,
92+
final @Nullable Baggage baggage) {
8793
this.scopes = Objects.requireNonNull(scopes, "scopes are required");
8894
this.span = new WeakReference<>(span);
8995
this.startTimestamp = startTimestamp;
90-
this.context = new OtelSpanContext(span, samplingDecision, parentSpan);
96+
97+
if (parentSpan != null) {
98+
this.baggage = parentSpan.getSpanContext().getBaggage();
99+
} else if (baggage != null) {
100+
this.baggage = baggage;
101+
} else {
102+
this.baggage = null;
103+
// this.baggage = new Baggage(scopes.getOptions().getLogger());
104+
}
105+
106+
this.context = new OtelSpanContext(span, samplingDecision, parentSpan, this.baggage);
91107
}
92108

93109
@Override
@@ -178,15 +194,43 @@ public OtelSpanWrapper(
178194

179195
@Override
180196
public @Nullable TraceContext traceContext() {
181-
// return transaction.traceContext();
182-
// TODO [POTEL]
197+
if (scopes.getOptions().isTraceSampling()) {
198+
if (baggage != null) {
199+
updateBaggageValues();
200+
return baggage.toTraceContext();
201+
}
202+
}
183203
return null;
184204
}
185205

206+
private void updateBaggageValues() {
207+
synchronized (this) {
208+
if (baggage != null && baggage.isMutable()) {
209+
final AtomicReference<User> userAtomicReference = new AtomicReference<>();
210+
scopes.configureScope(
211+
scope -> {
212+
userAtomicReference.set(scope.getUser());
213+
});
214+
baggage.setValuesFromTransaction(
215+
getSpanContext().getTraceId(),
216+
userAtomicReference.get(),
217+
scopes.getOptions(),
218+
this.getSamplingDecision(),
219+
getTransactionName(),
220+
getTransactionNameSource());
221+
baggage.freeze();
222+
}
223+
}
224+
}
225+
186226
@Override
187227
public @Nullable BaggageHeader toBaggageHeader(@Nullable List<String> thirdPartyBaggageHeaders) {
188-
// return transaction.toBaggageHeader(thirdPartyBaggageHeaders);
189-
// TODO [POTEL]
228+
if (scopes.getOptions().isTraceSampling()) {
229+
if (baggage != null) {
230+
updateBaggageValues();
231+
return BaggageHeader.fromBaggageAndOutgoingHeader(baggage, thirdPartyBaggageHeaders);
232+
}
233+
}
190234
return null;
191235
}
192236

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,19 +77,16 @@ public OtelTransactionSpanForwarder(final @NotNull OtelSpanWrapper rootSpan) {
7777

7878
@Override
7979
public @NotNull SentryTraceHeader toSentryTrace() {
80-
// TODO [POTEL] root span?
8180
return rootSpan.toSentryTrace();
8281
}
8382

8483
@Override
8584
public @Nullable TraceContext traceContext() {
86-
// TODO [POTEL] root span?
8785
return rootSpan.traceContext();
8886
}
8987

9088
@Override
9189
public @Nullable BaggageHeader toBaggageHeader(@Nullable List<String> thirdPartyBaggageHeaders) {
92-
// TODO [POTEL] root span?
9390
return rootSpan.toBaggageHeader(thirdPartyBaggageHeaders);
9491
}
9592

sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentryPropagator.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import io.opentelemetry.context.propagation.TextMapGetter;
1111
import io.opentelemetry.context.propagation.TextMapPropagator;
1212
import io.opentelemetry.context.propagation.TextMapSetter;
13+
import io.sentry.Baggage;
1314
import io.sentry.BaggageHeader;
1415
import io.sentry.IScopes;
1516
import io.sentry.PropagationContext;
@@ -100,6 +101,7 @@ public <C> Context extract(
100101
SentryTraceHeader sentryTraceHeader = new SentryTraceHeader(sentryTraceString);
101102

102103
final @Nullable String baggageString = getter.get(carrier, BaggageHeader.BAGGAGE_HEADER);
104+
final Baggage baggage = Baggage.fromHeader(baggageString);
103105
final @NotNull TraceState traceState = TraceState.getDefault();
104106

105107
SpanContext otelSpanContext =
@@ -112,7 +114,11 @@ public <C> Context extract(
112114
Span wrappedSpan = Span.wrap(otelSpanContext);
113115

114116
final @NotNull Context modifiedContext =
115-
context.with(wrappedSpan).with(SENTRY_SCOPES_KEY, scopesToUse);
117+
context
118+
.with(wrappedSpan)
119+
.with(SENTRY_SCOPES_KEY, scopesToUse)
120+
.with(SentryOtelKeys.SENTRY_TRACE_KEY, sentryTraceHeader)
121+
.with(SentryOtelKeys.SENTRY_BAGGAGE_KEY, baggage);
116122

117123
scopes
118124
.getOptions()

sentry-opentelemetry/sentry-opentelemetry-core/src/main/java/io/sentry/opentelemetry/PotelSentrySpanProcessor.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import io.opentelemetry.sdk.trace.ReadWriteSpan;
99
import io.opentelemetry.sdk.trace.ReadableSpan;
1010
import io.opentelemetry.sdk.trace.SpanProcessor;
11+
import io.sentry.Baggage;
1112
import io.sentry.IScopes;
1213
import io.sentry.PropagationContext;
1314
import io.sentry.SamplingContext;
@@ -16,6 +17,7 @@
1617
import io.sentry.SentryDate;
1718
import io.sentry.SentryLevel;
1819
import io.sentry.SentryLongDate;
20+
import io.sentry.SentryTraceHeader;
1921
import io.sentry.SpanId;
2022
import io.sentry.TracesSampler;
2123
import io.sentry.TracesSamplingDecision;
@@ -54,8 +56,20 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
5456
final @Nullable OtelSpanWrapper sentryParentSpan =
5557
spanStorage.getSentrySpan(otelSpan.getParentSpanContext());
5658
@Nullable TracesSamplingDecision samplingDecision = null;
59+
// TODO [POTEL] baggage from propagator should be honored
60+
@Nullable Baggage baggage = null;
5761
otelSpan.setAttribute(IS_REMOTE_PARENT, otelSpan.getParentSpanContext().isRemote());
5862
if (sentryParentSpan == null) {
63+
final @Nullable Boolean baggageMutable =
64+
otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE_MUTABLE);
65+
final @Nullable String baggageString =
66+
otelSpan.getAttribute(InternalSemanticAttributes.BAGGAGE);
67+
if (baggageString != null) {
68+
baggage = Baggage.fromHeader(baggageString);
69+
if (baggageMutable == true) {
70+
baggage.freeze();
71+
}
72+
}
5973
final @Nullable Boolean sampled = otelSpan.getAttribute(InternalSemanticAttributes.SAMPLED);
6074
final @Nullable Double sampleRate =
6175
otelSpan.getAttribute(InternalSemanticAttributes.SAMPLE_RATE);
@@ -73,7 +87,11 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
7387

7488
final @NotNull PropagationContext propagationContext =
7589
new PropagationContext(
76-
new SentryId(traceId), new SpanId(spanId), new SpanId(parentSpanId), null, sampled);
90+
new SentryId(traceId),
91+
new SpanId(spanId),
92+
new SpanId(parentSpanId),
93+
baggage,
94+
sampled);
7795

7896
scopes.configureScope(
7997
scope -> {
@@ -96,9 +114,19 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
96114

97115
final @NotNull String traceId = otelSpan.getSpanContext().getTraceId();
98116
final @NotNull String spanId = otelSpan.getSpanContext().getSpanId();
117+
final @NotNull SpanId sentrySpanId = new SpanId(spanId);
118+
119+
@Nullable
120+
SentryTraceHeader sentryTraceHeader = parentContext.get(SentryOtelKeys.SENTRY_TRACE_KEY);
121+
@Nullable Baggage baggageFromContext = parentContext.get(SentryOtelKeys.SENTRY_BAGGAGE_KEY);
122+
if (sentryTraceHeader != null) {
123+
baggage = baggageFromContext;
124+
}
99125

100126
final @NotNull PropagationContext propagationContext =
101-
new PropagationContext(new SentryId(traceId), new SpanId(spanId), null, null, null);
127+
sentryTraceHeader == null
128+
? new PropagationContext(new SentryId(traceId), sentrySpanId, null, baggage, null)
129+
: PropagationContext.fromHeaders(sentryTraceHeader, baggage, sentrySpanId);
102130

103131
scopes.configureScope(
104132
scope -> {
@@ -118,7 +146,8 @@ public void onStart(final @NotNull Context parentContext, final @NotNull ReadWri
118146
new SentryLongDate(otelSpan.toSpanData().getStartEpochNanos());
119147
spanStorage.storeSentrySpan(
120148
spanContext,
121-
new OtelSpanWrapper(otelSpan, scopes, startTimestamp, samplingDecision, sentryParentSpan));
149+
new OtelSpanWrapper(
150+
otelSpan, scopes, startTimestamp, samplingDecision, sentryParentSpan, baggage));
122151
}
123152

124153
@Override

0 commit comments

Comments
 (0)