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 { handleAnyComponentFocus } from "@/helpers/button-question-focus-helper";
|
||||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||||
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
import { showFmgLoadingModal } from "@/helpers/loading-modal-helper";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "app",
|
name: "app",
|
||||||
setup() {
|
setup() {
|
||||||
|
|
@ -33,6 +34,24 @@ export default {
|
||||||
loadingModal,
|
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>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<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 axios from "axios";
|
||||||
import analyticsMixIn from "@/mixins/analytics-mixin.js";
|
import analyticsMixIn from "@/mixins/analytics-mixin.js";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import router from "@/router";
|
|
||||||
|
|
||||||
import { applicationConfig } from "@/constants/application-config.js";
|
import { applicationConfig } from "@/constants/application-config.js";
|
||||||
import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
|
import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
|
||||||
import { headerKeys } from "@/constants/header-keys";
|
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 {
|
export default {
|
||||||
callHttpClient({
|
callHttpClient({
|
||||||
method,
|
method,
|
||||||
|
|
@ -50,8 +90,6 @@ export default {
|
||||||
return resolve(response);
|
return resolve(response);
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.error(error);
|
|
||||||
|
|
||||||
if (logApiCall) {
|
if (logApiCall) {
|
||||||
analyticsMixIn.methods.pushEventToGA(
|
analyticsMixIn.methods.pushEventToGA(
|
||||||
GaCategories.API_RESPONSE,
|
GaCategories.API_RESPONSE,
|
||||||
|
|
@ -61,12 +99,9 @@ export default {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not route to error logic when no wipers found or no promo found (404s)
|
global.$logger.logError(`${method}: ${endpoint}`, error);
|
||||||
if (error.response.status != "404") {
|
|
||||||
router.navigateError();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 analyticsMixin from "@/mixins/analytics-mixin.js";
|
||||||
import experimentMixin from "@/mixins/experiment-mixin.js";
|
import experimentMixin from "@/mixins/experiment-mixin.js";
|
||||||
import "../node_modules/bootstrap/dist/js/bootstrap.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
|
// Vue App Setup
|
||||||
const vueApp = createApp(App);
|
const vueApp = createApp(App);
|
||||||
|
|
@ -20,4 +24,17 @@ vueApp.mixin(baseMixin);
|
||||||
vueApp.mixin(analyticsMixin);
|
vueApp.mixin(analyticsMixin);
|
||||||
vueApp.mixin(experimentMixin);
|
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");
|
vueApp.mount("#app");
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,8 @@ const routes = [
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
||||||
|
global.$logger.logError(error);
|
||||||
|
|
||||||
if (to.query?.fmgPage === funnelStartPageName) {
|
if (to.query?.fmgPage === funnelStartPageName) {
|
||||||
deleteFunnelCookie();
|
deleteFunnelCookie();
|
||||||
await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
|
await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue