Skip to content

Commit

Permalink
Merge pull request #129 from coryasilva/AngularOverhaul
Browse files Browse the repository at this point in the history
AngularJS Client Overhaul (Rev1)
  • Loading branch information
oarevalo committed Sep 18, 2015
2 parents 9a1d15f + cd47613 commit 1ecb8e0
Showing 1 changed file with 155 additions and 123 deletions.
278 changes: 155 additions & 123 deletions client/bugLogClientAngular.js
Original file line number Diff line number Diff line change
@@ -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 += "<br><br><b>Extra Info:</b><br>";
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 += "<br><br><b>Stacktrace:</b><br><pre>"+stacktrace+"</pre>";
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 = '<h3>Bug Report</h3>';
var rootScope = getRootScope() || 'something went wrong';
var scripts = getScripts() || 'something went wrong';
var stylesheets = getStylesheets() || 'something went wrong';
html += '<h4>RootScope:</h4><pre>' + angular.toJson(rootScope, 2) + '</pre>';
html += '<h4>Scripts:</h4><pre>' + angular.toJson(scripts, 2) + '</pre>';
html += '<h4>Stylesheets:</h4><pre>' + angular.toJson(stylesheets, 2) + '</pre>';
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;
}
})
;

0 comments on commit 1ecb8e0

Please sign in to comment.