Skip to content

Conversation

@43jay
Copy link
Collaborator

@43jay 43jay commented Oct 9, 2025

📜 Description

Introduce new data classes (io.sentry.util.network pkg), following format of the javascript SDK.

SentryOkHttpInterceptor uses NetworkDetailCaptureUtils to construct these 'Network Details' data classes for insertion into the Breadcrumb Hint (name="sentry:replayNetworkDetails")).

Initalise DefaultReplayBreadcrumbConverter as the SDK-wide BeforeBreadcrumbCallback in order to capture the Network Details data from the Breadcrumb hint (placed there by SentryOkHttpInterceptor).

Later when converting Breadcrumb to RRWebSpanEvent, DefaultReplayBreadcrumbConverter looks up captured Network Details data for insertion into the replay performanceSpan when creating the replay segment.

Implementation

Current impl handles JSON, UrlFormEncoded, skips these binary content types, and falls back to raw String as possible. Notably, it does not handle xml bodies (treats them as plaintext).

Follow-ups

  1. networkDetail* SDK Options flags Will publish SDK Options in a Future PR along with changelog #skip-changelog

  2. handle case where SentryOkHttpEventListener handles http request instrumentation Will handle in a follow-up PR

  3. More unit tests after addressing the PR feedback, e.g. SentryOkHttpInterceptor, AndroidOptionsInitializer, NetworkBodyParser (at least the json part)

💡 Motivation and Context

Part of [Mobile Replay] Capture Network request/response bodies

Initially, we were trying to keep SDK changes simple and re-use the existing OKHTTP_REQUEST|RESPONSE hint data.
However, the :sentry-android-replay gradle module doesn't compile against any of the http libs (makes sense).

Because these okhttp3.Request, etc types don't exist in :sentry-android-replay, replay accesses the NetworkDetails data via an abstracted network details data object ("sentry:replayNetworkDetails") on the Breadcrumb. When the SDKOptions constraints have been met.

💚 How did you test it?

  1. DefaultReplayBreadcrumbConverterTest unit tests:

./gradlew clean :sentry-android-replay:testDebugUnitTest --no-build-cache --rerun-tasks --tests="DefaultReplayBreadcrumbConverterTest"

  1. Manually creating replay sessions:

this replay session is an example recording with no networkDetail* SDK options enabled
(replay sessions below were captured without commit 4bc5b7714).

recording segment data shows sample replay payload .

replay session respects networkDetail[Request|Response]Headers

https://sentry-sdks.sentry.io/explore/replays/f89535ea0e79499ca8d75e34e6270925

image image
replay session captures GET request bodies as null

replay session
image

replay session captures JSON bodies

1. Request body formatted as JSON key/values
ref
image

2. Response body formatted as JSON key/values
ref
image

replay session captures plaintext bodies

replay session
image

replay session captures form bodies (formurlencoded)

replay session
image

replay session captures binary bodies

1. binary body under size limit (summarized body content

https://sentry-sdks.sentry.io/explore/replays/830c3fa5675846b09575ed9ba06a6416
image

2. binary body over size limit (invalid response size, summarized body content
https://sentry-sdks.sentry.io/explore/replays/830c3fa5675846b09575ed9ba06a6416
image

replay session truncates plaintext bodies over 150KB (`MAX_NETWORK_BODY_SIZE`)

https://sentry-sdks.sentry.io/explore/replays/830c3fa5675846b09575ed9ba06a6416
image

replay sessions properly capture one-shot okhttp request bodies

One-shot HTTP Request Body request
image

Test: read(..) okhttp3.Request.body in an OkHttpInterceptor after cloning body into new request causes the request to succeed ✅ (current impl)

replay session

Valid response body logged to sentry:
image

Sanity Test: read(..) okhttp3.Request.body in an OkHttpInterceptor without cloning body into a new request causes the request to fail ✅

replay session

Invalid response body logged to sentry:
image

@linear
Copy link

linear bot commented Oct 9, 2025

@43jay 43jay marked this pull request as draft October 9, 2025 21:28
cursor[bot]

This comment was marked as outdated.

43jay added 4 commits October 13, 2025 10:52
added some unit tests:
 ./gradlew :sentry-android-replay:testDebugUnitTest --tests="*DefaultReplayBreadcrumbConverterTest*"
Breadcrumb.java has several timestamp fields:
`timestamp: Date`, `timestampMs: Long`, `nanos: Long`

`hashcode` was relying solely on `timestamp`, which can be null depending on which constructor was used.

=> Change to use getTimestamp as 1. this is what equals does (consistency) 2. getTimestamp initialises timestamp if null.
@43jay 43jay changed the title RFF(replay): Adding OkHttp Request/Response bodies for sentry-java replay(feature): Adding OkHttp Request/Response bodies for sentry-java Oct 24, 2025
@43jay 43jay marked this pull request as ready for review October 24, 2025 15:50
cursor[bot]

This comment was marked as outdated.

Entrypoint is NetworkDetailCaptureUtils (initializeForUrl) called from SentryOkHttpInterceptor
- common logic to handle checking sdk options.
- Accept data from http client via NetworkBodyExtractor, NetworkHeaderExtractor interfaces
that can be reused in future (if needed)

Placeholder impl for req/resp bodies.

From https://docs.sentry.io/platforms/javascript/session-replay/configuration/
- networkDetailAllowUrls, networkDetailDenyUrls,
- networkCaptureBodies
- networkRequestHeaders, networkResponseHeaders

These SDKOptions don't exist yet => impl acts as if they do, but have not been enabled.
Copy link
Member

@romtsn romtsn left a comment

Choose a reason for hiding this comment

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

LGTM! I guess we don't need a changelog entry yet because we don't expose the options to enable capturing req/resp bodies.

Still needs to rebase with main and the tests that I mentioned would be nice (but can be done in a separate PR too)

@github-actions
Copy link
Contributor

github-actions bot commented Nov 7, 2025

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

Generated by 🚫 dangerJS against 71c1f93

@github-actions
Copy link
Contributor

github-actions bot commented Nov 7, 2025

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 341.15 ms 378.86 ms 37.71 ms
Size 1.58 MiB 2.12 MiB 553.02 KiB

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
fcec2f2 328.91 ms 387.75 ms 58.84 ms
ee747ae 382.73 ms 435.41 ms 52.68 ms
7314dbe 437.83 ms 505.64 ms 67.81 ms
ee747ae 386.94 ms 431.43 ms 44.49 ms
27d7cf8 309.43 ms 364.27 ms 54.85 ms
b77456b 393.26 ms 441.10 ms 47.84 ms
17a0955 372.53 ms 446.70 ms 74.17 ms
27d7cf8 314.17 ms 347.00 ms 32.83 ms
b750b96 408.98 ms 480.32 ms 71.34 ms
d217708 355.34 ms 381.39 ms 26.05 ms

App size

Revision Plain With Sentry Diff
fcec2f2 1.58 MiB 2.12 MiB 551.50 KiB
ee747ae 1.58 MiB 2.10 MiB 530.95 KiB
7314dbe 1.58 MiB 2.10 MiB 533.45 KiB
ee747ae 1.58 MiB 2.10 MiB 530.95 KiB
27d7cf8 1.58 MiB 2.12 MiB 549.42 KiB
b77456b 1.58 MiB 2.12 MiB 548.11 KiB
17a0955 1.58 MiB 2.10 MiB 533.20 KiB
27d7cf8 1.58 MiB 2.12 MiB 549.42 KiB
b750b96 1.58 MiB 2.10 MiB 533.19 KiB
d217708 1.58 MiB 2.10 MiB 532.97 KiB

Previous results on branch: 43jay/MOBILE-935

Startup times

Revision Plain With Sentry Diff
790a163 339.64 ms 436.57 ms 96.94 ms

App size

Revision Plain With Sentry Diff
790a163 1.58 MiB 2.12 MiB 553.01 KiB

&& Objects.equals(a.getData("url"), b.getData("url"))
&& Objects.equals(a.getData("method"), b.getData("method"))
&& Objects.equals(a.getData("http.fragment"), b.getData("http.fragment"))
&& Objects.equals(a.getData("http.query"), b.getData("http.query"));
Copy link

Choose a reason for hiding this comment

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

Bug: Inconsistent Breadcrumb Equality Causes Data Mismatch

The httpBreadcrumbEquals method doesn't check fields like http.request_content_length and http.response_content_length, but these fields are added to HTTP breadcrumbs by SentryOkHttpInterceptor (lines 245, 249). This inconsistency means HTTP breadcrumbs with different content lengths will be considered equal when used as keys in the LinkedHashMap in DefaultReplayBreadcrumbConverter, potentially causing network details data to be associated with wrong breadcrumbs.

Fix in Cursor Fix in Web

Copy link
Collaborator Author

@43jay 43jay Nov 7, 2025

Choose a reason for hiding this comment

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

Including content_length for req/resp doesn't seem like it will add much as if everything else (url, timestamp, method) is equal likely req/resp bodies will be equal.

It could be worth adding SpanDataConvention.HTTP_START_TIMESTAMP, SpanDataConvention.HTTP_END_TIMESTAMP

but based on testing the current #equals imp is able to differentiate between 15 concurrent http requests adequately (i.e. millisecond granularity is sufficient)
https://sentry-sdks.sentry.io/explore/replays/b028d0401d424842bbc0546a217b9e61/?n_detail_row=13&n_detail_tab=response&project=5428559&query=&referrer=%2Fexplore%2Freplays%2F&statsPeriod=1h&t_main=network&yAxis=count%28%29

@43jay 43jay merged commit bbc35bb into main Nov 7, 2025
59 of 62 checks passed
@43jay 43jay deleted the 43jay/MOBILE-935 branch November 7, 2025 19:26
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.

4 participants