From 853ee24c9bce406f4801780b8458aa4c4e18a8e3 Mon Sep 17 00:00:00 2001 From: Anton Nikolaev Date: Tue, 18 May 2021 08:06:07 -0700 Subject: [PATCH] Add info about interactions to error reports Summary: When reporting errors we could add info about interactions which caused errors. Ability to connect errors and interactions could be quite helpful for analysing and debugging errors and where they are coming from. Reviewed By: passy, mweststrate Differential Revision: D28467575 fbshipit-source-id: bef69917a4d6c786d762a2f6eb75a47fd4e46b0f --- desktop/app/src/__mocks__/uuid.tsx | 16 ++++++++++++++++ desktop/app/src/fb-stubs/ErrorReporter.tsx | 9 +-------- desktop/app/src/utils/errors.tsx | 9 +++++++++ desktop/flipper-plugin/package.json | 4 +++- .../flipper-plugin/src/__tests__/api.node.tsx | 2 ++ desktop/flipper-plugin/src/index.ts | 2 ++ desktop/flipper-plugin/src/ui/Tracked.tsx | 15 ++++++++++++--- .../src/ui/__tests__/Tracked.node.tsx | 5 +++++ desktop/yarn.lock | 2 +- 9 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 desktop/app/src/__mocks__/uuid.tsx diff --git a/desktop/app/src/__mocks__/uuid.tsx b/desktop/app/src/__mocks__/uuid.tsx new file mode 100644 index 000000000..0f418648c --- /dev/null +++ b/desktop/app/src/__mocks__/uuid.tsx @@ -0,0 +1,16 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +export function v4() { + return '00000000-0000-0000-0000-000000000000'; +} + +export function v1() { + return '00000000-0000-0000-0000-000000000000'; +} diff --git a/desktop/app/src/fb-stubs/ErrorReporter.tsx b/desktop/app/src/fb-stubs/ErrorReporter.tsx index 7829e7abd..53a63d626 100644 --- a/desktop/app/src/fb-stubs/ErrorReporter.tsx +++ b/desktop/app/src/fb-stubs/ErrorReporter.tsx @@ -15,14 +15,7 @@ export function cleanStack(_stack: string, _loc?: string) {} import ScribeLogger from './ScribeLogger'; -export type ObjectError = - | Error - | { - message: string; - stack?: string; - }; - export default class ErrorReporter { constructor(_scribeLogger: ScribeLogger) {} - report(_err: ObjectError) {} + report(_err: Error) {} } diff --git a/desktop/app/src/utils/errors.tsx b/desktop/app/src/utils/errors.tsx index 1ea305e4f..28eb5cd07 100644 --- a/desktop/app/src/utils/errors.tsx +++ b/desktop/app/src/utils/errors.tsx @@ -7,11 +7,20 @@ * @format */ +import {InteractionReport} from 'flipper-plugin'; + export class CancelledPromiseError extends Error { constructor(msg: string) { super(msg); this.name = 'CancelledPromiseError'; } + name: 'CancelledPromiseError'; +} + +declare global { + interface Error { + interaction?: InteractionReport; + } } export function isError(obj: any): obj is Error { diff --git a/desktop/flipper-plugin/package.json b/desktop/flipper-plugin/package.json index f4d536784..aabd662e4 100644 --- a/desktop/flipper-plugin/package.json +++ b/desktop/flipper-plugin/package.json @@ -12,12 +12,14 @@ "@emotion/css": "^11.1.3", "@emotion/react": "^11.4.0", "@reach/observe-rect": "^1.2.0", + "@types/uuid": "^8.3.0", "immer": "^9.0.2", "lodash": "^4.17.21", "react-color": "^2.19.3", "react-element-to-jsx-string": "^14.3.2", "react-virtual": "^2.7.1", - "string-natural-compare": "^3.0.0" + "string-natural-compare": "^3.0.0", + "uuid": "^8.3.2" }, "devDependencies": { "@types/jest": "^26.0.23", diff --git a/desktop/flipper-plugin/src/__tests__/api.node.tsx b/desktop/flipper-plugin/src/__tests__/api.node.tsx index c2c663370..73edc900f 100644 --- a/desktop/flipper-plugin/src/__tests__/api.node.tsx +++ b/desktop/flipper-plugin/src/__tests__/api.node.tsx @@ -92,6 +92,8 @@ test('Correct top level API exposed', () => { "FlipperLib", "HighlightManager", "Idler", + "InteractionReport", + "InteractionReporter", "LogLevel", "LogTypes", "Logger", diff --git a/desktop/flipper-plugin/src/index.ts b/desktop/flipper-plugin/src/index.ts index 3215bae9f..31e956e68 100644 --- a/desktop/flipper-plugin/src/index.ts +++ b/desktop/flipper-plugin/src/index.ts @@ -69,6 +69,8 @@ export { withTrackingScope, useTrackedCallback, wrapInteractionHandler as _wrapInteractionHandler, + InteractionReport, + InteractionReporter, } from './ui/Tracked'; export {DataFormatter} from './ui/DataFormatter'; diff --git a/desktop/flipper-plugin/src/ui/Tracked.tsx b/desktop/flipper-plugin/src/ui/Tracked.tsx index 2dcf76e35..b1cf87b34 100644 --- a/desktop/flipper-plugin/src/ui/Tracked.tsx +++ b/desktop/flipper-plugin/src/ui/Tracked.tsx @@ -10,6 +10,7 @@ import React, {useMemo} from 'react'; import {Children, cloneElement, createContext, useContext} from 'react'; import reactElementToJSXString from 'react-element-to-jsx-string'; +import {v4 as uuid} from 'uuid'; export type InteractionReport = { // Duration of the event handler itself, not including any time the promise handler might have been pending @@ -22,6 +23,7 @@ export type InteractionReport = { action: string; componentType: string; event: string; + uuid: string; }; export type InteractionReporter = (report: InteractionReport) => void; @@ -124,8 +126,9 @@ export function wrapInteractionHandler( scope: string, action?: string, ): T { + const interaction_uuid = uuid(); function report(start: number, initialEnd: number, error?: any) { - globalInteractionReporter({ + const interactionReport: InteractionReport = { duration: initialEnd - start, totalDuration: Date.now() - start, success: error ? 0 : 1, @@ -143,7 +146,13 @@ export function wrapInteractionHandler( : 'unknown'), scope, event, - }); + uuid: interaction_uuid, + }; + if (error && typeof error === 'object') { + // associate the error with the interaction caused it + error.interaction = interactionReport; + } + globalInteractionReporter(interactionReport); } const res = function trappedInteractionHandler(this: any) { @@ -168,7 +177,7 @@ export function wrapInteractionHandler( (error: any) => r(initialEnd, error), ); res = res.catch((error: any) => { - // we need to create another rejected promise so error is again marked as "unhandled" + // we need to create another rejected promise so error is again marked as "unhandled". return Promise.reject(error); }); } else { diff --git a/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx b/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx index 9c5e69b83..663ff9065 100644 --- a/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx +++ b/desktop/flipper-plugin/src/ui/__tests__/Tracked.node.tsx @@ -54,6 +54,7 @@ test('Tracked button', () => { event: 'onClick', scope: 'Flipper', success: 1, + uuid: '00000000-0000-0000-0000-000000000000', }); }); @@ -74,6 +75,7 @@ test('Tracked button - custom handler', () => { event: 'onDoubleClick', scope: 'Flipper', success: 1, + uuid: '00000000-0000-0000-0000-000000000000', }); }); @@ -99,6 +101,7 @@ test('Throwing action', () => { event: 'click', scope: 'test', success: 0, + uuid: '00000000-0000-0000-0000-000000000000', }); }); @@ -124,6 +127,7 @@ test('Async action', async () => { event: 'click', scope: 'test', success: 1, + uuid: '00000000-0000-0000-0000-000000000000', }); }); @@ -155,6 +159,7 @@ test('Throwing async action', async () => { event: 'click', scope: 'test', success: 0, + uuid: '00000000-0000-0000-0000-000000000000', }); }); diff --git a/desktop/yarn.lock b/desktop/yarn.lock index d7691cea5..e16736821 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -3143,7 +3143,7 @@ resolved "https://registry.yarnpkg.com/@types/url-join/-/url-join-4.0.0.tgz#72eff71648a429c7d4acf94e03780e06671369bd" integrity sha512-awrJu8yML4E/xTwr2EMatC+HBnHGoDxc2+ImA9QyeUELI1S7dOCIZcyjki1rkwoA8P2D2NVgLAJLjnclkdLtAw== -"@types/uuid@^8.0.0", "@types/uuid@^8.0.1": +"@types/uuid@^8.0.0", "@types/uuid@^8.0.1", "@types/uuid@^8.3.0": version "8.3.0" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f" integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==