diff --git a/java/code/src/com/suse/manager/webui/controllers/login/templates/login.jade b/java/code/src/com/suse/manager/webui/controllers/login/templates/login.jade index ce35b02ab03c..488a1a0e1609 100644 --- a/java/code/src/com/suse/manager/webui/controllers/login/templates/login.jade +++ b/java/code/src/com/suse/manager/webui/controllers/login/templates/login.jade @@ -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}"; script(src='/javascript/jquery.js?cb=#{webBuildtimestamp}') script(src='/javascript/login.js?cb=#{webBuildtimestamp}') script(src='/javascript/bootstrap.js?cb=#{webBuildtimestamp}') @@ -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( diff --git a/java/spacewalk-java.changes.eth.network-debug b/java/spacewalk-java.changes.eth.network-debug new file mode 100644 index 000000000000..92985e4713a5 --- /dev/null +++ b/java/spacewalk-java.changes.eth.network-debug @@ -0,0 +1 @@ +- Improved logging to better capture third-party library issues diff --git a/web/html/src/build/webpack.config.js b/web/html/src/build/webpack.config.js index 373d6ca7e44a..4f46dd5658a9 100644 --- a/web/html/src/build/webpack.config.js +++ b/web/html/src/build/webpack.config.js @@ -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: { diff --git a/web/html/src/core/log/loggerhead.ts b/web/html/src/core/log/loggerhead.ts index d4d96ef756d5..c4d27f738841 100644 --- a/web/html/src/core/log/loggerhead.ts +++ b/web/html/src/core/log/loggerhead.ts @@ -1,61 +1,94 @@ // This file is allowed to use console, everyone else is not /* eslint-disable no-console */ + type Headers = Record; -type Level = "info" | "debug" | "warning" | "error"; +type Level = "info" | "debug" | "trace" | "warning" | "error"; +type LogParams = Parameters; 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 = { info: true, debug: true, warning: true, error: true }; - private console: Record = { 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; } @@ -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.` ); } } diff --git a/web/spacewalk-web.changes.eth.network-debug b/web/spacewalk-web.changes.eth.network-debug new file mode 100644 index 000000000000..92985e4713a5 --- /dev/null +++ b/web/spacewalk-web.changes.eth.network-debug @@ -0,0 +1 @@ +- Improved logging to better capture third-party library issues