Add RecurringError class for errors that should only be logged once per session

Summary:
Some errors such as UI errors should be logged whenever they occur, but some, such as those that occur when a device keeps trying to connect but can't, should not be.
This adds the class and replaces the top recurring errors with it.

Reviewed By: danielbuechele

Differential Revision: D8639448

fbshipit-source-id: f001aa1e90eae6d26a8dbfcd3175b51fc486eae9
This commit is contained in:
John Knox
2018-07-05 07:06:57 -07:00
committed by Facebook Github Bot
parent 0ddeb076bb
commit de353a7ed0
3 changed files with 34 additions and 13 deletions

View File

@@ -13,6 +13,7 @@ import CertificateProvider from './utils/CertificateProvider';
import {RSocketServer, ReactiveSocket} from 'rsocket-core'; import {RSocketServer, ReactiveSocket} from 'rsocket-core';
import RSocketTCPServer from 'rsocket-tcp-server'; import RSocketTCPServer from 'rsocket-tcp-server';
import Client from './Client.js'; import Client from './Client.js';
import {RecurringError} from './utils/errors';
const EventEmitter = (require('events'): any); const EventEmitter = (require('events'): any);
const invariant = require('invariant'); const invariant = require('invariant');
@@ -261,10 +262,11 @@ class ConnectionTracker {
this.connectionAttempts.set(key, entry); this.connectionAttempts.set(key, entry);
if (entry.length >= this.connectionProblemThreshold) { if (entry.length >= this.connectionProblemThreshold) {
console.error( console.error(
`Connection loop detected with ${key}. Connected ${ new RecurringError(
entry.length `Connection loop detected with ${key}. Connected ${
} times in ${(time - entry[0]) / 1000}s.`, this.connectionProblemThreshold
'ConnectionTracker', } times within ${this.timeWindowMillis / 1000}s.`,
),
); );
} }
} }

View File

@@ -6,6 +6,7 @@
*/ */
import LogManager from '../fb-stubs/Logger'; import LogManager from '../fb-stubs/Logger';
import {RecurringError} from './errors';
const fs = require('fs'); const fs = require('fs');
const adb = require('adbkit-fb'); const adb = require('adbkit-fb');
import { import {
@@ -82,7 +83,7 @@ export default class CertificateProvider {
this.ensureOpenSSLIsAvailable(); this.ensureOpenSSLIsAvailable();
if (!appDirectory.match(allowedAppDirectoryRegex)) { if (!appDirectory.match(allowedAppDirectoryRegex)) {
return Promise.reject( return Promise.reject(
new Error( new RecurringError(
`Invalid appDirectory recieved from ${os} device: ${appDirectory}`, `Invalid appDirectory recieved from ${os} device: ${appDirectory}`,
), ),
); );
@@ -174,7 +175,7 @@ export default class CertificateProvider {
fs.writeFileSync(destination + filename, contents); fs.writeFileSync(destination + filename, contents);
return Promise.resolve(); return Promise.resolve();
} }
return Promise.reject(new Error(`Unsupported device os: ${os}`)); return Promise.reject(new RecurringError(`Unsupported device os: ${os}`));
} }
getTargetDeviceId( getTargetDeviceId(
@@ -209,7 +210,9 @@ export default class CertificateProvider {
return Promise.all(deviceMatchList).then(devices => { return Promise.all(deviceMatchList).then(devices => {
const matchingIds = devices.filter(m => m.isMatch).map(m => m.id); const matchingIds = devices.filter(m => m.isMatch).map(m => m.id);
if (matchingIds.length == 0) { if (matchingIds.length == 0) {
throw new Error(`No matching device found for app: ${appName}`); throw new RecurringError(
`No matching device found for app: ${appName}`,
);
} }
return matchingIds[0]; return matchingIds[0];
}); });
@@ -261,11 +264,13 @@ export default class CertificateProvider {
command: string, command: string,
): Promise<string> { ): Promise<string> {
if (!user.match(allowedAppNameRegex)) { if (!user.match(allowedAppNameRegex)) {
return Promise.reject(new Error(`Disallowed run-as user: ${user}`)); return Promise.reject(
new RecurringError(`Disallowed run-as user: ${user}`),
);
} }
if (command.match(/[']/)) { if (command.match(/[']/)) {
return Promise.reject( return Promise.reject(
new Error(`Disallowed escaping command: ${command}`), new RecurringError(`Disallowed escaping command: ${command}`),
); );
} }
return this.adb return this.adb
@@ -274,14 +279,14 @@ export default class CertificateProvider {
.then(buffer => buffer.toString()) .then(buffer => buffer.toString())
.then(output => { .then(output => {
if (output.match(appNotDebuggableRegex)) { if (output.match(appNotDebuggableRegex)) {
const e = new Error( const e = new RecurringError(
`Android app ${user} is not debuggable. To use it with sonar, add android:debuggable="true" to the application section of AndroidManifest.xml`, `Android app ${user} is not debuggable. To use it with sonar, add android:debuggable="true" to the application section of AndroidManifest.xml`,
); );
this.server.emit('error', e); this.server.emit('error', e);
throw e; throw e;
} }
if (output.toLowerCase().match(operationNotPermittedRegex)) { if (output.toLowerCase().match(operationNotPermittedRegex)) {
const e = new Error( const e = new RecurringError(
`Your android device (${deviceId}) does not support the adb shell run-as command. We're tracking this at https://github.com/facebook/Sonar/issues/92`, `Your android device (${deviceId}) does not support the adb shell run-as command. We're tracking this at https://github.com/facebook/Sonar/issues/92`,
); );
this.server.emit('error', e); this.server.emit('error', e);
@@ -307,13 +312,13 @@ export default class CertificateProvider {
.then(subject => { .then(subject => {
const matches = subject.trim().match(x509SubjectCNRegex); const matches = subject.trim().match(x509SubjectCNRegex);
if (!matches || matches.length < 2) { if (!matches || matches.length < 2) {
throw new Error(`Cannot extract CN from ${subject}`); throw new RecurringError(`Cannot extract CN from ${subject}`);
} }
return matches[1]; return matches[1];
}) })
.then(appName => { .then(appName => {
if (!appName.match(allowedAppNameRegex)) { if (!appName.match(allowedAppNameRegex)) {
throw new Error( throw new RecurringError(
`Disallowed app name in CSR: ${appName}. Only alphanumeric characters and '.' allowed.`, `Disallowed app name in CSR: ${appName}. Only alphanumeric characters and '.' allowed.`,
); );
} }

14
src/utils/errors.js Normal file
View File

@@ -0,0 +1,14 @@
/**
* 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
*/
// Class of errors that may keep repeating but should only be logged once.
export class RecurringError extends Error {
constructor(message: string) {
super(message);
this.name = 'RecurringError';
}
}