diff --git a/client/bugLogClientAngular.js b/client/bugLogClientAngular.js
index 69e4486..30de9dc 100644
--- a/client/bugLogClientAngular.js
+++ b/client/bugLogClientAngular.js
@@ -1,134 +1,166 @@
-angular.module('BugLogHq', [])
-//the application to tell BugLog from where the report is coming from.
-.constant('appName', "app")
-.constant('bugLogConfig', {
- // this is the location of the BugLog server that will be receiving the error reports (needs to be a REST/POST endpoint)
- listener: "",
- // the hostname to tell BugLog from where the report is coming from. Leave empty to get the info from the browser.
- hostName: "",
- // If the BugLog server requires an API Key to talk to it, then here is where you put it.
- apiKey: ""
-})
-.config(['$provide',
- function ($provide) {
- $provide.decorator('$exceptionHandler', ['$delegate', '$log', 'BugLogService',
- function ($delegate, $log, BugLogService) {
- return function(exception, cause) {
- //Custom error handling.
- try {
- BugLogService.notifyService({
- message: cause,
- error: exception
- });
- }
- catch (e) {
- $log.warn("BugLog Service: Error logging bug");
- $log.warn(e);
- }
- // Calls the original $exceptionHandler.
- $delegate(exception, cause);
- };
- }
- ]);
- }
-])
-.factory('BugLogService', ["$window", "$document", "appName", "bugLogConfig",
- function BugLogService($window, $document, appName, bugLogConfig) {
- var BugLog = {
- listener: bugLogConfig.listener,
- hostName: bugLogConfig.hostName,
- appName: appName,
- apiKey: bugLogConfig.apiKey,
-
- notifyService: function(message) {
- var msg = {}; // set defaults
- if(typeof message == "string")
- msg.message = message;
- msg.message = message.message;
- msg.extraInfo = message.extraInfo || "";
- msg.severity = message.severity || "ERROR";
- msg.error = message.error || undefined;
- msg.stack = message.stack || undefined;
+/**
+ * This module requires stacktrace-js and angular, it has been tested with:
+ *
+ * "stacktrace-js": "~0.6.4"
+ * "angular": "~1.3.15"
+ */
- // get additional information (if any)
- // get assets here for file version reference
- var extra = {
- assets: BugLog.getAssets(),
- errorUrl: $window.location.href
- };
- if(msg.extraInfo) {
- extra += "
Extra Info:
";
- if(typeof msg.extraInfo=="string") {
- extra += msg.extraInfo;
- } else {
- if(window.JSON) {
- extra += JSON.stringify(msg.extraInfo);
+angular.module('BugLogHq', [])
+ .constant('bugLogConfig', {
+ // This is the location of the BugLog server that will be receiving the error reports
+ // example: 'https://domain/buglog/listeners/bugLogListenerREST.cfm'
+ listener: '',
+ // Tell BugLog which application is submitting this bug
+ applicationCode: '',
+ // If the BugLog server requires an API Key to talk to it, then here is where you put it.
+ apiKey: '',
+ // The hostname to tell BugLog from where the report is coming from. Leave empty to get the info from the browser.
+ hostName: '',
+ // Default bug serverity code
+ defaultSeverity: 'ERROR'
+ })
+ .config(['$provide',
+ function ($provide) {
+ $provide.decorator('$exceptionHandler', ['$delegate', '$log', 'BugLogService',
+ function ($delegate, $log, BugLogService) {
+ return function(exception, cause) {
+ // Custom error handling.
+ try {
+ BugLogService.logBug(cause, exception);
}
- }
- }
-
- // see if we can get a stacktrace
- var stacktrace = undefined;
- if(typeof msg.stack !== "undefined") {
- stacktrace = msg.stack;
- } else if(typeof msg.error !== "undefined") {
- stacktrace = (typeof msg.error.stack!=="undefined") ? msg.error.stack : $window.printStackTrace({e:msg.error}).join("\n");
- }
- if(typeof stacktrace !== "undefined") {
- extra += "
Stacktrace:
"+stacktrace+""; + catch (e) { + $log.warn("BugLog Service: Error logging bug"); + $log.warn(e); + } + // Calls the original $exceptionHandler. + $delegate(exception, cause); + }; } - - - // build the message in a format that can be passed along - var data = "message="+escape(msg.message) - +"&severityCode="+escape(msg.severity) - +"&hostName="+escape(BugLog.hostName || $window.location.host) - +"&applicationCode="+escape(BugLog.appName) - +"&apiKey="+escape(BugLog.apiKey) - +"&userAgent="+escape(navigator.userAgent) - +"&templatePath="+escape($document.URL) - +"&htmlReport=" + escape(extra); - - // create the notifier - var noti = BugLog.createNotifier("script"); - noti.src = BugLog.listener + "?" + data; + ]); + } + ]) + .factory('BugLogService', function BugLogService($window, bugLogConfig) { + var service = { + logBug: logBug + }; + return service; + ///////////////////// - // destroy the notifier - BugLog.destroyNotifier(noti); - }, + /** + * Communicates with a BugLogHq Server + * @param {string} cause + * @param {object} error + * @param {string} [severityCode] + */ + function logBug(cause, error, severityCode) { + var data = {}; + data.message = 'Caused by: ' + cause; + data.exceptionMessage = cause || 'Unknown'; + data.exceptionDetails = buildStacktrace(error) || 'something went wrong'; + data.severityCode = severityCode || bugLogConfig.defaultSeverity || 'ERROR'; + data.HTMLReport = buildHtmlReport() || 'something went wrong'; + data.userAgent = navigator.userAgent || 'Unknown'; + data.templatePath = document.URL || 'Unknown'; + data.applicationCode = bugLogConfig.applicationCode || 'Unknown'; + data.APIKey = bugLogConfig.apiKey || ''; + data.hostName = bugLogConfig.hostName || ''; + // Make server call; we could use the $http via the $injector service (to avoid circular dependencies) + // but customer header could be set that upset CORS requests to the bugLog server + // so a plain old javascript XHR request is used. + var xhr = new XMLHttpRequest(); + xhr.open('POST', encodeURI(bugLogConfig.listener)); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.send(angular.toJson(data)); + } - createNotifier: function(tagName) { - var notifier = document.createElement(tagName); - notifier.id = "buglog" + (+ new Date); - var head = document.getElementsByTagName("head")[0]; - head.appendChild(notifier); - return notifier; - }, + /** + * Build Stacktrace + * @param error + * @return {*} + */ + function buildStacktrace(error) { + var stack; + if(typeof error.stack !== 'undefined') { + stack = error.stack; + } + if ($window.printStackTrace) { + stack = $window.printStackTrace({e: error}).join('\n'); + } + return stack; + } - destroyNotifier: function(notifier) { - var elm = document.getElementById(notifier.id); - var head = document.getElementsByTagName("head")[0]; - head.removeChild(elm); - }, + /** + * Build HTML Report + * The $rootScope is included because it holds our user session object + * If your app uses a service instead the $injector service can be used to retrieve it + * @returns {string} + */ + function buildHtmlReport() { + var html = '
' + angular.toJson(rootScope, 2) + ''; + html += '
' + angular.toJson(scripts, 2) + ''; + html += '
' + angular.toJson(stylesheets, 2) + ''; + return html; + } - getAssets: function () { - var assets = []; - var links = document.getElementsByTagName("link"); - for(var i = 0; i < links.length; i++) { - if (links[i].href) { - assets.push(links[i].href); - } + /** + * Gets an Html Document's stylesheets + * @returns {Array} + */ + function getStylesheets() { + var assets = []; + var links = document.getElementsByTagName('link'); + for(var i = 0; i < links.length; i++) { + if (links[i].href && links[i].rel && links[i].rel.toLowerCase() === 'stylesheet') { + var href = links[i].href.split('/'); + assets.push(href[href.length-1]); } - var scripts = document.getElementsByTagName("script"); - for(var j = 0; j < scripts.length; j++) { - if (scripts[j].src) { - assets.push(scripts[j].src); - } + } + return assets; + } + + /** + * Gets an Html Document's scripts + * @returns {Array} + */ + function getScripts() { + var assets = []; + var scripts = document.getElementsByTagName('script'); + for(var j = 0; j < scripts.length; j++) { + if (scripts[j].src) { + var src = scripts[j].src.split('/'); + assets.push(src[src.length-1]); } - return assets; } - }; + return assets; + } + + /** + * Finds an AngularJs rootScope and returns its properties + * @returns {{}} + */ + function getRootScope() { + var body = angular.element(document.body); + var rootScope = body.scope().$root || undefined; + return cleanScope(rootScope); + } - return BugLog; - } -]); + /** + * Returns an object with copied properties from an AngularJs scope + * @param scope + * @returns {{}} + */ + function cleanScope(scope) { + var obj = {}; + for (var key in scope) { + if(key.substring(0, 1) !== '$' && key.substring(0,2) !== '__' && key !== 'constructor') { + obj[key] = angular.copy(scope[key]); + } + } + return obj; + } + }) +;