From dd15cffa6447a73e18d6135044efdb112da9337f Mon Sep 17 00:00:00 2001 From: Michel Weststrate Date: Thu, 20 Aug 2020 13:26:39 -0700 Subject: [PATCH] Introduced first class console to help users debugging issues (#1479) Summary: Handling issues typically start with: did you look at the Electron logs? Since Flipper is such an extensible tool, running in varying environments I think the console should be support as first class concept. Many errors are currently not shown to the user. This PR is a first attempt to fix that. The implementation is based on https://github.com/samdenty/console-feed, which is used by quite some web based IDE like tools (like codesandbox), and offers a lot of goodies out of the box, like collapsing errors, objects, etc. Edit: also added a counter keeping track of the amount of errors N.B. no need to immediately review this diff, I'll import it to phabricator as soon as I can :) ## Changelog changelog: Introduce 'Debug Logs' section to help users to troubleshoot issues or to provide more accurate reports. Pull Request resolved: https://github.com/facebook/flipper/pull/1479 Test Plan: ![Screenshot from 2020-08-18 15-29-55](https://user-images.githubusercontent.com/1820292/90526011-c9b22d80-e167-11ea-88cf-7b4e07918a96.png) Reviewed By: jknoxville Differential Revision: D23198103 Pulled By: passy fbshipit-source-id: a2505f9fa59e10676a44ffa33312efe83c7be55d --- desktop/app/package.json | 1 + desktop/app/src/chrome/ConsoleLogs.tsx | 93 +++++++++++++++++++ .../mainsidebar/MainSidebarUtilsSection.tsx | 26 ++++++ .../src/chrome/mainsidebar/sidebarUtils.tsx | 1 + desktop/app/src/init.tsx | 2 + desktop/app/src/reducers/connections.tsx | 4 +- desktop/static/icons.json | 1 + desktop/yarn.lock | 64 +++++++++++-- 8 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 desktop/app/src/chrome/ConsoleLogs.tsx diff --git a/desktop/app/package.json b/desktop/app/package.json index aed730f0a..1f6d50498 100644 --- a/desktop/app/package.json +++ b/desktop/app/package.json @@ -23,6 +23,7 @@ "archiver": "^5.0.0", "async-mutex": "^0.1.3", "axios": "^0.19.2", + "console-feed": "^3.0.1", "deep-equal": "^2.0.1", "emotion": "^10.0.23", "expand-tilde": "^2.0.2", diff --git a/desktop/app/src/chrome/ConsoleLogs.tsx b/desktop/app/src/chrome/ConsoleLogs.tsx new file mode 100644 index 000000000..70b3ab7ef --- /dev/null +++ b/desktop/app/src/chrome/ConsoleLogs.tsx @@ -0,0 +1,93 @@ +/** + * 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 + */ + +import {useMemo} from 'react'; +import {Button, Toolbar, ButtonGroup, Layout} from '../ui'; +import React from 'react'; +import {Console, Hook} from 'console-feed'; +import type {Methods} from 'console-feed/lib/definitions/Methods'; +import {createState, useValue} from 'flipper-plugin'; +import {useLocalStorage} from '../utils/useLocalStorage'; + +const logsAtom = createState([]); +export const errorCounterAtom = createState(0); + +export function enableConsoleHook() { + console.log('enabling hooks'); + Hook( + window.console, + (log) => { + logsAtom.set([...logsAtom.get(), log]); + if (log.method === 'error' || log.method === 'assert') { + errorCounterAtom.set(errorCounterAtom.get() + 1); + } + }, + false, + ); +} + +function clearLogs() { + logsAtom.set([]); + errorCounterAtom.set(0); +} + +const allLogLevels: Methods[] = [ + 'log', + 'debug', + 'info', + 'warn', + 'error', + 'table', + 'clear', + 'time', + 'timeEnd', + 'count', + 'assert', +]; + +const defaultLogLevels: Methods[] = ['warn', 'error', 'table', 'assert']; + +export function ConsoleLogs() { + const logs = useValue(logsAtom); + const [logLevels, setLogLevels] = useLocalStorage( + 'console-logs-loglevels', + defaultLogLevels, + ); + + const dropdown = useMemo(() => { + return allLogLevels.map( + (l): Electron.MenuItemConstructorOptions => ({ + label: l, + checked: logLevels.includes(l), + type: 'checkbox', + click() { + setLogLevels((state) => + state.includes(l) + ? state.filter((level) => level !== l) + : [l, ...state], + ); + }, + }), + ); + }, [logLevels, setLogLevels]); + + return ( + + + + + + + + + + ); +} diff --git a/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx b/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx index 7d666e161..e2b67e291 100644 --- a/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx +++ b/desktop/app/src/chrome/mainsidebar/MainSidebarUtilsSection.tsx @@ -29,6 +29,8 @@ import { } from './sidebarUtils'; import {Group} from '../../reducers/supportForm'; import {getInstance} from '../../fb-stubs/Logger'; +import {ConsoleLogs, errorCounterAtom} from '../ConsoleLogs'; +import {useValue} from 'flipper-plugin'; type OwnProps = {}; @@ -103,6 +105,7 @@ function MainSidebarUtilsSection({ /> Manage Plugins + {config.showLogin && } ); @@ -171,3 +174,26 @@ const RenderNotificationsEntry = connect< ); }); + +function DebugLogsEntry({ + staticView, + setStaticView, +}: { + staticView: StaticView; + setStaticView: (payload: StaticView) => void; +}) { + const active = isStaticViewActive(staticView, ConsoleLogs); + const errorCount = useValue(errorCounterAtom); + return ( + setStaticView(ConsoleLogs)} active={active}> + + + Debug Logs + + + ); +} diff --git a/desktop/app/src/chrome/mainsidebar/sidebarUtils.tsx b/desktop/app/src/chrome/mainsidebar/sidebarUtils.tsx index f5a5a79d4..3a0117378 100644 --- a/desktop/app/src/chrome/mainsidebar/sidebarUtils.tsx +++ b/desktop/app/src/chrome/mainsidebar/sidebarUtils.tsx @@ -85,6 +85,7 @@ const PluginShape = styled(FlexBox)<{ export const PluginName = styled(Text)<{isActive?: boolean; count?: number}>( (props) => ({ + cursor: 'default', minWidth: 0, textOverflow: 'ellipsis', whiteSpace: 'nowrap', diff --git a/desktop/app/src/init.tsx b/desktop/app/src/init.tsx index 711bb23cc..c9950ae47 100644 --- a/desktop/app/src/init.tsx +++ b/desktop/app/src/init.tsx @@ -38,6 +38,7 @@ import os from 'os'; import QuickPerformanceLogger, {FLIPPER_QPL_EVENTS} from './fb-stubs/QPL'; import {PopoverProvider} from './ui/components/PopoverProvider'; import {initializeFlipperLibImplementation} from './utils/flipperLibImplementation'; +import {enableConsoleHook} from './chrome/ConsoleLogs'; if (process.env.NODE_ENV === 'development' && os.platform() === 'darwin') { // By default Node.JS has its internal certificate storage and doesn't use @@ -119,6 +120,7 @@ function init() { const sessionId = store.getState().application.sessionId; initCrashReporter(sessionId || ''); registerRecordingHooks(store); + enableConsoleHook(); window.flipperGlobalStoreDispatch = store.dispatch; } diff --git a/desktop/app/src/reducers/connections.tsx b/desktop/app/src/reducers/connections.tsx index d68a420e5..91b110b3c 100644 --- a/desktop/app/src/reducers/connections.tsx +++ b/desktop/app/src/reducers/connections.tsx @@ -27,13 +27,15 @@ import {getPluginKey, isDevicePluginDefinition} from '../utils/pluginUtils'; import {deconstructClientId} from '../utils/clientUtils'; import {PluginDefinition} from '../plugin'; import {RegisterPluginAction} from './plugins'; +import {ConsoleLogs} from '../chrome/ConsoleLogs'; export type StaticView = | null | typeof WelcomeScreen | typeof NotificationScreen | typeof SupportRequestFormV2 - | typeof SupportRequestDetails; + | typeof SupportRequestDetails + | typeof ConsoleLogs; export type FlipperError = { occurrences?: number; diff --git a/desktop/static/icons.json b/desktop/static/icons.json index 7171e92e4..3927c176e 100644 --- a/desktop/static/icons.json +++ b/desktop/static/icons.json @@ -50,6 +50,7 @@ 16 ], "caution-octagon": [ + 12, 16, 20 ], diff --git a/desktop/yarn.lock b/desktop/yarn.lock index 13708265b..ebe066b39 100644 --- a/desktop/yarn.lock +++ b/desktop/yarn.lock @@ -1240,10 +1240,10 @@ "@emotion/utils" "0.11.3" "@emotion/weak-memoize" "0.2.5" -"@emotion/core@^10.0.22": - version "10.0.28" - resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.28.tgz#bb65af7262a234593a9e952c041d0f1c9b9bef3d" - integrity sha512-pH8UueKYO5jgg0Iq+AmCLxBsvuGtvlmiDCOuv8fGNYn3cowFpLN98L8zO56U0H1PjDIyAlXymgL3Wu7u7v6hbA== +"@emotion/core@^10.0.10", "@emotion/core@^10.0.22": + version "10.0.35" + resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.0.35.tgz#513fcf2e22cd4dfe9d3894ed138c9d7a859af9b3" + integrity sha512-sH++vJCdk025fBlRZSAhkRlSUoqSqgCzYf5fMOmqqi3bM6how+sQpg3hkgJonj8GxXM4WbD7dRO+4tegDB9fUw== dependencies: "@babel/runtime" "^7.5.5" "@emotion/cache" "^10.0.27" @@ -1304,7 +1304,7 @@ "@emotion/serialize" "^0.11.15" "@emotion/utils" "0.11.3" -"@emotion/styled@^10.0.23": +"@emotion/styled@^10.0.12", "@emotion/styled@^10.0.23": version "10.0.27" resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-10.0.27.tgz#12cb67e91f7ad7431e1875b1d83a94b814133eaf" integrity sha512-iK/8Sh7+NLJzyp9a5+vIQIXTYxfT4yB/OJbjzQanB2RZpvmzBQOHZWhpAMZWYEKRNNbsD6WfBw5sVWkb6WzS/Q== @@ -4134,6 +4134,17 @@ connect@^3.6.5: parseurl "~1.3.3" utils-merge "1.0.1" +console-feed@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/console-feed/-/console-feed-3.0.1.tgz#2ca6dec57aee15ff3af300c323008fba542536a0" + integrity sha512-RD49E64OOm9qRAsbvTB0UnMFTtjEo7fKW8cSwqcgA9B45EraNFtFJjWppje+YPZpfhHHX579u+RtzLFm77mhTA== + dependencies: + "@emotion/core" "^10.0.10" + "@emotion/styled" "^10.0.12" + emotion-theming "^10.0.10" + linkifyjs "^2.1.6" + react-inspector "^3.0.2" + contains-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" @@ -4966,6 +4977,15 @@ emoji-regex@^9.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.0.0.tgz#48a2309cc8a1d2e9d23bc6a67c39b63032e76ea4" integrity sha512-6p1NII1Vm62wni/VR/cUMauVQoxmLVb9csqQlvLz+hO2gk8U2UYDfXHQSUYIBKmZwAKz867IDqG7B+u0mj+M6w== +emotion-theming@^10.0.10: + version "10.0.27" + resolved "https://registry.yarnpkg.com/emotion-theming/-/emotion-theming-10.0.27.tgz#1887baaec15199862c89b1b984b79806f2b9ab10" + integrity sha512-MlF1yu/gYh8u+sLUqA0YuA9JX0P4Hb69WlKc/9OLo+WCXuX6sy/KoIa+qJimgmr2dWqnypYKYPX37esjDBbhdw== + dependencies: + "@babel/runtime" "^7.5.5" + "@emotion/weak-memoize" "0.2.5" + hoist-non-react-statics "^3.3.0" + emotion@^10.0.23: version "10.0.27" resolved "https://registry.yarnpkg.com/emotion/-/emotion-10.0.27.tgz#f9ca5df98630980a23c819a56262560562e5d75e" @@ -6827,6 +6847,14 @@ is-docker@^2.0.0: resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.0.0.tgz#2cb0df0e75e2d064fe1864c37cdeacb7b2dcf25b" integrity sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ== +is-dom@^1.0.9: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-dom/-/is-dom-1.1.0.tgz#af1fced292742443bb59ca3f76ab5e80907b4e8a" + integrity sha512-u82f6mvhYxRPKpw8V1N0W8ce1xXwOrQtgGcxl6UCL5zBmZu3is/18K0rR7uFCnMDuAsS/3W54mGL4vsaFUQlEQ== + dependencies: + is-object "^1.0.1" + is-window "^1.0.2" + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -6916,6 +6944,11 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== +is-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" + integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + is-path-inside@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017" @@ -7012,6 +7045,11 @@ is-whitespace-character@^1.0.0: resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7" integrity sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w== +is-window@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d" + integrity sha1-LIlspT25feRdPDMTOmXYyfVjSA0= + is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -8194,6 +8232,11 @@ linked-list@0.1.0: resolved "https://registry.yarnpkg.com/linked-list/-/linked-list-0.1.0.tgz#798b0ff97d1b92a4fd08480f55aea4e9d49d37bf" integrity sha1-eYsP+X0bkqT9CEgPVa6k6dSdN78= +linkifyjs@^2.1.6: + version "2.1.9" + resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-2.1.9.tgz#af06e45a2866ff06c4766582590d098a4d584702" + integrity sha512-74ivurkK6WHvHFozVaGtQWV38FzBwSTGNmJolEgFp7QgR2bl6ArUWlvT4GcHKbPe1z3nWYi+VUdDZk16zDOVug== + live-plugin-manager@^0.14.1: version "0.14.1" resolved "https://registry.yarnpkg.com/live-plugin-manager/-/live-plugin-manager-0.14.1.tgz#34b6bb8f3d2062ec2557c857ca028fea9dc2b6db" @@ -9797,7 +9840,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.3" -prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.10, prop-types@^15.5.6, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -9995,6 +10038,15 @@ react-dom@^16.13.0: prop-types "^15.6.2" scheduler "^0.19.1" +react-inspector@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-3.0.2.tgz#c530a06101f562475537e47df428e1d7aff16ed8" + integrity sha512-PSR8xDoGFN8R3LKmq1NT+hBBwhxjd9Qwz8yKY+5NXY/CHpxXHm01CVabxzI7zFwFav/M3JoC/Z0Ro2kSX6Ef2Q== + dependencies: + babel-runtime "^6.26.0" + is-dom "^1.0.9" + prop-types "^15.6.1" + react-is@^16.12.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6, react-is@^16.9.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"