Skip to content

Commit 7d080dc

Browse files
authored
feat(replay): Add responseStatus, decodedBodySize to perf entries (#7613)
Add a few more properties to `resource` performance entries (e.g. script, img, etc). * Adds `responseStatus` which is new in Chrome 109 for status code * Adds `decodedBodySize` so that we can tell user if their resource was compressed or not.
1 parent ee59584 commit 7d080dc

File tree

12 files changed

+261
-43
lines changed

12 files changed

+261
-43
lines changed

packages/browser-integration-tests/utils/replayEventTemplates.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,16 @@ export const expectedNavigationPerformanceSpan = {
6666
startTimestamp: expect.any(Number),
6767
endTimestamp: expect.any(Number),
6868
data: {
69+
decodedBodySize: expect.any(Number),
70+
encodedBodySize: expect.any(Number),
6971
duration: expect.any(Number),
72+
domInteractive: expect.any(Number),
73+
domContentLoadedEventEnd: expect.any(Number),
74+
domContentLoadedEventStart: expect.any(Number),
75+
loadEventStart: expect.any(Number),
76+
loadEventEnd: expect.any(Number),
77+
domComplete: expect.any(Number),
78+
redirectCount: expect.any(Number),
7079
size: expect.any(Number),
7180
},
7281
};
@@ -85,7 +94,16 @@ export const expectedReloadPerformanceSpan = {
8594
startTimestamp: expect.any(Number),
8695
endTimestamp: expect.any(Number),
8796
data: {
97+
decodedBodySize: expect.any(Number),
98+
encodedBodySize: expect.any(Number),
8899
duration: expect.any(Number),
100+
domInteractive: expect.any(Number),
101+
domContentLoadedEventEnd: expect.any(Number),
102+
domContentLoadedEventStart: expect.any(Number),
103+
loadEventStart: expect.any(Number),
104+
loadEventEnd: expect.any(Number),
105+
domComplete: expect.any(Number),
106+
redirectCount: expect.any(Number),
89107
size: expect.any(Number),
90108
},
91109
};

packages/e2e-tests/test-applications/standard-frontend-react/tests/fixtures/ReplayRecordingData.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,19 @@ export const ReplayRecordingData = [
143143
description: 'http://localhost:3000/',
144144
startTimestamp: expect.any(Number),
145145
endTimestamp: expect.any(Number),
146-
data: { size: expect.any(Number), duration: expect.any(Number) },
146+
data: {
147+
decodedBodySize: expect.any(Number),
148+
encodedBodySize: expect.any(Number),
149+
duration: expect.any(Number),
150+
domInteractive: expect.any(Number),
151+
domContentLoadedEventEnd: expect.any(Number),
152+
domContentLoadedEventStart: expect.any(Number),
153+
loadEventStart: expect.any(Number),
154+
loadEventEnd: expect.any(Number),
155+
domComplete: expect.any(Number),
156+
redirectCount: expect.any(Number),
157+
size: expect.any(Number),
158+
},
147159
},
148160
},
149161
},
@@ -157,7 +169,11 @@ export const ReplayRecordingData = [
157169
description: expect.stringMatching(/http:\/\/localhost:3000\/static\/js\/main.(\w+).js/),
158170
startTimestamp: expect.any(Number),
159171
endTimestamp: expect.any(Number),
160-
data: { size: expect.any(Number), encodedBodySize: expect.any(Number) },
172+
data: {
173+
decodedBodySize: expect.any(Number),
174+
encodedBodySize: expect.any(Number),
175+
size: expect.any(Number),
176+
},
161177
},
162178
},
163179
},

packages/replay/src/coreHandlers/addNetworkBreadcrumb.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import type { ReplayContainer, ReplayPerformanceEntry } from '../types';
1+
import type { NetworkRequestData, ReplayContainer, ReplayPerformanceEntry } from '../types';
22
import { createPerformanceSpans } from '../util/createPerformanceSpans';
33
import { shouldFilterRequest } from '../util/shouldFilterRequest';
44

55
/** Add a performance entry breadcrumb */
6-
export function addNetworkBreadcrumb(replay: ReplayContainer, result: ReplayPerformanceEntry | null): void {
6+
export function addNetworkBreadcrumb(
7+
replay: ReplayContainer,
8+
result: ReplayPerformanceEntry<NetworkRequestData> | null,
9+
): void {
710
if (!replay.isEnabled()) {
811
return;
912
}

packages/replay/src/coreHandlers/handleFetch.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { HandlerDataFetch } from '@sentry/types';
22

3-
import type { ReplayContainer, ReplayPerformanceEntry } from '../types';
3+
import type { NetworkRequestData, ReplayContainer, ReplayPerformanceEntry } from '../types';
44
import { addNetworkBreadcrumb } from './addNetworkBreadcrumb';
55

66
/** only exported for tests */
7-
export function handleFetch(handlerData: HandlerDataFetch): null | ReplayPerformanceEntry {
7+
export function handleFetch(handlerData: HandlerDataFetch): null | ReplayPerformanceEntry<NetworkRequestData> {
88
const { startTimestamp, endTimestamp, fetchData, response } = handlerData;
99

1010
if (!endTimestamp) {

packages/replay/src/coreHandlers/handleHistory.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import type { ReplayContainer, ReplayPerformanceEntry } from '../types';
1+
import type { HistoryData, ReplayContainer, ReplayPerformanceEntry } from '../types';
22
import { createPerformanceSpans } from '../util/createPerformanceSpans';
33

44
interface HistoryHandlerData {
55
from: string;
66
to: string;
77
}
88

9-
function handleHistory(handlerData: HistoryHandlerData): ReplayPerformanceEntry {
9+
function handleHistory(handlerData: HistoryHandlerData): ReplayPerformanceEntry<HistoryData> {
1010
const { from, to } = handlerData;
1111

1212
const now = Date.now() / 1000;

packages/replay/src/coreHandlers/handleNetworkBreadcrumbs.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import type {
1212
} from '@sentry/types';
1313
import { addInstrumentationHandler, logger } from '@sentry/utils';
1414

15-
import type { ReplayContainer, ReplayPerformanceEntry } from '../types';
15+
import type { NetworkRequestData, ReplayContainer, ReplayPerformanceEntry } from '../types';
1616
import { addNetworkBreadcrumb } from './addNetworkBreadcrumb';
1717
import { handleFetchSpanListener } from './handleFetch';
1818
import { handleXhrSpanListener } from './handleXhr';
@@ -148,7 +148,7 @@ function _makeNetworkReplayBreadcrumb(
148148
type: string,
149149
breadcrumb: Breadcrumb & { data: FetchBreadcrumbData | XhrBreadcrumbData },
150150
hint: FetchBreadcrumbHint | XhrBreadcrumbHint,
151-
): ReplayPerformanceEntry | null {
151+
): ReplayPerformanceEntry<NetworkRequestData> | null {
152152
const { startTimestamp, endTimestamp } = hint;
153153

154154
if (!endTimestamp) {
@@ -167,7 +167,7 @@ function _makeNetworkReplayBreadcrumb(
167167
return null;
168168
}
169169

170-
const result: ReplayPerformanceEntry & { data: object } = {
170+
const result: ReplayPerformanceEntry<NetworkRequestData> = {
171171
type,
172172
start: startTimestamp / 1000,
173173
end: endTimestamp / 1000,

packages/replay/src/coreHandlers/handleXhr.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { HandlerDataXhr } from '@sentry/types';
22

3-
import type { ReplayContainer, ReplayPerformanceEntry } from '../types';
3+
import type { NetworkRequestData, ReplayContainer, ReplayPerformanceEntry } from '../types';
44
import { addNetworkBreadcrumb } from './addNetworkBreadcrumb';
55

66
/** only exported for tests */
7-
export function handleXhr(handlerData: HandlerDataXhr): ReplayPerformanceEntry | null {
7+
export function handleXhr(handlerData: HandlerDataXhr): ReplayPerformanceEntry<NetworkRequestData> | null {
88
const { startTimestamp, endTimestamp, xhr } = handlerData;
99

1010
if (!startTimestamp || !endTimestamp || !xhr.__sentry_xhr__) {

packages/replay/src/types.ts

Lines changed: 130 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,135 @@ export interface WorkerRequest {
3535
// PerformancePaintTiming and PerformanceNavigationTiming are only available with TS 4.4 and newer
3636
// Therefore, we're exporting them here to make them available in older TS versions
3737
export type PerformancePaintTiming = PerformanceEntry;
38-
export type PerformanceNavigationTiming = PerformanceEntry & {
39-
type: string;
40-
transferSize: number;
41-
domComplete: number;
38+
export type PerformanceNavigationTiming = PerformanceEntry &
39+
PerformanceResourceTiming & {
40+
type: string;
41+
transferSize: number;
42+
43+
/**
44+
* A DOMHighResTimeStamp representing the time immediately before the user agent
45+
* sets the document's readyState to "interactive".
46+
*/
47+
domInteractive: number;
48+
49+
/**
50+
* A DOMHighResTimeStamp representing the time immediately before the current
51+
* document's DOMContentLoaded event handler starts.
52+
*/
53+
domContentLoadedEventStart: number;
54+
/**
55+
* A DOMHighResTimeStamp representing the time immediately after the current
56+
* document's DOMContentLoaded event handler completes.
57+
*/
58+
domContentLoadedEventEnd: number;
59+
60+
/**
61+
* A DOMHighResTimeStamp representing the time immediately before the current
62+
* document's load event handler starts.
63+
*/
64+
loadEventStart: number;
65+
66+
/**
67+
* A DOMHighResTimeStamp representing the time immediately after the current
68+
* document's load event handler completes.
69+
*/
70+
loadEventEnd: number;
71+
72+
/**
73+
* A DOMHighResTimeStamp representing the time immediately before the user agent
74+
* sets the document's readyState to "complete".
75+
*/
76+
domComplete: number;
77+
78+
/**
79+
* A number representing the number of redirects since the last non-redirect
80+
* navigation in the current browsing context.
81+
*/
82+
redirectCount: number;
83+
};
84+
export type ExperimentalPerformanceResourceTiming = PerformanceResourceTiming & {
85+
// Experimental, see: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming/responseStatus
86+
// Requires Chrome 109
87+
responseStatus?: number;
88+
};
89+
90+
export type PaintData = undefined;
91+
92+
/**
93+
* See https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigationTiming
94+
*
95+
* Note `navigation.push` will not have any data
96+
*/
97+
export type NavigationData = Partial<
98+
Pick<
99+
PerformanceNavigationTiming,
100+
| 'decodedBodySize'
101+
| 'encodedBodySize'
102+
| 'duration'
103+
| 'domInteractive'
104+
| 'domContentLoadedEventEnd'
105+
| 'domContentLoadedEventStart'
106+
| 'loadEventStart'
107+
| 'loadEventEnd'
108+
| 'domComplete'
109+
| 'redirectCount'
110+
>
111+
> & {
112+
/**
113+
* Transfer size of resource
114+
*/
115+
size?: number;
116+
};
117+
118+
export type ResourceData = Pick<PerformanceResourceTiming, 'decodedBodySize' | 'encodedBodySize'> & {
119+
/**
120+
* Transfer size of resource
121+
*/
122+
size: number;
123+
/**
124+
* HTTP status code. Note this is experimental and not available on all browsers.
125+
*/
126+
statusCode?: number;
42127
};
128+
129+
export interface LargestContentfulPaintData {
130+
/**
131+
* Render time (in ms) of the LCP
132+
*/
133+
value: number;
134+
size: number;
135+
/**
136+
* The recording id of the LCP node. -1 if not found
137+
*/
138+
nodeId?: number;
139+
}
140+
141+
/**
142+
* Entries that come from window.performance
143+
*/
144+
export type AllPerformanceEntryData = PaintData | NavigationData | ResourceData | LargestContentfulPaintData;
145+
146+
export interface MemoryData {
147+
memory: {
148+
jsHeapSizeLimit: number;
149+
totalJSHeapSize: number;
150+
usedJSHeapSize: number;
151+
};
152+
}
153+
154+
export interface NetworkRequestData {
155+
method?: string;
156+
statusCode?: number;
157+
requestBodySize?: number;
158+
responseBodySize?: number;
159+
}
160+
161+
export interface HistoryData {
162+
previous: string;
163+
}
164+
165+
export type AllEntryData = AllPerformanceEntryData | MemoryData | NetworkRequestData | HistoryData;
166+
43167
/**
44168
* The response from the worker
45169
*/
@@ -319,7 +443,7 @@ export interface ReplayContainer {
319443
setInitialState(): void;
320444
}
321445

322-
export interface ReplayPerformanceEntry {
446+
export interface ReplayPerformanceEntry<T> {
323447
/**
324448
* One of these types https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry/entryType
325449
*/
@@ -343,5 +467,5 @@ export interface ReplayPerformanceEntry {
343467
/**
344468
* Additional unstructured data to be included
345469
*/
346-
data?: Record<string, unknown>;
470+
data: T;
347471
}

packages/replay/src/util/addMemoryEntry.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { WINDOW } from '../constants';
2-
import type { AddEventResult, ReplayContainer, ReplayPerformanceEntry } from '../types';
2+
import type { AddEventResult, MemoryData, ReplayContainer, ReplayPerformanceEntry } from '../types';
33
import { createPerformanceSpans } from './createPerformanceSpans';
44

5-
type ReplayMemoryEntry = ReplayPerformanceEntry & { data: { memory: MemoryInfo } };
5+
type ReplayMemoryEntry = ReplayPerformanceEntry<MemoryData> & { data: { memory: MemoryInfo } };
66

77
interface MemoryInfo {
88
jsHeapSizeLimit: number;

0 commit comments

Comments
 (0)