add rudimentary error reporting in a new tab
This commit is contained in:
parent
bf70e752b6
commit
21c176a69e
@ -208,6 +208,44 @@ export async function recordException(msg: string, e: any): Promise<void> {
|
|||||||
return record("error", e.toString(), stack, frame.file, frame.line, frame.column);
|
return record("error", e.toString(), stack, frame.file, frame.line, frame.column);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache for reports. Also used when something is so broken that we can't even
|
||||||
|
* access the database.
|
||||||
|
*/
|
||||||
|
const reportCache: { [reportId: string]: any } = {};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a UUID that does not use cryptographically secure randomness.
|
||||||
|
* Formatted as RFC4122 version 4 UUID.
|
||||||
|
*/
|
||||||
|
function getInsecureUuid() {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a report and return a unique identifier to retrieve it later.
|
||||||
|
*/
|
||||||
|
export async function storeReport(report: any): Promise<string> {
|
||||||
|
const uid = getInsecureUuid();
|
||||||
|
reportCache[uid] = report;
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a report by its unique identifier.
|
||||||
|
*/
|
||||||
|
export async function getReport(reportUid: string): Promise<any> {
|
||||||
|
return reportCache[reportUid];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record a log entry in the database.
|
* Record a log entry in the database.
|
||||||
*/
|
*/
|
||||||
@ -218,6 +256,8 @@ export async function record(level: Level,
|
|||||||
line?: number,
|
line?: number,
|
||||||
col?: number): Promise<void> {
|
col?: number): Promise<void> {
|
||||||
if (typeof indexedDB === "undefined") {
|
if (typeof indexedDB === "undefined") {
|
||||||
|
console.log("can't access DB for logging in this context");
|
||||||
|
console.log("log was", { level, msg, detail, source, line, col });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +297,7 @@ export async function record(level: Level,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const loggingDbVersion = 1;
|
const loggingDbVersion = 2;
|
||||||
|
|
||||||
const logsStore: Store<LogEntry> = new Store<LogEntry>("logs");
|
const logsStore: Store<LogEntry> = new Store<LogEntry>("logs");
|
||||||
|
|
||||||
@ -284,6 +324,7 @@ export function openLoggingDb(): Promise<IDBDatabase> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
resDb.createObjectStore("logs", { keyPath: "id", autoIncrement: true });
|
resDb.createObjectStore("logs", { keyPath: "id", autoIncrement: true });
|
||||||
|
resDb.createObjectStore("reports", { keyPath: "uid", autoIncrement: false });
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -176,6 +176,14 @@ export interface MessageMap {
|
|||||||
request: { };
|
request: { };
|
||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
|
"log-and-display-error": {
|
||||||
|
request: any;
|
||||||
|
response: void;
|
||||||
|
};
|
||||||
|
"get-report": {
|
||||||
|
request: { reportUid: string };
|
||||||
|
response: void;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,40 +22,69 @@
|
|||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ReactDOM from "react-dom";
|
import * as ReactDOM from "react-dom";
|
||||||
import URI = require("urijs");
|
import URI = require("urijs");
|
||||||
|
|
||||||
|
import * as wxApi from "../wxApi";
|
||||||
|
|
||||||
interface ErrorProps {
|
interface ErrorProps {
|
||||||
message: string;
|
report: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ErrorView extends React.Component<ErrorProps, { }> {
|
class ErrorView extends React.Component<ErrorProps, { }> {
|
||||||
render(): JSX.Element {
|
render(): JSX.Element {
|
||||||
|
const report = this.props.report;
|
||||||
|
if (!report) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
An error occurred: {this.props.message}
|
<h1>Error Report Not Found</h1>
|
||||||
|
<p>This page is supposed to display an error reported by the GNU Taler wallet,
|
||||||
|
but the corresponding error report can't be found.</p>
|
||||||
|
<p>Maybe the error occured before the browser was restarted or the wallet was reloaded.</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
switch (report.name) {
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Unknown Error</h1>
|
||||||
|
The GNU Taler wallet reported an unknown error. Here are the details:
|
||||||
|
<pre>
|
||||||
|
{JSON.stringify(report, null, " ")}
|
||||||
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
|
||||||
const url = new URI(document.location.href);
|
const url = new URI(document.location.href);
|
||||||
const query: any = URI.parseQuery(url.query());
|
const query: any = URI.parseQuery(url.query());
|
||||||
|
|
||||||
const message: string = query.message || "unknown error";
|
const container = document.getElementById("container");
|
||||||
|
if (!container) {
|
||||||
ReactDOM.render(<ErrorView message={message} />, document.getElementById(
|
console.error("fatal: can't mount component, countainer missing");
|
||||||
"container")!);
|
return;
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
// TODO: provide more context information, maybe factor it out into a
|
|
||||||
// TODO:generic error reporting function or component.
|
|
||||||
document.body.innerText = `Fatal error: "${e.message}".`;
|
|
||||||
console.error(`got error "${e.message}"`, e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// report that we'll render, either looked up from the
|
||||||
|
// logging module or synthesized here for fixed/fatal errors
|
||||||
|
let report;
|
||||||
|
|
||||||
|
const reportUid: string = query.reportUid;
|
||||||
|
if (!reportUid) {
|
||||||
|
report = {
|
||||||
|
name: "missing-error",
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
report = await wxApi.getReport(reportUid);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReactDOM.render(<ErrorView report={report} />, container);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => main());
|
document.addEventListener("DOMContentLoaded", () => main());
|
||||||
|
@ -321,3 +321,11 @@ export function getSenderWireInfos(): Promise<SenderWireInfos> {
|
|||||||
export function returnCoins(args: { amount: AmountJson, exchange: string, senderWire: object }): Promise<void> {
|
export function returnCoins(args: { amount: AmountJson, exchange: string, senderWire: object }): Promise<void> {
|
||||||
return callBackend("return-coins", args);
|
return callBackend("return-coins", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function logAndDisplayError(args: any): Promise<void> {
|
||||||
|
return callBackend("log-and-display-error", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getReport(reportUid: string): Promise<void> {
|
||||||
|
return callBackend("get-report", { reportUid });
|
||||||
|
}
|
||||||
|
@ -303,6 +303,15 @@ function handleMessage(sender: MessageSender,
|
|||||||
}
|
}
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
case "log-and-display-error":
|
||||||
|
logging.storeReport(detail).then((reportUid) => {
|
||||||
|
chrome.tabs.create({
|
||||||
|
url: chrome.extension.getURL(`/src/webex/pages/error.html?reportUid=${reportUid}`),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
case "get-report":
|
||||||
|
return logging.getReport(detail.reportUid);
|
||||||
default:
|
default:
|
||||||
// Exhaustiveness check.
|
// Exhaustiveness check.
|
||||||
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html
|
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html
|
||||||
|
Loading…
Reference in New Issue
Block a user