mirror of
https://github.com/facebook/react.git
synced 2026-02-26 07:55:55 +00:00
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.
167 lines
4.5 KiB
JavaScript
167 lines
4.5 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
/* eslint-disable react-internal/no-production-logging */
|
|
|
|
import type {ReactComponentInfo} from 'shared/ReactTypes';
|
|
|
|
import {enableProfilerTimer} from 'shared/ReactFeatureFlags';
|
|
|
|
const supportsUserTiming =
|
|
enableProfilerTimer &&
|
|
typeof console !== 'undefined' &&
|
|
typeof console.timeStamp === 'function';
|
|
|
|
const COMPONENTS_TRACK = 'Server Components ⚛';
|
|
|
|
export function markAllTracksInOrder() {
|
|
if (supportsUserTiming) {
|
|
// Ensure we create the Server Component track groups earlier than the Client Scheduler
|
|
// and Client Components. We can always add the 0 time slot even if it's in the past.
|
|
// That's still considered for ordering.
|
|
console.timeStamp(
|
|
'Server Components Track',
|
|
0.001,
|
|
0.001,
|
|
'Primary',
|
|
COMPONENTS_TRACK,
|
|
'primary-light',
|
|
);
|
|
}
|
|
}
|
|
|
|
const trackNames = [
|
|
'Primary',
|
|
'Parallel',
|
|
'Parallel\u200b', // Padded with zero-width space to give each track a unique name.
|
|
'Parallel\u200b\u200b',
|
|
'Parallel\u200b\u200b\u200b',
|
|
'Parallel\u200b\u200b\u200b\u200b',
|
|
'Parallel\u200b\u200b\u200b\u200b\u200b',
|
|
'Parallel\u200b\u200b\u200b\u200b\u200b\u200b',
|
|
'Parallel\u200b\u200b\u200b\u200b\u200b\u200b\u200b',
|
|
'Parallel\u200b\u200b\u200b\u200b\u200b\u200b\u200b\u200b',
|
|
];
|
|
|
|
export function logComponentRender(
|
|
componentInfo: ReactComponentInfo,
|
|
trackIdx: number,
|
|
startTime: number,
|
|
endTime: number,
|
|
childrenEndTime: number,
|
|
rootEnv: string,
|
|
): void {
|
|
if (supportsUserTiming && childrenEndTime >= 0 && trackIdx < 10) {
|
|
const env = componentInfo.env;
|
|
const name = componentInfo.name;
|
|
const isPrimaryEnv = env === rootEnv;
|
|
const selfTime = endTime - startTime;
|
|
const color =
|
|
selfTime < 0.5
|
|
? isPrimaryEnv
|
|
? 'primary-light'
|
|
: 'secondary-light'
|
|
: selfTime < 50
|
|
? isPrimaryEnv
|
|
? 'primary'
|
|
: 'secondary'
|
|
: selfTime < 500
|
|
? isPrimaryEnv
|
|
? 'primary-dark'
|
|
: 'secondary-dark'
|
|
: 'error';
|
|
const entryName =
|
|
isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
|
|
console.timeStamp(
|
|
entryName,
|
|
startTime < 0 ? 0 : startTime,
|
|
childrenEndTime,
|
|
trackNames[trackIdx],
|
|
COMPONENTS_TRACK,
|
|
color,
|
|
);
|
|
}
|
|
}
|
|
|
|
export function logComponentErrored(
|
|
componentInfo: ReactComponentInfo,
|
|
trackIdx: number,
|
|
startTime: number,
|
|
endTime: number,
|
|
childrenEndTime: number,
|
|
rootEnv: string,
|
|
error: mixed,
|
|
): void {
|
|
if (supportsUserTiming) {
|
|
const env = componentInfo.env;
|
|
const name = componentInfo.name;
|
|
const isPrimaryEnv = env === rootEnv;
|
|
const entryName =
|
|
isPrimaryEnv || env === undefined ? name : name + ' [' + env + ']';
|
|
if (
|
|
__DEV__ &&
|
|
typeof performance !== 'undefined' &&
|
|
// $FlowFixMe[method-unbinding]
|
|
typeof performance.measure === 'function'
|
|
) {
|
|
const message =
|
|
typeof error === 'object' &&
|
|
error !== null &&
|
|
typeof error.message === 'string'
|
|
? // eslint-disable-next-line react-internal/safe-string-coercion
|
|
String(error.message)
|
|
: // eslint-disable-next-line react-internal/safe-string-coercion
|
|
String(error);
|
|
const properties = [['Error', message]];
|
|
performance.measure(entryName, {
|
|
start: startTime < 0 ? 0 : startTime,
|
|
end: childrenEndTime,
|
|
detail: {
|
|
devtools: {
|
|
color: 'error',
|
|
track: trackNames[trackIdx],
|
|
trackGroup: COMPONENTS_TRACK,
|
|
tooltipText: entryName + ' Errored',
|
|
properties,
|
|
},
|
|
},
|
|
});
|
|
} else {
|
|
console.timeStamp(
|
|
entryName,
|
|
startTime < 0 ? 0 : startTime,
|
|
childrenEndTime,
|
|
trackNames[trackIdx],
|
|
COMPONENTS_TRACK,
|
|
'error',
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function logDedupedComponentRender(
|
|
componentInfo: ReactComponentInfo,
|
|
trackIdx: number,
|
|
startTime: number,
|
|
endTime: number,
|
|
): void {
|
|
if (supportsUserTiming && endTime >= 0 && trackIdx < 10) {
|
|
const name = componentInfo.name;
|
|
const entryName = name + ' [deduped]';
|
|
console.timeStamp(
|
|
entryName,
|
|
startTime < 0 ? 0 : startTime,
|
|
endTime,
|
|
trackNames[trackIdx],
|
|
COMPONENTS_TRACK,
|
|
'tertiary-light',
|
|
);
|
|
}
|
|
}
|