Skip to content

Commit 6411611

Browse files
rubennortemeta-codesync[bot]
authored andcommitted
Hoist PerformanceLogger primitives out of react-native (facebook#56580)
Summary: Pull Request resolved: facebook#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) stays as a public type in its own `Libraries/ReactNative/IPerformanceLogger.{flow.js,d.ts}` file. 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(...)` keeps its 2-arg signature, and the `'GlobalPerformanceLogger'` callable native module is still registered. Both now point at a shared no-op `IPerformanceLogger` stub that emits a one-time `console.warn` flagging deprecation. - `Libraries/Core/InitializeCore.js` is stripped of the `initializeCore_*` markPoints; everything else (the call into `setUpDefaultReactNativeEnvironment`) is unchanged. ## Impact Non-breaking for OSS consumers — nothing that was importable from `react-native` becomes unavailable. Embedders that were consuming the internal telemetry through their own integration can re-emit it at the call site that registers the surface. Reviewed By: javache Differential Revision: D102166537 fbshipit-source-id: 241ca7e7fb48d6a9d7afe4c30bb7bd370f8a6bb8
1 parent ea3b825 commit 6411611

16 files changed

Lines changed: 155 additions & 818 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: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ registerModule(
2727
);
2828
registerModule(
2929
'GlobalPerformanceLogger',
30-
() => require('../Utilities/GlobalPerformanceLogger').default,
30+
() => require('../ReactNative/DeprecatedPerformanceLoggerStub').default,
3131
);
3232

3333
if (__DEV__) {

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: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ import type {
2222
WrapperComponentProvider,
2323
} from './AppRegistry.flow';
2424

25-
import createPerformanceLogger from '../Utilities/createPerformanceLogger';
2625
import SceneTracker from '../Utilities/SceneTracker';
26+
import DeprecatedPerformanceLoggerStub from './DeprecatedPerformanceLoggerStub';
2727
import {coerceDisplayMode} from './DisplayMode';
2828
import HeadlessJsTaskError from './HeadlessJsTaskError';
2929
import invariant from 'invariant';
@@ -81,20 +81,19 @@ export function registerComponent(
8181
componentProvider: ComponentProvider,
8282
section?: boolean,
8383
): string {
84-
const scopedPerformanceLogger = createPerformanceLogger();
8584
runnables[appKey] = (appParameters, displayMode) => {
8685
const renderApplication = require('./renderApplication').default;
8786
renderApplication(
8887
componentProviderInstrumentationHook(
8988
componentProvider,
90-
scopedPerformanceLogger,
89+
DeprecatedPerformanceLoggerStub,
9190
),
9291
appParameters.initialProps,
9392
appParameters.rootTag,
9493
wrapperComponentProvider && wrapperComponentProvider(appParameters),
9594
rootViewStyleProvider && rootViewStyleProvider(appParameters),
9695
true, // fabric - deprecated, always true
97-
scopedPerformanceLogger,
96+
undefined, // formerly scopedPerformanceLogger; reserved positional slot
9897
appKey === 'LogBox', // is logbox
9998
appKey,
10099
displayMode,
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @format
9+
*/
10+
11+
import type {IPerformanceLogger} from './IPerformanceLogger.flow';
12+
13+
let didWarn: boolean = false;
14+
function warnDeprecated(): void {
15+
if (didWarn) {
16+
return;
17+
}
18+
didWarn = true;
19+
console.warn(
20+
'The default `IPerformanceLogger` provided by `react-native` (the ' +
21+
"`'GlobalPerformanceLogger'` callable native module and the " +
22+
'`scopedPerformanceLogger` argument passed to the hook registered with ' +
23+
'`AppRegistry.setComponentProviderInstrumentationHook`) is deprecated and ' +
24+
'will be removed in a future release. The instance supplied today is a ' +
25+
'no-op stub. Embedders that need a per-app performance logger should ' +
26+
'attach their own implementation.',
27+
);
28+
}
29+
30+
/**
31+
* @deprecated The `react-native` package no longer ships a real
32+
* `IPerformanceLogger`. This stub is exposed to keep existing extension points
33+
* (the `'GlobalPerformanceLogger'` callable native module and the second
34+
* argument of `AppRegistry.setComponentProviderInstrumentationHook`'s hook)
35+
* source-compatible for embedders. Every method is a no-op that emits a
36+
* one-time deprecation warning on first use.
37+
*/
38+
const DeprecatedPerformanceLoggerStub: IPerformanceLogger = {
39+
addTimespan: () => warnDeprecated(),
40+
append: () => warnDeprecated(),
41+
clear: () => warnDeprecated(),
42+
clearCompleted: () => warnDeprecated(),
43+
close: () => warnDeprecated(),
44+
currentTimestamp: () => {
45+
warnDeprecated();
46+
return 0;
47+
},
48+
getExtras: () => {
49+
warnDeprecated();
50+
return {};
51+
},
52+
getPoints: () => {
53+
warnDeprecated();
54+
return {};
55+
},
56+
getPointExtras: () => {
57+
warnDeprecated();
58+
return {};
59+
},
60+
getTimespans: () => {
61+
warnDeprecated();
62+
return {};
63+
},
64+
hasTimespan: () => {
65+
warnDeprecated();
66+
return false;
67+
},
68+
isClosed: () => {
69+
warnDeprecated();
70+
return false;
71+
},
72+
logEverything: () => warnDeprecated(),
73+
markPoint: () => warnDeprecated(),
74+
removeExtra: () => {
75+
warnDeprecated();
76+
return undefined;
77+
},
78+
setExtra: () => warnDeprecated(),
79+
startTimespan: () => warnDeprecated(),
80+
stopTimespan: () => warnDeprecated(),
81+
};
82+
83+
export default DeprecatedPerformanceLoggerStub;

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)