Code shuffle
Summary: Moved a lot of utility logic in separate files for Android and iOS, to make cleanup in next diffs easier by having a bit smaller files. Purely a code shuffle, no functional changes Reviewed By: passy Differential Revision: D28102399 fbshipit-source-id: 2fd8f6669bdd2804fa8a7e1791c610ae7883eda6
This commit is contained in:
committed by
Facebook GitHub Bot
parent
c0633652e8
commit
01ea822341
@@ -45,7 +45,6 @@ export {Idler, Notification} from 'flipper-plugin';
|
|||||||
export {Store, MiddlewareAPI, State as ReduxState} from './reducers/index';
|
export {Store, MiddlewareAPI, State as ReduxState} from './reducers/index';
|
||||||
export {default as BaseDevice} from './devices/BaseDevice';
|
export {default as BaseDevice} from './devices/BaseDevice';
|
||||||
export {DeviceLogEntry, LogLevel, DeviceLogListener} from 'flipper-plugin';
|
export {DeviceLogEntry, LogLevel, DeviceLogListener} from 'flipper-plugin';
|
||||||
export {shouldParseAndroidLog} from './utils/crashReporterUtility';
|
|
||||||
export {deconstructClientId} from './utils/clientUtils';
|
export {deconstructClientId} from './utils/clientUtils';
|
||||||
export {default as isProduction} from './utils/isProduction';
|
export {default as isProduction} from './utils/isProduction';
|
||||||
export {createTablePlugin} from './createTablePlugin';
|
export {createTablePlugin} from './createTablePlugin';
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 {DeviceLogEntry} from 'flipper-plugin';
|
|
||||||
|
|
||||||
export function shouldParseAndroidLog(
|
|
||||||
entry: DeviceLogEntry,
|
|
||||||
date: Date,
|
|
||||||
): boolean {
|
|
||||||
return (
|
|
||||||
entry.date.getTime() - date.getTime() > 0 && // The log should have arrived after the device has been registered
|
|
||||||
((entry.type === 'error' && entry.tag === 'AndroidRuntime') ||
|
|
||||||
entry.type === 'fatal')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {shouldParseAndroidLog} from '../crashReporterUtility.tsx';
|
import {DeviceLogEntry, LogLevel} from 'flipper-plugin';
|
||||||
import type {DeviceLogEntry, LogLevel} from '../../';
|
import {shouldParseAndroidLog} from '../android-crash-utils';
|
||||||
|
|
||||||
function getAndroidLog(
|
function getAndroidLog(
|
||||||
date: Date,
|
date: Date,
|
||||||
@@ -8,11 +8,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {BaseDevice} from 'flipper';
|
import {BaseDevice} from 'flipper';
|
||||||
import {Crash, shouldShowiOSCrashNotification} from '../index';
|
import {Crash} from '../index';
|
||||||
import {parseCrashLog, parsePath} from '../index';
|
|
||||||
import {TestUtils} from 'flipper-plugin';
|
import {TestUtils} from 'flipper-plugin';
|
||||||
import {getPluginKey} from 'flipper';
|
import {getPluginKey} from 'flipper';
|
||||||
import * as CrashReporterPlugin from '../index';
|
import * as CrashReporterPlugin from '../index';
|
||||||
|
import {parseCrashLog} from '../crash-utils';
|
||||||
|
import {parsePath, shouldShowiOSCrashNotification} from '../ios-crash-utils';
|
||||||
|
|
||||||
function getCrash(
|
function getCrash(
|
||||||
id: number,
|
id: number,
|
||||||
@@ -41,7 +42,7 @@ function assertCrash(crash: Crash, expectedCrash: Crash) {
|
|||||||
test('test the parsing of the date and crash info for the log which matches the predefined regex', () => {
|
test('test the parsing of the date and crash info for the log which matches the predefined regex', () => {
|
||||||
const log =
|
const log =
|
||||||
'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa Date/Time: 2019-03-21 12:07:00.861 +0000 \n Blaa balaaa';
|
'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa Date/Time: 2019-03-21 12:07:00.861 +0000 \n Blaa balaaa';
|
||||||
const crash = parseCrashLog(log, 'iOS', null);
|
const crash = parseCrashLog(log, 'iOS', undefined);
|
||||||
expect(crash.callstack).toEqual(log);
|
expect(crash.callstack).toEqual(log);
|
||||||
expect(crash.reason).toEqual('SIGSEGV');
|
expect(crash.reason).toEqual('SIGSEGV');
|
||||||
expect(crash.name).toEqual('SIGSEGV');
|
expect(crash.name).toEqual('SIGSEGV');
|
||||||
@@ -105,7 +106,7 @@ test('test the parsing of the Android crash log for the unknown crash format and
|
|||||||
});
|
});
|
||||||
test('test the parsing of the Android crash log for the partial format matching the crash format', () => {
|
test('test the parsing of the Android crash log for the partial format matching the crash format', () => {
|
||||||
const log = 'First Line Break \n Blaa Blaa \n Blaa Blaa ';
|
const log = 'First Line Break \n Blaa Blaa \n Blaa Blaa ';
|
||||||
const crash = parseCrashLog(log, 'Android', null);
|
const crash = parseCrashLog(log, 'Android', undefined);
|
||||||
expect(crash.callstack).toEqual(log);
|
expect(crash.callstack).toEqual(log);
|
||||||
expect(crash.reason).toEqual('Cannot figure out the cause');
|
expect(crash.reason).toEqual('Cannot figure out the cause');
|
||||||
expect(crash.name).toEqual('First Line Break ');
|
expect(crash.name).toEqual('First Line Break ');
|
||||||
@@ -113,7 +114,7 @@ test('test the parsing of the Android crash log for the partial format matching
|
|||||||
test('test the parsing of the Android crash log with os being iOS', () => {
|
test('test the parsing of the Android crash log with os being iOS', () => {
|
||||||
const log =
|
const log =
|
||||||
'FATAL EXCEPTION: main\nProcess: com.facebook.flipper.sample, PID: 27026\njava.lang.IndexOutOfBoundsException: Index: 190, Size: 0\n\tat java.util.ArrayList.get(ArrayList.java:437)\n\tat com.facebook.flipper.sample.RootComponentSpec.hitGetRequest(RootComponentSpec.java:72)\n\tat com.facebook.flipper.sample.RootComponent.hitGetRequest(RootComponent.java:46)\n';
|
'FATAL EXCEPTION: main\nProcess: com.facebook.flipper.sample, PID: 27026\njava.lang.IndexOutOfBoundsException: Index: 190, Size: 0\n\tat java.util.ArrayList.get(ArrayList.java:437)\n\tat com.facebook.flipper.sample.RootComponentSpec.hitGetRequest(RootComponentSpec.java:72)\n\tat com.facebook.flipper.sample.RootComponent.hitGetRequest(RootComponent.java:46)\n';
|
||||||
const crash = parseCrashLog(log, 'iOS', null);
|
const crash = parseCrashLog(log, 'iOS', undefined);
|
||||||
expect(crash.callstack).toEqual(log);
|
expect(crash.callstack).toEqual(log);
|
||||||
expect(crash.reason).toEqual('Cannot figure out the cause');
|
expect(crash.reason).toEqual('Cannot figure out the cause');
|
||||||
expect(crash.name).toEqual('Cannot figure out the cause');
|
expect(crash.name).toEqual('Cannot figure out the cause');
|
||||||
@@ -164,7 +165,7 @@ test('test getNewPersistedStateFromCrashLog for non-empty defaultPersistedState
|
|||||||
const pluginStateCrash = getCrash(0, 'callstack', 'crash1', 'crash1');
|
const pluginStateCrash = getCrash(0, 'callstack', 'crash1', 'crash1');
|
||||||
plugin.instance.reportCrash(pluginStateCrash);
|
plugin.instance.reportCrash(pluginStateCrash);
|
||||||
const content = 'Blaa Blaaa \n Blaa Blaaa';
|
const content = 'Blaa Blaaa \n Blaa Blaaa';
|
||||||
plugin.instance.reportCrash(parseCrashLog(content, 'iOS', null));
|
plugin.instance.reportCrash(parseCrashLog(content, 'iOS', undefined));
|
||||||
const crashes = plugin.instance.crashes.get();
|
const crashes = plugin.instance.crashes.get();
|
||||||
expect(crashes.length).toEqual(2);
|
expect(crashes.length).toEqual(2);
|
||||||
assertCrash(crashes[0], pluginStateCrash);
|
assertCrash(crashes[0], pluginStateCrash);
|
||||||
@@ -183,7 +184,9 @@ test('test getNewPersistedStateFromCrashLog when os is undefined', () => {
|
|||||||
const plugin = TestUtils.startDevicePlugin(CrashReporterPlugin);
|
const plugin = TestUtils.startDevicePlugin(CrashReporterPlugin);
|
||||||
const content = 'Blaa Blaaa \n Blaa Blaaa';
|
const content = 'Blaa Blaaa \n Blaa Blaaa';
|
||||||
expect(() => {
|
expect(() => {
|
||||||
plugin.instance.reportCrash(parseCrashLog(content, undefined as any, null));
|
plugin.instance.reportCrash(
|
||||||
|
parseCrashLog(content, undefined as any, undefined),
|
||||||
|
);
|
||||||
}).toThrowErrorMatchingInlineSnapshot(`"Unsupported OS"`);
|
}).toThrowErrorMatchingInlineSnapshot(`"Unsupported OS"`);
|
||||||
const crashes = plugin.instance.crashes.get();
|
const crashes = plugin.instance.crashes.get();
|
||||||
expect(crashes.length).toEqual(0);
|
expect(crashes.length).toEqual(0);
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* 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 type {DeviceLogEntry} from 'flipper-plugin';
|
||||||
|
import type {CrashLog} from './index';
|
||||||
|
|
||||||
|
export function parseAndroidCrash(
|
||||||
|
content: string,
|
||||||
|
fallbackReason: string,
|
||||||
|
logDate?: Date,
|
||||||
|
) {
|
||||||
|
const regForName = /.*\n/;
|
||||||
|
const nameRegArr = regForName.exec(content);
|
||||||
|
let name = nameRegArr ? nameRegArr[0] : fallbackReason;
|
||||||
|
const regForCallStack = /\tat[\w\s\n\.$&+,:;=?@#|'<>.^*()%!-]*$/;
|
||||||
|
const callStackArray = regForCallStack.exec(content);
|
||||||
|
const callStack = callStackArray ? callStackArray[0] : '';
|
||||||
|
let remainingString =
|
||||||
|
callStack.length > 0 ? content.replace(callStack, '') : '';
|
||||||
|
if (remainingString[remainingString.length - 1] === '\n') {
|
||||||
|
remainingString = remainingString.slice(0, -1);
|
||||||
|
}
|
||||||
|
const reasonText =
|
||||||
|
remainingString.length > 0
|
||||||
|
? remainingString.split('\n').pop()
|
||||||
|
: fallbackReason;
|
||||||
|
const reason = reasonText ? reasonText : fallbackReason;
|
||||||
|
if (name[name.length - 1] === '\n') {
|
||||||
|
name = name.slice(0, -1);
|
||||||
|
}
|
||||||
|
const crash: CrashLog = {
|
||||||
|
callstack: content,
|
||||||
|
name: name,
|
||||||
|
reason: reason,
|
||||||
|
date: logDate,
|
||||||
|
};
|
||||||
|
return crash;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldParseAndroidLog(
|
||||||
|
entry: DeviceLogEntry,
|
||||||
|
date: Date,
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
entry.date.getTime() - date.getTime() > 0 && // The log should have arrived after the device has been registered
|
||||||
|
((entry.type === 'error' && entry.tag === 'AndroidRuntime') ||
|
||||||
|
entry.type === 'fatal')
|
||||||
|
);
|
||||||
|
}
|
||||||
48
desktop/plugins/public/crash_reporter/crash-utils.tsx
Normal file
48
desktop/plugins/public/crash_reporter/crash-utils.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* 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 unicodeSubstring from 'unicode-substring';
|
||||||
|
import type {CrashLog} from './index';
|
||||||
|
import {parseAndroidCrash} from './android-crash-utils';
|
||||||
|
import {parseIosCrash} from './ios-crash-utils';
|
||||||
|
|
||||||
|
export const UNKNOWN_CRASH_REASON = 'Cannot figure out the cause';
|
||||||
|
|
||||||
|
export function parseCrashLog(
|
||||||
|
content: string,
|
||||||
|
os: string,
|
||||||
|
logDate?: Date,
|
||||||
|
): CrashLog {
|
||||||
|
const fallbackReason = UNKNOWN_CRASH_REASON;
|
||||||
|
switch (os) {
|
||||||
|
case 'iOS': {
|
||||||
|
return parseIosCrash(content, fallbackReason, logDate);
|
||||||
|
}
|
||||||
|
case 'Android': {
|
||||||
|
return parseAndroidCrash(content, fallbackReason, logDate);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new Error('Unsupported OS');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function truncate(baseString: string, numOfChars: number): string {
|
||||||
|
if (baseString.length <= numOfChars) {
|
||||||
|
return baseString;
|
||||||
|
}
|
||||||
|
const truncated_string = unicodeSubstring(baseString, 0, numOfChars - 1);
|
||||||
|
return truncated_string + '\u2026';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function trimCallStackIfPossible(callstack: string): string {
|
||||||
|
const regex = /Application Specific Information:/;
|
||||||
|
const query = regex.exec(callstack);
|
||||||
|
return query ? callstack.substring(0, query.index) : callstack;
|
||||||
|
}
|
||||||
@@ -15,27 +15,29 @@ import {
|
|||||||
ContextMenu,
|
ContextMenu,
|
||||||
clipboard,
|
clipboard,
|
||||||
Button,
|
Button,
|
||||||
shouldParseAndroidLog,
|
|
||||||
Text,
|
Text,
|
||||||
colors,
|
colors,
|
||||||
Toolbar,
|
Toolbar,
|
||||||
Spacer,
|
Spacer,
|
||||||
Select,
|
Select,
|
||||||
} from 'flipper';
|
} from 'flipper';
|
||||||
import unicodeSubstring from 'unicode-substring';
|
|
||||||
import fs from 'fs';
|
|
||||||
import os from 'os';
|
|
||||||
import path from 'path';
|
|
||||||
import {promisify} from 'util';
|
|
||||||
import type {DeviceLogEntry} from 'flipper';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
createState,
|
createState,
|
||||||
|
DeviceLogEntry,
|
||||||
DevicePluginClient,
|
DevicePluginClient,
|
||||||
usePlugin,
|
usePlugin,
|
||||||
useValue,
|
useValue,
|
||||||
} from 'flipper-plugin';
|
} from 'flipper-plugin';
|
||||||
import type {FSWatcher} from 'fs';
|
import type {FSWatcher} from 'fs';
|
||||||
|
import {
|
||||||
|
parseCrashLog,
|
||||||
|
trimCallStackIfPossible,
|
||||||
|
truncate,
|
||||||
|
UNKNOWN_CRASH_REASON,
|
||||||
|
} from './crash-utils';
|
||||||
|
import {addFileWatcherForiOSCrashLogs} from './ios-crash-utils';
|
||||||
|
import {shouldParseAndroidLog} from './android-crash-utils';
|
||||||
|
|
||||||
type Maybe<T> = T | null | undefined;
|
type Maybe<T> = T | null | undefined;
|
||||||
|
|
||||||
@@ -176,129 +178,6 @@ const StackTraceContainer = styled(FlexColumn)({
|
|||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
const UNKNOWN_CRASH_REASON = 'Cannot figure out the cause';
|
|
||||||
|
|
||||||
export function parseCrashLog(
|
|
||||||
content: string,
|
|
||||||
os: string,
|
|
||||||
logDate: Maybe<Date>,
|
|
||||||
): CrashLog {
|
|
||||||
const fallbackReason = UNKNOWN_CRASH_REASON;
|
|
||||||
switch (os) {
|
|
||||||
case 'iOS': {
|
|
||||||
const regex = /Exception Type: *\w*/;
|
|
||||||
const arr = regex.exec(content);
|
|
||||||
const exceptionString = arr ? arr[0] : '';
|
|
||||||
const exceptionRegex = /\w*$/;
|
|
||||||
const tmp = exceptionRegex.exec(exceptionString);
|
|
||||||
const exception = tmp && tmp[0].length ? tmp[0] : fallbackReason;
|
|
||||||
|
|
||||||
let date = logDate;
|
|
||||||
if (!date) {
|
|
||||||
const dateRegex = /Date\/Time: *[\w\s\.:-]*/;
|
|
||||||
const dateArr = dateRegex.exec(content);
|
|
||||||
const dateString = dateArr ? dateArr[0] : '';
|
|
||||||
const dateRegex2 = /[\w\s\.:-]*$/;
|
|
||||||
const tmp1 = dateRegex2.exec(dateString);
|
|
||||||
const extractedDateString: Maybe<string> =
|
|
||||||
tmp1 && tmp1[0].length ? tmp1[0] : null;
|
|
||||||
date = extractedDateString ? new Date(extractedDateString) : logDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
const crash: CrashLog = {
|
|
||||||
callstack: content,
|
|
||||||
name: exception,
|
|
||||||
reason: exception,
|
|
||||||
date,
|
|
||||||
};
|
|
||||||
return crash;
|
|
||||||
}
|
|
||||||
case 'Android': {
|
|
||||||
const regForName = /.*\n/;
|
|
||||||
const nameRegArr = regForName.exec(content);
|
|
||||||
let name = nameRegArr ? nameRegArr[0] : fallbackReason;
|
|
||||||
const regForCallStack = /\tat[\w\s\n\.$&+,:;=?@#|'<>.^*()%!-]*$/;
|
|
||||||
const callStackArray = regForCallStack.exec(content);
|
|
||||||
const callStack = callStackArray ? callStackArray[0] : '';
|
|
||||||
let remainingString =
|
|
||||||
callStack.length > 0 ? content.replace(callStack, '') : '';
|
|
||||||
if (remainingString[remainingString.length - 1] === '\n') {
|
|
||||||
remainingString = remainingString.slice(0, -1);
|
|
||||||
}
|
|
||||||
const reasonText =
|
|
||||||
remainingString.length > 0
|
|
||||||
? remainingString.split('\n').pop()
|
|
||||||
: fallbackReason;
|
|
||||||
const reason = reasonText ? reasonText : fallbackReason;
|
|
||||||
if (name[name.length - 1] === '\n') {
|
|
||||||
name = name.slice(0, -1);
|
|
||||||
}
|
|
||||||
const crash: CrashLog = {
|
|
||||||
callstack: content,
|
|
||||||
name: name,
|
|
||||||
reason: reason,
|
|
||||||
date: logDate,
|
|
||||||
};
|
|
||||||
return crash;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
throw new Error('Unsupported OS');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function truncate(baseString: string, numOfChars: number): string {
|
|
||||||
if (baseString.length <= numOfChars) {
|
|
||||||
return baseString;
|
|
||||||
}
|
|
||||||
const truncated_string = unicodeSubstring(baseString, 0, numOfChars - 1);
|
|
||||||
return truncated_string + '\u2026';
|
|
||||||
}
|
|
||||||
|
|
||||||
export function parsePath(content: string): Maybe<string> {
|
|
||||||
const regex = /(?<=.*Path: *)[^\n]*/;
|
|
||||||
const arr = regex.exec(content);
|
|
||||||
if (!arr || arr.length <= 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const path = arr[0];
|
|
||||||
return path.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
function addFileWatcherForiOSCrashLogs(
|
|
||||||
deviceOs: string,
|
|
||||||
serial: string,
|
|
||||||
reportCrash: (payload: CrashLog | Crash) => void,
|
|
||||||
) {
|
|
||||||
const dir = path.join(os.homedir(), 'Library', 'Logs', 'DiagnosticReports');
|
|
||||||
if (!fs.existsSync(dir)) {
|
|
||||||
// Directory doesn't exist
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return fs.watch(dir, (_eventType, filename) => {
|
|
||||||
// We just parse the crash logs with extension `.crash`
|
|
||||||
const checkFileExtension = /.crash$/.exec(filename);
|
|
||||||
if (!filename || !checkFileExtension) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const filepath = path.join(dir, filename);
|
|
||||||
promisify(fs.exists)(filepath).then((exists) => {
|
|
||||||
if (!exists) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fs.readFile(filepath, 'utf8', function (err, data) {
|
|
||||||
if (err) {
|
|
||||||
console.warn('Failed to read crash file', err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (shouldShowiOSCrashNotification(serial, data)) {
|
|
||||||
reportCrash(parseCrashLog(data, deviceOs, null));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class CrashSelector extends React.Component<CrashSelectorProps> {
|
class CrashSelector extends React.Component<CrashSelectorProps> {
|
||||||
render() {
|
render() {
|
||||||
const {crashes, selectedCrashID, orderedIDs, onCrashChange} = this.props;
|
const {crashes, selectedCrashID, orderedIDs, onCrashChange} = this.props;
|
||||||
@@ -632,21 +511,3 @@ export function Component() {
|
|||||||
</StyledFlexGrowColumn>
|
</StyledFlexGrowColumn>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function trimCallStackIfPossible(callstack: string): string {
|
|
||||||
const regex = /Application Specific Information:/;
|
|
||||||
const query = regex.exec(callstack);
|
|
||||||
return query ? callstack.substring(0, query.index) : callstack;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function shouldShowiOSCrashNotification(
|
|
||||||
serial: string,
|
|
||||||
content: string,
|
|
||||||
): boolean {
|
|
||||||
const appPath = parsePath(content);
|
|
||||||
if (!appPath || !appPath.includes(serial)) {
|
|
||||||
// Do not show notifications for the app which are not running on this device
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|||||||
104
desktop/plugins/public/crash_reporter/ios-crash-utils.tsx
Normal file
104
desktop/plugins/public/crash_reporter/ios-crash-utils.tsx
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* 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 type {Crash, CrashLog} from './index';
|
||||||
|
import {parseCrashLog} from './crash-utils';
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
import path from 'path';
|
||||||
|
import {promisify} from 'util';
|
||||||
|
|
||||||
|
export function parseIosCrash(
|
||||||
|
content: string,
|
||||||
|
fallbackReason: string,
|
||||||
|
logDate?: Date,
|
||||||
|
) {
|
||||||
|
const regex = /Exception Type: *\w*/;
|
||||||
|
const arr = regex.exec(content);
|
||||||
|
const exceptionString = arr ? arr[0] : '';
|
||||||
|
const exceptionRegex = /\w*$/;
|
||||||
|
const tmp = exceptionRegex.exec(exceptionString);
|
||||||
|
const exception = tmp && tmp[0].length ? tmp[0] : fallbackReason;
|
||||||
|
|
||||||
|
let date = logDate;
|
||||||
|
if (!date) {
|
||||||
|
const dateRegex = /Date\/Time: *[\w\s\.:-]*/;
|
||||||
|
const dateArr = dateRegex.exec(content);
|
||||||
|
const dateString = dateArr ? dateArr[0] : '';
|
||||||
|
const dateRegex2 = /[\w\s\.:-]*$/;
|
||||||
|
const tmp1 = dateRegex2.exec(dateString);
|
||||||
|
const extractedDateString: string | null =
|
||||||
|
tmp1 && tmp1[0].length ? tmp1[0] : null;
|
||||||
|
date = extractedDateString ? new Date(extractedDateString) : logDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
const crash: CrashLog = {
|
||||||
|
callstack: content,
|
||||||
|
name: exception,
|
||||||
|
reason: exception,
|
||||||
|
date,
|
||||||
|
};
|
||||||
|
return crash;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shouldShowiOSCrashNotification(
|
||||||
|
serial: string,
|
||||||
|
content: string,
|
||||||
|
): boolean {
|
||||||
|
const appPath = parsePath(content);
|
||||||
|
if (!appPath || !appPath.includes(serial)) {
|
||||||
|
// Do not show notifications for the app which are not running on this device
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parsePath(content: string): string | null {
|
||||||
|
const regex = /(?<=.*Path: *)[^\n]*/;
|
||||||
|
const arr = regex.exec(content);
|
||||||
|
if (!arr || arr.length <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const path = arr[0];
|
||||||
|
return path.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addFileWatcherForiOSCrashLogs(
|
||||||
|
deviceOs: string,
|
||||||
|
serial: string,
|
||||||
|
reportCrash: (payload: CrashLog | Crash) => void,
|
||||||
|
) {
|
||||||
|
const dir = path.join(os.homedir(), 'Library', 'Logs', 'DiagnosticReports');
|
||||||
|
if (!fs.existsSync(dir)) {
|
||||||
|
// Directory doesn't exist
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return fs.watch(dir, (_eventType, filename) => {
|
||||||
|
// We just parse the crash logs with extension `.crash`
|
||||||
|
const checkFileExtension = /.crash$/.exec(filename);
|
||||||
|
if (!filename || !checkFileExtension) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const filepath = path.join(dir, filename);
|
||||||
|
promisify(fs.exists)(filepath).then((exists) => {
|
||||||
|
if (!exists) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fs.readFile(filepath, 'utf8', function (err, data) {
|
||||||
|
if (err) {
|
||||||
|
console.warn('Failed to read crash file', err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (shouldShowiOSCrashNotification(serial, data)) {
|
||||||
|
reportCrash(parseCrashLog(data, deviceOs, undefined));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user