feat(fe): enable error logger and reporting
This commit is contained in:
parent
3cb711b37e
commit
9a7cfcb097
10 changed files with 153 additions and 35 deletions
|
@ -270,12 +270,33 @@
|
|||
padding: 1rem 0 1rem 0;
|
||||
}
|
||||
|
||||
.theme-default .upload-item .error {
|
||||
.theme-default .error {
|
||||
font-size: 1.4rem;
|
||||
padding: 1rem;
|
||||
color: #f1c40f;
|
||||
margin: 1rem 0 0 0;
|
||||
background-color: #2c3e50;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.theme-default .error-inline {
|
||||
font-size: 1.4rem;
|
||||
padding: 1rem;
|
||||
color: #f1c40f;
|
||||
margin: 1rem 0 0 0;
|
||||
background-color: #2c3e50;
|
||||
white-space: nowrap;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.theme-default .error {
|
||||
font-size: 1.4rem;
|
||||
padding: 1rem;
|
||||
color: #f1c40f;
|
||||
margin: 1rem 0 0 0;
|
||||
background-color: #2c3e50;
|
||||
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.theme-default #sharing-list .info {
|
||||
|
|
|
@ -6,6 +6,7 @@ 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";
|
||||
|
||||
const errorVer = "0.0.1";
|
||||
const cookieKeyClErrs = "qs_cli_errs";
|
||||
|
@ -13,6 +14,7 @@ const cookieKeyClErrs = "qs_cli_errs";
|
|||
export interface ClientErrorV001 {
|
||||
version: string;
|
||||
error: string;
|
||||
timestamp: string;
|
||||
state: ICoreState;
|
||||
}
|
||||
|
||||
|
@ -22,9 +24,10 @@ export interface IErrorLogger {
|
|||
error: (msg: string) => null | Error;
|
||||
report: () => Promise<null | Error>;
|
||||
readErrs: () => Map<string, ClientErrorV001>;
|
||||
truncate: () => void;
|
||||
}
|
||||
|
||||
export class ErrorLog {
|
||||
export class SimpleErrorLogger {
|
||||
private client: ISettingsClient;
|
||||
private storage: ILocalStorage = Storage();
|
||||
|
||||
|
@ -45,9 +48,18 @@ export class ErrorLog {
|
|||
};
|
||||
|
||||
readErrs = (): Map<string, ClientErrorV001> => {
|
||||
const errsStr = this.storage.get(cookieKeyClErrs);
|
||||
const errsObj = JSON.parse(errsStr);
|
||||
return Map(errsObj);
|
||||
try {
|
||||
const errsStr = this.storage.get(cookieKeyClErrs);
|
||||
if (errsStr === "") {
|
||||
return Map();
|
||||
}
|
||||
|
||||
const errsObj = JSON.parse(errsStr);
|
||||
return Map(errsObj);
|
||||
} catch (e: any) {
|
||||
this.truncate(); // reset
|
||||
}
|
||||
return Map();
|
||||
};
|
||||
|
||||
private writeErrs = (errs: Map<string, ClientErrorV001>) => {
|
||||
|
@ -62,6 +74,7 @@ export class ErrorLog {
|
|||
const clientErr: ClientErrorV001 = {
|
||||
version: errorVer,
|
||||
error: msg,
|
||||
timestamp: `${Date.now()}`,
|
||||
state: updater().props,
|
||||
};
|
||||
let errs = this.readErrs();
|
||||
|
@ -88,17 +101,20 @@ export class ErrorLog {
|
|||
}
|
||||
}
|
||||
|
||||
// truncate errors
|
||||
this.writeErrs(Map());
|
||||
this.truncate();
|
||||
} catch (e: any) {
|
||||
return Error(e);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
truncate = () => {
|
||||
this.writeErrs(Map());
|
||||
};
|
||||
}
|
||||
|
||||
const errorLogger = new ErrorLog(new SettingsClient(""));
|
||||
const errorLogger = new SimpleErrorLogger(new SettingsClient(""));
|
||||
export const ErrorLogger = (): IErrorLogger => {
|
||||
return errorLogger;
|
||||
};
|
||||
|
|
|
@ -98,12 +98,8 @@ export class Rows extends React.Component<Props, State, {}> {
|
|||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
id={this.props.id}
|
||||
style={this.props.style}
|
||||
className={this.props.className}
|
||||
>
|
||||
const orderByList =
|
||||
sortBtns.size > 0 ? (
|
||||
<div className="margin-b-l">
|
||||
<Flexbox
|
||||
children={List([
|
||||
|
@ -116,6 +112,15 @@ export class Rows extends React.Component<Props, State, {}> {
|
|||
childrenStyles={List([{ flex: "0 0 auto" }, { flex: "0 0 auto" }])}
|
||||
/>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
id={this.props.id}
|
||||
style={this.props.style}
|
||||
className={this.props.className}
|
||||
>
|
||||
{orderByList}
|
||||
{bodyRows}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -6,9 +6,11 @@ import { ICoreState, UIProps, MsgProps } from "./core_state";
|
|||
import { LoginProps } from "./pane_login";
|
||||
import { Flexbox } from "./layout/flexbox";
|
||||
import { updater } from "./state_updater";
|
||||
import { alertMsg } from "../common/env";
|
||||
import { alertMsg, confirmMsg } from "../common/env";
|
||||
import { Container } from "./layout/container";
|
||||
import { Card } from "./layout/card";
|
||||
import { Rows, Row } from "./layout/rows";
|
||||
import { ClientErrorV001, ErrorLogger } from "../common/log_error";
|
||||
export interface Props {
|
||||
login: LoginProps;
|
||||
msg: MsgProps;
|
||||
|
@ -138,12 +140,53 @@ export class PaneSettings extends React.Component<Props, State, {}> {
|
|||
});
|
||||
};
|
||||
|
||||
truncateErrors = () => {
|
||||
if (confirmMsg(this.props.msg.pkg.get("op.confirm"))) {
|
||||
ErrorLogger().truncate();
|
||||
}
|
||||
};
|
||||
|
||||
reportErrors = () => {
|
||||
if (confirmMsg(this.props.msg.pkg.get("op.confirm"))) {
|
||||
ErrorLogger().report();
|
||||
}
|
||||
};
|
||||
|
||||
prepareErrorRows = (): List<Row> => {
|
||||
let errRows = List<Row>();
|
||||
|
||||
ErrorLogger()
|
||||
.readErrs()
|
||||
.forEach((clientErr: ClientErrorV001, sign: string) => {
|
||||
const elem = (
|
||||
<div>
|
||||
<div className="error-inline">{JSON.stringify(clientErr)}</div>
|
||||
<div className="hr"></div>
|
||||
</div>
|
||||
);
|
||||
const val = clientErr;
|
||||
const sortVals = List<string>([]);
|
||||
|
||||
errRows = errRows.push({
|
||||
elem,
|
||||
val,
|
||||
sortVals,
|
||||
});
|
||||
});
|
||||
|
||||
return errRows;
|
||||
};
|
||||
|
||||
render() {
|
||||
const errRows = this.prepareErrorRows();
|
||||
|
||||
return (
|
||||
<div id="pane-settings">
|
||||
<Container>
|
||||
<div id="profile">
|
||||
<h5 className="pane-title">{this.props.msg.pkg.get("user.profile")}</h5>
|
||||
<h5 className="pane-title">
|
||||
{this.props.msg.pkg.get("user.profile")}
|
||||
</h5>
|
||||
|
||||
<div className="hr"></div>
|
||||
|
||||
|
@ -372,6 +415,30 @@ 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>
|
||||
|
||||
{/* <div className="hr"></div>
|
||||
<div>
|
||||
<Flexbox
|
||||
|
|
|
@ -8,11 +8,11 @@ import { RiArchiveDrawerFill } from "@react-icons/all-files/ri/RiArchiveDrawerFi
|
|||
import { RiFile2Fill } from "@react-icons/all-files/ri/RiFile2Fill";
|
||||
import { RiFileList2Fill } from "@react-icons/all-files/ri/RiFileList2Fill";
|
||||
import { RiCheckboxFill } from "@react-icons/all-files/ri/RiCheckboxFill";
|
||||
import { RiCheckboxBlankFill } from "@react-icons/all-files/ri/RiCheckboxBlankFill";
|
||||
import { RiInformationFill } from "@react-icons/all-files/ri/RiInformationFill";
|
||||
import { BiTable } from "@react-icons/all-files/bi/BiTable";
|
||||
import { BiListUl } from "@react-icons/all-files/bi/BiListUl";
|
||||
|
||||
import { ErrorLogger } from "../common/log_error";
|
||||
import { alertMsg, confirmMsg } from "../common/env";
|
||||
import { getErrMsg } from "../common/utils";
|
||||
import { updater } from "./state_updater";
|
||||
|
@ -699,12 +699,12 @@ export class FilesPanel extends React.Component<Props, State, {}> {
|
|||
};
|
||||
|
||||
setView = (opt: string) => {
|
||||
if (opt === "rows" || opt === "table") {
|
||||
updater().setControlOption(filesViewCtrl, opt);
|
||||
this.props.update(updater().updateUI);
|
||||
if (opt !== "rows" && opt !== "table") {
|
||||
ErrorLogger().error(`FilesPanel:setView: unknown view ${opt}`);
|
||||
return;
|
||||
}
|
||||
// TODO: log error
|
||||
updater().setControlOption(filesViewCtrl, opt);
|
||||
this.props.update(updater().updateUI);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -35,6 +35,7 @@ import { controlName as panelTabs } from "./root_frame";
|
|||
import { settingsTabsCtrl } from "./dialog_settings";
|
||||
import { settingsDialogCtrl } from "./layers";
|
||||
import { errUpdater, errServer } from "../common/errors";
|
||||
import { ErrorLogger } from "../common/log_error";
|
||||
|
||||
import { MsgPackage, isValidLanPack } from "../i18n/msger";
|
||||
|
||||
|
@ -135,8 +136,11 @@ export class Updater {
|
|||
refreshUploadings = async (): Promise<string> => {
|
||||
const luResp = await this.filesClient.listUploadings();
|
||||
if (luResp.status !== 200) {
|
||||
// TODO: log error
|
||||
console.error(luResp.data);
|
||||
// this method is called for authed users
|
||||
// other status codes are unexpected, including 401
|
||||
ErrorLogger().error(
|
||||
`refreshUploadings: unexpected response ${luResp.status} ${luResp.data}`
|
||||
);
|
||||
return errServer;
|
||||
}
|
||||
|
||||
|
@ -767,16 +771,15 @@ export class Updater {
|
|||
const url = this.props.login.preferences.lanPackURL;
|
||||
if (url === "") {
|
||||
const lan = this.props.login.preferences.lan;
|
||||
if (lan == "en_US" || lan == "zh_CN") {
|
||||
if (lan === "en_US" || lan === "zh_CN") {
|
||||
// fallback to build-in language pack
|
||||
this.props.msg.lan = lan;
|
||||
this.props.msg.pkg = MsgPackage.get(lan);
|
||||
} else {
|
||||
// fallback to english
|
||||
ErrorLogger().error(`syncLan: unexpected lan ${lan}`);
|
||||
this.props.msg.lan = "en_US";
|
||||
this.props.msg.pkg = MsgPackage.get("en_US");
|
||||
}
|
||||
// TODO: should warning here
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -126,4 +126,8 @@ export const msgs: Map<string, string> = Map({
|
|||
"item.modTime": "Mod Time",
|
||||
"item.size": "Size",
|
||||
"item.progress": "Progress",
|
||||
"error.report.title": "Error Report",
|
||||
"op.truncate": "Truncate",
|
||||
"op.submit": "Submit",
|
||||
"term.time": "Time",
|
||||
});
|
||||
|
|
|
@ -123,4 +123,8 @@ export const msgs: Map<string, string> = Map({
|
|||
"item.modTime": "修改时间",
|
||||
"item.size": "大小",
|
||||
"item.progress": "进度",
|
||||
"error.report.title": "报告错误",
|
||||
"op.truncate": "清空",
|
||||
"op.submit": "提交",
|
||||
"term.time": "时间",
|
||||
});
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
UploadState,
|
||||
} from "./interface";
|
||||
import { errUploadMgr } from "../common/errors";
|
||||
import { ErrorLogger } from "../common/log_error";
|
||||
|
||||
const win: Window = self as any;
|
||||
|
||||
|
@ -197,8 +198,7 @@ export class UploadMgr {
|
|||
err: errResp.err,
|
||||
});
|
||||
} else {
|
||||
// TODO: refine this
|
||||
console.error(`uploading ${errResp.filePath} may already be deleted`);
|
||||
ErrorLogger().error(`respHandler: entry not found ${errResp.err}`);
|
||||
}
|
||||
|
||||
this.statusCb(this.infos.toMap(), false);
|
||||
|
@ -225,17 +225,16 @@ export class UploadMgr {
|
|||
this.statusCb(this.infos.toMap(), false);
|
||||
}
|
||||
} else {
|
||||
// TODO: refine this
|
||||
console.error(
|
||||
`respHandler: may already be deleted: file(${
|
||||
infoResp.filePath
|
||||
}) infos(${this.infos.toObject()})`
|
||||
ErrorLogger().error(
|
||||
`respHandler: entry(uploadInfoKind) not found ${infoResp.err}`
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
console.error(`respHandler: response kind not found: ${resp}`);
|
||||
ErrorLogger().error(
|
||||
`respHandler: unknown kind: ${JSON.stringify(resp)}`
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import { FilesClient } from "../client/files";
|
|||
import { IFilesClient, Response, isFatalErr } from "../client";
|
||||
|
||||
// TODO: get settings from server
|
||||
// TODO: move chunk copying to worker
|
||||
const defaultChunkLen = 1024 * 1024 * 1;
|
||||
const speedDownRatio = 0.5;
|
||||
const speedUpRatio = 1.05;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue