diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index bceb40abb3..e58398dd0f 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -226,6 +226,7 @@ export default () => { User Interface Profiles + Replays Metrics Check-Ins Sessions diff --git a/src/docs/sdk/envelopes.mdx b/src/docs/sdk/envelopes.mdx index 0e816c83c8..4974ce8f97 100644 --- a/src/docs/sdk/envelopes.mdx +++ b/src/docs/sdk/envelopes.mdx @@ -537,6 +537,48 @@ details. *None* +### Replay Event + +Item type `"replay_event"` contains a replay payload encoded in JSON. + +See the replays documentation for the payload +details. + +**Constraints:** + +- This Item may occur at most once per Envelope. +- This Item must be sent with a Replay Recording Item. + +**Envelope Headers:** + +*None* + +**Additional Item Headers:** + +*None* + +### Replay Recording + +Item type `"replay_recording"` contains a replay recording payload encoded in JSON *or* a gzipped JSON. + +See the replays documentation for the payload +details. + +**Constraints:** + +- This Item may occur at most once per Envelope. +- This Item must be sent with a Replay Recording Item. + +**Envelope Headers:** + +*None* + +**Additional Item Headers:** + +`length` + +: **integer, required.** The size of the Replay recording payload + ### Profile Item type `"profile"`. This Item contains a profile payload encoded diff --git a/src/docs/sdk/event-payloads/replay-recording.mdx b/src/docs/sdk/event-payloads/replay-recording.mdx new file mode 100644 index 0000000000..19f7617bf3 --- /dev/null +++ b/src/docs/sdk/event-payloads/replay-recording.mdx @@ -0,0 +1,206 @@ +--- +title: Replay Recordings +--- + +A replay recording consists of a list of instructions for the replayer to play back for the viewer. This page aims to document the custom events that are used on top of the events provided by [rrweb](https://github.com/rrweb-io/rrweb), the recording library that powers Session Replay. + +The recording event is an object with the following required properties: + +`type` + +: **EventTypeEnum** The `type` describes what type of event it is. See the enum below. + +``` +EventTypeEnum { + DomContentLoaded, + Load, + FullSnapshot, + IncrementalSnapshot, + Meta, + Custom, + Plugin, +} +``` + +`timestamp` + +: **float** The timestamp (in milliseconds) at which the event occurs + +`data` + +: **unknown** `data` schema is dependent on the `type`. View the [base types](https://github.com/rrweb-io/rrweb/blob/master/packages/types/src/index.ts) or Sentry's [custom types](https://github.com/getsentry/sentry-javascript/blob/master/packages/replay-internal/src/types/replayFrame.ts). + +## Custom Events + +Sentry custom events augment the replay with additional context in order to help the user debug issues. Custom events have the following format for the `data` property of the event: + +`tag` + +: **string** The tag is used to describe the "type" of custom event. This will determine how the event gets used in the UI. + +`payload` + +: **object** The `payload` is similar to the upper level `data` attribute, whose schema is dependent on the `tag`. + +In addition to the TypeScript references linked above, the following sections will describe the expected `payload` for custom events. + +### options + +This recording event is used to record the SDK configuration options. This should only be sent on the first segment as we do not expect the options to change throughout the lifecycle of a replay. See [this issue](https://github.com/getsentry/sentry-javascript/issues/7140) for more context around the decision to send this as a recording event rather than part of the `"replay_event"`. Note that these options are generally used internally to debug rather than a customer-facing feature (e.g. there is no way to query/filter replays using these options). + +The payload for `"options"` is a record of configuration name -> configuration value. There is no defined schema as options can vary by SDKs. + +```json +{ + "type": 5, + "timestamp": 1709218280301, + "data": { + "tag": "options", + "payload": { + "shouldRecordCanvas": false, + "sessionSampleRate": 1, + "errorSampleRate": 1, + "useCompressionOption": true, + "blockAllMedia": false, + "maskAllText": false, + "maskAllInputs": false, + "useCompression": false, + "networkDetailHasUrls": false, + "networkCaptureBodies": true, + "networkRequestHasHeaders": true, + "networkResponseHasHeaders": true + } + } +} +``` + +### breadcrumb + +Breadcrumbs are named as such because they are intercepted from the web SDK and forwarded as a custom recording event. They have the same structure as describe in breadcrumbs interface. + +```json +{ + "type": 5, + "timestamp": 1710853999781, + "data": { + "tag": "breadcrumb", + "payload": { + "timestamp": 1710853999.781, + "type": "default", + "category": "navigation", + "data": { + "from": "/issues/4893218638/", + "to": "/performance/trace/b6e75da452bf40ee8330af41c5989545/" + } + } + } +} +``` + +### performanceSpan + +Similar to breadcrumbs, `"performanceSpan"` has a similar interface to Spans. The following is an example of a network request made in the browser. We have additional instrumentation to capture request and response payloads when configured to do so. + +```json +{ + "type": 5, + "timestamp": 1710854008431, + "data": { + "tag": "performanceSpan", + "payload": { + "op": "resource.fetch", + "description": "https://us.sentry.io/api/0/projects/foo/javascript/events/eea92bc02315448591e159d8138ef3e8/owners/", + "startTimestamp": 1710854008.431, + "endTimestamp": 1710854009.234, + "data": { + "method": "GET", + "statusCode": 200, + "request": { + "headers": { + "content-type": "application/json", + "accept": "application/json; charset=utf-8", + "sentry-trace": "b07d0e356aa1477bb279d9fe5680fcd0-83881f299ca64b4f-1" + } + }, + "response": { + "headers": { + "content-length": "36", + "content-type": "application/json" + }, + "size": 36, + "body": { + "owners": [], + "rule": null, + "rules": [] + } + } + } + } + } +}, + +``` + +## Mobile Replay Events + +Mobile replays are captured as video rather than a series of mutations. In order to support mobile replays, the following custom event is required: + +| property | type | description | +| ---------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------ | +| type | `5` | Custom event | +| data.tag | `"video"` | The string `"video"` | +| data.payload.duration | integer | The video duration in milliseconds | +| data.payload.segmentId | integer | The segment id. This should be the same as the replay segment id. This will be used by the UI to fetch the video from the Sentry API | + +```json +{ + "type": 5, + "timestamp": 1710854008431, + "data": { + "tag": "video", + "payload": { + "duration": 5000, + "segmentId": 0 + } + } +} +``` + +### Additional Events + +Mobile replays follow the same rrweb protocol, but because mobile replays are captured by video, a majority of the rrweb events do not apply. Outlined below are the ones that do. + +#### Meta Event + +This should be emitted at the start of a replay (i.e. on the first segment) and when the user's viewport dimensions change. + +| property | type | description | +| ----------- | ------- | ---------------------------------- | +| type | `4` | "Meta" event | +| data.href | string | The current URI | +| data.width | integer | The width of the current viewport | +| data.height | integer | The height of the current viewport | + +```json +{ + "type": 4, + "timestamp": 1710854008431, + "data": { + "href": "file://screen", + "width": 360, + "height": 800 + } +} +``` + +#### Touch Interactions + +| property | type | description | +| ---------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| type | `3` | "Incremental mutation" event | +| data.source | `2` | "Mouse Interaction" | +| data.type | integer | [MouseInteraction](https://github.com/getsentry/rrweb/blob/0722035e75a9aeedd716107cbcc949eba6da2a6a/packages/types/src/index.ts#L361-L373) enum. TouchStart = 7, TouchMove_Departed = 8, TouchEnd = 9, TouchCancel = 10 | +| data.id | integer | Unique id of the event | +| data.x | integer | The x-coordinate of the touch event | +| data.y | integer | The y-coordinate of the touch event | +| data.pointerType | `2` | [PointerTypes](https://github.com/getsentry/rrweb/blob/0722035e75a9aeedd716107cbcc949eba6da2a6a/packages/types/src/index.ts#L375-L379) enum. Touch = 2 | diff --git a/src/docs/sdk/replays.mdx b/src/docs/sdk/replays.mdx new file mode 100644 index 0000000000..13c5ddcaf6 --- /dev/null +++ b/src/docs/sdk/replays.mdx @@ -0,0 +1,174 @@ +--- +title: Replays +sidebar_order: 20 +--- + +This document defines the format used by Sentry to ingest replays. This +document is meant for Sentry SDK developers and maintainers of the Session Replay +ingestion pipeline. + +A `"replay_event"` Item should always be sent with a `"replay_recording"` Item +in the same envelope. + +## `"replay_event"` Item + +### Replay Attributes + +The following attributes are specific to the `"replay_event"` Item type. + +| Key | Type | Description | +| ---------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| type | `"replay_event"` | Must be `"replay_event"` | +| replay_id | string | A unique ID for the replay. Follows the [same requirements](https://develop.sentry.dev/sdk/event-payloads/#required-attributes) as an `event_id`: Hexadecimal string representing a uuid4 value. The length is exactly 32 characters. Dashes are not allowed. Has to be lowercase. | +| replay_type | `"session"` \| `"buffer"` | Describes the type of replay. `buffer` means the replay is buffered while waiting for an error to occur or until a manual flush. `session` means the replay starts recording immediately and continues through the lifespan of the replay session. | +| segment_id | number | The segment id. | +| replay_start_timestamp | number | UNIX timestamp of the start of the replay (in seconds). This is only required on the first segment. | +| urls | list[string] | List of URLs in order of visitation. | +| trace_ids | list[string] | List of trace ids that occurred during the replay. | +| error_ids | list[string] | **DEPRECATED ** | + +### Event Attributes + +The following attributes are a subset of the [optional attributes](https://develop.sentry.dev/sdk/event-payloads/#optional-attributes) of an Event. + +| Key | Type | Description | +| -------------------------- | ------ | ------------------------------------------------ | +| timestamp | number | UNIX timestamp of the current time (in seconds). | +| event_id | string | This should be the same as `replay_id` | +| dist | string | - | +| platform | string | - | +| environment | string | - | +| release | string | - | +| user.id | string | - | +| user.username | string | - | +| user.email | string | - | +| user.ip_address | string | - | +| sdk.name | string | - | +| sdk.version | string | - | +| request.url | string | - | +| request.headers.User-Agent | string | - | + +### Example + +```json +{ + "type": "replay_event", + "replay_id": "36b75d9fa11f45459412a96c41bdf691", + "replay_start_timestamp": 1710861499.287, + "replay_type": "session", + "segment_id": 0, + "trace_ids": ["905aef2282af5fe2ab2c93aa7a340521"], + "urls": [ + "https://sentry.io/issues/", + "https://sentry.io/issues/?project=0&statsPeriod=7d&utc=true" + ], + + "request": { + "url": "https://sentry.io/issues/?project=0&statsPeriod=7d&utc=true", + "headers": { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" + } + }, + "timestamp": 1710861507.579, + "event_id": "36b75d9fa11f45459412a96c41bdf691", + "environment": "prod", + "release": "frontend@f00", + "sdk": { + "integrations": ["BrowserTracing", "BrowserProfiling", "Replay", "ReplayCanvas"], + "name": "sentry.javascript.react", + "version": "7.105.0" + }, + "tags": { + "sentry_version": "24.4.0.dev0" + }, + "user": { + "ip_address": "127.0.0.1", + "email": "admin@sentry.io", + "id": 1, + "name": "Admin" + }, + "contexts": {"organization": {"id": "0", "slug": "sentry"}}, + "platform": "javascript" +} +``` + +## `"replay_recording"` Item + +The `"replay_recording"` item consists of two sub-items that are delimited by newlines. The first is a JSON of the replay recording's metadata. Currently, only `segment_id` is required. + +```json +{"segment_id": 0} +``` + +The other sub-item is the replay recording's instructions set. This payload should be gzipped, but uncompressed payloads are also accepted. Read more about replay recording events here. + +```json +[ + { + "type": 5, + "timestamp": 1710861507579, + "data": {} + } +] +``` + +### Example + +``` +{"segment_id": 0} +\x00\x00\x00\x14ftypqt \x00\x00\x00\x00qt \x00\x00\x00\x08wide\x03\xbdd\x11mdat +``` + +## Full Envelope Example + +```json +{"event_id":"36b75d9fa11f45459412a96c41bdf691","sent_at":"2024-03-19T15:18:27.581Z","sdk":{"name":"sentry.javascript.react","version":"7.105.0"}} +{"type":"replay_event"} +{ + "type": "replay_event", + "replay_id": "36b75d9fa11f45459412a96c41bdf691", + "replay_start_timestamp": 1710861499.287, + "replay_type": "session", + "segment_id": 0, + "trace_ids": ["905aef2282af5fe2ab2c93aa7a340521"], + "urls": [ + "https://sentry.io/issues/", + "https://sentry.io/issues/?project=0&statsPeriod=7d&utc=true" + ], + + "request": { + "url": "https://sentry.io/issues/?project=0&statsPeriod=7d&utc=true", + "headers": { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" + } + }, + "timestamp": 1710861507.579, + "event_id": "36b75d9fa11f45459412a96c41bdf691", + "environment": "prod", + "release": "frontend@f00", + "sdk": { + "integrations": [ + "BrowserTracing", + "BrowserProfiling", + "Replay", + "ReplayCanvas" + ], + "name": "sentry.javascript.react", + "version": "7.105.0" + }, + "tags": { + "sentry_version": "24.4.0.dev0", + }, + "user": { + "ip_address": "127.0.0.1", + "email": "admin@sentry.io", + "id": 1, + "name": "Admin" + }, + "contexts": { "organization": { "id": "0", "slug": "sentry" } }, + "platform": "javascript" +} +{"type":"replay_recording","length":141666} +{"segment_id":0} +/* gzipped JSON payload */ +```