Skip to content

Conversation

@jamescrosswell
Copy link
Collaborator

@jamescrosswell jamescrosswell commented Apr 23, 2025

Part of #2136

Associate Errors and Traces with the active Session Replay (if one exists) on Android.

Protocol

Although there is a new Replay context, I found that just setting the replay_id on that context (e.g. for an event) did not result in the event showing in the errors pane for session replays.

What was recommended, and work better, is to store the replay_id on the DSC (which will accompany all events) and let Relay unpack it from there. The DSC specification mentions replay_id as an optional property on the dynamic sampling context.

Getting the Replay ID

There are three ways the DSC can be created:

  1. From the currently active transaction
  2. From the propagation context
  3. From a BaggageHeader (in distributed tracing scenarios)

In all 3 cases, we check to see if there's a Session Replay actively being recorded. If there is, we either set (or overwrite) the replay_id that int he DSC. If there's not, we just propagate whatever is in the header.

This is a bit different to what we normally do with the baggage headers, so there are quite a few new scenarios to unit test.

Part of #2136

Associate Errors and Traces with the active Session Replay (if one exists) on Android.
@github-actions
Copy link
Contributor

github-actions bot commented Apr 23, 2025

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 438c4e6

@jamescrosswell jamescrosswell marked this pull request as ready for review April 28, 2025 08:42
@jamescrosswell jamescrosswell marked this pull request as draft April 29, 2025 06:16
@jamescrosswell jamescrosswell marked this pull request as ready for review April 29, 2025 22:43
@jamescrosswell jamescrosswell marked this pull request as draft April 29, 2025 22:53
@bruno-garcia bruno-garcia requested a review from vaind May 12, 2025 22:42
var replayId = JavaSdk.ScopesAdapter.Instance?.Options?.ReplayController?.ReplayId?.ToSentryId();
return (replayId is { } id && id != SentryId.Empty) ? id : null;
#else
return null;
Copy link
Member

Choose a reason for hiding this comment

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

Is the idea to add iOS here later? potentially Web for example if we were brave enough?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, I was planning for at least iOS...

Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

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

approving to unblock, lets see if @vaind has comments then we can fix in follow up PRs

Copy link
Collaborator

@vaind vaind left a comment

Choose a reason for hiding this comment

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

Looks reasonable to me. I'm a bit out of context on how replay is implemented in .net so if you want me to review/check this more in-depth, let me know.

Comment on lines +221 to +228
public static DynamicSamplingContext? CreateDynamicSamplingContext(this BaggageHeader baggage, IReplaySession? replaySession = null)
=> DynamicSamplingContext.CreateFromBaggageHeader(baggage, replaySession);

public static DynamicSamplingContext CreateDynamicSamplingContext(this TransactionTracer transaction, SentryOptions options)
=> DynamicSamplingContext.CreateFromTransaction(transaction, options);
public static DynamicSamplingContext CreateDynamicSamplingContext(this TransactionTracer transaction, SentryOptions options, IReplaySession? replaySession)
=> DynamicSamplingContext.CreateFromTransaction(transaction, options, replaySession);

public static DynamicSamplingContext CreateDynamicSamplingContext(this SentryPropagationContext propagationContext, SentryOptions options)
=> DynamicSamplingContext.CreateFromPropagationContext(propagationContext, options);
public static DynamicSamplingContext CreateDynamicSamplingContext(this SentryPropagationContext propagationContext, SentryOptions options, IReplaySession? replaySession)
=> DynamicSamplingContext.CreateFromPropagationContext(propagationContext, options, replaySession);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of adding replay to all the factories, how about we use just the WithReplayId()?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'd rather avoid WithReplayId where possible as it has to recreate the DynamicSamplingContext in order to add the replay id... that would result in us creating the DynamicSamplingContext twice for each of the factories if there was an active replay session 😞

The other option would be to change DynamicSamplingContext.Items from a read only dictionary to a mutable dictionary. Most of the stuff in there really shouldn't be messed with once it's created (we're supposed to be propagating stuff verbatim), so until now it's been nice to have this be immutable.

All of this stuff is internal, so we can easily refactor in a separate PR whenever we like, if we want to go with a different coding style.

@jamescrosswell jamescrosswell merged commit 2e1fff1 into main May 13, 2025
26 checks passed
@jamescrosswell jamescrosswell deleted the replay-link-events branch May 13, 2025 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants