Repository: facebook/react
Pull request: [DevTools] Log basic usage events

## Summary

This commit adds a few additional events to the `Logger` to capture basic usage events for when DevTools is loaded, and when different tabs are selected.

**NOTE** that this only applies when the `enableLogger` feature flag is enabled, which is only the case for internal builds of DevTools


## How did you test this change?

- yarn flow
- yarn test
- yarn test-build-devtools
- ran extension with logging enabled, verified that works and events are logged when switching tabs:
![image](https://user-images.githubusercontent.com/1271509/135526052-ed71aa40-b655-4fc3-82fd-0590bed4f2a5.png)
- ran standalone app and verified that switching apps works correctly and that event is logged correctly in that case


Changed files:
filename: packages/react-devtools-extensions/src/main.js
patch: @@ -21,6 +21,7 @@ import {
 import DevTools from 'react-devtools-shared/src/devtools/views/DevTools';
 import {__DEBUG__} from 'react-devtools-shared/src/constants';
 import {registerExtensionsEventLogger} from './registerExtensionsEventLogger';
+import {logEvent} from 'react-devtools-shared/src/Logger';
 
 const LOCAL_STORAGE_SUPPORTS_PROFILING_KEY =
   'React::DevTools::supportsProfiling';
@@ -449,6 +450,7 @@ function createPanelIfReactLoaded() {
               ensureInitialHTMLIsCleared(componentsPortalContainer);
               render('components');
               panel.injectStyles(cloneStyleTags);
+              logEvent({event_name: 'selected-components-tab'});
             }
           });
           extensionPanel.onHidden.addListener(panel => {
@@ -474,6 +476,7 @@ function createPanelIfReactLoaded() {
               ensureInitialHTMLIsCleared(profilerPortalContainer);
               render('profiler');
               panel.injectStyles(cloneStyleTags);
+              logEvent({event_name: 'selected-profiler-tab'});
             }
           });
         },

filename: packages/react-devtools-shared/src/Logger.js
patch: @@ -9,17 +9,23 @@
 
 import {enableLogger} from 'react-devtools-feature-flags';
 
-type LoadHookNamesEvent = {|
-  +event_name: 'loadHookNames',
-  +event_status: 'success' | 'error' | 'timeout' | 'unknown',
-  +duration_ms: number,
-  +inspected_element_display_name: string | null,
-  +inspected_element_number_of_hooks: number | null,
-|};
-
-// prettier-ignore
 export type LogEvent =
-  | LoadHookNamesEvent;
+  | {|
+      +event_name: 'loaded-dev-tools',
+    |}
+  | {|
+      +event_name: 'selected-components-tab',
+    |}
+  | {|
+      +event_name: 'selected-profiler-tab',
+    |}
+  | {|
+      +event_name: 'load-hook-names',
+      +event_status: 'success' | 'error' | 'timeout' | 'unknown',
+      +duration_ms: number,
+      +inspected_element_display_name: string | null,
+      +inspected_element_number_of_hooks: number | null,
+    |};
 
 export type LogFunction = LogEvent => void;
 

filename: packages/react-devtools-shared/src/devtools/views/DevTools.js
patch: @@ -13,7 +13,7 @@ import '@reach/menu-button/styles.css';
 import '@reach/tooltip/styles.css';
 
 import * as React from 'react';
-import {useEffect, useLayoutEffect, useMemo, useRef} from 'react';
+import {useCallback, useEffect, useLayoutEffect, useMemo, useRef} from 'react';
 import Store from '../store';
 import {
   BridgeContext,
@@ -47,6 +47,7 @@ import type {InspectedElement} from 'react-devtools-shared/src/devtools/views/Co
 import type {FetchFileWithCaching} from './Components/FetchFileWithCachingContext';
 import type {HookNamesModuleLoaderFunction} from 'react-devtools-shared/src/devtools/views/Components/HookNamesModuleLoaderContext';
 import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
+import {logEvent} from '../../Logger';
 
 export type BrowserTheme = 'dark' | 'light';
 export type TabID = 'components' | 'profiler';
@@ -152,6 +153,24 @@ export default function DevTools({
     tab = overrideTab;
   }
 
+  const selectTab = useCallback(
+    (tabId: TabID) => {
+      // We show the TabBar when DevTools is NOT rendered as a browser extension.
+      // In this case, we want to capture when people select tabs with the TabBar.
+      // When DevTools is rendered as an extension, we capture this event when
+      // the browser devtools panel changes.
+      if (showTabBar === true) {
+        if (tabId === 'components') {
+          logEvent({event_name: 'selected-components-tab'});
+        } else {
+          logEvent({event_name: 'selected-profiler-tab'});
+        }
+      }
+      setTab(tabId);
+    },
+    [setTab, showTabBar],
+  );
+
   const options = useMemo(
     () => ({
       readOnly: readOnly || false,
@@ -204,12 +223,12 @@ export default function DevTools({
       if (event.ctrlKey || event.metaKey) {
         switch (event.key) {
           case '1':
-            setTab(tabs[0].id);
+            selectTab(tabs[0].id);
             event.preventDefault();
             event.stopPropagation();
             break;
           case '2':
-            setTab(tabs[1].id);
+            selectTab(tabs[1].id);
             event.preventDefault();
             event.stopPropagation();
             break;
@@ -232,6 +251,10 @@ export default function DevTools({
       }
     };
   }, [bridge]);
+
+  useEffect(() => {
+    logEvent({event_name: 'loaded-dev-tools'});
+  }, []);
   return (
     <BridgeContext.Provider value={bridge}>
       <StoreContext.Provider value={store}>
@@ -265,7 +288,7 @@ export default function DevTools({
                                     <TabBar
                                       currentTab={tab}
                                       id="DevTools"
-                                      selectTab={setTab}
+                                      selectTab={selectTab}
                                       tabs={tabs}
                                       type="navigation"
                                     />

filename: packages/react-devtools-shared/src/hookNamesCache.js
patch: @@ -116,7 +116,7 @@ export function loadHookNames(
     const handleLoadComplete = (durationMs: number): void => {
       // Log duration for parsing hook names
       logEvent({
-        event_name: 'loadHookNames',
+        event_name: 'load-hook-names',
         event_status: status,
         duration_ms: durationMs,
         inspected_element_display_name: element.displayName,