Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Loggerhead log include error logs from third-party code #7598

Merged
merged 9 commits into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ html(lang=window.preferredLocale.replace("_", "-"))
// import styles
link(rel='stylesheet', href='/css/#{webTheme}.css?cb=#{webBuildtimestamp}', id='web-theme')
link(rel='stylesheet', href='/css/updated-#{webTheme}.css?cb=#{webBuildtimestamp}', id='updated-web-theme', disabled="disabled")
script(type='text/javascript').
window.preferredLocale = "#{preferredLocale}";
window.docsLocale = "#{docsLocale}";
Etheryte marked this conversation as resolved.
Show resolved Hide resolved
script(src='/javascript/jquery.js?cb=#{webBuildtimestamp}')
script(src='/javascript/login.js?cb=#{webBuildtimestamp}')
script(src='/javascript/bootstrap.js?cb=#{webBuildtimestamp}')
Expand All @@ -38,8 +41,6 @@ html(lang=window.preferredLocale.replace("_", "-"))
| #{request_method}

script(type='text/javascript').
window.preferredLocale = "#{preferredLocale}";
window.docsLocale = "#{docsLocale}";
spaImportReactPage('login/login')
.then(function (module) {
module.renderer(
Expand Down
1 change: 1 addition & 0 deletions java/spacewalk-java.changes.eth.network-debug
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Improved logging to better capture third-party library issues
1 change: 1 addition & 0 deletions web/html/src/build/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ module.exports = (env, argv) => {
// Hardcode this so it always matches
pathname: DEVSERVER_WEBSOCKET_PATHNAME,
},
logging: "error",
},
// Override CORS headers for `yarn storybook`, these are not required otherwise
headers: {
Expand Down
117 changes: 74 additions & 43 deletions web/html/src/core/log/loggerhead.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,94 @@
// This file is allowed to use console, everyone else is not
/* eslint-disable no-console */

type Headers = Record<string, string>;
type Level = "info" | "debug" | "warning" | "error";
type Level = "info" | "debug" | "trace" | "warning" | "error";
type LogParams = Parameters<typeof console["log"]>;

export default class Loggerhead {
private _log = console.log.bind(console);
private _info = console.info.bind(console);
private _debug = console.debug.bind(console);
private _warn = console.warn.bind(console);
private _error = console.error.bind(console);

private url = "";
private levels: Record<Level, boolean> = { info: true, debug: true, warning: true, error: true };
private console: Record<Level, boolean> = { info: true, debug: true, warning: true, error: true };
private setHeaders: (headers: Headers) => Headers;
private setHeaders: (headers: Headers) => Headers = (headers) => headers;

constructor(url: string, setHeaders: (headers: Headers) => Headers) {
this.url = url;
this.setHeaders = setHeaders;
}

info(message: string) {
if (this.levels.info) {
this.postData({ level: "info", message });
}
if (this.console.info) {
console.info(message);
}
}
// We hijack the global console to ensure errors thrown in third-party code get logged too
// If we're running unit tests in a Node env, skip this
if (typeof window !== "undefined") {
console.log = this.log;
console.info = this.info;
console.debug = this.debug;
console.warn = this.warn;
console.error = this.error;

debug(message: string) {
if (this.levels.debug) {
this.postData({ level: "debug", message });
}
if (this.console.debug) {
console.debug(message);
// See https://stackoverflow.com/q/12571650/1470607
window.addEventListener("unhandledrejection", (event) => {
this.error(`Unhandled promise rejection: ${String(event.reason)}`);
return false;
});
window.addEventListener("error", (event) => {
this.error(event.error);
return false;
});
}
}

warn(message: string) {
if (this.levels.warning) {
this.postData({ level: "warning", message });
}
if (this.console.warning) {
console.warn(message);
}
}
// This is only used for binding third-party code, to log with the default level, use info()
private log = (
// Since we also wrap logging for external code, we need to support a variable number of arguments here
...args: LogParams
) => {
// Use level "info" for our own logs, console.log() for the browser
this._log(...args);
const message = String(args);
this.postData({ level: "info", message });
this.mark({ level: "info", message });
};

error(message: string) {
if (this.levels.error) {
this.postData({ level: "error", message });
}
if (this.console.error) {
console.error(message);
info = (...args: LogParams) => {
this._info(...args);
const message = String(args);
this.postData({ level: "info", message });
this.mark({ level: "info", message });
};

debug = (...args: LogParams) => {
this._debug(...args);
const message = String(args);
this.postData({ level: "debug", message });
this.mark({ level: "debug", message });
};

warn = (...args: LogParams) => {
this._warn(...args);
const message = String(args);
this.postData({ level: "warning", message });
this.mark({ level: "warning", message });
};

error = (...args: LogParams) => {
this._error(...args);
const message = String(args);
this.postData({ level: "error", message });
this.mark({ level: "error", message });
};

private mark(input: { level: Level; message: string }) {
// Older Node doesn't have this method, this check should be safe to remove once we upgrade to Node 20
if (typeof performance !== "undefined" && "mark" in performance) {
performance.mark(JSON.stringify(input));
}
}

private postData(data: { level: Level; message: string }) {
if (this.url === "") {
const errorMessage = "[Loggerhead] ERROR: no server enpoint URL set to send the POST request!! ";
if (this.console.error) {
console.error(errorMessage);
}
if (!this.url) {
return;
}

Expand All @@ -74,12 +107,10 @@ export default class Loggerhead {
if (xhr.status !== 200) {
// Try to parse the xhr response, but catch if it fails to avoid an infinite loop of failure-and-logging
try {
console.error(JSON.parse(xhr.response));
this._error(JSON.parse(xhr.response));
} catch (e) {
console.error(
"The POST request to the url: '" +
this.url +
"' was not successfully completed and the response cannot be parsed."
this._error(
`The POST request to the url '${this.url}' completed with status ${xhr.status} and the response cannot be parsed.`
);
}
}
Expand Down
1 change: 1 addition & 0 deletions web/spacewalk-web.changes.eth.network-debug
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Improved logging to better capture third-party library issues
Loading