From cde18f565ea8037e2c1411fa28bbfed9aa4fcf34 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Sat, 31 May 2025 19:44:15 -0400 Subject: [PATCH 1/2] Log Server Requests track --- .../react-client/src/ReactFlightClient.js | 3 ++ .../src/ReactFlightPerformanceTrack.js | 45 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 8d2c215a6b9..8a70ea190be 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -78,6 +78,7 @@ import { logComponentRender, logDedupedComponentRender, logComponentErrored, + logIOInfo, } from './ReactFlightPerformanceTrack'; import { @@ -2769,6 +2770,8 @@ function initializeIOInfo(response: Response, ioInfo: ReactIOInfo): void { ioInfo.start += response._timeOrigin; // $FlowFixMe[cannot-write] ioInfo.end += response._timeOrigin; + + logIOInfo(ioInfo); } function resolveIOInfo( diff --git a/packages/react-client/src/ReactFlightPerformanceTrack.js b/packages/react-client/src/ReactFlightPerformanceTrack.js index ed0f67a4f31..9c1b1a67a8a 100644 --- a/packages/react-client/src/ReactFlightPerformanceTrack.js +++ b/packages/react-client/src/ReactFlightPerformanceTrack.js @@ -9,7 +9,7 @@ /* eslint-disable react-internal/no-production-logging */ -import type {ReactComponentInfo} from 'shared/ReactTypes'; +import type {ReactComponentInfo, ReactIOInfo} from 'shared/ReactTypes'; import {enableProfilerTimer} from 'shared/ReactFeatureFlags'; @@ -18,6 +18,7 @@ const supportsUserTiming = typeof console !== 'undefined' && typeof console.timeStamp === 'function'; +const IO_TRACK = 'Server Requests ⚛'; const COMPONENTS_TRACK = 'Server Components ⚛'; export function markAllTracksInOrder() { @@ -25,6 +26,14 @@ export function markAllTracksInOrder() { // 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 Requests Track', + 0.001, + 0.001, + IO_TRACK, + undefined, + 'primary-light', + ); console.timeStamp( 'Server Components Track', 0.001, @@ -196,3 +205,37 @@ export function logDedupedComponentRender( } } } + +export function logIOInfo(ioInfo: ReactIOInfo): void { + const startTime = ioInfo.start; + const endTime = ioInfo.end; + if (supportsUserTiming && endTime >= 0) { + const name = ioInfo.name; + const debugTask = ioInfo.debugTask; + // TODO: Add more built-in color assignment. + const color = name === 'fetch' ? 'primary-light' : 'secondary-light'; + if (__DEV__ && debugTask) { + debugTask.run( + // $FlowFixMe[method-unbinding] + console.timeStamp.bind( + console, + name, + startTime < 0 ? 0 : startTime, + endTime, + IO_TRACK, + undefined, + color, + ), + ); + } else { + console.timeStamp( + name, + startTime < 0 ? 0 : startTime, + endTime, + IO_TRACK, + undefined, + color, + ); + } + } +} From 8ce8c1af64075d3df6d8888d33b0bb28d2a8f4cc Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Sun, 1 Jun 2025 20:38:29 -0400 Subject: [PATCH 2/2] Add color variation automatically based on mod of the name Helps distinguish various helpers like fetch vs query. I also switched dedupes to use primary/secondary color. It was standing out too much. By using the tertiary color for I/O we can avoid confusing the color with the color Server Components. This lets us associate the I/O track with the await in the Server Components track by using the same color scheme but make it different from Server Component rendering. --- .../react-client/src/ReactFlightClient.js | 1 + .../src/ReactFlightPerformanceTrack.js | 25 ++++++++++++++++--- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index 8a70ea190be..3385a3d6327 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -2893,6 +2893,7 @@ function flushComponentPerformance( trackIdx, parentEndTime, previousEndTime, + response._rootEnvironmentName, ); } // Since we didn't bump the track this time, we just return the same track. diff --git a/packages/react-client/src/ReactFlightPerformanceTrack.js b/packages/react-client/src/ReactFlightPerformanceTrack.js index 9c1b1a67a8a..8284ac8b6e4 100644 --- a/packages/react-client/src/ReactFlightPerformanceTrack.js +++ b/packages/react-client/src/ReactFlightPerformanceTrack.js @@ -175,9 +175,13 @@ export function logDedupedComponentRender( trackIdx: number, startTime: number, endTime: number, + rootEnv: string, ): void { if (supportsUserTiming && endTime >= 0 && trackIdx < 10) { + const env = componentInfo.env; const name = componentInfo.name; + const isPrimaryEnv = env === rootEnv; + const color = isPrimaryEnv ? 'primary-light' : 'secondary-light'; const entryName = name + ' [deduped]'; const debugTask = componentInfo.debugTask; if (__DEV__ && debugTask) { @@ -190,7 +194,7 @@ export function logDedupedComponentRender( endTime, trackNames[trackIdx], COMPONENTS_TRACK, - 'tertiary-light', + color, ), ); } else { @@ -200,20 +204,33 @@ export function logDedupedComponentRender( endTime, trackNames[trackIdx], COMPONENTS_TRACK, - 'tertiary-light', + color, ); } } } +function getIOColor( + functionName: string, +): 'tertiary-light' | 'tertiary' | 'tertiary-dark' { + // Add some color variation to be able to distinguish various sources. + switch (functionName.charCodeAt(0) % 3) { + case 0: + return 'tertiary-light'; + case 1: + return 'tertiary'; + default: + return 'tertiary-dark'; + } +} + export function logIOInfo(ioInfo: ReactIOInfo): void { const startTime = ioInfo.start; const endTime = ioInfo.end; if (supportsUserTiming && endTime >= 0) { const name = ioInfo.name; const debugTask = ioInfo.debugTask; - // TODO: Add more built-in color assignment. - const color = name === 'fetch' ? 'primary-light' : 'secondary-light'; + const color = getIOColor(name); if (__DEV__ && debugTask) { debugTask.run( // $FlowFixMe[method-unbinding]