Files
zulip-desktop/app/common/logger-util.ts
Anders Kaseorg 9b550d6e4f Logger: Remove dynamic method assignment.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2021-04-01 14:06:43 -07:00

148 lines
3.6 KiB
TypeScript

import {Console} from "console"; // eslint-disable-line node/prefer-global/console
import electron from "electron";
import fs from "fs";
import os from "os";
import {initSetUp} from "./default-util";
import {sentryInit, captureException} from "./sentry-util";
const {app} = process.type === "renderer" ? electron.remote : electron;
interface LoggerOptions {
timestamp?: true | (() => string);
file?: string;
level?: boolean;
logInDevMode?: boolean;
}
initSetUp();
let reportErrors = true;
if (process.type === "renderer") {
// Report Errors to Sentry only if it is enabled in settings
// Gets the value of reportErrors from config-util for renderer process
// For main process, sentryInit() is handled in index.js
const {ipcRenderer} = electron;
ipcRenderer.send("error-reporting");
ipcRenderer.on(
"error-reporting-val",
(_event: Event, errorReporting: boolean) => {
reportErrors = errorReporting;
if (reportErrors) {
sentryInit();
}
},
);
}
const logDir = `${app.getPath("userData")}/Logs`;
type Level = "log" | "debug" | "info" | "warn" | "error";
export default class Logger {
nodeConsole: Console;
timestamp?: () => string;
level: boolean;
logInDevMode: boolean;
constructor(options: LoggerOptions = {}) {
let {
timestamp = true,
file = "console.log",
level = true,
logInDevMode = false,
} = options;
file = `${logDir}/${file}`;
if (timestamp === true) {
timestamp = this.getTimestamp;
}
// Trim log according to type of process
if (process.type === "renderer") {
requestIdleCallback(async () => this.trimLog(file));
} else {
process.nextTick(async () => this.trimLog(file));
}
const fileStream = fs.createWriteStream(file, {flags: "a"});
const nodeConsole = new Console(fileStream);
this.nodeConsole = nodeConsole;
this.timestamp = timestamp;
this.level = level;
this.logInDevMode = logInDevMode;
}
_log(type: Level, ...args: unknown[]): void {
const {nodeConsole, timestamp, level, logInDevMode} = this;
switch (true) {
case typeof timestamp === "function":
args.unshift(timestamp() + " |\t");
// Fall through
case level:
args.unshift(type.toUpperCase() + " |");
// Fall through
case !app.isPackaged || logInDevMode:
nodeConsole[type](...args);
break;
default:
}
console[type](...args);
}
log(...args: unknown[]): void {
this._log("log", ...args);
}
debug(...args: unknown[]): void {
this._log("debug", ...args);
}
info(...args: unknown[]): void {
this._log("info", ...args);
}
warn(...args: unknown[]): void {
this._log("warn", ...args);
}
error(...args: unknown[]): void {
this._log("error", ...args);
}
getTimestamp(): string {
const date = new Date();
const timestamp =
`${date.getMonth()}/${date.getDate()} ` +
`${date.getMinutes()}:${date.getSeconds()}`;
return timestamp;
}
reportSentry(error: unknown): void {
if (reportErrors) {
captureException(error);
}
}
async trimLog(file: string): Promise<void> {
const data = await fs.promises.readFile(file, "utf8");
const MAX_LOG_FILE_LINES = 500;
const logs = data.split(os.EOL);
const logLength = logs.length - 1;
// Keep bottom MAX_LOG_FILE_LINES of each log instance
if (logLength > MAX_LOG_FILE_LINES) {
const trimmedLogs = logs.slice(logLength - MAX_LOG_FILE_LINES);
const toWrite = trimmedLogs.join(os.EOL);
await fs.promises.writeFile(file, toWrite);
}
}
}