feat(fe/log_error): add error reporter

This commit is contained in:
hexxa 2021-12-27 21:54:16 +08:00 committed by Hexxa
parent 46e40d1831
commit 3cb711b37e
7 changed files with 107 additions and 15 deletions

View file

@ -19,6 +19,7 @@
"@types/assert": "^1.4.2", "@types/assert": "^1.4.2",
"@types/deep-diff": "^1.0.0", "@types/deep-diff": "^1.0.0",
"@types/jest": "^27.0.1", "@types/jest": "^27.0.1",
"@types/object-hash": "^2.2.1",
"assert": "^2.0.0", "assert": "^2.0.0",
"babel-loader": "^8.2.2", "babel-loader": "^8.2.2",
"deep-diff": "^1.0.2", "deep-diff": "^1.0.2",
@ -52,6 +53,7 @@
"css-loader": "^5.0.0", "css-loader": "^5.0.0",
"filesize": "^6.1.0", "filesize": "^6.1.0",
"immutable": "^4.0.0-rc.12", "immutable": "^4.0.0-rc.12",
"object-hash": "^2.2.0",
"react": "^16.8.6", "react": "^16.8.6",
"react-copy-to-clipboard": "^5.0.1", "react-copy-to-clipboard": "^5.0.1",
"react-dom": "^16.8.6", "react-dom": "^16.8.6",

View file

@ -14,9 +14,9 @@ window.onerror = (
) => { ) => {
const lowerMsg = msg.toLowerCase(); const lowerMsg = msg.toLowerCase();
if (lowerMsg.indexOf("script error") > -1) { if (lowerMsg.indexOf("script error") > -1) {
ErrorLogger().error(errCorsScript, "Check Browser Console for Detail"); ErrorLogger().error("Check Browser Console for Detail");
} }
ErrorLogger().error(`${source}:${lineno}:${colno}: ${error.toString()}`, ""); ErrorLogger().error(`${source}:${lineno}:${colno}: ${error.toString()}`);
}; };
ReactDOM.render(<StateMgr />, document.getElementById("mount")); ReactDOM.render(<StateMgr />, document.getElementById("mount"));

View file

@ -139,6 +139,7 @@ export interface ISettingsClient {
health: () => Promise<Response>; health: () => Promise<Response>;
getClientCfg: () => Promise<Response>; getClientCfg: () => Promise<Response>;
setClientCfg: (cfg: ClientConfig) => Promise<Response>; setClientCfg: (cfg: ClientConfig) => Promise<Response>;
reportError: (content: string, version: string) => Promise<Response>;
} }
export interface Response<T = any> { export interface Response<T = any> {

View file

@ -30,4 +30,15 @@ export class SettingsClient extends BaseClient {
}, },
}); });
}; };
reportError = (content: string, version: string): Promise<Response> => {
return this.do({
method: "post",
url: `${this.url}/v1/settings/errors`,
data: {
content,
version,
},
});
};
} }

View file

@ -7,6 +7,7 @@ export interface SettingsClientResps {
healthMockResp?: Response; healthMockResp?: Response;
setClientCfgMockResp?: Response; setClientCfgMockResp?: Response;
getClientCfgMockResp?: Response<ClientConfigMsg>; getClientCfgMockResp?: Response<ClientConfigMsg>;
reportErrorResp?: Response;
} }
export const resps = { export const resps = {
@ -25,9 +26,14 @@ export const resps = {
position: "clientCfg_bg_position", position: "clientCfg_bg_position",
align: "clientCfg_bg_align", align: "clientCfg_bg_align",
}, },
}
}, },
}, },
},
reportErrorResp: {
status: 200,
statusText: "",
data: {},
},
}; };
export class MockSettingsClient { export class MockSettingsClient {
private url: string; private url: string;
@ -59,4 +65,8 @@ export class MockSettingsClient {
getClientCfg = (): Promise<Response> => { getClientCfg = (): Promise<Response> => {
return this.wrapPromise(this.resps.getClientCfgMockResp); return this.wrapPromise(this.resps.getClientCfgMockResp);
}; };
reportError = (): Promise<Response> => {
return this.wrapPromise(this.resps.reportErrorResp);
};
} }

View file

@ -1,12 +1,27 @@
import { Map } from "immutable";
import { sha1 } from "object-hash";
import { ILocalStorage, Storage } from "./localstorage"; import { ILocalStorage, Storage } from "./localstorage";
import { ISettingsClient } from "../client"; import { ISettingsClient } from "../client";
import { SettingsClient } from "../client/settings"; import { SettingsClient } from "../client/settings";
import { ICoreState } from "../components/core_state";
import { updater } from "../components/state_updater";
const errorVer = "0.0.1";
const cookieKeyClErrs = "qs_cli_errs";
export interface ClientErrorV001 {
version: string;
error: string;
state: ICoreState;
}
export interface IErrorLogger { export interface IErrorLogger {
setClient: (client: ISettingsClient) => void; setClient: (client: ISettingsClient) => void;
setStorage: (storage: ILocalStorage) => void; setStorage: (storage: ILocalStorage) => void;
error: (key: string, msg: string) => void; error: (msg: string) => null | Error;
report: () => void; report: () => Promise<null | Error>;
readErrs: () => Map<string, ClientErrorV001>;
} }
export class ErrorLog { export class ErrorLog {
@ -25,18 +40,61 @@ export class ErrorLog {
this.storage = storage; this.storage = storage;
} }
error = (key: string, msg: string) => { private getErrorSign = (errMsg: string): string => {
const existKey = this.storage.get(key); return `e:${sha1(errMsg)}`;
if (existKey === "") {
this.storage.set(key, msg);
}
}; };
report = () => { readErrs = (): Map<string, ClientErrorV001> => {
// TODO: const errsStr = this.storage.get(cookieKeyClErrs);
// check last submitting, and set submit time const errsObj = JSON.parse(errsStr);
// report all errors to backend return Map(errsObj);
// clean storage };
private writeErrs = (errs: Map<string, ClientErrorV001>) => {
const errsObj = errs.toObject();
const errsStr = JSON.stringify(errsObj);
this.storage.set(cookieKeyClErrs, errsStr);
};
error = (msg: string): null | Error => {
try {
const sign = this.getErrorSign(msg);
const clientErr: ClientErrorV001 = {
version: errorVer,
error: msg,
state: updater().props,
};
let errs = this.readErrs();
if (!errs.has(sign)) {
errs = errs.set(sign, clientErr);
this.writeErrs(errs);
}
} catch (err: any) {
return Error(`failed to save err log: ${err}`);
}
return null;
};
report = async (): Promise<null | Error> => {
try {
const errs = this.readErrs();
for (let sign of errs.keySeq().toArray()) {
const err = errs.get(sign);
const resp = await this.client.reportError(sign, JSON.stringify(err));
if (resp.status !== 200) {
return Error(`failed to report error: ${resp.data}`);
}
}
// truncate errors
this.writeErrs(Map());
} catch (e: any) {
return Error(e);
}
return null;
}; };
} }

View file

@ -1320,6 +1320,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.2.tgz#81f5a039d6ed1941f8cc57506c74e7c2b8fc64b9" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.9.2.tgz#81f5a039d6ed1941f8cc57506c74e7c2b8fc64b9"
integrity sha512-ZHty/hKoOLZvSz6BtP1g7tc7nUeJhoCf3flLjh8ZEv1vFKBWHXcnMbJMyN/pftSljNyy0kNW/UqI3DccnBnZ8w== integrity sha512-ZHty/hKoOLZvSz6BtP1g7tc7nUeJhoCf3flLjh8ZEv1vFKBWHXcnMbJMyN/pftSljNyy0kNW/UqI3DccnBnZ8w==
"@types/object-hash@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-2.2.1.tgz#67c169f8f033e0b62abbf81df2d00f4598d540b9"
integrity sha512-i/rtaJFCsPljrZvP/akBqEwUP2y5cZLOmvO+JaYnz01aPknrQ+hB5MRcO7iqCUsFaYfTG8kGfKUyboA07xeDHQ==
"@types/prettier@^2.1.5": "@types/prettier@^2.1.5":
version "2.3.2" version "2.3.2"
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.3.2.tgz#fc8c2825e4ed2142473b4a81064e6e081463d1b3"
@ -3768,6 +3773,11 @@ object-assign@^4.1.1:
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-hash@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5"
integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==
object-inspect@^1.11.0, object-inspect@^1.9.0: object-inspect@^1.11.0, object-inspect@^1.9.0:
version "1.11.0" version "1.11.0"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1"