Frontend Logging
This commit is contained in:
parent
43df7efcde
commit
763956caef
7 changed files with 157 additions and 8 deletions
19
src/App.vue
19
src/App.vue
|
|
@ -12,6 +12,7 @@
|
|||
import { handleAnyComponentFocus } from "@/helpers/button-question-focus-helper";
|
||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
||||
|
||||
export default {
|
||||
name: "app",
|
||||
setup() {
|
||||
|
|
@ -33,6 +34,24 @@ export default {
|
|||
loadingModal,
|
||||
},
|
||||
};
|
||||
|
||||
// Add a global Javascript exception handler for logging exceptions, this handler will log when there is an uncaught exception
|
||||
window.onerror = (msg, url, line, col, error) => {
|
||||
// Log the windows error
|
||||
// Note that col & error are new to the HTML 5 spec and may not be
|
||||
// supported in every browser. It worked for me in Chrome.
|
||||
global.$logger.logError(msg, {
|
||||
url: url,
|
||||
line: line,
|
||||
column: col ?? "",
|
||||
error: error ?? "",
|
||||
});
|
||||
|
||||
// If you return true, then error alerts (like in older versions of
|
||||
// Internet Explorer) will be suppressed.
|
||||
var suppressErrorAlert = true;
|
||||
return suppressErrorAlert;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
|||
6
src/constants/logger.js
Normal file
6
src/constants/logger.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
const logEntryTypes = {
|
||||
information: "info",
|
||||
warning: "warn",
|
||||
error: "fail",
|
||||
critical: "crit",
|
||||
};
|
||||
8
src/constants/logging-endpoints.js
Normal file
8
src/constants/logging-endpoints.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
const loggingEndpoints = {
|
||||
LOG_INFORMATION: '/analytics/api/v1/analytics/log-information',
|
||||
LOG_WARNING: '/analytics/api/v1/analytics/log-warning',
|
||||
LOG_ERROR: '/analytics/api/v1/analytics/log-error',
|
||||
LOG_CRITICAL: '/analytics/api/v1/analytics/log-critical',
|
||||
}
|
||||
|
||||
export default loggingEndpoints;
|
||||
|
|
@ -1,12 +1,52 @@
|
|||
import axios from "axios";
|
||||
import analyticsMixIn from "@/mixins/analytics-mixin.js";
|
||||
import store from "@/store";
|
||||
import router from "@/router";
|
||||
|
||||
import { applicationConfig } from "@/constants/application-config.js";
|
||||
import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
|
||||
import { headerKeys } from "@/constants/header-keys";
|
||||
|
||||
// Add a response interceptor for global axios error handing.
|
||||
axios.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
let rejectionError = "";
|
||||
if (typeof error.response === "undefined") {
|
||||
// The request was not made, could be a bad connection or a CORS error.
|
||||
rejectionError = {
|
||||
message:
|
||||
"A network error occurred. " +
|
||||
"This could be a CORS issue or a dropped internet connection. " +
|
||||
"It is impossible for us to know.",
|
||||
cause: error,
|
||||
};
|
||||
} else if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
rejectionError = {
|
||||
message: "Status Code Error",
|
||||
cause: error.response,
|
||||
};
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
rejectionError = {
|
||||
message: "The request was made but no response was received",
|
||||
cause: error.request,
|
||||
};
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
rejectionError = {
|
||||
message: "Error",
|
||||
cause: error.message,
|
||||
};
|
||||
}
|
||||
|
||||
return Promise.reject(rejectionError);
|
||||
}
|
||||
);
|
||||
|
||||
export default {
|
||||
callHttpClient({
|
||||
method,
|
||||
|
|
@ -50,8 +90,6 @@ export default {
|
|||
return resolve(response);
|
||||
},
|
||||
(error) => {
|
||||
console.error(error);
|
||||
|
||||
if (logApiCall) {
|
||||
analyticsMixIn.methods.pushEventToGA(
|
||||
GaCategories.API_RESPONSE,
|
||||
|
|
@ -61,12 +99,9 @@ export default {
|
|||
);
|
||||
}
|
||||
|
||||
// do not route to error logic when no wipers found or no promo found (404s)
|
||||
if (error.response.status != "404") {
|
||||
router.navigateError();
|
||||
}
|
||||
global.$logger.logError(`${method}: ${endpoint}`, error);
|
||||
|
||||
return reject(error.response);
|
||||
return reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
|||
62
src/helpers/logger.js
Normal file
62
src/helpers/logger.js
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import loggingEndpoints from "@/constants/logging-endpoints"
|
||||
import { applicationConfig } from "@/constants/application-config.js";
|
||||
import axios from "axios";
|
||||
import store from "@/store";
|
||||
|
||||
import { headerKeys } from "@/constants/header-keys";
|
||||
|
||||
export class Logger {
|
||||
logInformation(message, details) {
|
||||
this.writeLogEntry(loggingEndpoints.LOG_INFORMATION, this.formatLogEntry(message, details));
|
||||
}
|
||||
|
||||
logWarning(message, details) {
|
||||
this.writeLogEntry(loggingEndpoints.LOG_WARNING, this.formatLogEntry(message, details));
|
||||
}
|
||||
|
||||
logError(message, details) {
|
||||
this.writeLogEntry(loggingEndpoints.LOG_ERROR, this.formatLogEntry(message, details));
|
||||
}
|
||||
|
||||
logCritical(message, details) {
|
||||
this.writeLogEntry(loggingEndpoints.LOG_CRITICAL, this.formatLogEntry(message, details));
|
||||
}
|
||||
|
||||
formatLogEntry(message, details) {
|
||||
return `${message}\n${details ? JSON.stringify(details, undefined, 2) : ""}`;
|
||||
}
|
||||
|
||||
writeLogEntry(endpoint, logEntry) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = applicationConfig.FRONTEND_LOGGER_URL;
|
||||
const payload = Object.assign({}, payload);
|
||||
const headers = {
|
||||
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings),
|
||||
};
|
||||
|
||||
axios({
|
||||
method: "POST",
|
||||
url: `${url}${endpoint}`,
|
||||
data: logEntry,
|
||||
crossDomain: true,
|
||||
responseType: {},
|
||||
headers: headers,
|
||||
}).then(
|
||||
(response) => {
|
||||
return resolve(response);
|
||||
},
|
||||
(error) => {
|
||||
return reject(error);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
saveLogEntry(logEntry) {
|
||||
console.log(logEntry);
|
||||
}
|
||||
}
|
||||
|
||||
export default Logger;
|
||||
|
||||
17
src/main.js
17
src/main.js
|
|
@ -8,6 +8,10 @@ import baseMixin from "@/mixins/base-mixin.js";
|
|||
import analyticsMixin from "@/mixins/analytics-mixin.js";
|
||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||
import "../node_modules/bootstrap/dist/js/bootstrap.js";
|
||||
import Logger from "@/helpers/logger";
|
||||
|
||||
// Instantiate global logging object
|
||||
global.$logger = new Logger();
|
||||
|
||||
// Vue App Setup
|
||||
const vueApp = createApp(App);
|
||||
|
|
@ -20,4 +24,17 @@ vueApp.mixin(baseMixin);
|
|||
vueApp.mixin(analyticsMixin);
|
||||
vueApp.mixin(experimentMixin);
|
||||
|
||||
// Vue Error Handling
|
||||
vueApp.config.errorHandler = (err, vm, info) => {
|
||||
global.$logger.logError(err.message, {
|
||||
info: info,
|
||||
vm: vm,
|
||||
});
|
||||
};
|
||||
|
||||
// Vue Router Error Handling
|
||||
router.onError((err) => {
|
||||
global.$logger.logError(err.message, err.cause);
|
||||
});
|
||||
|
||||
vueApp.mount("#app");
|
||||
|
|
|
|||
|
|
@ -154,6 +154,8 @@ const routes = [
|
|||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
global.$logger.logError(error);
|
||||
|
||||
if (to.query?.fmgPage === funnelStartPageName) {
|
||||
deleteFunnelCookie();
|
||||
await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
|
||||
|
|
|
|||
Loading…
Reference in a new issue