Skip to content

Commit a9ca16a

Browse files
authored
Merge 8f56e3f into 36ed84a
2 parents 36ed84a + 8f56e3f commit a9ca16a

File tree

7 files changed

+136
-21
lines changed

7 files changed

+136
-21
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ public final class io/sentry/opentelemetry/OtelContextScopesStorage : io/sentry/
1616
public fun set (Lio/sentry/IScopes;)Lio/sentry/ISentryLifecycleToken;
1717
}
1818

19+
public final class io/sentry/opentelemetry/OtelSpanContext : io/sentry/SpanContext {
20+
public fun <init> (Lio/opentelemetry/sdk/trace/ReadWriteSpan;Lio/opentelemetry/api/trace/Span;)V
21+
public fun getStatus ()Lio/sentry/SpanStatus;
22+
public fun setStatus (Lio/sentry/SpanStatus;)V
23+
}
24+
1925
public final class io/sentry/opentelemetry/OtelSpanFactory : io/sentry/ISpanFactory {
2026
public fun <init> ()V
2127
public fun createSpan (Ljava/lang/String;Ljava/lang/String;Lio/sentry/IScopes;Lio/sentry/SpanOptions;Lio/sentry/ISpan;)Lio/sentry/ISpan;
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package io.sentry.opentelemetry;
2+
3+
import io.opentelemetry.api.trace.Span;
4+
import io.opentelemetry.api.trace.StatusCode;
5+
import io.opentelemetry.sdk.trace.ReadWriteSpan;
6+
import io.opentelemetry.sdk.trace.data.StatusData;
7+
import io.sentry.SpanContext;
8+
import io.sentry.SpanId;
9+
import io.sentry.SpanStatus;
10+
import io.sentry.protocol.SentryId;
11+
import java.lang.ref.WeakReference;
12+
import org.jetbrains.annotations.NotNull;
13+
import org.jetbrains.annotations.Nullable;
14+
15+
public final class OtelSpanContext extends SpanContext {
16+
17+
/**
18+
* OpenTelemetry span which this wrapper wraps. Needs to be referenced weakly as otherwise we'd
19+
* create a circular reference from {@link io.opentelemetry.sdk.trace.data.SpanData} to {@link
20+
* OtelSpanWrapper} and indirectly back to {@link io.opentelemetry.sdk.trace.data.SpanData} via
21+
* {@link Span}. Also see {@link SentryWeakSpanStorage}.
22+
*/
23+
private final @NotNull WeakReference<ReadWriteSpan> span;
24+
25+
public OtelSpanContext(final @NotNull ReadWriteSpan span, final @Nullable Span parentSpan) {
26+
// TODO [POTEL] tracesSamplingDecision
27+
super(
28+
new SentryId(span.getSpanContext().getTraceId()),
29+
new SpanId(span.getSpanContext().getSpanId()),
30+
parentSpan == null ? null : new SpanId(parentSpan.getSpanContext().getSpanId()),
31+
span.getName(),
32+
null,
33+
null,
34+
null,
35+
null);
36+
this.span = new WeakReference<>(span);
37+
}
38+
39+
@Override
40+
public @Nullable SpanStatus getStatus() {
41+
final @Nullable ReadWriteSpan otelSpan = span.get();
42+
43+
if (otelSpan != null) {
44+
final @NotNull StatusData otelStatus = otelSpan.toSpanData().getStatus();
45+
final @NotNull String otelStatusDescription = otelStatus.getDescription();
46+
if (otelStatusDescription.isEmpty()) {
47+
return otelStatusCodeFallback(otelStatus);
48+
}
49+
final @Nullable SpanStatus spanStatus = SpanStatus.fromApiNameSafely(otelStatusDescription);
50+
if (spanStatus == null) {
51+
return otelStatusCodeFallback(otelStatus);
52+
}
53+
return spanStatus;
54+
}
55+
56+
return null;
57+
}
58+
59+
@Override
60+
public void setStatus(@Nullable SpanStatus status) {
61+
if (status != null) {
62+
final @Nullable ReadWriteSpan otelSpan = span.get();
63+
if (otelSpan != null) {
64+
final @NotNull StatusCode statusCode = translateStatusCode(status);
65+
otelSpan.setStatus(statusCode, status.apiName());
66+
}
67+
}
68+
}
69+
70+
private @Nullable SpanStatus otelStatusCodeFallback(final @NotNull StatusData otelStatus) {
71+
if (otelStatus.getStatusCode() == StatusCode.ERROR) {
72+
return SpanStatus.UNKNOWN_ERROR;
73+
} else if (otelStatus.getStatusCode() == StatusCode.OK) {
74+
return SpanStatus.OK;
75+
}
76+
return null;
77+
}
78+
79+
private @NotNull StatusCode translateStatusCode(final @Nullable SpanStatus status) {
80+
if (status == null) {
81+
return StatusCode.UNSET;
82+
} else if (status == SpanStatus.OK) {
83+
return StatusCode.OK;
84+
} else {
85+
return StatusCode.ERROR;
86+
}
87+
}
88+
}

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

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,7 @@ public OtelSpanWrapper(
8282
this.scopes = Objects.requireNonNull(scopes, "scopes are required");
8383
this.span = new WeakReference<>(span);
8484
this.startTimestamp = startTimestamp;
85-
final @NotNull SentryId traceId = new SentryId(span.getSpanContext().getTraceId());
86-
final @NotNull SpanId spanId = new SpanId(span.getSpanContext().getSpanId());
87-
final @Nullable SpanId parentSpanId =
88-
parentSpan == null ? null : new SpanId(parentSpan.getSpanContext().getSpanId());
89-
@NotNull String operation = span.getName();
90-
91-
// TODO [POTEL] tracesSamplingDecision
92-
this.context = new SpanContext(traceId, spanId, operation, parentSpanId, null);
85+
this.context = new OtelSpanContext(span, parentSpan);
9386
}
9487

9588
@Override
@@ -228,15 +221,12 @@ public void setDescription(@Nullable String description) {
228221
}
229222

230223
@Override
231-
public void setStatus(@Nullable SpanStatus status) {
232-
// TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter
233-
// ^ could go in span attributes
234-
this.context.setStatus(status);
224+
public void setStatus(final @Nullable SpanStatus status) {
225+
context.setStatus(status);
235226
}
236227

237228
@Override
238229
public @Nullable SpanStatus getStatus() {
239-
// TODO [POTEL] need to find a way to transfer data from this wrapper to SpanExporter
240230
return context.getStatus();
241231
}
242232

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ private List<SpanData> maybeSend(final @NotNull List<SpanData> spans) {
198198

199199
// spanStorage.getScope()
200200
// transaction.finishWithScope
201-
transaction.finish(mapOtelStatus(span), new SentryLongDate(span.getEndEpochNanos()));
201+
transaction.finish(
202+
mapOtelStatus(span, transaction), new SentryLongDate(span.getEndEpochNanos()));
202203
}
203204

204205
return remaining.stream()
@@ -244,6 +245,9 @@ private void createAndFinishSpanForOtelSpan(
244245
parentSentrySpan.startChild(
245246
spanInfo.getOp(), spanInfo.getDescription(), startDate, Instrumenter.OTEL);
246247

248+
// TODO [POTEL] Check if we want to use `instrumentationScopeInfo.name` and append it to
249+
// `auto.otel`
250+
// TODO [POTEL] For spans manually created via Sentry API we should set manual, not auto.otel
247251
sentryChildSpan.getSpanContext().setOrigin(TRACE_ORIGN);
248252
for (Map.Entry<String, Object> dataField : spanInfo.getDataFields().entrySet()) {
249253
sentryChildSpan.setData(dataField.getKey(), dataField.getValue());
@@ -256,7 +260,7 @@ private void createAndFinishSpanForOtelSpan(
256260
}
257261

258262
sentryChildSpan.finish(
259-
mapOtelStatus(spanData), new SentryLongDate(spanData.getEndEpochNanos()));
263+
mapOtelStatus(spanData, sentryChildSpan), new SentryLongDate(spanData.getEndEpochNanos()));
260264
}
261265

262266
private void transferSpanDetails(
@@ -285,6 +289,8 @@ private void transferSpanDetails(
285289
for (Map.Entry<String, String> entry : tags.entrySet()) {
286290
targetSpan.setTag(entry.getKey(), entry.getValue());
287291
}
292+
293+
targetSpan.setStatus(sourceSpan.getStatus());
288294
}
289295
}
290296

@@ -463,7 +469,14 @@ private void createOrUpdateSpanNodeAndRefs(
463469
}
464470

465471
@SuppressWarnings("deprecation")
466-
private SpanStatus mapOtelStatus(final @NotNull SpanData otelSpanData) {
472+
private SpanStatus mapOtelStatus(
473+
final @NotNull SpanData otelSpanData, final @NotNull ISpan sentrySpan) {
474+
final @Nullable SpanStatus existingStatus = sentrySpan.getStatus();
475+
// TODO [POTEL] do we want the unknown error check here?
476+
if (existingStatus != null && existingStatus != SpanStatus.UNKNOWN_ERROR) {
477+
return existingStatus;
478+
}
479+
467480
final @NotNull StatusData otelStatus = otelSpanData.getStatus();
468481
final @NotNull StatusCode otelStatusCode = otelStatus.getStatusCode();
469482

sentry/api/sentry.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3255,6 +3255,8 @@ public final class io/sentry/SpanStatus : java/lang/Enum, io/sentry/JsonSerializ
32553255
public static final field UNIMPLEMENTED Lio/sentry/SpanStatus;
32563256
public static final field UNKNOWN Lio/sentry/SpanStatus;
32573257
public static final field UNKNOWN_ERROR Lio/sentry/SpanStatus;
3258+
public fun apiName ()Ljava/lang/String;
3259+
public static fun fromApiNameSafely (Ljava/lang/String;)Lio/sentry/SpanStatus;
32583260
public static fun fromHttpStatusCode (I)Lio/sentry/SpanStatus;
32593261
public static fun fromHttpStatusCode (Ljava/lang/Integer;Lio/sentry/SpanStatus;)Lio/sentry/SpanStatus;
32603262
public fun serialize (Lio/sentry/ObjectWriter;Lio/sentry/ILogger;)V

sentry/src/main/java/io/sentry/SpanContext.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ public SpanId getParentSpanId() {
145145
}
146146

147147
public @NotNull String getOperation() {
148+
// TODO [POTEL] use span name here
148149
return op;
149150
}
150151

@@ -223,12 +224,12 @@ public boolean equals(Object o) {
223224
&& Objects.equals(parentSpanId, that.parentSpanId)
224225
&& op.equals(that.op)
225226
&& Objects.equals(description, that.description)
226-
&& status == that.status;
227+
&& getStatus() == that.getStatus();
227228
}
228229

229230
@Override
230231
public int hashCode() {
231-
return Objects.hash(traceId, spanId, parentSpanId, op, description, status);
232+
return Objects.hash(traceId, spanId, parentSpanId, op, description, getStatus());
232233
}
233234

234235
// region JsonSerializable
@@ -260,8 +261,8 @@ public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger
260261
if (description != null) {
261262
writer.name(JsonKeys.DESCRIPTION).value(description);
262263
}
263-
if (status != null) {
264-
writer.name(JsonKeys.STATUS).value(logger, status);
264+
if (getStatus() != null) {
265+
writer.name(JsonKeys.STATUS).value(logger, getStatus());
265266
}
266267
if (origin != null) {
267268
writer.name(JsonKeys.ORIGIN).value(logger, origin);

sentry/src/main/java/io/sentry/SpanStatus.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,27 @@ private boolean matches(int httpStatusCode) {
103103
return httpStatusCode >= minHttpStatusCode && httpStatusCode <= maxHttpStatusCode;
104104
}
105105

106+
public @NotNull String apiName() {
107+
return name().toLowerCase(Locale.ROOT);
108+
}
109+
110+
public static @Nullable SpanStatus fromApiNameSafely(final @Nullable String apiName) {
111+
if (apiName == null) {
112+
return null;
113+
}
114+
try {
115+
return SpanStatus.valueOf(apiName.toUpperCase(Locale.ROOT));
116+
} catch (IllegalArgumentException ex) {
117+
return null;
118+
}
119+
}
120+
106121
// JsonSerializable
107122

108123
@Override
109124
public void serialize(final @NotNull ObjectWriter writer, final @NotNull ILogger logger)
110125
throws IOException {
111-
writer.value(name().toLowerCase(Locale.ROOT));
126+
writer.value(apiName());
112127
}
113128

114129
public static final class Deserializer implements JsonDeserializer<SpanStatus> {

0 commit comments

Comments
 (0)