feat(fe/log_error): add error reporter
This commit is contained in:
parent
46e40d1831
commit
3cb711b37e
7 changed files with 107 additions and 15 deletions
|
@ -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",
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
yarn.lock
10
yarn.lock
|
@ -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"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue