UNPKG

react-native-performance-flipper-reporter

Version:

This package feeds metrics, marks and measures collected by the `react-native-performance` library to the `Performance` Flipper plugin.

198 lines (182 loc) 5.51 kB
import { addPlugin } from 'react-native-flipper'; import performance, { PerformanceObserver } from 'react-native-performance'; const IDENTIFIER = 'flipper-plugin-performance'; const SCHEMA_VERSION = 1; const getNativeMarkMap = ( entries = performance.getEntriesByType('react-native-mark') ) => entries.reduce((acc, item) => acc.set(item.name, item), new Map()); // The bundle download trace can be very long but has no real impact on // perf so we try to alter the marks to act as if it wasn't there const subtractDownloadDuration = (entries, entryMap = getNativeMarkMap()) => entries.map((entry) => { const downloadEnd = entryMap.get('downloadEnd'); let transformed = entry.toJSON ? entry.toJSON() : { ...entry }; if ( downloadEnd && entry.name !== 'downloadStart' && entry.name !== 'downloadEnd' && entry.startTime <= downloadEnd.startTime ) { const downloadStart = entryMap.get('downloadStart'); transformed.startTime += downloadEnd.startTime - downloadStart.startTime; } return transformed; }); const calculateNativeMeasures = (newEntries, entryMap = getNativeMarkMap()) => newEntries .filter( (entry) => entry.name.endsWith('End') && entry.name !== 'nativeLaunchEnd' && entry.name !== 'downloadEnd' ) .map((end) => { const name = end.name.replace(/End$/, ''); const { startTime } = entryMap.get(`${name}Start`); const duration = end.startTime - startTime; return { name, startTime, duration, category: 'Native', }; }); const calculateNativeMetrics = (newEntries, entryMap = getNativeMarkMap()) => newEntries .filter((entry) => entry.name === 'nativeLaunchEnd') .map((end) => { const name = end.name.replace(/End$/, ''); const { startTime } = entryMap.get(`${name}Start`); const value = end.startTime - startTime; return { name, startTime, value, unit: 'milliseconds', }; }); const calculateNativeMarks = (newEntries, entryMap = getNativeMarkMap()) => newEntries .filter((entry) => !isMeasureMark(entry)) .map((entry) => ({ name: entry.name, startTime: entry.startTime, })); const isMeasureMark = (entry) => entry.name.endsWith('End') || entry.name.endsWith('Start'); const getResourceName = (url) => { const [urlSansQuery] = url.split('?'); return urlSansQuery.replace(/^https?:\/\//i, ''); }; export function setupDefaultFlipperReporter() { let observers = []; const sessionStartedAt = Date.now(); const addObserver = (callback, options) => { const observer = new PerformanceObserver(callback); observer.observe(options); observers.push(observer); }; addPlugin({ getId() { return IDENTIFIER; }, onConnect(connection) { connection.send('setSession', { schemaVersion: SCHEMA_VERSION, sessionStartedAt, }); const appendMeasures = (measures) => { connection.send('appendMeasures', { schemaVersion: SCHEMA_VERSION, measures, }); }; const appendMarks = (marks) => { connection.send('appendMarks', { schemaVersion: SCHEMA_VERSION, marks, }); }; const setMetrics = (metrics) => { connection.send('setMetrics', { schemaVersion: SCHEMA_VERSION, metrics, }); }; addObserver( (list) => { const entries = subtractDownloadDuration(list.getEntries()); const entryMap = getNativeMarkMap(entries); const measures = calculateNativeMeasures(entries, entryMap); if (measures.length !== 0) { appendMeasures(measures); } const marks = calculateNativeMarks(entries, entryMap); if (marks.length !== 0) { appendMarks(marks); } const metrics = calculateNativeMetrics(entries, entryMap); if (metrics.length !== 0) { setMetrics(metrics); } }, { type: 'react-native-mark', buffered: true, } ); addObserver( (list) => { appendMeasures( list.getEntries().map((entry) => ({ name: getResourceName(entry.name), startTime: entry.startTime, duration: entry.duration, category: 'Network', })) ); }, { type: 'resource', buffered: true, } ); addObserver( (list) => { appendMeasures( list.getEntries().map((entry) => ({ name: entry.name, startTime: entry.startTime, duration: entry.duration, category: 'App', })) ); }, { type: 'measure', buffered: true, } ); addObserver( (list) => { setMetrics( list.getEntries().map((entry) => ({ name: entry.name, startTime: entry.startTime, value: entry.value, unit: entry.name === 'bundleSize' ? 'bytes' : undefined, })) ); }, { type: 'metric', buffered: true, } ); }, onDisconnect() { observers.forEach((observer) => observer.disconnect()); observers = []; }, }); }