From a1e7535db70f84f4cbc3dbf08a31b52a81a30c61 Mon Sep 17 00:00:00 2001 From: Ldoppea Date: Tue, 26 Nov 2024 16:42:49 +0100 Subject: [PATCH] feat: Add conversion script to allow opening logs in Chrome devtools In previous commit we allowed the users to send their performance logs by email to Cozy's support The sent file contains raw measurement data that cannot be easily read This script allows to convert the sent file to a format supported by Google Chrome's performance devtools In order to make it work, retrieve some user's logs and run the following command: ```shell yarn perf:convert ``` A new file with the same name suffixed by `_converted` will be generated and can be imported to Google Chrome's performance devtools --- package.json | 1 + scripts/convert-perf-logs.cmd.ts | 169 +++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+) create mode 100644 scripts/convert-perf-logs.cmd.ts diff --git a/package.json b/package.json index ad2d3857c..33cef5797 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "ios": "react-native run-ios", "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", "log:android": "adb logcat *:S ReactNative:V ReactNativeJS:V", + "perf:convert": "yarn tsc --project scripts && node scripts/dist/convert-perf-logs.cmd.js", "postinstall": "patch-package && yarn install:scripts && react-native setup-ios-permissions && pod-install", "pre-commit": "yarn lint", "start": "react-native start", diff --git a/scripts/convert-perf-logs.cmd.ts b/scripts/convert-perf-logs.cmd.ts new file mode 100644 index 000000000..0de680385 --- /dev/null +++ b/scripts/convert-perf-logs.cmd.ts @@ -0,0 +1,169 @@ +// This script can be called using `yarn perf:convert ` + +import path from 'path' + +import fs from 'fs-extra' + +interface PerfEntry { + name: string + startTime: number + duration: number + initiatorType: string + entryType: string + detail?: { + category?: string + } +} + +interface TrackInfo { + color: string + track: string +} + +const convertFile = (): void => { + const sourcePath = process.argv[2] + + if (!sourcePath) { + console.error( + '⚠️ Missing file path argument. Please provide a path to the source performance logs file' + ) + return + } + + const sourceFile = fs.readFileSync(sourcePath, 'utf-8') + + const performanceLogs = JSON.parse(sourceFile) as PerfEntry[] + + const minTimestamp = + Math.min(...performanceLogs.map(entry => entry.startTime)) * 1000 + const maxTimestamp = + Math.max( + ...performanceLogs.map(entry => entry.startTime + entry.duration) + ) * 1000 + + const final = { + metadata: { + source: 'Cozy Flagship App', + startTime: '2024-11-24T11:41:40.862Z', + dataOrigin: 'TraceEvents' + }, + traceEvents: [ + { + args: { + data: { + frameTreeNodeId: 1602, + frames: [ + { + frame: '58D933E9AC2121C7555CECAF5DABE9EA', + isInPrimaryMainFrame: true, + isOutermostMainFrame: true, + name: '', + processId: 1119, + url: 'chrome://newtab/' + } + ], + persistentIds: true + } + }, + cat: 'disabled-by-default-devtools.timeline', + name: 'TracingStartedInBrowser', + ph: 'I', + pid: 1083, + s: 't', + tid: 259, + ts: minTimestamp, + tts: 1604839675 + }, + { + args: {}, + cat: 'disabled-by-default-devtools.timeline', + name: 'RunTask', + ph: 'B', + pid: 1094, + tid: 20483, + ts: maxTimestamp + }, + ...performanceLogs.map((entry, index) => { + return { + args: { + startTime: entry.startTime, + detail: JSON.stringify({ + devtools: { + dataType: 'track-entry', + ...entryToTrack(entry), + trackGroup: 'Cozy Flagship App' + } + }) + }, + cat: 'blink.user_timing', + id2: { + local: '0x' + index.toString(16) + }, + name: entry.name, + ph: 'b', + pid: index, + tid: 259, + ts: entry.startTime * 1000 + } + }), + ...performanceLogs.map((entry, index) => { + return { + args: {}, + cat: 'blink.user_timing', + id2: { + local: '0x' + index.toString(16) + }, + name: entry.name, + ph: 'e', + pid: index, + tid: 259, + ts: (entry.startTime + entry.duration) * 1000 + } + }) + ] + } + + const sourceFileName = path.basename(sourcePath, '.json') + const sourceBasePath = path.dirname(sourcePath) + const destinationPath = path.join( + sourceBasePath, + `${sourceFileName}_converted.json` + ) + + fs.writeFileSync(destinationPath, JSON.stringify(final), 'utf-8') +} + +const entryToTrack = (entry: PerfEntry): TrackInfo => { + const category = entry.detail?.category + if (category) { + return { + color: 'primary', + track: category + } + } + if (entry.initiatorType === 'xmlhttprequest') { + return { + color: 'secondary', + track: 'network' + } + } + if (entry.entryType === 'mark') { + if (entry.name.includes('SplashScreen')) { + return { + color: 'primary-light', + track: 'splashscreen' + } + } + + return { + color: 'primary-light', + track: 'mark' + } + } + return { + color: 'tertiary', + track: 'global' + } +} + +convertFile()