feat(error_reporting): integrate error reporting

This commit is contained in:
hexxa 2021-12-30 10:33:13 +08:00 committed by Hexxa
parent 9a7cfcb097
commit 711a3a874f
9 changed files with 86 additions and 57 deletions

View file

@ -55,9 +55,9 @@ func (cl *SettingsClient) SetClientCfg(cfg *sitestore.ClientConfig, token *http.
End()
}
func (cl *SettingsClient) ReportError(report *settings.ClientErrorReport, token *http.Cookie) (*http.Response, string, []error) {
func (cl *SettingsClient) ReportErrors(reports *settings.ClientErrorReports, token *http.Cookie) (*http.Response, string, []error) {
return cl.r.Post(cl.url("/v1/settings/errors")).
AddCookie(token).
Send(report).
Send(reports).
End()
}

View file

@ -1,4 +1,5 @@
import axios, { AxiosRequestConfig } from "axios";
import { List } from "immutable";
export const defaultTimeout = 10000;
export const userIDParam = "uid";
@ -88,6 +89,11 @@ export interface ClientConfig {
bg: BgConfig;
}
export interface ClientErrorReport {
report: string;
version: string;
}
export interface IUsersClient {
login: (
user: string,
@ -139,7 +145,7 @@ export interface ISettingsClient {
health: () => Promise<Response>;
getClientCfg: () => Promise<Response>;
setClientCfg: (cfg: ClientConfig) => Promise<Response>;
reportError: (content: string, version: string) => Promise<Response>;
reportErrors: (reports: List<ClientErrorReport>) => Promise<Response>;
}
export interface Response<T = any> {

View file

@ -1,6 +1,7 @@
import { BaseClient, Response, userIDParam, Quota } from ".";
import { List } from "immutable";
import { ClientConfig } from "./";
import { BaseClient, Response, userIDParam, Quota } from ".";
import { ClientConfig, ClientErrorReport } from "./";
export class SettingsClient extends BaseClient {
constructor(url: string) {
@ -31,13 +32,12 @@ export class SettingsClient extends BaseClient {
});
};
reportError = (content: string, version: string): Promise<Response> => {
reportErrors = (reports: List<ClientErrorReport>): Promise<Response> => {
return this.do({
method: "post",
url: `${this.url}/v1/settings/errors`,
data: {
content,
version,
reports: reports.toArray(),
},
});
};

View file

@ -7,7 +7,7 @@ export interface SettingsClientResps {
healthMockResp?: Response;
setClientCfgMockResp?: Response;
getClientCfgMockResp?: Response<ClientConfigMsg>;
reportErrorResp?: Response;
reportErrorsResp?: Response;
}
export const resps = {
@ -29,7 +29,7 @@ export const resps = {
},
},
},
reportErrorResp: {
reportErrorsResp: {
status: 200,
statusText: "",
data: {},
@ -66,7 +66,7 @@ export class MockSettingsClient {
return this.wrapPromise(this.resps.getClientCfgMockResp);
};
reportError = (): Promise<Response> => {
return this.wrapPromise(this.resps.reportErrorResp);
reportErrors = (): Promise<Response> => {
return this.wrapPromise(this.resps.reportErrorsResp);
};
}

View file

@ -1,4 +1,4 @@
import { Map } from "immutable";
import { Map, List } from "immutable";
import { sha1 } from "object-hash";
import { ILocalStorage, Storage } from "./localstorage";
@ -6,13 +6,12 @@ import { ISettingsClient } from "../client";
import { SettingsClient } from "../client/settings";
import { ICoreState } from "../components/core_state";
import { updater } from "../components/state_updater";
import { alertMsg } from "./env";
import { ClientErrorReport } from "../client";
const errorVer = "0.0.1";
const cookieKeyClErrs = "qs_cli_errs";
export interface ClientErrorV001 {
version: string;
error: string;
timestamp: string;
state: ICoreState;
@ -72,7 +71,6 @@ export class SimpleErrorLogger {
try {
const sign = this.getErrorSign(msg);
const clientErr: ClientErrorV001 = {
version: errorVer,
error: msg,
timestamp: `${Date.now()}`,
state: updater().props,
@ -92,16 +90,21 @@ export class SimpleErrorLogger {
report = async (): Promise<null | Error> => {
try {
const errs = this.readErrs();
let reports = List<ClientErrorReport>();
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}`);
}
const errObj = errs.get(sign);
reports = reports.push({
report: JSON.stringify(errObj),
version: errorVer,
});
}
this.truncate();
const resp = await this.client.reportErrors(reports);
if (resp.status !== 200) {
return Error(`failed to report error: ${resp.data}`);
} else {
this.truncate();
}
} catch (e: any) {
return Error(e);
}

View file

@ -179,6 +179,32 @@ export class PaneSettings extends React.Component<Props, State, {}> {
render() {
const errRows = this.prepareErrorRows();
const errorReportPane =
errRows.size > 0 ? (
<Container>
<Flexbox
children={List([
<h5 className="pane-title">
{this.props.msg.pkg.get("error.report.title")}
</h5>,
<span>
<button className="margin-r-m" onClick={this.reportErrors}>
{this.props.msg.pkg.get("op.submit")}
</button>
<button onClick={this.truncateErrors}>
{this.props.msg.pkg.get("op.truncate")}
</button>
</span>,
])}
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
/>
<div className="hr"></div>
<Rows rows={errRows} sortKeys={List([])} />
</Container>
) : null;
return (
<div id="pane-settings">
@ -415,29 +441,7 @@ export class PaneSettings extends React.Component<Props, State, {}> {
</div>
</Container>
<Container>
<Flexbox
children={List([
<h5 className="pane-title">
{this.props.msg.pkg.get("error.report.title")}
</h5>,
<span>
<button className="margin-r-m" onClick={this.reportErrors}>
{this.props.msg.pkg.get("op.submit")}
</button>
<button onClick={this.truncateErrors}>
{this.props.msg.pkg.get("op.truncate")}
</button>
</span>,
])}
childrenStyles={List([{}, { justifyContent: "flex-end" }])}
/>
<div className="hr"></div>
<Rows rows={errRows} sortKeys={List([])} />
</Container>
{errorReportPane}
{/* <div className="hr"></div>
<div>