Crash reporter plugin

Summary: This diff adds a static function `onRegisterDevice` which is being called whenever an  device gets registered. This callback is used to add loglisterner for android. I even moved the logic of iOS from `onRegisterPlugin` to this callback. The reason for not adding android log listener in `onRegisterPlugin` was that there were cases when baseDevice was not yet registered before calling `onRegisterPlugin`. For android, I want the instance of `BaseDevice` so that I can add logListener on it.

Reviewed By: danielbuechele

Differential Revision: D13563282

fbshipit-source-id: b5be40f3dbc808bdaeabae28423c563cf2345a22
This commit is contained in:
Pritesh Nandgaonkar
2019-01-09 10:40:22 -08:00
committed by Facebook Github Bot
parent c6efea991d
commit 0048fc6e4a
7 changed files with 109 additions and 38 deletions

View File

@@ -11,6 +11,7 @@ import promiseRetry from 'promise-retry';
import type {Store} from '../reducers/index.js'; import type {Store} from '../reducers/index.js';
import type BaseDevice from '../devices/BaseDevice'; import type BaseDevice from '../devices/BaseDevice';
import type Logger from '../fb-stubs/Logger.js'; import type Logger from '../fb-stubs/Logger.js';
import {registerDeviceCallbackOnPlugins} from '../utils/onRegisterDevice.js';
const adb = require('adbkit-fb'); const adb = require('adbkit-fb');
function createDevice(adbClient, device): Promise<AndroidDevice> { function createDevice(adbClient, device): Promise<AndroidDevice> {
@@ -165,6 +166,13 @@ export default (store: Store, logger: Logger) => {
type: 'REGISTER_DEVICE', type: 'REGISTER_DEVICE',
payload: androidDevice, payload: androidDevice,
}); });
registerDeviceCallbackOnPlugins(
store,
store.getState().plugins.devicePlugins,
store.getState().plugins.clientPlugins,
androidDevice,
);
} }
async function unregisterDevices(deviceIds: Array<string>) { async function unregisterDevices(deviceIds: Array<string>) {

View File

@@ -18,7 +18,7 @@ import IOSDevice from '../devices/IOSDevice';
import iosUtil from '../fb-stubs/iOSContainerUtility'; import iosUtil from '../fb-stubs/iOSContainerUtility';
import isProduction from '../utils/isProduction.js'; import isProduction from '../utils/isProduction.js';
import GK from '../fb-stubs/GK'; import GK from '../fb-stubs/GK';
import {registerDeviceCallbackOnPlugins} from '../utils/onRegisterDevice.js';
type iOSSimulatorDevice = {| type iOSSimulatorDevice = {|
state: 'Booted' | 'Shutdown' | 'Shutting Down', state: 'Booted' | 'Shutdown' | 'Shutting Down',
availability?: string, availability?: string,
@@ -70,10 +70,17 @@ function queryDevices(store: Store, logger: Logger): Promise<void> {
name: name, name: name,
serial: udid, serial: udid,
}); });
const iOSDevice = new IOSDevice(udid, type, name);
store.dispatch({ store.dispatch({
type: 'REGISTER_DEVICE', type: 'REGISTER_DEVICE',
payload: new IOSDevice(udid, type, name), payload: iOSDevice,
}); });
registerDeviceCallbackOnPlugins(
store,
store.getState().plugins.devicePlugins,
store.getState().plugins.clientPlugins,
iOSDevice,
);
} }
} }

View File

@@ -56,26 +56,6 @@ export default (store: Store, logger: Logger) => {
store.dispatch(addFailedPlugins(failedPlugins)); store.dispatch(addFailedPlugins(failedPlugins));
store.dispatch(registerPlugins(initialPlugins)); store.dispatch(registerPlugins(initialPlugins));
initialPlugins.forEach(p => {
if (p.onRegisterPlugin) {
p.onRegisterPlugin(store, (pluginKey: string, newPluginState: any) => {
const persistedState = getPersistedState(
pluginKey,
p,
store.getState().pluginStates,
);
if (newPluginState && newPluginState !== persistedState) {
store.dispatch(
setPluginState({
pluginKey: pluginKey,
state: newPluginState,
}),
);
}
});
}
});
let state: ?State = null; let state: ?State = null;
store.subscribe(() => { store.subscribe(() => {
const newState = store.getState().plugins; const newState = store.getState().plugins;

View File

@@ -26,7 +26,7 @@ export type {Store} from './reducers/index.js';
export { export {
default as SidebarExtensions, default as SidebarExtensions,
} from './fb-stubs/LayoutInspectorSidebarExtensions.js'; } from './fb-stubs/LayoutInspectorSidebarExtensions.js';
export {DeviceLogListener, DeviceLogEntry} from './devices/BaseDevice.js';
export {createTablePlugin} from './createTablePlugin.js'; export {createTablePlugin} from './createTablePlugin.js';
export {default as DetailSidebar} from './chrome/DetailSidebar.js'; export {default as DetailSidebar} from './chrome/DetailSidebar.js';

View File

@@ -70,8 +70,9 @@ export class FlipperBasePlugin<
static getActiveNotifications: ?( static getActiveNotifications: ?(
persistedState: PersistedState, persistedState: PersistedState,
) => Array<Notification>; ) => Array<Notification>;
static onRegisterPlugin: ?( static onRegisterDevice: ?(
store: Store, store: Store,
baseDevice: BaseDevice,
setPersistedState: ( setPersistedState: (
pluginKey: string, pluginKey: string,
newPluginState: ?PersistedState, newPluginState: ?PersistedState,

View File

@@ -26,7 +26,7 @@ import os from 'os';
import util from 'util'; import util from 'util';
import path from 'path'; import path from 'path';
import type {Notification} from '../../plugin'; import type {Notification} from '../../plugin';
import type {Store} from 'flipper'; import type {Store, DeviceLogEntry} from 'flipper';
export type Crash = {| export type Crash = {|
notificationID: string, notificationID: string,
@@ -107,6 +107,7 @@ export function parseCrashLogAndUpdateState(
!shouldShowCrashNotification( !shouldShowCrashNotification(
store.getState().connections.selectedDevice, store.getState().connections.selectedDevice,
content, content,
store.getState().connections.selectedDevice?.os,
) )
) { ) {
return; return;
@@ -140,7 +141,11 @@ export function parseCrashLogAndUpdateState(
export function shouldShowCrashNotification( export function shouldShowCrashNotification(
baseDevice: ?BaseDevice, baseDevice: ?BaseDevice,
content: string, content: string,
os: ?string,
): boolean { ): boolean {
if (os && os.includes('Android')) {
return true;
}
const appPath = parsePath(content); const appPath = parsePath(content);
const serial: string = baseDevice?.serial || 'unknown'; const serial: string = baseDevice?.serial || 'unknown';
if (!appPath || !appPath.includes(serial)) { if (!appPath || !appPath.includes(serial)) {
@@ -253,19 +258,6 @@ export default class CrashReporterPlugin extends FlipperDevicePlugin<
return persistedState; return persistedState;
}; };
/*
* This function gets called whenever plugin is registered
*/
static onRegisterPlugin = (
store: Store,
setPersistedState: (
pluginKey: string,
newPluginState: ?PersistedState,
) => void,
): void => {
addFileWatcherForiOSCrashLogs(store, setPersistedState);
};
static trimCallStackIfPossible = (callstack: string): string => { static trimCallStackIfPossible = (callstack: string): string => {
let regex = /Application Specific Information:/; let regex = /Application Specific Information:/;
const query = regex.exec(callstack); const query = regex.exec(callstack);
@@ -289,6 +281,45 @@ export default class CrashReporterPlugin extends FlipperDevicePlugin<
}); });
}; };
/*
* This function gets called whenever the device is registered
*/
static onRegisterDevice = (
store: Store,
baseDevice: BaseDevice,
setPersistedState: (
pluginKey: string,
newPluginState: ?PersistedState,
) => void,
): void => {
if (baseDevice.os.includes('iOS')) {
addFileWatcherForiOSCrashLogs(store, setPersistedState);
} else {
const referenceDate = new Date();
(function(
store: Store,
date: Date,
setPersistedState: (
pluginKey: string,
newPluginState: ?PersistedState,
) => void,
) {
baseDevice.addLogListener((entry: DeviceLogEntry) => {
if (
entry.type === 'error' &&
entry.tag === 'AndroidRuntime' &&
entry.date.getTime() - date.getTime() > 0
) {
parseCrashLogAndUpdateState(
store,
entry.message,
setPersistedState,
);
}
});
})(store, referenceDate, setPersistedState);
}
};
openInLogs = (callstack: string) => { openInLogs = (callstack: string) => {
this.props.selectPlugin('DeviceLogs', callstack); this.props.selectPlugin('DeviceLogs', callstack);
}; };

View File

@@ -0,0 +1,44 @@
/**
* Copyright 2018-present Facebook.
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
* @format
*/
import type {Store} from '../reducers/index.js';
import {FlipperPlugin, FlipperDevicePlugin} from '../plugin.js';
import type BaseDevice from '../devices/BaseDevice.js';
import {setPluginState} from '../reducers/pluginStates.js';
import {getPersistedState} from '../utils/pluginUtils.js';
export function registerDeviceCallbackOnPlugins(
store: Store,
devicePlugins: Map<string, Class<FlipperDevicePlugin<>>>,
clientPlugins: Map<string, Class<FlipperPlugin<>>>,
device: BaseDevice,
) {
const callRegisterDeviceHook = plugin => {
if (plugin.onRegisterDevice) {
plugin.onRegisterDevice(
store,
device,
(pluginKey: string, newPluginState: any) => {
const persistedState = getPersistedState(
pluginKey,
plugin,
store.getState().pluginStates,
);
if (newPluginState && newPluginState !== persistedState) {
store.dispatch(
setPluginState({
pluginKey,
state: newPluginState,
}),
);
}
},
);
}
};
devicePlugins.forEach(callRegisterDeviceHook);
clientPlugins.forEach(callRegisterDeviceHook);
}