mirror of https://github.com/zulip/zulip.git
125 lines
3.9 KiB
TypeScript
125 lines
3.9 KiB
TypeScript
/* eslint-disable no-console */
|
|
|
|
// System documented in https://zulip.readthedocs.io/en/latest/subsystems/logging.html
|
|
|
|
// This must be included before the first call to $(document).ready
|
|
// in order to be able to report exceptions that occur during their
|
|
// execution.
|
|
|
|
import * as Sentry from "@sentry/browser";
|
|
import $ from "jquery";
|
|
|
|
import {BlueslipError, display_stacktrace} from "./blueslip_stacktrace";
|
|
|
|
if (Error.stackTraceLimit !== undefined) {
|
|
Error.stackTraceLimit = 100000;
|
|
}
|
|
|
|
function make_logger_func(name: "debug" | "log" | "info" | "warn" | "error") {
|
|
return function Logger_func(this: Logger, ...args: unknown[]) {
|
|
const date_str = new Date().toISOString();
|
|
|
|
const str_args = args.map((x) => (typeof x === "object" ? JSON.stringify(x) : x));
|
|
|
|
const log_entry = date_str + " " + name.toUpperCase() + ": " + str_args.join("");
|
|
this._memory_log.push(log_entry);
|
|
|
|
// Don't let the log grow without bound
|
|
if (this._memory_log.length > 1000) {
|
|
this._memory_log.shift();
|
|
}
|
|
|
|
if (console[name] !== undefined) {
|
|
console[name](...args);
|
|
}
|
|
};
|
|
}
|
|
|
|
class Logger {
|
|
debug = make_logger_func("debug");
|
|
log = make_logger_func("log");
|
|
info = make_logger_func("info");
|
|
warn = make_logger_func("warn");
|
|
error = make_logger_func("error");
|
|
|
|
_memory_log: string[] = [];
|
|
get_log(): string[] {
|
|
return this._memory_log;
|
|
}
|
|
}
|
|
|
|
const logger = new Logger();
|
|
|
|
export function get_log(): string[] {
|
|
return logger.get_log();
|
|
}
|
|
|
|
function build_arg_list(msg: string, more_info?: unknown): [string, string?, unknown?] {
|
|
const args: [string, string?, unknown?] = [msg];
|
|
if (more_info !== undefined) {
|
|
args.push("\nAdditional information: ", more_info);
|
|
}
|
|
return args;
|
|
}
|
|
|
|
export function debug(msg: string, more_info?: unknown): void {
|
|
const args = build_arg_list(msg, more_info);
|
|
logger.debug(...args);
|
|
}
|
|
|
|
export function log(msg: string, more_info?: unknown): void {
|
|
const args = build_arg_list(msg, more_info);
|
|
logger.log(...args);
|
|
}
|
|
|
|
export function info(msg: string, more_info?: unknown): void {
|
|
const args = build_arg_list(msg, more_info);
|
|
logger.info(...args);
|
|
}
|
|
|
|
export function warn(msg: string, more_info?: unknown): void {
|
|
const args = build_arg_list(msg, more_info);
|
|
logger.warn(...args);
|
|
if (DEVELOPMENT) {
|
|
console.trace();
|
|
}
|
|
}
|
|
|
|
export function error(msg: string, more_info?: object, original_error?: unknown): void {
|
|
// Log the Sentry error before the console warning, so we don't
|
|
// end up with a doubled message in the Sentry logs.
|
|
Sentry.setContext("more_info", more_info ?? null);
|
|
|
|
// Note that original_error could be of any type, because you can "raise"
|
|
// any type -- something we do see in practice with the error
|
|
// object being "dead": https://github.com/zulip/zulip/issues/18374
|
|
Sentry.captureException(new Error(msg, {cause: original_error}));
|
|
|
|
const args = build_arg_list(msg, more_info);
|
|
logger.error(...args);
|
|
|
|
// Throw an error in development; this will show a dialog (see below).
|
|
if (DEVELOPMENT) {
|
|
throw new BlueslipError(msg, more_info, original_error);
|
|
}
|
|
// This function returns to its caller in production! To raise a
|
|
// fatal error even in production, use throw new Error(…) instead.
|
|
}
|
|
|
|
// Install a window-wide onerror handler in development to display the stacktraces, to make them
|
|
// hard to miss
|
|
if (DEVELOPMENT) {
|
|
$(window).on("error", (event: JQuery.TriggeredEvent) => {
|
|
const {originalEvent} = event;
|
|
if (originalEvent instanceof ErrorEvent) {
|
|
void display_stacktrace(originalEvent.error, originalEvent.message);
|
|
}
|
|
});
|
|
$(window).on("unhandledrejection", (event: JQuery.TriggeredEvent) => {
|
|
const {originalEvent} = event;
|
|
if (originalEvent instanceof PromiseRejectionEvent) {
|
|
void display_stacktrace(originalEvent.reason);
|
|
}
|
|
});
|
|
}
|