Skip to content

Commit 5464e39

Browse files
rubennortemeta-codesync[bot]
authored andcommitted
Hoist PerformanceLogger primitives out of react-native (#56580)
Summary: Pull Request resolved: #56580 Changelog: [General][Deprecated] Deprecated second argument for hook function passed to `AppRegistry.setComponentProviderInstrumentationHook` as it's not usable by apps. Passed stub that warns when used. ## Context The performance-logger primitives (`createPerformanceLogger`, `GlobalPerformanceLogger`, `PerformanceLoggerContext`, the `IPerformanceLogger` implementation) and the telemetry they emitted have always been internal — they were never part of the OSS package's public API. Keeping them inside `react-native` is dead weight for external consumers and forces every change to that surface to land through OSS sync. ## Changes Removes the perf-logger source files and their telemetry emission from `react-native`. The `IPerformanceLogger` interface (the only consumer-visible artifact) is kept in its own `Libraries/ReactNative/IPerformanceLogger.{flow.js,d.ts}` file so embedders can still type their own implementations against it. Public API surface kept stable: - `XMLHttpRequest.setPerformanceLogger(...)` is preserved, typed against a thin local `XHRPerformanceLogger` interface (just `startTimespan` / `stopTimespan`). Defaults to `null`; emits no `network_XMLHttpRequest_<friendlyName>` timespan when no logger is set. - `AppRegistry.setComponentProviderInstrumentationHook(...)` is preserved with its 2-arg signature. The second argument is now a no-op `IPerformanceLogger` stub that emits a one-time `console.warn` flagging it as deprecated — embedders should provide their own per-app context if they need it. ## Impact Non-breaking for OSS consumers — nothing that was importable from `react-native` becomes unavailable. Telemetry that the package used to emit (`renderApplication_React_render`, `usedReactConcurrentRoot/Fabric/Profiler` extras, `initializeCore_*` markers, the `'GlobalPerformanceLogger'` callable native module) was never wired to anything in the OSS package; embedders that were consuming those signals through their own integration can re-emit them at the call site that registers the surface. Differential Revision: D102166537
1 parent 9d18367 commit 5464e39

15 files changed

Lines changed: 130 additions & 823 deletions

packages/react-native/Libraries/Core/InitializeCore.js

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,4 @@
2626

2727
'use strict';
2828

29-
const start = Date.now();
30-
3129
require('../../src/private/setup/setUpDefaultReactNativeEnvironment').default();
32-
33-
const GlobalPerformanceLogger =
34-
require('../Utilities/GlobalPerformanceLogger').default;
35-
// We could just call GlobalPerformanceLogger.markPoint at the top of the file,
36-
// but then we'd be excluding the time it took to require the logger.
37-
// Instead, we just use Date.now and backdate the timestamp.
38-
GlobalPerformanceLogger.markPoint(
39-
'initializeCore_start',
40-
GlobalPerformanceLogger.currentTimestamp() - (Date.now() - start),
41-
);
42-
GlobalPerformanceLogger.markPoint('initializeCore_end');

packages/react-native/Libraries/Core/setUpBatchedBridge.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ registerModule(
2525
'RCTNativeAppEventEmitter',
2626
() => require('../EventEmitter/RCTNativeAppEventEmitter').default,
2727
);
28-
registerModule(
29-
'GlobalPerformanceLogger',
30-
() => require('../Utilities/GlobalPerformanceLogger').default,
31-
);
3228

3329
if (__DEV__) {
3430
registerModule('HMRClient', () => require('../Utilities/HMRClient').default);

packages/react-native/Libraries/Network/XMLHttpRequest.js

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import type {
1414
EventCallback,
1515
EventListener,
1616
} from '../../src/private/webapis/dom/events/EventTarget';
17-
import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
1817

1918
import Event from '../../src/private/webapis/dom/events/Event';
2019
import {
@@ -27,8 +26,6 @@ import ProgressEvent from '../../src/private/webapis/xhr/events/ProgressEvent';
2726
import {type EventSubscription} from '../vendor/emitter/EventEmitter';
2827

2928
const BlobManager = require('../Blob/BlobManager').default;
30-
const GlobalPerformanceLogger =
31-
require('../Utilities/GlobalPerformanceLogger').default;
3229
const RCTNetworking = require('./RCTNetworking').default;
3330
const base64 = require('base64-js');
3431
const invariant = require('invariant');
@@ -58,6 +55,17 @@ type XHRInterceptor = interface {
5855
loadingFailed(id: number, error: string): void,
5956
};
6057

58+
/**
59+
* Minimal contract for the optional performance logger that callers may attach
60+
* via `setPerformanceLogger(...)`. Defined locally so this module stays
61+
* self-contained and does not depend on any specific logger implementation.
62+
* Any object satisfying these two methods structurally is accepted.
63+
*/
64+
type XHRPerformanceLogger = interface {
65+
startTimespan(key: string): void,
66+
stopTimespan(key: string): void,
67+
};
68+
6169
// The native blob module is optional so inject it here if available.
6270
if (BlobManager.isAvailable) {
6371
BlobManager.addNetworkingHandler();
@@ -167,8 +175,7 @@ class XMLHttpRequest extends EventTarget {
167175
_timedOut: boolean = false;
168176
_trackingName: ?string;
169177
_incrementalEvents: boolean = false;
170-
_startTime: ?number = null;
171-
_performanceLogger: IPerformanceLogger = GlobalPerformanceLogger;
178+
_performanceLogger: ?XHRPerformanceLogger = null;
172179

173180
static __setInterceptor_DO_NOT_USE(interceptor: ?XHRInterceptor) {
174181
XMLHttpRequest._interceptor = interceptor;
@@ -334,8 +341,10 @@ class XMLHttpRequest extends EventTarget {
334341
responseURL: ?string,
335342
): void {
336343
if (requestId === this._requestId) {
337-
this._perfKey != null &&
338-
this._performanceLogger.stopTimespan(this._perfKey);
344+
const performanceLogger = this._performanceLogger;
345+
if (this._perfKey != null && performanceLogger != null) {
346+
performanceLogger.stopTimespan(this._perfKey);
347+
}
339348
this.status = status;
340349
this.setResponseHeaders(responseHeaders);
341350
this.setReadyState(this.HEADERS_RECEIVED);
@@ -521,9 +530,15 @@ class XMLHttpRequest extends EventTarget {
521530
}
522531

523532
/**
524-
* Custom extension for setting a custom performance logger
533+
* Custom extension that lets callers attach a performance logger receiving
534+
* a `network_XMLHttpRequest_<friendlyName>` start/stop timespan around each
535+
* dispatched request. The logger only needs to implement
536+
* `startTimespan(key)` / `stopTimespan(key)` (see the `XHRPerformanceLogger`
537+
* interface above). When no logger is set the timespan is not emitted.
525538
*/
526-
setPerformanceLogger(performanceLogger: IPerformanceLogger): XMLHttpRequest {
539+
setPerformanceLogger(
540+
performanceLogger: XHRPerformanceLogger,
541+
): XMLHttpRequest {
527542
this._performanceLogger = performanceLogger;
528543
return this;
529544
}
@@ -598,9 +613,11 @@ class XMLHttpRequest extends EventTarget {
598613

599614
const doSend = () => {
600615
const friendlyName = this._trackingName ?? this._url;
601-
this._perfKey = 'network_XMLHttpRequest_' + String(friendlyName);
602-
this._performanceLogger.startTimespan(this._perfKey);
603-
this._startTime = performance.now();
616+
const performanceLogger = this._performanceLogger;
617+
if (performanceLogger != null) {
618+
this._perfKey = 'network_XMLHttpRequest_' + String(friendlyName);
619+
performanceLogger.startTimespan(this._perfKey);
620+
}
604621
invariant(
605622
this._method,
606623
'XMLHttpRequest method needs to be defined (%s).',

packages/react-native/Libraries/Network/__tests__/XMLHttpRequest-test.js

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,10 @@
1010

1111
'use strict';
1212

13-
const createPerformanceLogger =
14-
require('../../Utilities/createPerformanceLogger').default;
15-
const GlobalPerformanceLogger =
16-
require('../../Utilities/GlobalPerformanceLogger').default;
1713
const Platform = require('../../Utilities/Platform').default;
1814
const XMLHttpRequest = require('../XMLHttpRequest').default;
1915

2016
jest.unmock('../../Utilities/Platform');
21-
jest.mock('../../Utilities/GlobalPerformanceLogger');
2217
let requestId = 1;
2318
function setRequestId(id: number) {
2419
if (Platform.OS === 'ios') {
@@ -246,30 +241,11 @@ describe('XMLHttpRequest', function () {
246241
);
247242
});
248243

249-
it('should log to GlobalPerformanceLogger if a custom performance logger is not set', () => {
250-
xhr.open('GET', 'blabla');
251-
xhr.send();
252-
253-
expect(GlobalPerformanceLogger.startTimespan).toHaveBeenCalledWith(
254-
'network_XMLHttpRequest_blabla',
255-
);
256-
expect(GlobalPerformanceLogger.stopTimespan).not.toHaveBeenCalled();
257-
258-
setRequestId(8);
259-
xhr.__didReceiveResponse(requestId, 200, {
260-
'Content-Type': 'text/plain; charset=utf-8',
261-
'Content-Length': '32',
262-
});
263-
264-
expect(GlobalPerformanceLogger.stopTimespan).toHaveBeenCalledWith(
265-
'network_XMLHttpRequest_blabla',
266-
);
267-
});
268-
269244
it('should log to a custom performance logger if set', () => {
270-
const performanceLogger = createPerformanceLogger();
271-
jest.spyOn(performanceLogger, 'startTimespan');
272-
jest.spyOn(performanceLogger, 'stopTimespan');
245+
const performanceLogger = {
246+
startTimespan: jest.fn(),
247+
stopTimespan: jest.fn(),
248+
};
273249

274250
xhr.setPerformanceLogger(performanceLogger);
275251

@@ -279,7 +255,6 @@ describe('XMLHttpRequest', function () {
279255
expect(performanceLogger.startTimespan).toHaveBeenCalledWith(
280256
'network_XMLHttpRequest_blabla',
281257
);
282-
expect(GlobalPerformanceLogger.startTimespan).not.toHaveBeenCalled();
283258
expect(performanceLogger.stopTimespan).not.toHaveBeenCalled();
284259

285260
setRequestId(9);
@@ -291,7 +266,6 @@ describe('XMLHttpRequest', function () {
291266
expect(performanceLogger.stopTimespan).toHaveBeenCalledWith(
292267
'network_XMLHttpRequest_blabla',
293268
);
294-
expect(GlobalPerformanceLogger.stopTimespan).not.toHaveBeenCalled();
295269
});
296270

297271
it('should sort and lowercase response headers', function () {

packages/react-native/Libraries/ReactNative/AppRegistry.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
*/
99

1010
import type * as React from 'react';
11-
import type {IPerformanceLogger} from '../Utilities/IPerformanceLogger';
1211
import type {ViewStyle} from '../StyleSheet/StyleSheetTypes';
12+
import type {IPerformanceLogger} from './IPerformanceLogger';
1313

1414
type Task = (taskData: any) => Promise<void>;
1515
type TaskProvider = () => Task;

packages/react-native/Libraries/ReactNative/AppRegistry.flow.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
import type {ViewStyleProp} from '../StyleSheet/StyleSheet';
1212
import type {RootTag} from '../Types/RootTagTypes';
13-
import type {IPerformanceLogger} from '../Utilities/createPerformanceLogger';
1413
import type {DisplayModeType} from './DisplayMode';
14+
import type {IPerformanceLogger} from './IPerformanceLogger.flow';
1515

1616
type HeadlessTask = (taskData: any) => Promise<void>;
1717
export type TaskProvider = () => HeadlessTask;

packages/react-native/Libraries/ReactNative/AppRegistryImpl.js

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import type {
2121
TaskProvider,
2222
WrapperComponentProvider,
2323
} from './AppRegistry.flow';
24+
import type {IPerformanceLogger} from './IPerformanceLogger.flow';
2425

25-
import createPerformanceLogger from '../Utilities/createPerformanceLogger';
2626
import SceneTracker from '../Utilities/SceneTracker';
2727
import {coerceDisplayMode} from './DisplayMode';
2828
import HeadlessJsTaskError from './HeadlessJsTaskError';
@@ -31,6 +31,65 @@ import invariant from 'invariant';
3131
type TaskCanceller = () => void;
3232
type TaskCancelProvider = () => TaskCanceller;
3333

34+
let didWarnDeprecatedScopedPerformanceLogger: boolean = false;
35+
function warnDeprecatedScopedPerformanceLogger(): void {
36+
if (didWarnDeprecatedScopedPerformanceLogger) {
37+
return;
38+
}
39+
didWarnDeprecatedScopedPerformanceLogger = true;
40+
console.warn(
41+
'AppRegistry: the `scopedPerformanceLogger` argument passed to ' +
42+
'`componentProviderInstrumentationHook` is deprecated and will be ' +
43+
'removed in a future release. The instance provided today is a no-op ' +
44+
'stub.',
45+
);
46+
}
47+
48+
const DEPRECATED_NOOP_SCOPED_PERFORMANCE_LOGGER: IPerformanceLogger = {
49+
addTimespan: () => warnDeprecatedScopedPerformanceLogger(),
50+
append: () => warnDeprecatedScopedPerformanceLogger(),
51+
clear: () => warnDeprecatedScopedPerformanceLogger(),
52+
clearCompleted: () => warnDeprecatedScopedPerformanceLogger(),
53+
close: () => warnDeprecatedScopedPerformanceLogger(),
54+
currentTimestamp: () => {
55+
warnDeprecatedScopedPerformanceLogger();
56+
return 0;
57+
},
58+
getExtras: () => {
59+
warnDeprecatedScopedPerformanceLogger();
60+
return {};
61+
},
62+
getPoints: () => {
63+
warnDeprecatedScopedPerformanceLogger();
64+
return {};
65+
},
66+
getPointExtras: () => {
67+
warnDeprecatedScopedPerformanceLogger();
68+
return {};
69+
},
70+
getTimespans: () => {
71+
warnDeprecatedScopedPerformanceLogger();
72+
return {};
73+
},
74+
hasTimespan: () => {
75+
warnDeprecatedScopedPerformanceLogger();
76+
return false;
77+
},
78+
isClosed: () => {
79+
warnDeprecatedScopedPerformanceLogger();
80+
return false;
81+
},
82+
logEverything: () => warnDeprecatedScopedPerformanceLogger(),
83+
markPoint: () => warnDeprecatedScopedPerformanceLogger(),
84+
removeExtra: () => {
85+
warnDeprecatedScopedPerformanceLogger();
86+
return undefined;
87+
},
88+
setExtra: () => warnDeprecatedScopedPerformanceLogger(),
89+
startTimespan: () => warnDeprecatedScopedPerformanceLogger(),
90+
stopTimespan: () => warnDeprecatedScopedPerformanceLogger(),
91+
};
92+
3493
const runnables: Runnables = {};
3594
const sections: Runnables = {};
3695
const taskProviders: Map<string, TaskProvider> = new Map();
@@ -81,20 +140,19 @@ export function registerComponent(
81140
componentProvider: ComponentProvider,
82141
section?: boolean,
83142
): string {
84-
const scopedPerformanceLogger = createPerformanceLogger();
85143
runnables[appKey] = (appParameters, displayMode) => {
86144
const renderApplication = require('./renderApplication').default;
87145
renderApplication(
88146
componentProviderInstrumentationHook(
89147
componentProvider,
90-
scopedPerformanceLogger,
148+
DEPRECATED_NOOP_SCOPED_PERFORMANCE_LOGGER,
91149
),
92150
appParameters.initialProps,
93151
appParameters.rootTag,
94152
wrapperComponentProvider && wrapperComponentProvider(appParameters),
95153
rootViewStyleProvider && rootViewStyleProvider(appParameters),
96154
true, // fabric - deprecated, always true
97-
scopedPerformanceLogger,
155+
undefined, // formerly scopedPerformanceLogger; reserved positional slot
98156
appKey === 'LogBox', // is logbox
99157
appKey,
100158
displayMode,

packages/react-native/Libraries/Utilities/IPerformanceLogger.d.ts renamed to packages/react-native/Libraries/ReactNative/IPerformanceLogger.d.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
* @format
88
*/
99

10+
export type ExtraValue = number | string | boolean;
11+
export type Extras = {[key: string]: ExtraValue};
1012
export type Timespan = {
1113
startTime: number;
1214
endTime?: number | undefined;
@@ -15,11 +17,12 @@ export type Timespan = {
1517
endExtras?: Extras | undefined;
1618
};
1719

18-
// Extra values should be serializable primitives
19-
export type ExtraValue = number | string | boolean;
20-
21-
export type Extras = {[key: string]: ExtraValue};
22-
20+
/**
21+
* @deprecated The scoped performance logger argument passed to the
22+
* `componentProviderInstrumentationHook` is no longer used by `react-native`
23+
* itself and will be removed in a future release. The instance supplied today
24+
* is a no-op stub.
25+
*/
2326
export interface IPerformanceLogger {
2427
addTimespan(
2528
key: string,

packages/react-native/Libraries/Utilities/IPerformanceLogger.js renamed to packages/react-native/Libraries/ReactNative/IPerformanceLogger.flow.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
* @format
99
*/
1010

11+
export type ExtraValue = number | string | boolean;
12+
export type Extras = {[key: string]: ExtraValue};
1113
export type Timespan = {
1214
startTime: number,
1315
endTime?: number,
@@ -16,11 +18,12 @@ export type Timespan = {
1618
endExtras?: Extras,
1719
};
1820

19-
// Extra values should be serializable primitives
20-
export type ExtraValue = number | string | boolean;
21-
22-
export type Extras = {[key: string]: ExtraValue};
23-
21+
/**
22+
* @deprecated The scoped performance logger argument passed to the
23+
* `componentProviderInstrumentationHook` is no longer used by `react-native`
24+
* itself and will be removed in a future release. The instance supplied today
25+
* is a no-op stub.
26+
*/
2427
export interface IPerformanceLogger {
2528
addTimespan(
2629
key: string,
@@ -34,10 +37,10 @@ export interface IPerformanceLogger {
3437
clearCompleted(): void;
3538
close(): void;
3639
currentTimestamp(): number;
37-
getExtras(): Readonly<{[key: string]: ?ExtraValue, ...}>;
38-
getPoints(): Readonly<{[key: string]: ?number, ...}>;
39-
getPointExtras(): Readonly<{[key: string]: ?Extras, ...}>;
40-
getTimespans(): Readonly<{[key: string]: ?Timespan, ...}>;
40+
getExtras(): Readonly<{[key: string]: ?ExtraValue}>;
41+
getPoints(): Readonly<{[key: string]: ?number}>;
42+
getPointExtras(): Readonly<{[key: string]: ?Extras}>;
43+
getTimespans(): Readonly<{[key: string]: ?Timespan}>;
4144
hasTimespan(key: string): boolean;
4245
isClosed(): boolean;
4346
logEverything(): void;

0 commit comments

Comments
 (0)