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

feat(replay): Capture hydration error breadcrumb #9759

Merged
merged 13 commits into from
Dec 12, 2023
3 changes: 3 additions & 0 deletions packages/replay/src/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ export class Replay implements Integration {
mutationBreadcrumbLimit = 750,
mutationLimit = 10_000,

hydrationBreadcrumbLimit = 1,

slowClickTimeout = 7_000,
slowClickIgnoreSelectors = [],

Expand Down Expand Up @@ -171,6 +173,7 @@ export class Replay implements Integration {
maskAllText,
mutationBreadcrumbLimit,
mutationLimit,
hydrationBreadcrumbLimit,
slowClickTimeout,
slowClickIgnoreSelectors,
networkDetailAllowUrls,
Expand Down
37 changes: 36 additions & 1 deletion packages/replay/src/replay.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable max-lines */ // TODO: We might want to split this file up
import { EventType, record } from '@sentry-internal/rrweb';
import { captureException, getClient, getCurrentHub } from '@sentry/core';
import type { ReplayRecordingMode, Transaction } from '@sentry/types';
import type { Event as SentryEvent, ReplayRecordingMode, Transaction } from '@sentry/types';
import { logger } from '@sentry/utils';

import {
Expand Down Expand Up @@ -138,6 +138,8 @@ export class ReplayContainer implements ReplayContainerInterface {

private _context: InternalEventContext;

private _hydrationBreadcrumbCount: number;

public constructor({
options,
recordingOptions,
Expand All @@ -164,6 +166,7 @@ export class ReplayContainer implements ReplayContainerInterface {
initialTimestamp: Date.now(),
initialUrl: '',
};
this._hydrationBreadcrumbCount = 0;

this._recordingOptions = recordingOptions;
this._options = options;
Expand Down Expand Up @@ -830,6 +833,10 @@ export class ReplayContainer implements ReplayContainerInterface {
}

this._performanceCleanupCallback = setupPerformanceObserver(this);

const client = getClient();
// Start listening for errors that occur in the core SDK
client && client.on && client.on('beforeSendEvent', this._handleSentryEvent);
scttcper marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down Expand Up @@ -1244,4 +1251,32 @@ export class ReplayContainer implements ReplayContainerInterface {
// `true` means we use the regular mutation handling by rrweb
return true;
};

private _handleSentryEvent = (event: SentryEvent): void => {
const exceptionValue = event.exception && event.exception.values && event.exception.values[0].value;
if (typeof exceptionValue !== 'string') {
return;
}

const hydrationBreadcrumbLimit = this._options.hydrationBreadcrumbLimit;
const overHydrationBreadcrumbLimit =
hydrationBreadcrumbLimit && this._hydrationBreadcrumbCount > hydrationBreadcrumbLimit;

if (
!overHydrationBreadcrumbLimit &&
// Only matches errors in production builds of react-dom
// Example https://reactjs.org/docs/error-decoder.html?invariant=423
(exceptionValue.match(/reactjs\.org\/docs\/error-decoder\.html\?invariant=(418|419|422|423|425)/) ||
// Development builds of react-dom
// Example Text content did not match. Server: "A" Client: "B"
exceptionValue.match(/(hydration|content does not match|did not match)/i))
) {
const breadcrumb = createBreadcrumb({
category: 'replay.hydrate',
data: {},
});
this._createCustomBreadcrumb(breadcrumb);
this._hydrationBreadcrumbCount++;
}
};
}
5 changes: 5 additions & 0 deletions packages/replay/src/types/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,11 @@ export interface ReplayPluginOptions extends ReplayNetworkOptions {
*/
mutationBreadcrumbLimit: number;

/**
* Maximum number of breadcrumbs to record for hydration error events.
*/
hydrationBreadcrumbLimit: number;

/**
* A high number of DOM mutations (in a single event loop) can cause
* performance regressions in end-users' browsers. This setting will cause
Expand Down