Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Bugsense.js implementation

  • Loading branch information...
commit a663e9a8f963687838f4626225229fcf46d653a8 0 parents
Yves Van Goethem yvg authored
Showing with 196 additions and 0 deletions.
  1. +9 −0 LICENSE.txt
  2. +45 −0 README.md
  3. +9 −0 bugsense.css
  4. +133 −0 bugsense.js
9 LICENSE.txt
@@ -0,0 +1,9 @@
+Copyright (c) 2011, SoundCloud Ltd., Yves Van Goethem
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 README.md
@@ -0,0 +1,45 @@
+# JavaScript BugSense Notifier
+
+A front-end implementation of the BugSense API to track errors.
+You can find the license in the LICENSE file at the root of this project.
+
+## Running
+
+We don't recommend you to implement this code as-is because it is designed for our specific needs and relies on top of different frameworks that we use across our site: jQuery, underscore.js, JSON.js, Backbone.js,…
+But it is easily adaptable to any other context as described bellow.
+
+## API Structure
+
+The BugSense API is designed in 4 different parts:
+
+- application_environment (describes the environment the script runs in).
+- client (defines the client name, protocol,…)
+- exception (logs the "Class", message, backtrace,…)
+- request (logs the URL and any other custom data (like xhr responses or requests))
+
+You will easily recognize these different parts in our source besides required parameters like api key, bugsense url, etc…
+
+## Calling the notifier
+
+If you use jQuery you are good to go:
+
+ $(document).ajaxError(function(event, xhr, settings, error) {
+ SC.Bugsense.notify({
+ request: xhr,
+ settings: settings,
+ error: error
+ });
+ });
+
+As we use it we stick to the jQuery XHR structure to pass the different objects.
+
+## Tackle limitations
+
+### Class
+
+There are no proper Classes in JS, but this doesn't mean you can't have an equivalent structure.
+As we are using Backbone, we extended their model and name them, this permits us to integrate it in the `exception.klass` object of BugSense.
+
+### TraceStack
+
+This is a little more tricky, looking into console.trace might be a good alternative to our code.
9 bugsense.css
@@ -0,0 +1,9 @@
+#bugsense-iframe {
+ max-width: 1px !important;
+ max-height: 1px !important;
+ top: 0;
+ left: 0;
+ position: absolute;
+ z-index: -1;
+ visibility: hidden;
+}
133 bugsense.js
@@ -0,0 +1,133 @@
+SC.Bugsense = {
+ notify: function(notice) {
+
+ var that = this;
+ this.notice = notice;
+ this.status = ( this.notice && this.notice.request && this.notice.request.status ).toString();
+ this.errors = ( this.notice && this.notice.request && this.notice.request.errors );
+ var res = this.notice.request || { message: 'Unknown Error' };
+ var stack = ( this.notice.response && this.notice.response.stack );
+
+ // filter out some errors (e.g. 404, 422,…)
+ if (this.errorFilters()) {
+ return;
+ }
+
+ // bugsense parameters (required)
+ this.defaults = {
+ apiKey: 'FOOBAR', // modify me
+ url: 'https://bugsense.appspot.com/api/errors?api_key=' // SSL
+ // url: 'http://www.bugsense.com/api/js/errors?api_key=' // NON-SSL
+ };
+
+ this.data = {
+
+ // basic data (required)
+ application_environment: {
+ environment: 'development', // modify me if you like
+ // TODO: find a way to detect the mobile device, maybe with WURFL or so…?
+ appver: window.navigator.userAgent || 'unknown',
+ osver: window.navigator.oscpu || 'unknown'
+ },
+
+ // bugsense client
+ client: {
+ name : 'SC Mobile Bugsense Notifier',
+ protocol_version: 1,
+ version: '0.1'
+ },
+
+ // basics about the exception
+ exception: {
+ klass: ( that.notice.settings && that.notice.settings.modelType ) || 'Unknown Component',
+ message: that.notice.error || res.message,
+ backtrace: that.generateBackTrace(stack),
+ where:"n/a:0" // can't take this out or the API breaks.
+ },
+
+ // details & custom data about the exception including url, request, response,…
+ request: (function() {
+ var request = {
+ // Collecting IPs is illegal in some countries that's why we don't do it, if you'd like to, just remove this ligne
+ remote_ip: '0.0.0.0',
+ url: window.location.href,
+ custom_data: {
+ // You can remove & add custom data here from session/localStorage, cookies, geolocation, language, mimetypes,…
+ document_referrer : that.escapeText(document.referrer),
+ http_status : that.escapeText(this.status),
+ navigator_user_agent : that.escapeText(navigator.userAgent),
+ navigator_platform : that.escapeText(navigator.platform),
+ navigator_vendor : that.escapeText(navigator.vendor),
+ navigator_language : that.escapeText(navigator.language),
+ screen_width : that.escapeText(screen.width),
+ screen_height : that.escapeText(screen.height),
+ response : that.escapeText(that.notice.request.responseText),
+ request : {}
+ }
+ };
+ if (that.notice.settings) {
+ var req = that.notice.settings;
+ _.each(req, function(value, key) {
+ // whitelist to avoid functions
+ if (/boolean|number|string/.test($.type(value))) {
+ request.custom_data.request[key] = value;
+ }
+ });
+ }
+ // stringify it
+ request.custom_data.request = JSON.stringify(request.custom_data.request);
+ return request;
+ }())
+
+ };
+
+ // all ready? lets make a get request with the data
+ if (this.data && this.defaults.url && this.defaults.apiKey) {
+ var url = this.defaults.url + this.defaults.apiKey + '&data=' + escape( JSON.stringify(this.data) );
+ if ($('#bugsense-iframe')[0]) {
+ $('#bugsense-iframe').attr('src', url);
+ } else {
+ $('body').append('<iframe id="bugsense-iframe" src="' + url + '" width="1" height="1">');
+ }
+ }
+ },
+ errorFilters: function() {
+ return _.any([
+ // not found
+ this.status === '404',
+ // add whatever you want in here
+ ]);
+ },
+ escapeText: function(text) {
+ text = text.toString() || '';
+ return text.replace(/&/g, '&#38;')
+ .replace(/</g, '&#60;')
+ .replace(/>/g, '&#62;')
+ .replace(/'/g, '&#39;')
+ .replace(/"/g, '&#34;');
+ },
+ generateBackTrace: function(stack) {
+ if (stack) {
+ return stack.file + ':' + stack.line;
+ }
+ try {
+ throw new Error();
+ } catch (e) {
+ if (e.stack) {
+ var matcher = /\s+at\s(.+)\s\((.+?):(\d+)(:\d+)?\)/;
+ return $.map(e.stack.split("\n").slice(4), _.bind(function(line) {
+ var match = line.match(matcher);
+ var method = escapeText(match[1]);
+ var file = escapeText(match[2]);
+ var number = match[3];
+ return file + ':' + number + 'in' + method;
+ }, this)).join("\n");
+ } else if (e.sourceURL) {
+ // note: this is completely useless, as it just points back at itself but is needed on Safari
+ // keeping it around in case they ever end up providing actual stacktraces
+ return e.sourceURL + ':' + e.line;
+ }
+ }
+ return 'n/a:0';
+ }
+};
Please sign in to comment.
Something went wrong with that request. Please try again.