Log refactoring (#1393)

* Create Logger class and log instance

* Replace yomichan.logWarning with log.warn

* Replace yomichan.logError with log.error

* Replace yomichan.log with log.log

* Update the Yomichan class to use the global log object

* Update lint rules
This commit is contained in:
toasted-nutbread 2021-02-14 17:52:01 -05:00 committed by GitHub
parent 286534e648
commit 9279ced686
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 144 additions and 96 deletions

View File

@ -111,9 +111,11 @@
"deepEqual": "readonly",
"generateId": "readonly",
"promiseAnimationFrame": "readonly",
"log": "readonly",
"DynamicProperty": "readonly",
"EventDispatcher": "readonly",
"EventListenerCollection": "readonly"
"EventListenerCollection": "readonly",
"Logger": "readonly"
}
},
{

View File

@ -52,6 +52,6 @@
yomichan.ready();
} catch (e) {
yomichan.logError(e);
log.error(e);
}
})();

View File

@ -276,7 +276,7 @@ class Frontend {
this._showExtensionUnloaded(textSource);
}
} else {
yomichan.logError(error);
log.error(error);
}
} if (type !== null) {
this._stopClearSelectionDelayed();
@ -563,7 +563,7 @@ class Frontend {
);
this._lastShowPromise.catch((error) => {
if (yomichan.isExtensionUnloaded) { return; }
yomichan.logError(error);
log.error(error);
});
return this._lastShowPromise;
}

View File

@ -201,7 +201,7 @@ class PopupProxy extends EventDispatcher {
}
this._frameOffsetUpdatedAt = now;
} catch (e) {
yomichan.logError(e);
log.error(e);
} finally {
this._frameOffsetPromise = null;
}

View File

@ -204,7 +204,7 @@ class Backend {
try {
await this._dictionaryDatabase.prepare();
} catch (e) {
yomichan.logError(e);
log.error(e);
}
const deinflectionReasions = await this._fetchAsset('/data/deinflect.json', true);
@ -226,7 +226,7 @@ class Backend {
this._sendMessageAllTabsIgnoreResponse('backendReady', {});
this._sendMessageIgnoreResponse({action: 'backendReady', params: {}});
} catch (e) {
yomichan.logError(e);
log.error(e);
throw e;
} finally {
if (this._badgePrepareDelayTimer !== null) {
@ -358,7 +358,7 @@ class Backend {
forwardPort.onDisconnect.addListener(cleanup);
} catch (e) {
port.disconnect();
yomichan.logError(e);
log.error(e);
}
}
@ -612,7 +612,7 @@ class Backend {
}
_onApiLog({error, level, context}) {
yomichan.log(deserializeError(error), level, context);
log.log(deserializeError(error), level, context);
}
_onApiLogIndicatorClear() {

View File

@ -111,7 +111,7 @@ class CrossFrameAPIPort extends EventDispatcher {
_onAck(id) {
const invocation = this._activeInvocations.get(id);
if (typeof invocation === 'undefined') {
yomichan.logWarning(new Error(`Request ${id} not found for acknowledgement`));
log.warn(new Error(`Request ${id} not found for acknowledgement`));
return;
}
@ -140,7 +140,7 @@ class CrossFrameAPIPort extends EventDispatcher {
_onResult(id, data) {
const invocation = this._activeInvocations.get(id);
if (typeof invocation === 'undefined') {
yomichan.logWarning(new Error(`Request ${id} not found`));
log.warn(new Error(`Request ${id} not found`));
return;
}
@ -269,7 +269,7 @@ class CrossFrameAPI {
this._setupCommPort(otherTabId, otherFrameId, port);
} catch (e) {
port.disconnect();
yomichan.logError(e);
log.error(e);
}
}

View File

@ -607,3 +607,111 @@ class DynamicProperty extends EventDispatcher {
this.trigger('change', {value});
}
}
/**
* This class handles logging of messages to the console and triggering
* an event for log calls.
*/
class Logger extends EventDispatcher {
/**
* Creates a new instance.
*/
constructor() {
super();
this._extensionName = 'Yomichan';
try {
const {name, version} = chrome.runtime.getManifest();
this._extensionName = `${name} ${version}`;
} catch (e) {
// NOP
}
}
/**
* Logs a generic error. This will trigger the 'log' event with the same arguments as the function invocation.
* @param error The error to log. This is typically an `Error` or `Error`-like object.
* @param level The level to log at. Values include `'info'`, `'debug'`, `'warn'`, and `'error'`.
* Other values will be logged at a non-error level.
* @param context An optional context object for the error which should typically include a `url` field.
*/
log(error, level, context=null) {
if (!isObject(context)) {
context = {url: location.href};
}
let errorString;
try {
if (typeof error === 'string') {
errorString = error;
} else {
errorString = error.toString();
if (/^\[object \w+\]$/.test(errorString)) {
errorString = JSON.stringify(error);
}
}
} catch (e) {
errorString = `${error}`;
}
let errorStack;
try {
errorStack = (typeof error.stack === 'string' ? error.stack.trimRight() : '');
} catch (e) {
errorStack = '';
}
let errorData;
try {
errorData = error.data;
} catch (e) {
// NOP
}
if (errorStack.startsWith(errorString)) {
errorString = errorStack;
} else if (errorStack.length > 0) {
errorString += `\n${errorStack}`;
}
let message = `${this._extensionName} has encountered a problem.`;
message += `\nOriginating URL: ${context.url}\n`;
message += errorString;
if (typeof errorData !== 'undefined') {
message += `\nData: ${JSON.stringify(errorData, null, 4)}`;
}
message += '\n\nIssues can be reported at https://github.com/FooSoft/yomichan/issues';
switch (level) {
case 'info': console.info(message); break;
case 'debug': console.debug(message); break;
case 'warn': console.warn(message); break;
case 'error': console.error(message); break;
default: console.log(message); break;
}
this.trigger('log', {error, level, context});
}
/**
* Logs a warning. This function invokes `log` internally.
* @param error The error to log. This is typically an `Error` or `Error`-like object.
* @param context An optional context object for the error which should typically include a `url` field.
*/
warn(error, context=null) {
this.log(error, 'warn', context);
}
/**
* Logs an error. This function invokes `log` internally.
* @param error The error to log. This is typically an `Error` or `Error`-like object.
* @param context An optional context object for the error which should typically include a `url` field.
*/
error(error, context=null) {
this.log(error, 'error', context);
}
}
/**
* This object is the default logger used by the runtime.
*/
const log = new Logger();

View File

@ -272,7 +272,7 @@ class Display extends EventDispatcher {
onError(error) {
if (yomichan.isExtensionUnloaded) { return; }
yomichan.logError(error);
log.error(error);
}
getOptions() {
@ -1552,7 +1552,7 @@ class Display extends EventDispatcher {
}
await this._frontendSetupPromise;
} catch (e) {
yomichan.logError(e);
log.error(e);
return;
} finally {
this._frontendSetupPromise = null;
@ -1721,7 +1721,7 @@ class Display extends EventDispatcher {
_onDefinitionTextScannerSearched({type, definitions, sentence, textSource, optionsContext, error}) {
if (error !== null && !yomichan.isExtensionUnloaded) {
yomichan.logError(error);
log.error(error);
}
if (type === null) { return; }

View File

@ -49,6 +49,6 @@
yomichan.ready();
} catch (e) {
yomichan.logError(e);
log.error(e);
}
})();

View File

@ -89,7 +89,7 @@ class QueryParser extends EventDispatcher {
_onSearched(e) {
const {error} = e;
if (error !== null) {
yomichan.logError(error);
log.error(error);
return;
}
if (e.type === null) { return; }

View File

@ -50,6 +50,6 @@
yomichan.ready();
} catch (e) {
yomichan.logError(e);
log.error(e);
}
})();

View File

@ -69,7 +69,7 @@ class TaskAccumulator {
];
await this._runTasks(allTasks);
} catch (e) {
yomichan.logError(e);
log.error(e);
}
}

View File

@ -122,7 +122,7 @@ class DictionaryDatabase {
await Database.deleteDatabase(this._dbName);
result = true;
} catch (e) {
yomichan.logError(e);
log.error(e);
}
await this.prepare();
return result;

View File

@ -830,7 +830,7 @@ class TextScanner extends EventDispatcher {
}
}
} catch (e) {
yomichan.logError(e);
log.error(e);
} finally {
this._pendingLookup = false;
}

View File

@ -120,6 +120,6 @@ function getOperatingSystemDisplayName(os) {
document.documentElement.dataset.loaded = 'true';
} catch (e) {
yomichan.logError(e);
log.error(e);
}
})();

View File

@ -96,6 +96,6 @@ function setupPermissionsToggles() {
document.documentElement.dataset.loaded = 'true';
} catch (e) {
yomichan.logError(e);
log.error(e);
}
})();

View File

@ -81,6 +81,6 @@ async function setupGenericSettingsController(genericSettingController) {
const settingsDisplayController = new SettingsDisplayController(settingsController, modalController);
settingsDisplayController.prepare();
} catch (e) {
yomichan.logError(e);
log.error(e);
}
})();

View File

@ -638,7 +638,7 @@ class AnkiCardController {
try {
await this._settingsController.permissionsUtil.setPermissionsGranted({permissions}, true);
} catch (e) {
yomichan.logError(e);
log.error(e);
}
}

View File

@ -173,7 +173,7 @@ class BackupController {
}
_showSettingsImportError(error) {
yomichan.logError(error);
log.error(error);
document.querySelector('#settings-import-error-message').textContent = `${error}`;
this._settingsImportErrorModal.setVisible(true);
}
@ -412,7 +412,7 @@ class BackupController {
try {
await this._settingsImportSetOptionsFull(optionsFull);
} catch (e) {
yomichan.logError(e);
log.error(e);
}
}
}

View File

@ -476,7 +476,7 @@ class DictionaryController {
await this._deleteDictionaryInternal(dictionaryTitle, onProgress);
await this._deleteDictionarySettings(dictionaryTitle);
} catch (e) {
yomichan.logError(e);
log.error(e);
} finally {
prevention.end();
for (const progress of progressContainers) { progress.hidden = true; }

View File

@ -253,7 +253,7 @@ class DictionaryImportController {
_showErrors(errors) {
const uniqueErrors = new Map();
for (const error of errors) {
yomichan.logError(error);
log.error(error);
const errorString = this._errorToString(error);
let count = uniqueErrors.get(errorString);
if (typeof count === 'undefined') {

View File

@ -104,6 +104,6 @@ async function setupEnvironmentInfo() {
yomichan.ready();
} catch (e) {
yomichan.logError(e);
log.error(e);
}
})();

View File

@ -30,6 +30,6 @@
await displayGenerator.prepare();
displayGenerator.preparePitchAccents();
} catch (e) {
yomichan.logError(e);
log.error(e);
}
})();

View File

@ -38,6 +38,6 @@
document.documentElement.dataset.loaded = 'true';
} catch (e) {
yomichan.logError(e);
log.error(e);
}
})();

View File

@ -149,6 +149,6 @@ async function setupGenericSettingsController(genericSettingController) {
const settingsDisplayController = new SettingsDisplayController(settingsController, modalController);
settingsDisplayController.prepare();
} catch (e) {
yomichan.logError(e);
log.error(e);
}
})();

View File

@ -103,7 +103,7 @@ class Yomichan extends EventDispatcher {
this.sendMessage({action: 'requestBackendReadySignal'});
await this._isBackendReadyPromise;
this.on('log', this._onForwardLog.bind(this));
log.on('log', this._onForwardLog.bind(this));
}
}
@ -156,68 +156,6 @@ class Yomichan extends EventDispatcher {
});
}
logWarning(error) {
this.log(error, 'warn');
}
logError(error) {
this.log(error, 'error');
}
log(error, level, context=null) {
if (!isObject(context)) {
context = this._getLogContext();
}
let errorString;
try {
errorString = error.toString();
if (/^\[object \w+\]$/.test(errorString)) {
errorString = JSON.stringify(error);
}
} catch (e) {
errorString = `${error}`;
}
let errorStack;
try {
errorStack = (typeof error.stack === 'string' ? error.stack.trimRight() : '');
} catch (e) {
errorStack = '';
}
let errorData;
try {
errorData = error.data;
} catch (e) {
// NOP
}
if (errorStack.startsWith(errorString)) {
errorString = errorStack;
} else if (errorStack.length > 0) {
errorString += `\n${errorStack}`;
}
let message = `${this._extensionName} has encountered a problem.`;
message += `\nOriginating URL: ${context.url}\n`;
message += errorString;
if (typeof errorData !== 'undefined') {
message += `\nData: ${JSON.stringify(errorData, null, 4)}`;
}
message += '\n\nIssues can be reported at https://github.com/FooSoft/yomichan/issues';
switch (level) {
case 'info': console.info(message); break;
case 'debug': console.debug(message); break;
case 'warn': console.warn(message); break;
case 'error': console.error(message); break;
default: console.log(message); break;
}
this.trigger('log', {error, level, context});
}
sendMessage(...args) {
try {
return chrome.runtime.sendMessage(...args);