Skip to content

Commit cd4e4d7

Browse files
authored
Use console.timeStamp instead of performance.measure in Component Performance Track (facebook#32736)
This is a new extension that Chrome added to the existing `console.timeStamp` similar to the extensions added to `performance.measure`. This one should be significantly faster because it doesn't have the extra object indirection, it doesn't return a `PerformanceMeasure` entry and doesn't register itself with the global system of entries. I also use `performance.measure` in DEV for errors since we can attach the error to the `properties` extension which doesn't exist for `console.timeStamp`. A downside of using this API is that there's no programmatic API for the site itself to collect its own logs from React. Which the previous allowed us to use the standard `performance.getEntries()` for. The recommendation instead will be for the site to patch `console.timeStamp` if it wants to collect measurements from React just like you're recommended to patch `console.error` or `fetch` or whatever to collect other instrumentation metrics. This extension works in Chrome canary but it doesn't yet work fully in Chrome stable. We might want to wait until it has propagated to Chrome to stable. It should be in Chrome 136.
1 parent 18212ca commit cd4e4d7

File tree

3 files changed

+438
-296
lines changed

3 files changed

+438
-296
lines changed

packages/react-client/src/ReactFlightPerformanceTrack.js

Lines changed: 63 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,52 +7,35 @@
77
* @flow
88
*/
99

10+
/* eslint-disable react-internal/no-production-logging */
11+
1012
import type {ReactComponentInfo} from 'shared/ReactTypes';
1113

1214
import {enableProfilerTimer} from 'shared/ReactFeatureFlags';
1315

1416
const supportsUserTiming =
1517
enableProfilerTimer &&
16-
typeof performance !== 'undefined' &&
17-
// $FlowFixMe[method-unbinding]
18-
typeof performance.measure === 'function';
18+
typeof console !== 'undefined' &&
19+
typeof console.timeStamp === 'function';
1920

2021
const COMPONENTS_TRACK = 'Server Components ⚛';
2122

22-
const componentsTrackMarker = {
23-
startTime: 0.001,
24-
detail: {
25-
devtools: {
26-
color: 'primary-light',
27-
track: 'Primary',
28-
trackGroup: COMPONENTS_TRACK,
29-
},
30-
},
31-
};
32-
3323
export function markAllTracksInOrder() {
3424
if (supportsUserTiming) {
3525
// Ensure we create the Server Component track groups earlier than the Client Scheduler
3626
// and Client Components. We can always add the 0 time slot even if it's in the past.
3727
// That's still considered for ordering.
38-
performance.mark('Server Components Track', componentsTrackMarker);
28+
console.timeStamp(
29+
'Server Components Track',
30+
0.001,
31+
0.001,
32+
'Primary',
33+
COMPONENTS_TRACK,
34+
'primary-light',
35+
);
3936
}
4037
}
4138

42-
// Reused to avoid thrashing the GC.
43-
const reusableComponentDevToolDetails = {
44-
color: 'primary',
45-
track: '',
46-
trackGroup: COMPONENTS_TRACK,
47-
};
48-
const reusableComponentOptions = {
49-
start: -0,
50-
end: -0,
51-
detail: {
52-
devtools: reusableComponentDevToolDetails,
53-
},
54-
};
55-
5639
const trackNames = [
5740
'Primary',
5841
'Parallel',
@@ -79,7 +62,7 @@ export function logComponentRender(
7962
const name = componentInfo.name;
8063
const isPrimaryEnv = env === rootEnv;
8164
const selfTime = endTime - startTime;
82-
reusableComponentDevToolDetails.color =
65+
const color =
8366
selfTime < 0.5
8467
? isPrimaryEnv
8568
? 'primary-light'
@@ -93,12 +76,16 @@ export function logComponentRender(
9376
? 'primary-dark'
9477
: 'secondary-dark'
9578
: 'error';
96-
reusableComponentDevToolDetails.track = trackNames[trackIdx];
97-
reusableComponentOptions.start = startTime < 0 ? 0 : startTime;
98-
reusableComponentOptions.end = childrenEndTime;
9979
const entryName =
10080
isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
101-
performance.measure(entryName, reusableComponentOptions);
81+
console.timeStamp(
82+
entryName,
83+
startTime < 0 ? 0 : startTime,
84+
childrenEndTime,
85+
trackNames[trackIdx],
86+
COMPONENTS_TRACK,
87+
color,
88+
);
10289
}
10390
}
10491

@@ -112,8 +99,17 @@ export function logComponentErrored(
11299
error: mixed,
113100
): void {
114101
if (supportsUserTiming) {
115-
const properties = [];
116-
if (__DEV__) {
102+
const env = componentInfo.env;
103+
const name = componentInfo.name;
104+
const isPrimaryEnv = env === rootEnv;
105+
const entryName =
106+
isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
107+
if (
108+
__DEV__ &&
109+
typeof performance !== 'undefined' &&
110+
// $FlowFixMe[method-unbinding]
111+
typeof performance.measure === 'function'
112+
) {
117113
const message =
118114
typeof error === 'object' &&
119115
error !== null &&
@@ -122,26 +118,30 @@ export function logComponentErrored(
122118
String(error.message)
123119
: // eslint-disable-next-line react-internal/safe-string-coercion
124120
String(error);
125-
properties.push(['Error', message]);
126-
}
127-
const env = componentInfo.env;
128-
const name = componentInfo.name;
129-
const isPrimaryEnv = env === rootEnv;
130-
const entryName =
131-
isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
132-
performance.measure(entryName, {
133-
start: startTime < 0 ? 0 : startTime,
134-
end: childrenEndTime,
135-
detail: {
136-
devtools: {
137-
color: 'error',
138-
track: trackNames[trackIdx],
139-
trackGroup: COMPONENTS_TRACK,
140-
tooltipText: entryName + ' Errored',
141-
properties,
121+
const properties = [['Error', message]];
122+
performance.measure(entryName, {
123+
start: startTime < 0 ? 0 : startTime,
124+
end: childrenEndTime,
125+
detail: {
126+
devtools: {
127+
color: 'error',
128+
track: trackNames[trackIdx],
129+
trackGroup: COMPONENTS_TRACK,
130+
tooltipText: entryName + ' Errored',
131+
properties,
132+
},
142133
},
143-
},
144-
});
134+
});
135+
} else {
136+
console.timeStamp(
137+
entryName,
138+
startTime < 0 ? 0 : startTime,
139+
childrenEndTime,
140+
trackNames[trackIdx],
141+
COMPONENTS_TRACK,
142+
'error',
143+
);
144+
}
145145
}
146146
}
147147

@@ -153,11 +153,14 @@ export function logDedupedComponentRender(
153153
): void {
154154
if (supportsUserTiming && endTime >= 0 && trackIdx < 10) {
155155
const name = componentInfo.name;
156-
reusableComponentDevToolDetails.color = 'tertiary-light';
157-
reusableComponentDevToolDetails.track = trackNames[trackIdx];
158-
reusableComponentOptions.start = startTime < 0 ? 0 : startTime;
159-
reusableComponentOptions.end = endTime;
160156
const entryName = name + ' [deduped]';
161-
performance.measure(entryName, reusableComponentOptions);
157+
console.timeStamp(
158+
entryName,
159+
startTime < 0 ? 0 : startTime,
160+
endTime,
161+
trackNames[trackIdx],
162+
COMPONENTS_TRACK,
163+
'tertiary-light',
164+
);
162165
}
163166
}

0 commit comments

Comments
 (0)