Skip to content

feat: Allow Hybrid SDK to setTrace #4137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c233756
added option to disable trace ID generation
bitsandfoxes Feb 20, 2025
d0f9805
rename. recheck
bitsandfoxes Feb 20, 2025
c124eb1
LifeCycleIntegrationTest
bitsandfoxes Feb 20, 2025
ac7be23
GestureListener tests
bitsandfoxes Feb 20, 2025
46039a5
spotless
bitsandfoxes Feb 20, 2025
14ab70e
apidump
bitsandfoxes Feb 20, 2025
4a94bdd
Updated CHANGELOG.md
bitsandfoxes Feb 20, 2025
ef20aae
Merge branch 'main' into feat/auto-trace-id-generation
bitsandfoxes Feb 20, 2025
461aa8a
sync trace as well
bitsandfoxes Feb 4, 2025
b95088c
passing span ID as well
bitsandfoxes Feb 5, 2025
19c0eaa
added API taking traceId and spanID
bitsandfoxes Feb 5, 2025
d7316ea
renamed
bitsandfoxes Feb 6, 2025
3d99e63
.
bitsandfoxes Feb 6, 2025
b427553
Update sentry/src/main/java/io/sentry/JsonSerializer.java
bitsandfoxes Feb 7, 2025
82deae2
Merge branch 'feat/auto-trace-id-generation' into feat/set-trace-id
bitsandfoxes Feb 21, 2025
4152092
Format code
getsentry-bot Feb 21, 2025
2af13de
merge mixup
bitsandfoxes Feb 21, 2025
cba1f4c
merge?
bitsandfoxes Feb 25, 2025
05147f9
merge
bitsandfoxes Feb 25, 2025
cbe837c
moved 'settrace' to internal
bitsandfoxes Feb 25, 2025
9a10493
updated settrace to accept sample rate and sample rand
bitsandfoxes Feb 26, 2025
a589c3d
renamed
bitsandfoxes Feb 26, 2025
093e907
docs
bitsandfoxes Feb 26, 2025
0682748
Format code
getsentry-bot Feb 26, 2025
dc0a6d5
.
bitsandfoxes Feb 26, 2025
7447f11
Merge branch 'main' into feat/set-trace-id
bitsandfoxes Feb 26, 2025
68fea13
test
bitsandfoxes Feb 26, 2025
31c6718
merged
bitsandfoxes Feb 26, 2025
2243841
make the repo häppy
bitsandfoxes Feb 26, 2025
d9d43ee
reviewref
bitsandfoxes Feb 27, 2025
f8f097a
bumped native ndk
bitsandfoxes Feb 28, 2025
e6a7438
Updated CHANGELOG.md
bitsandfoxes Feb 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

### Features

- The SDK now automatically propagates the trace-context to the native layer. This allows to connect errors on different layers of the application. ([#4137](https://github.com/getsentry/sentry-java/pull/4137))

### Dependencies

- Bump Native SDK from v0.7.20 to v0.8.1 ([#4137](https://github.com/getsentry/sentry-java/pull/4137))
- [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#0810)
- [diff](https://github.com/getsentry/sentry-native/compare/v0.7.20...0.8.1)

## 8.3.0

### Features
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ object Config {
val apolloKotlin = "com.apollographql.apollo3:apollo-runtime:3.8.2"
val apolloKotlin4 = "com.apollographql.apollo:apollo-runtime:4.1.1"

val sentryNativeNdk = "io.sentry:sentry-native-ndk:0.7.20"
val sentryNativeNdk = "io.sentry:sentry-native-ndk:0.8.1"

object OpenTelemetry {
val otelVersion = "1.44.1"
Expand Down
1 change: 1 addition & 0 deletions sentry-android-core/api/sentry-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ public final class io/sentry/android/core/InternalSentrySdk {
public static fun getAppStartMeasurement ()Ljava/util/Map;
public static fun getCurrentScope ()Lio/sentry/IScope;
public static fun serializeScope (Landroid/content/Context;Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/IScope;)Ljava/util/Map;
public static fun setTrace (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Double;)V
}

public final class io/sentry/android/core/LoadClass : io/sentry/util/LoadClass {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.sentry.android.core;

import static io.sentry.Sentry.getCurrentScopes;
import static io.sentry.SentryLevel.DEBUG;
import static io.sentry.SentryLevel.INFO;
import static io.sentry.SentryLevel.WARNING;
Expand All @@ -13,6 +14,7 @@
import io.sentry.IScopes;
import io.sentry.ISerializer;
import io.sentry.ObjectWriter;
import io.sentry.PropagationContext;
import io.sentry.ScopeType;
import io.sentry.ScopesAdapter;
import io.sentry.SentryEnvelope;
Expand All @@ -30,6 +32,7 @@
import io.sentry.protocol.SentryId;
import io.sentry.protocol.User;
import io.sentry.util.MapObjectWriter;
import io.sentry.util.TracingUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
Expand Down Expand Up @@ -329,4 +332,22 @@ private static Session updateSession(
});
return sessionRef.get();
}

/**
* Allows a Hybrid SDK to set the trace on the native layer
*
* @param traceId the trace ID
* @param spanId the trace origin's span ID
* @param sampleRate the sample rate used by the origin of the trace
* @param sampleRand the random value used to sample with by the origin of the trace
*/
public static void setTrace(
final @NotNull String traceId,
final @NotNull String spanId,
final @Nullable Double sampleRate,
final @Nullable Double sampleRand) {
TracingUtils.setTrace(
getCurrentScopes(),
PropagationContext.fromExistingTrace(traceId, spanId, sampleRate, sampleRand));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import io.sentry.SentryExceptionFactory
import io.sentry.SentryItemType
import io.sentry.SentryOptions
import io.sentry.Session
import io.sentry.SpanId
import io.sentry.android.core.performance.ActivityLifecycleTimeSpan
import io.sentry.android.core.performance.AppStartMetrics
import io.sentry.exception.ExceptionMechanismException
Expand Down Expand Up @@ -505,4 +506,25 @@ class InternalSentrySdkTest {
assertEquals(20.toLong(), actualProcessSpan["start_timestamp_ms"])
assertEquals(100.toLong(), actualProcessSpan["end_timestamp_ms"])
}

@Test
fun `setTrace sets correct propagation context`() {
val fixture = Fixture()
fixture.init(context)

val traceId = "771a43a4192642f0b136d5159a501700"
val spanId = "771a43a4192642f0"
val sampleRate = 0.5
val sampleRand = 0.3

InternalSentrySdk.setTrace(traceId, spanId, sampleRate, sampleRand)

Sentry.configureScope { scope ->
val propagationContext = scope.propagationContext
assertEquals(SentryId(traceId), propagationContext.traceId)
assertEquals(SpanId(spanId), propagationContext.parentSpanId)
assertEquals(sampleRate, propagationContext.baggage.sampleRateDouble)
assertEquals(sampleRand, propagationContext.baggage.sampleRandDouble)
}
}
}
1 change: 1 addition & 0 deletions sentry-android-ndk/api/sentry-android-ndk.api
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public final class io/sentry/android/ndk/NdkScopeObserver : io/sentry/ScopeObser
public fun removeTag (Ljava/lang/String;)V
public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V
public fun setTag (Ljava/lang/String;Ljava/lang/String;)V
public fun setTrace (Lio/sentry/SpanContext;Lio/sentry/IScope;)V
public fun setUser (Lio/sentry/protocol/User;)V
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import io.sentry.Breadcrumb;
import io.sentry.DateUtils;
import io.sentry.IScope;
import io.sentry.ScopeObserverAdapter;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.SpanContext;
import io.sentry.ndk.INativeScope;
import io.sentry.ndk.NativeScope;
import io.sentry.protocol.User;
Expand Down Expand Up @@ -125,4 +127,22 @@ public void removeExtra(final @NotNull String key) {
.log(SentryLevel.ERROR, e, "Scope sync removeExtra(%s) has an error.", key);
}
}

@Override
public void setTrace(@Nullable SpanContext spanContext, @NotNull IScope scope) {
if (spanContext == null) {
return;
}

try {
options
.getExecutorService()
.submit(
() ->
nativeScope.setTrace(
spanContext.getTraceId().toString(), spanContext.getSpanId().toString()));
} catch (Throwable e) {
options.getLogger().log(SentryLevel.ERROR, e, "Scope sync setTrace failed.");
}
}
}
2 changes: 2 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -1952,6 +1952,7 @@ public final class io/sentry/PropagationContext {
public fun <init> ()V
public fun <init> (Lio/sentry/PropagationContext;)V
public fun <init> (Lio/sentry/protocol/SentryId;Lio/sentry/SpanId;Lio/sentry/SpanId;Lio/sentry/Baggage;Ljava/lang/Boolean;)V
public static fun fromExistingTrace (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Double;Ljava/lang/Double;)Lio/sentry/PropagationContext;
public static fun fromHeaders (Lio/sentry/ILogger;Ljava/lang/String;Ljava/lang/String;)Lio/sentry/PropagationContext;
public static fun fromHeaders (Lio/sentry/ILogger;Ljava/lang/String;Ljava/util/List;)Lio/sentry/PropagationContext;
public static fun fromHeaders (Lio/sentry/SentryTraceHeader;Lio/sentry/Baggage;Lio/sentry/SpanId;)Lio/sentry/PropagationContext;
Expand Down Expand Up @@ -6360,6 +6361,7 @@ public final class io/sentry/util/TracingUtils {
public static fun ensureBaggage (Lio/sentry/Baggage;Ljava/lang/Boolean;Ljava/lang/Double;Ljava/lang/Double;)Lio/sentry/Baggage;
public static fun isIgnored (Ljava/util/List;Ljava/lang/String;)Z
public static fun maybeUpdateBaggage (Lio/sentry/IScope;Lio/sentry/SentryOptions;)Lio/sentry/PropagationContext;
public static fun setTrace (Lio/sentry/IScopes;Lio/sentry/PropagationContext;)V
public static fun startNewTrace (Lio/sentry/IScopes;)V
public static fun trace (Lio/sentry/IScopes;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders;
public static fun traceIfAllowed (Lio/sentry/IScopes;Ljava/lang/String;Ljava/util/List;Lio/sentry/ISpan;)Lio/sentry/util/TracingUtils$TracingHeaders;
Expand Down
13 changes: 13 additions & 0 deletions sentry/src/main/java/io/sentry/PropagationContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,19 @@ public static PropagationContext fromHeaders(
sentryTraceHeader.isSampled());
}

public static @NotNull PropagationContext fromExistingTrace(
final @NotNull String traceId,
final @NotNull String spanId,
final @Nullable Double decisionSampleRate,
final @Nullable Double decisionSampleRand) {
Copy link
Member

@adinauer adinauer Feb 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add an optional Baggage param here as well?
I would assume we want to sync that between the SDKs as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the need or benefit of doing that from the Unity SDK's perspective. @krystofwoldrich would this help RN?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, I don't see why we would need Baggage synced as well.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we pass replayId via baggage to downstream services
Might need that on React Native (which has Replay)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need that because the ReplayID is generated on native and synced up to RN Replay Integration which adds it to DSC.

https://github.com/getsentry/sentry-react-native/blob/5dff5ee95fb4c3d8f2589b80d4d355fecde17778/packages/core/src/js/replay/mobilereplay.ts#L107

The traceID is the opposite, hybrid (RN) generates it and we need to sync it down to native.

return new PropagationContext(
new SentryId(traceId),
new SpanId(),
new SpanId(spanId),
TracingUtils.ensureBaggage(null, null, decisionSampleRate, decisionSampleRand),
null);
}

private @NotNull SentryId traceId;
private @NotNull SpanId spanId;
private @Nullable SpanId parentSpanId;
Expand Down
11 changes: 11 additions & 0 deletions sentry/src/main/java/io/sentry/util/TracingUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ public static void startNewTrace(final @NotNull IScopes scopes) {
});
}

public static void setTrace(
final @NotNull IScopes scopes, final @NotNull PropagationContext propagationContext) {
scopes.configureScope(
scope -> {
scope.withPropagationContext(
oldPropagationContext -> {
scope.setPropagationContext(propagationContext);
});
});
}

public static @Nullable TracingHeaders traceIfAllowed(
final @NotNull IScopes scopes,
final @NotNull String requestUrl,
Expand Down
Loading