Skip to content
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

[SR] Capture Replays for ANRs and crashes #3565

Merged
merged 255 commits into from
Jul 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
255 commits
Select commit Hold shift + click to select a range
dd0e9a4
Api dump
romtsn Feb 13, 2024
d34ddee
Formatting
romtsn Feb 13, 2024
0cca47c
Lint
romtsn Feb 13, 2024
0a26e8d
Merge branch 'rz/feat/session-replay' into rz/feat/session-replay-sou…
romtsn Feb 13, 2024
5ebdfed
Format code
getsentry-bot Feb 13, 2024
04f43ed
More comments
romtsn Feb 13, 2024
b461847
Disable detekt plugin for now
romtsn Feb 13, 2024
f8419d1
Merge branch 'rz/feat/session-replay-sources' into rz/feat/session-re…
romtsn Feb 13, 2024
a63cac1
WIP
romtsn Feb 15, 2024
fa72057
Add replay envelopes
romtsn Feb 19, 2024
6cfb511
Remove jsonValue
romtsn Feb 19, 2024
0d031d7
Remove
romtsn Feb 19, 2024
07e6b26
Fix json
romtsn Feb 19, 2024
18af924
Finalize replay envelopes
romtsn Feb 20, 2024
64cedfa
Introduce MapObjectReader
romtsn Feb 20, 2024
b8cb924
Add missing test
romtsn Feb 20, 2024
28d341f
Merge branch 'rz/feat/session-replay-envelopes' into rz/feat/session-…
romtsn Feb 20, 2024
1e76fc7
Add test for MapObjectReader
romtsn Feb 22, 2024
13c1971
Add MapObjectWriter change
romtsn Feb 22, 2024
a14e090
Merge branch 'rz/feat/session-replay-envelopes' into rz/feat/session-…
romtsn Feb 22, 2024
86baf7f
Add finals
romtsn Feb 22, 2024
f1ca9f6
Fix test
romtsn Feb 22, 2024
fbbe0d9
Fix test
romtsn Feb 22, 2024
688233f
Merge branch 'rz/feat/session-replay-envelopes' into rz/feat/session-…
romtsn Feb 22, 2024
fd63960
Address review
romtsn Feb 28, 2024
93785cc
Add finals and annotations
romtsn Feb 28, 2024
4db19e0
Merge pull request #3215 from getsentry/rz/feat/session-replay-map-ob…
romtsn Feb 28, 2024
4e55ec0
Specify SHA for license headers
romtsn Feb 28, 2024
9603672
Address review from Dhiogo
romtsn Feb 28, 2024
1ce57cb
Address review from Markus
romtsn Mar 1, 2024
62477b4
Remove public captureReplay method
romtsn Mar 1, 2024
af42fb3
Fix test
romtsn Mar 1, 2024
1951891
api dump
romtsn Mar 1, 2024
cd09739
Merge branch 'rz/feat/session-replay-sources' into rz/feat/session-re…
romtsn Mar 1, 2024
4e54c77
api dump
romtsn Mar 1, 2024
b2940c4
Address review from Markus
romtsn Mar 4, 2024
002a0f3
Api dump
romtsn Mar 4, 2024
64f70c5
Merge pull request #3203 from getsentry/rz/feat/session-replay-sources
romtsn Mar 4, 2024
fb14ecb
Merge branch 'rz/feat/session-replay' into rz/feat/session-replay-env…
romtsn Mar 4, 2024
79151e9
Merge pull request #3214 from getsentry/rz/feat/session-replay-envelopes
romtsn Mar 4, 2024
9e87fe8
Add replay integration
romtsn Mar 19, 2024
2e954f7
Uncomment redacting
romtsn Mar 19, 2024
8bc6219
Update proguard rules
romtsn Mar 20, 2024
a5aa4be
Add missing rule for AndroidTest
romtsn Mar 20, 2024
f72e45f
Add ReplayCache tests
romtsn Mar 21, 2024
545712c
Add tests
romtsn Mar 21, 2024
2df34a3
Add SessionReplayOptions
romtsn Mar 25, 2024
c02f1db
Call listeners when installing RootViewsSpy
romtsn Mar 25, 2024
bee240b
Call listeners when installing RootViewsSpy
romtsn Mar 25, 2024
e2a821c
Merge branch 'rz/feat/session-replay-integration' into rz/feat/sessio…
romtsn Mar 25, 2024
d6bb9ab
SessionReplayOptions -> SentryReplayOptions
romtsn Mar 25, 2024
7854e4f
Fix test
romtsn Mar 26, 2024
2cddcc4
Add AndroidManifest options for replays
romtsn Mar 26, 2024
ef3d62c
Add buffer mode and link replays with events/transactions
romtsn Mar 28, 2024
f7ac74f
Pass hint to captureReplay
romtsn Mar 28, 2024
5faeb4e
Better error handling
romtsn Mar 28, 2024
65d35ec
recycler lastScreenshot before re-assigning
romtsn Mar 28, 2024
0f4e718
Expose ReplayCache as public api
romtsn Mar 28, 2024
fd6e633
Fix redacting out of sync
romtsn Mar 28, 2024
915bd29
Merge branch 'rz/feat/session-replay-integration' into rz/feat/sessio…
romtsn Mar 28, 2024
1a5c4da
_experimental -> experimental
romtsn Mar 28, 2024
fd92dc7
Merge branch 'rz/feat/session-replay-options' into rz/feat/session-re…
romtsn Mar 28, 2024
da37c89
Merge conflicts
romtsn Mar 28, 2024
c53a975
Fix tests
romtsn Apr 2, 2024
18eb67e
Add more tests
romtsn Apr 2, 2024
a7ae2b7
Improve ReplayCache logic
romtsn Apr 2, 2024
bf14d83
frameUsec -> frameDurationUsec
romtsn Apr 2, 2024
82fe21a
bottom/right -> height/width
romtsn Apr 2, 2024
de56e35
add todos
romtsn Apr 2, 2024
fa8c527
duration -> durationMs
romtsn Apr 2, 2024
dfbb992
replaId non-nullable
romtsn Apr 2, 2024
4ada96f
Merge branch 'rz/feat/session-replay-integration' into rz/feat/sessio…
romtsn Apr 2, 2024
c6a993f
Merge branch 'rz/feat/session-replay-options' into rz/feat/session-re…
romtsn Apr 2, 2024
ad7d78d
More conflicts
romtsn Apr 2, 2024
ab00547
More conflicts
romtsn Apr 2, 2024
7f78fee
Fix tests
romtsn Apr 2, 2024
9f252bc
Address PR review
romtsn Apr 3, 2024
27b15d7
Add kdoc
romtsn Apr 3, 2024
5278c86
Merge branch 'rz/feat/session-replay-integration' into rz/feat/sessio…
romtsn Apr 3, 2024
27674b1
Merge branch 'rz/feat/session-replay-options' into rz/feat/session-re…
romtsn Apr 3, 2024
957f0cf
Add kdoc
romtsn Apr 3, 2024
da3560d
Fix tests
romtsn Apr 3, 2024
b04aaf2
Merge pull request #3272 from getsentry/rz/feat/session-replay-integr…
romtsn Apr 3, 2024
023cb5f
Merge pull request #3283 from getsentry/rz/feat/session-replay-options
romtsn Apr 3, 2024
1a77d17
Add comment for experimental options
romtsn Apr 3, 2024
b88b1b9
Do not run recorder if full session was not sampled
romtsn Apr 3, 2024
4d533fb
Add more tests
romtsn Apr 3, 2024
4276264
Add session deadline of 1h
romtsn Apr 3, 2024
3fe5e0f
Clean up older replays when starting a new one
romtsn Apr 3, 2024
ca9f9d4
Remove unnecessary extension fun
romtsn Apr 3, 2024
7079d7a
[SR] Add buffer mode and link replays with events/transactions
romtsn Apr 4, 2024
ea417e4
Safe executors
romtsn Apr 4, 2024
f994ac9
Merge branch 'rz/feat/session-replay' into rz/feat/session-replay-cle…
romtsn Apr 4, 2024
0c5e4b0
Fix crashing MediaCodec and use density to determine recording resolu…
romtsn Apr 5, 2024
3e0894d
Add redact options and align naming
romtsn Apr 5, 2024
1fc9aa2
Fix tests
romtsn Apr 5, 2024
15e61dd
Merge branch 'rz/fix/session-replay-media-codec' into rz/feat/session…
romtsn Apr 5, 2024
ad98acd
Fix tests
romtsn Apr 5, 2024
5b8ed7c
Merge branch 'rz/fix/session-replay-media-codec' into rz/feat/session…
romtsn Apr 5, 2024
227c22a
WIP
romtsn Apr 5, 2024
0fca8ad
Try-catch release of encoder
romtsn Apr 5, 2024
5e37622
Support orientation change for session mode
romtsn Apr 8, 2024
d0b4d5c
WIP
romtsn Apr 8, 2024
f0fcf5d
Merge branch 'rz/fix/session-replay-media-codec' into rz/feat/session…
romtsn Apr 8, 2024
d7a7123
Merge branch 'rz/feat/session-replay-redact-options' into rz/feat/ses…
romtsn Apr 8, 2024
236ee2c
Spotless
romtsn Apr 8, 2024
e9bf0b3
TODO
romtsn Apr 8, 2024
bf8f49a
[SR] Cleanups and session deadline
romtsn Apr 9, 2024
71837c1
Update sentry/src/main/java/io/sentry/SentryReplayOptions.java
romtsn Apr 9, 2024
1430e7e
[SR] Fix crashing MediaCodec and use density to determine recording r…
romtsn Apr 9, 2024
a0c2678
[SR] Handle orientation change
romtsn Apr 9, 2024
3ef9f06
[SR] Add redact options and align naming
romtsn Apr 9, 2024
59b63e0
More gates
romtsn Apr 9, 2024
a9e3405
Merge branch 'rz/feat/session-replay' into rz/feat/session-replay-gates
romtsn Apr 9, 2024
21239ca
Revert addAll
romtsn Apr 9, 2024
b27a905
[SR] More gates
romtsn Apr 9, 2024
b83a894
Merge branch 'main' into rz/feat/session-replay
romtsn Apr 9, 2024
9cafe43
Fix conflicts
romtsn Apr 9, 2024
6c9baea
fix test
romtsn Apr 9, 2024
62a0984
Merge branch 'main' into rz/feat/session-replay
romtsn Apr 9, 2024
2057e22
release: 7.8.0-alpha.0
getsentry-bot Apr 9, 2024
7b24e7c
Merge branch 'release/7.8.0-alpha.0' into rz/feat/session-replay
Apr 9, 2024
5e33e95
Merge branch 'rz/feat/session-replay' into rz/feat/session-replay-cap…
romtsn Apr 9, 2024
f8f5698
Introduce CaptureStrategy for buffer and session modes
romtsn Apr 15, 2024
5c59bf7
Formatting
romtsn Apr 15, 2024
247f1c9
WIP
romtsn Apr 16, 2024
c2be16f
Expose public API for flutter
romtsn Apr 16, 2024
06d4b6d
Spotless
romtsn Apr 16, 2024
59056ef
Spotless
romtsn Apr 16, 2024
06495d8
Merge branch 'rz/feat/session-replay-breadcrumbs' into rz/feat/sessio…
romtsn Apr 16, 2024
0e951eb
Remove breadcrumb import
romtsn Apr 16, 2024
9686a74
Merge branch 'rz/feat/session-replay-breadcrumbs' into rz/feat/sessio…
romtsn Apr 16, 2024
e33b29c
Send temporary breadcrumbs and add test
romtsn Apr 18, 2024
27e17c1
Formatting
romtsn Apr 18, 2024
51cc432
Sort rrweb events
romtsn Apr 18, 2024
82680fb
Merge branch 'rz/feat/session-replay-breadcrumbs' into rz/feat/sessio…
romtsn Apr 18, 2024
4c7d1a0
Formatting
romtsn Apr 18, 2024
32fb5f0
Merge branch 'rz/feat/session-replay-breadcrumbs' into rz/feat/sessio…
romtsn Apr 18, 2024
c6b16ed
Expose replayCacheDir
romtsn Apr 18, 2024
6f3dc0a
Capture network requests
romtsn Apr 22, 2024
4c562fb
Change op name to resource.http
romtsn Apr 22, 2024
cd4fd9e
feat(replay): Add `sendReplay` method for Hybrid SDKs
krystofwoldrich Apr 23, 2024
e836f49
fix apiDump
krystofwoldrich Apr 23, 2024
4f240d4
Address PR review
romtsn Apr 23, 2024
12c0eb7
Merge pull request #3357 from getsentry/rz/feat/session-replay-captur…
romtsn Apr 25, 2024
db66737
[SR] Capture breadcrumbs (temporary)
romtsn Apr 25, 2024
33cd776
[SR] Expose public API for flutter
romtsn Apr 25, 2024
bdd9db5
[SR] Capture network requests
romtsn Apr 25, 2024
9295f0f
[SR] Add `sendReplay` method for Hybrid SDKs
romtsn Apr 25, 2024
f6b464b
Capture motion events as incremental rrweb events
romtsn Apr 25, 2024
2d508c9
Spotless
romtsn Apr 25, 2024
c0158eb
Merge branch 'rz/feat/session-replay' into rz/feat/session-replay-tou…
romtsn Apr 25, 2024
234b789
Revert
romtsn Apr 25, 2024
a68e88e
Merge branch 'main' into rz/feat/session-replay
romtsn Apr 26, 2024
b3ee659
Changelog
romtsn Apr 26, 2024
e89a3ef
release: 7.9.0-alpha.1
getsentry-bot Apr 26, 2024
2c0977b
Fix test
romtsn Apr 26, 2024
d4ac484
Merge branch 'release/7.9.0-alpha.1' into rz/feat/session-replay
Apr 26, 2024
5c05b6f
WIP
romtsn May 1, 2024
0476132
Adhere to rrweb move event expectations
romtsn May 2, 2024
7823d87
formatting
romtsn May 2, 2024
9596bb9
Align breadcrumbs with frontend and iOS
romtsn May 3, 2024
733b490
Add tests and fix deserialization
romtsn May 6, 2024
1217bb1
Rotate buffered motion events in buffer mode
romtsn May 6, 2024
647822c
Merge branch 'rz/feat/session-replay' into rz/feat/session-replay-tou…
romtsn May 6, 2024
6f83386
Merge branch 'rz/feat/session-replay-touch-events' into rz/feat/sessi…
romtsn May 6, 2024
69e5144
Add Nullables
romtsn May 29, 2024
a3d581c
Address PR feedback
romtsn May 29, 2024
d93e609
Formatting
romtsn May 29, 2024
0af0984
[SR] Capture gestures/motion events
romtsn May 29, 2024
5e119af
Merge branch 'rz/feat/session-replay' into rz/feat/session-replay-bre…
romtsn May 29, 2024
b9b78df
Rotate current events until segment end exclusively
romtsn May 29, 2024
50443a4
[SR] Align breadcrumbs with frontend (#3406)
romtsn May 29, 2024
f37c593
Allow rrweb breadcrumb customization from hybrid SDKs
romtsn May 30, 2024
730dc66
Fix proguard rules
romtsn Jun 3, 2024
69b23cc
WIP
romtsn Jun 3, 2024
c2dcad5
Add tests
romtsn Jun 3, 2024
d8fda33
Detect obscured views
romtsn Jun 7, 2024
26df8c6
revert some thigns
romtsn Jun 7, 2024
9c874d7
Remove commented code
romtsn Jun 7, 2024
db4ed1b
Merge branch 'rz/feat/session-replay-breadcrumbs-customizer' into rz/…
romtsn Jun 7, 2024
7b01de3
Suppress lint
romtsn Jun 10, 2024
aa2a5a9
Support multi-touch gestures
romtsn Jun 11, 2024
4aa50c2
Address PR feedback
romtsn Jun 18, 2024
11437d4
[SR] Allow RRWeb breadcrumb customization from hybrid SDKs
romtsn Jun 18, 2024
e3623e5
[SR] Redaction fixes part 1
romtsn Jun 18, 2024
5b412ae
Merge branch 'main' into rz/feat/session-replay
romtsn Jun 18, 2024
522b586
Changelog
romtsn Jun 18, 2024
263e147
release: 7.11.0-alpha.2
getsentry-bot Jun 18, 2024
a83b5d9
Merge branch 'release/7.11.0-alpha.2' into rz/feat/session-replay
Jun 18, 2024
9444da9
Make multi-touch work
romtsn Jun 18, 2024
54056cb
Fix tests
romtsn Jun 19, 2024
587b6d0
Merge branch 'rz/feat/session-replay' into rz/feat/session-replay-mul…
romtsn Jun 19, 2024
df7270e
WIP
romtsn Jun 20, 2024
53d5fbc
Capture screen names as urls for replay
romtsn Jun 21, 2024
a783326
Fix
romtsn Jun 21, 2024
01b7d67
Ignore warning
romtsn Jun 22, 2024
c832416
Address PR feedback
romtsn Jun 22, 2024
cfc52d9
[SR] Support multi-touch gestures
romtsn Jun 22, 2024
eef3c11
Tests
romtsn Jun 24, 2024
97d530c
Add quality settings
romtsn Jun 24, 2024
5557450
Fix redacting out of sync
romtsn Jun 24, 2024
213ff9e
Remove time measuring
romtsn Jul 2, 2024
fc7138b
Mark isEnableScreenTracking as experimental
romtsn Jul 2, 2024
d655d52
Format code
getsentry-bot Jul 2, 2024
a154cd0
Address PR feedback
romtsn Jul 2, 2024
2f727bc
[SR] Capture screen names as urls
romtsn Jul 2, 2024
17a4d22
[SR] Add quality settings
romtsn Jul 2, 2024
402587d
Clean up
romtsn Jul 2, 2024
71bdb5a
Spotless
romtsn Jul 2, 2024
a54f92f
[SR] Redaction fixes pt. 2
romtsn Jul 3, 2024
bc18c8b
feat(replay): Add `redactClasses` option
krystofwoldrich Jul 3, 2024
8f9a6d3
update api dump
krystofwoldrich Jul 3, 2024
da59406
Merge branch 'main' into rz/feat/session-replay
romtsn Jul 3, 2024
67efa10
Format code
getsentry-bot Jul 3, 2024
89f5186
Changelog
romtsn Jul 3, 2024
befe3fe
release: 7.12.0-alpha.3
getsentry-bot Jul 3, 2024
4866d54
Update CHANGELOG.md
krystofwoldrich Jul 3, 2024
615bb0e
Merge branch 'release/7.12.0-alpha.3' into rz/feat/session-replay
Jul 3, 2024
837e911
[SR] Add `redactClasses` option (#3546)
krystofwoldrich Jul 8, 2024
50c1d50
misc(changelog): Prepare for next alpha
krystofwoldrich Jul 9, 2024
361f73a
fix(changelog): Bump alpha version number
krystofwoldrich Jul 9, 2024
061ac4b
release: 7.12.0-alpha.4
getsentry-bot Jul 9, 2024
092f714
Merge branch 'release/7.12.0-alpha.4' into rz/feat/session-replay
Jul 9, 2024
c9b0804
Capture replay in session mode for ANRs
romtsn Jul 11, 2024
641a434
Tests
romtsn Jul 11, 2024
d5b213f
Merge branch 'rz/feat/session-replay' into rz/feat/session-replay-anrs
romtsn Jul 11, 2024
8cf2d1c
Api dump
romtsn Jul 11, 2024
ff1e3fe
Merge branch 'main' into rz/feat/session-replay-anrs
romtsn Jul 16, 2024
544bd00
Format code
getsentry-bot Jul 16, 2024
c0ebbed
Resolve merge conflicts
romtsn Jul 17, 2024
7fd180a
Fix tests
romtsn Jul 17, 2024
eced6ae
Format code
getsentry-bot Jul 17, 2024
09fb0e4
Add SessionCaptureStrategyTest
romtsn Jul 23, 2024
78b08b1
Infer duration from last frame for the unsent segment
romtsn Jul 24, 2024
d7dd9c4
Support replays for crashes in buffer and session modes
romtsn Jul 26, 2024
b303380
Fix breadcrumb http timestamps type
romtsn Jul 26, 2024
dffa4a9
Always do partial updates to the video
romtsn Jul 26, 2024
8d86dab
Make buffer mode work for ANRs
romtsn Jul 29, 2024
2267b1d
Add tests
romtsn Jul 29, 2024
4e4a437
Add buffer strategy test
romtsn Jul 29, 2024
a45ade2
Improve ReplayCache persistence logic
romtsn Jul 30, 2024
c503b1d
Address PR feedback
romtsn Jul 30, 2024
2df4894
Merge branch 'main' into rz/feat/session-replay-anrs
romtsn Jul 30, 2024
d43e70d
Merge branch 'rz/feat/session-replay-anrs' into rz/feat/session-repla…
romtsn Jul 30, 2024
aadb364
Format code
getsentry-bot Jul 30, 2024
5c2f8fc
Revert sample rate to session
romtsn Jul 30, 2024
b984455
Address PR feedback
romtsn Jul 30, 2024
67fede6
[SR] Support replays for crashes in buffer and session modes
romtsn Jul 30, 2024
dc8b49a
Changelog
romtsn Jul 30, 2024
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
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

## Unreleased

### Features

- Session Replay: ([#3565](https://github.com/getsentry/sentry-java/pull/3565)) ([#3609](https://github.com/getsentry/sentry-java/pull/3609))
- Capture remaining replay segment for ANRs on next app launch
- Capture remaining replay segment for unhandled crashes on next app launch

### Fixes

- Session Replay: ([#3565](https://github.com/getsentry/sentry-java/pull/3565)) ([#3609](https://github.com/getsentry/sentry-java/pull/3609))
- Fix stopping replay in `session` mode at 1 hour deadline
- Never encode full frames for a video segment, only do partial updates. This further reduces size of the replay segment
- Use propagation context when no active transaction for ANRs

### Dependencies

- Bump Spring Boot to 3.3.2 ([#3541](https://github.com/getsentry/sentry-java/pull/3541))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,19 @@
import static io.sentry.cache.PersistingOptionsObserver.ENVIRONMENT_FILENAME;
import static io.sentry.cache.PersistingOptionsObserver.PROGUARD_UUID_FILENAME;
import static io.sentry.cache.PersistingOptionsObserver.RELEASE_FILENAME;
import static io.sentry.cache.PersistingOptionsObserver.REPLAY_ERROR_SAMPLE_RATE_FILENAME;
import static io.sentry.cache.PersistingOptionsObserver.SDK_VERSION_FILENAME;
import static io.sentry.cache.PersistingScopeObserver.BREADCRUMBS_FILENAME;
import static io.sentry.cache.PersistingScopeObserver.CONTEXTS_FILENAME;
import static io.sentry.cache.PersistingScopeObserver.EXTRAS_FILENAME;
import static io.sentry.cache.PersistingScopeObserver.FINGERPRINT_FILENAME;
import static io.sentry.cache.PersistingScopeObserver.LEVEL_FILENAME;
import static io.sentry.cache.PersistingScopeObserver.REPLAY_FILENAME;
import static io.sentry.cache.PersistingScopeObserver.REQUEST_FILENAME;
import static io.sentry.cache.PersistingScopeObserver.TRACE_FILENAME;
import static io.sentry.cache.PersistingScopeObserver.TRANSACTION_FILENAME;
import static io.sentry.cache.PersistingScopeObserver.USER_FILENAME;
import static io.sentry.protocol.Contexts.REPLAY_ID;

import android.annotation.SuppressLint;
import android.app.ActivityManager;
Expand Down Expand Up @@ -51,6 +54,8 @@
import io.sentry.protocol.SentryTransaction;
import io.sentry.protocol.User;
import io.sentry.util.HintUtils;
import java.io.File;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -78,13 +83,24 @@ public final class AnrV2EventProcessor implements BackfillingEventProcessor {

private final @NotNull SentryExceptionFactory sentryExceptionFactory;

private final @Nullable SecureRandom random;

public AnrV2EventProcessor(
final @NotNull Context context,
final @NotNull SentryAndroidOptions options,
final @NotNull BuildInfoProvider buildInfoProvider) {
this(context, options, buildInfoProvider, null);
}

AnrV2EventProcessor(
final @NotNull Context context,
final @NotNull SentryAndroidOptions options,
final @NotNull BuildInfoProvider buildInfoProvider,
final @Nullable SecureRandom random) {
this.context = context;
this.options = options;
this.buildInfoProvider = buildInfoProvider;
this.random = random;

final SentryStackTraceFactory sentryStackTraceFactory =
new SentryStackTraceFactory(this.options);
Expand Down Expand Up @@ -151,6 +167,72 @@ private void backfillScope(final @NotNull SentryEvent event, final @NotNull Obje
setFingerprints(event, hint);
setLevel(event);
setTrace(event);
setReplayId(event);
}

private boolean sampleReplay(final @NotNull SentryEvent event) {
final @Nullable String replayErrorSampleRate =
PersistingOptionsObserver.read(options, REPLAY_ERROR_SAMPLE_RATE_FILENAME, String.class);

if (replayErrorSampleRate == null) {
return false;
}

try {
// we have to sample here with the old sample rate, because it may change between app launches
final @NotNull SecureRandom random = this.random != null ? this.random : new SecureRandom();
final double replayErrorSampleRateDouble = Double.parseDouble(replayErrorSampleRate);
if (replayErrorSampleRateDouble < random.nextDouble()) {
options
.getLogger()
.log(
SentryLevel.DEBUG,
"Not capturing replay for ANR %s due to not being sampled.",
event.getEventId());
return false;
}
} catch (Throwable e) {
options.getLogger().log(SentryLevel.ERROR, "Error parsing replay sample rate.", e);
return false;
}

return true;
}

private void setReplayId(final @NotNull SentryEvent event) {
@Nullable
String persistedReplayId = PersistingScopeObserver.read(options, REPLAY_FILENAME, String.class);
final @NotNull File replayFolder =
new File(options.getCacheDirPath(), "replay_" + persistedReplayId);
if (!replayFolder.exists()) {
if (!sampleReplay(event)) {
return;
}
// if the replay folder does not exist (e.g. running in buffer mode), we need to find the
// latest replay folder that was modified before the ANR event.
persistedReplayId = null;
long lastModified = Long.MIN_VALUE;
final File[] dirs = new File(options.getCacheDirPath()).listFiles();
if (dirs != null) {
for (File dir : dirs) {
if (dir.isDirectory() && dir.getName().startsWith("replay_")) {
if (dir.lastModified() > lastModified
&& dir.lastModified() <= event.getTimestamp().getTime()) {
lastModified = dir.lastModified();
persistedReplayId = dir.getName().substring("replay_".length());
}
}
}
}
}

if (persistedReplayId == null) {
return;
}

// store the relevant replayId so ReplayIntegration can pick it up and finalize that replay
PersistingScopeObserver.store(options, persistedReplayId, REPLAY_FILENAME);
event.getContexts().put(REPLAY_ID, persistedReplayId);
}

private void setTrace(final @NotNull SentryEvent event) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ import io.sentry.SentryEvent
import io.sentry.SentryLevel
import io.sentry.SentryLevel.DEBUG
import io.sentry.SpanContext
import io.sentry.cache.PersistingOptionsObserver
import io.sentry.cache.PersistingOptionsObserver.DIST_FILENAME
import io.sentry.cache.PersistingOptionsObserver.ENVIRONMENT_FILENAME
import io.sentry.cache.PersistingOptionsObserver.OPTIONS_CACHE
import io.sentry.cache.PersistingOptionsObserver.PROGUARD_UUID_FILENAME
import io.sentry.cache.PersistingOptionsObserver.RELEASE_FILENAME
import io.sentry.cache.PersistingOptionsObserver.REPLAY_ERROR_SAMPLE_RATE_FILENAME
import io.sentry.cache.PersistingOptionsObserver.SDK_VERSION_FILENAME
import io.sentry.cache.PersistingScopeObserver
import io.sentry.cache.PersistingScopeObserver.BREADCRUMBS_FILENAME
import io.sentry.cache.PersistingScopeObserver.CONTEXTS_FILENAME
import io.sentry.cache.PersistingScopeObserver.EXTRAS_FILENAME
import io.sentry.cache.PersistingScopeObserver.FINGERPRINT_FILENAME
import io.sentry.cache.PersistingScopeObserver.LEVEL_FILENAME
import io.sentry.cache.PersistingScopeObserver.REPLAY_FILENAME
import io.sentry.cache.PersistingScopeObserver.REQUEST_FILENAME
import io.sentry.cache.PersistingScopeObserver.SCOPE_CACHE
import io.sentry.cache.PersistingScopeObserver.TAGS_FILENAME
Expand All @@ -44,6 +46,7 @@ import io.sentry.protocol.OperatingSystem
import io.sentry.protocol.Request
import io.sentry.protocol.Response
import io.sentry.protocol.SdkVersion
import io.sentry.protocol.SentryId
import io.sentry.protocol.SentryStackFrame
import io.sentry.protocol.SentryStackTrace
import io.sentry.protocol.SentryThread
Expand Down Expand Up @@ -75,7 +78,9 @@ class AnrV2EventProcessorTest {
val tmpDir = TemporaryFolder()

class Fixture {

companion object {
const val REPLAY_ID = "64cf554cc8d74c6eafa3e08b7c984f6d"
}
val buildInfo = mock<BuildInfoProvider>()
lateinit var context: Context
val options = SentryAndroidOptions().apply {
Expand All @@ -87,7 +92,8 @@ class AnrV2EventProcessorTest {
dir: TemporaryFolder,
currentSdk: Int = Build.VERSION_CODES.LOLLIPOP,
populateScopeCache: Boolean = false,
populateOptionsCache: Boolean = false
populateOptionsCache: Boolean = false,
replayErrorSampleRate: Double? = null
): AnrV2EventProcessor {
options.cacheDirPath = dir.newFolder().absolutePath
options.environment = "release"
Expand Down Expand Up @@ -118,6 +124,7 @@ class AnrV2EventProcessorTest {
REQUEST_FILENAME,
Request().apply { url = "google.com"; method = "GET" }
)
persistScope(REPLAY_FILENAME, SentryId(REPLAY_ID))
}

if (populateOptionsCache) {
Expand All @@ -126,7 +133,10 @@ class AnrV2EventProcessorTest {
persistOptions(SDK_VERSION_FILENAME, SdkVersion("sentry.java.android", "6.15.0"))
persistOptions(DIST_FILENAME, "232")
persistOptions(ENVIRONMENT_FILENAME, "debug")
persistOptions(PersistingOptionsObserver.TAGS_FILENAME, mapOf("option" to "tag"))
persistOptions(TAGS_FILENAME, mapOf("option" to "tag"))
replayErrorSampleRate?.let {
persistOptions(REPLAY_ERROR_SAMPLE_RATE_FILENAME, it.toString())
}
}

return AnrV2EventProcessor(context, options, buildInfo)
Expand Down Expand Up @@ -544,6 +554,65 @@ class AnrV2EventProcessorTest {
assertEquals(listOf("{{ default }}", "foreground-anr"), processedForeground.fingerprints)
}

@Test
fun `sets replayId when replay folder exists`() {
val hint = HintUtils.createWithTypeCheckHint(BackfillableHint())
val processor = fixture.getSut(tmpDir, populateScopeCache = true)
val replayFolder = File(fixture.options.cacheDirPath, "replay_${Fixture.REPLAY_ID}").also { it.mkdirs() }

val processed = processor.process(SentryEvent(), hint)!!

assertEquals(Fixture.REPLAY_ID, processed.contexts[Contexts.REPLAY_ID].toString())
}

@Test
fun `does not set replayId when replay folder does not exist and no sample rate persisted`() {
val hint = HintUtils.createWithTypeCheckHint(BackfillableHint())
val processor = fixture.getSut(tmpDir, populateScopeCache = true)
val replayId1 = SentryId()
val replayId2 = SentryId()

val replayFolder1 = File(fixture.options.cacheDirPath, "replay_$replayId1").also { it.mkdirs() }
val replayFolder2 = File(fixture.options.cacheDirPath, "replay_$replayId2").also { it.mkdirs() }

val processed = processor.process(SentryEvent(), hint)!!

assertNull(processed.contexts[Contexts.REPLAY_ID])
}

@Test
fun `does not set replayId when replay folder does not exist and not sampled`() {
val hint = HintUtils.createWithTypeCheckHint(BackfillableHint())
val processor = fixture.getSut(tmpDir, populateScopeCache = true, populateOptionsCache = true, replayErrorSampleRate = 0.0)
val replayId1 = SentryId()
val replayId2 = SentryId()

val replayFolder1 = File(fixture.options.cacheDirPath, "replay_$replayId1").also { it.mkdirs() }
val replayFolder2 = File(fixture.options.cacheDirPath, "replay_$replayId2").also { it.mkdirs() }

val processed = processor.process(SentryEvent(), hint)!!

assertNull(processed.contexts[Contexts.REPLAY_ID])
}

@Test
fun `set replayId of the last modified folder`() {
val hint = HintUtils.createWithTypeCheckHint(BackfillableHint())
val processor = fixture.getSut(tmpDir, populateScopeCache = true, populateOptionsCache = true, replayErrorSampleRate = 1.0)
val replayId1 = SentryId()
val replayId2 = SentryId()

val replayFolder1 = File(fixture.options.cacheDirPath, "replay_$replayId1").also { it.mkdirs() }
val replayFolder2 = File(fixture.options.cacheDirPath, "replay_$replayId2").also { it.mkdirs() }
replayFolder1.setLastModified(1000)
replayFolder2.setLastModified(500)

val processed = processor.process(SentryEvent(), hint)!!

assertEquals(replayId1.toString(), processed.contexts[Contexts.REPLAY_ID].toString())
assertEquals(replayId1.toString(), PersistingScopeObserver.read(fixture.options, REPLAY_FILENAME, String::class.java))
}

private fun processEvent(
hint: Hint,
populateScopeCache: Boolean = false,
Expand Down
9 changes: 7 additions & 2 deletions sentry-android-replay/api/sentry-android-replay.api
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,25 @@ public abstract interface class io/sentry/android/replay/Recorder : java/io/Clos
}

public final class io/sentry/android/replay/ReplayCache : java/io/Closeable {
public static final field Companion Lio/sentry/android/replay/ReplayCache$Companion;
public fun <init> (Lio/sentry/SentryOptions;Lio/sentry/protocol/SentryId;Lio/sentry/android/replay/ScreenshotRecorderConfig;)V
public final fun addFrame (Ljava/io/File;J)V
public fun close ()V
public final fun createVideoOf (JJIIILjava/io/File;)Lio/sentry/android/replay/GeneratedVideo;
public static synthetic fun createVideoOf$default (Lio/sentry/android/replay/ReplayCache;JJIIILjava/io/File;ILjava/lang/Object;)Lio/sentry/android/replay/GeneratedVideo;
public final fun persistSegmentValues (Ljava/lang/String;Ljava/lang/String;)V
public final fun rotate (J)V
}

public final class io/sentry/android/replay/ReplayCache$Companion {
public final fun makeReplayCacheDir (Lio/sentry/SentryOptions;Lio/sentry/protocol/SentryId;)Ljava/io/File;
}

public final class io/sentry/android/replay/ReplayIntegration : android/content/ComponentCallbacks, io/sentry/Integration, io/sentry/ReplayController, io/sentry/android/replay/ScreenshotRecorderCallback, io/sentry/android/replay/TouchRecorderCallback, java/io/Closeable {
public fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;)V
public fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V
public synthetic fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun captureReplay (Ljava/lang/Boolean;)V
public fun close ()V
public fun getBreadcrumbConverter ()Lio/sentry/ReplayBreadcrumbConverter;
public final fun getReplayCacheDir ()Ljava/io/File;
Expand All @@ -59,8 +66,6 @@ public final class io/sentry/android/replay/ReplayIntegration : android/content/
public fun pause ()V
public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V
public fun resume ()V
public fun sendReplay (Ljava/lang/Boolean;Ljava/lang/String;Lio/sentry/Hint;)V
public fun sendReplayForEvent (Lio/sentry/SentryEvent;Lio/sentry/Hint;)V
public fun setBreadcrumbConverter (Lio/sentry/ReplayBreadcrumbConverter;)V
public fun start ()V
public fun stop ()V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,23 @@ public open class DefaultReplayBreadcrumbConverter : ReplayBreadcrumbConverter {

private fun Breadcrumb.toRRWebSpanEvent(): RRWebSpanEvent {
val breadcrumb = this
val httpStartTimestamp = breadcrumb.data[SpanDataConvention.HTTP_START_TIMESTAMP]
val httpEndTimestamp = breadcrumb.data[SpanDataConvention.HTTP_END_TIMESTAMP]
return RRWebSpanEvent().apply {
timestamp = breadcrumb.timestamp.time
op = "resource.http"
description = breadcrumb.data["url"] as String
startTimestamp =
(breadcrumb.data[SpanDataConvention.HTTP_START_TIMESTAMP] as Long) / 1000.0
endTimestamp =
(breadcrumb.data[SpanDataConvention.HTTP_END_TIMESTAMP] as Long) / 1000.0
// can be double if it was serialized to disk
startTimestamp = if (httpStartTimestamp is Double) {
httpStartTimestamp / 1000.0
} else {
(httpStartTimestamp as Long) / 1000.0
}
endTimestamp = if (httpEndTimestamp is Double) {
httpEndTimestamp / 1000.0
} else {
(httpEndTimestamp as Long) / 1000.0
}

val breadcrumbData = mutableMapOf<String, Any?>()
for ((key, value) in breadcrumb.data) {
Expand Down
Loading
Loading