honeybadger-js
Advanced tools
Comparing version 0.3.1 to 0.4.0
{ | ||
"name": "honeybadger-js", | ||
"version": "0.3.1", | ||
"name": "honeybadger", | ||
"version": "0.4.0", | ||
"homepage": "https://github.com/honeybadger-io/honeybadger-js", | ||
@@ -5,0 +5,0 @@ "authors": [ |
/* | ||
honeybadger.js v0.3.1 | ||
honeybadger.js v0.4.0 | ||
A JavaScript Notifier for Honeybadger | ||
@@ -8,623 +8,534 @@ https://github.com/honeybadger-io/honeybadger-js | ||
*/ | ||
(function(window) { | ||
// Generated by CoffeeScript 1.9.3 | ||
var helpers; | ||
(function (root, builder) { | ||
// Read default configuration from script tag if available. | ||
var scriptConfig = {}; | ||
(function() { | ||
var tags = document.getElementsByTagName("script"); | ||
var tag = tags[tags.length - 1]; | ||
if (!tag) { return; } | ||
var attrs = tag.attributes; | ||
var value; | ||
for (i = 0, len = attrs.length; i < len; i++) { | ||
if (/data-(\w+)$/.test(attrs[i].nodeName)) { | ||
value = attrs[i].nodeValue; | ||
if (value === 'false') { value = false; } | ||
scriptConfig[RegExp.$1] = value; | ||
} | ||
} | ||
})(); | ||
helpers = {}; | ||
// Build the singleton factory. The factory can be accessed through | ||
// singleton.factory() to instantiate a new instance. | ||
var factory = function(){ | ||
var f = builder(); | ||
var singleton = f(scriptConfig); | ||
singleton.factory = f; | ||
return singleton; | ||
}; | ||
helpers.String = function(obj, fallback) { | ||
if ((obj == null) && (fallback != null)) { | ||
return String(fallback); | ||
// UMD (Universal Module Definition) | ||
// See https://github.com/umdjs/umd | ||
if (typeof define === 'function' && define.amd) { | ||
// AMD. Register as an anonymous module. | ||
define([], factory); | ||
} else if (typeof module === 'object' && module.exports) { | ||
// Browserfy. Does not work with strict CommonJS, but | ||
// only CommonJS-like environments that support module.exports, | ||
// like Browserfy/Node. | ||
module.exports = factory(); | ||
} else { | ||
// Browser globals (root is window). | ||
root.Honeybadger = factory(); | ||
} | ||
if (obj == null) { | ||
return null; | ||
}(this, function () { | ||
var VERSION = '0.4.0', | ||
NOTIFIER = { | ||
name: 'honeybadger.js', | ||
url: 'https://github.com/honeybadger-io/honeybadger-js', | ||
version: VERSION, | ||
language: 'javascript' | ||
}; | ||
// Used to control initial setup across clients. | ||
var loaded = false, | ||
installed = false; | ||
// Used to prevent reporting duplicate errors across instances. | ||
var currentErr, | ||
currentPayload; | ||
// Utilities. | ||
function merge(obj1, obj2) { | ||
var obj3 = {}; | ||
for (k in obj1) { obj3[k] = obj1[k]; } | ||
for (k in obj2) { obj3[k] = obj2[k]; } | ||
return obj3; | ||
} | ||
return String(obj); | ||
}; | ||
// Generated by CoffeeScript 1.9.3 | ||
var Configuration; | ||
Configuration = (function() { | ||
Configuration.defaults = { | ||
api_key: null, | ||
host: 'api.honeybadger.io', | ||
ssl: true, | ||
project_root: window.location.protocol + '//' + window.location.host, | ||
environment: 'production', | ||
component: null, | ||
action: null, | ||
disabled: false, | ||
onerror: false, | ||
debug: false, | ||
timeout: false | ||
}; | ||
function currentErrIs(err) { | ||
if (!currentErr) { return false; } | ||
if (currentErr.name !== err.name) { return false; } | ||
if (currentErr.message !== err.message) { return false; } | ||
if (currentErr.stack !== err.stack) { return false; } | ||
return true; | ||
} | ||
function Configuration(options) { | ||
var k, ref, v; | ||
if (options == null) { | ||
options = {}; | ||
function cgiData() { | ||
var data = {}; | ||
data['HTTP_USER_AGENT'] = navigator.userAgent; | ||
if (document.referrer.match(/\S/)) { | ||
data['HTTP_REFERER'] = document.referrer; | ||
} | ||
ref = this.constructor.defaults; | ||
for (k in ref) { | ||
v = ref[k]; | ||
this[k] = v; | ||
return data; | ||
} | ||
function stackTrace(err) { | ||
// From TraceKit: Opera 10 *destroys* its stacktrace property if you try to | ||
// access the stack property first. | ||
return err.stacktrace || err.stack || undefined | ||
} | ||
function generateStackTrace(err) { | ||
var stack; | ||
var maxStackSize = 10; | ||
if (err && (stack = stackTrace(err))) { | ||
return {stack: stack, generator: undefined}; | ||
} | ||
for (k in options) { | ||
v = options[k]; | ||
this[k] = v; | ||
try { | ||
throw new Error(''); | ||
} catch(e) { | ||
if (stack = stackTrace(e)) { | ||
return {stack: stack, generator: 'throw'}; | ||
} | ||
} | ||
stack = ['<call-stack>']; | ||
var curr = arguments.callee; | ||
while (curr && stack.length < maxStackSize) { | ||
if (/function(?:\s+([\w$]+))+\s*\(/.test(curr.toString())) { | ||
stack.push(RegExp.$1 || '<anonymous>'); | ||
} else { | ||
stack.push('<anonymous>'); | ||
} | ||
try { | ||
curr = curr.caller; | ||
} catch (e) { | ||
break; | ||
} | ||
} | ||
return {stack: stack.join('\n'), generator: 'walk'}; | ||
} | ||
Configuration.prototype.reset = function() { | ||
var k, ref, v; | ||
ref = this.constructor.defaults; | ||
for (k in ref) { | ||
v = ref[k]; | ||
this[k] = v; | ||
function checkHandlers(handlers, err) { | ||
var handler, i, len; | ||
for (i = 0, len = handlers.length; i < len; i++) { | ||
handler = handlers[i]; | ||
if (handler(err) === false) { | ||
return true; | ||
} | ||
} | ||
return this; | ||
}; | ||
return false; | ||
} | ||
return Configuration; | ||
function request(url) { | ||
// Use XHR when available. | ||
try { | ||
// Inspired by https://gist.github.com/Xeoncross/7663273 | ||
x = new(this.XMLHttpRequest || ActiveXObject)('MSXML2.XMLHTTP.3.0'); | ||
x.open('GET', url, true); | ||
x.send(); | ||
return; | ||
} catch(e) { | ||
log('Error encountered during XHR request (will retry): ' + e); | ||
} | ||
})(); | ||
// Generated by CoffeeScript 1.9.3 | ||
var Notice, | ||
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | ||
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; | ||
// Fall back to Image transport. | ||
img = new Image(); | ||
img.src = url; | ||
} | ||
Notice = (function() { | ||
function Notice(opts, config) { | ||
var k, ref, ref1, v; | ||
if (opts == null) { | ||
opts = {}; | ||
// Client factory. | ||
var factory = (function(opts) { | ||
var defaultProps = []; | ||
var queue = []; | ||
var self = { | ||
context: {}, | ||
beforeNotifyHandlers: [] | ||
} | ||
if (config == null) { | ||
config = Honeybadger.configuration; | ||
if (opts instanceof Object) { | ||
for (k in opts) { self[k] = opts[k]; } | ||
} | ||
this._sanitize = bind(this._sanitize, this); | ||
this.log = opts.log || function() {}; | ||
this.stack = helpers.String(opts.stack); | ||
this.generator = helpers.String(opts.generator); | ||
this["class"] = helpers.String(opts.name, 'Error'); | ||
this.message = helpers.String(opts.message, 'No message provided'); | ||
this.source = null; | ||
this.url = helpers.String(document.URL); | ||
this.project_root = helpers.String(config.project_root); | ||
this.environment = helpers.String(config.environment); | ||
this.component = helpers.String(opts.component || config.component); | ||
this.action = helpers.String(opts.action || config.action); | ||
this.cgi_data = this._cgiData(); | ||
this.fingerprint = helpers.String(opts.fingerprint); | ||
this.context = {}; | ||
ref = Honeybadger.context; | ||
for (k in ref) { | ||
v = ref[k]; | ||
this.context[k] = v; | ||
} | ||
if (opts.context && typeof opts.context === 'object') { | ||
ref1 = opts.context; | ||
for (k in ref1) { | ||
v = ref1[k]; | ||
this.context[k] = v; | ||
function log(msg){ | ||
if (config('debug') && this.console) { | ||
console.log( msg ); | ||
} | ||
} | ||
} | ||
Notice.prototype.payload = function() { | ||
return this._sanitize({ | ||
notifier: { | ||
name: 'honeybadger.js', | ||
url: 'https://github.com/honeybadger-io/honeybadger-js', | ||
version: Honeybadger.version, | ||
language: 'javascript' | ||
}, | ||
error: { | ||
"class": this["class"], | ||
message: this.message, | ||
backtrace: this.stack, | ||
generator: this.generator, | ||
source: this.source, | ||
fingerprint: this.fingerprint | ||
}, | ||
request: { | ||
url: this.url, | ||
component: this.component, | ||
action: this.action, | ||
context: this.context, | ||
cgi_data: this.cgi_data | ||
}, | ||
server: { | ||
project_root: this.project_root, | ||
environment_name: this.environment | ||
function config(key, fallback) { | ||
var value; | ||
if (self[key] !== undefined) { | ||
value = self[key]; | ||
} | ||
}); | ||
}; | ||
if (value === 'false') { value = false; } | ||
if (value !== undefined) { return value; } | ||
return fallback; | ||
} | ||
Notice.prototype._cgiData = function() { | ||
var data, k, v; | ||
data = {}; | ||
if (typeof navigator !== "undefined" && navigator !== null) { | ||
for (k in navigator) { | ||
v = navigator[k]; | ||
if ((k != null) && (v != null) && !(typeof v === 'object')) { | ||
data[k.replace(/(?=[A-Z][a-z]*)/g, '_').toUpperCase()] = v; | ||
} | ||
} | ||
data['HTTP_USER_AGENT'] = data['USER_AGENT']; | ||
delete data['USER_AGENT']; | ||
function baseURL() { | ||
return 'http' + ((config('ssl', true) && 's') || '') + '://' + config('host', 'api.honeybadger.io'); | ||
} | ||
if (document.referrer.match(/\S/)) { | ||
data['HTTP_REFERER'] = document.referrer; | ||
} | ||
return data; | ||
}; | ||
Notice.prototype._sanitize = function(obj, seen) { | ||
var e, k, new_obj, v; | ||
if (seen == null) { | ||
seen = []; | ||
} | ||
if (obj instanceof Function) { | ||
return "[FUNC]"; | ||
} else if (obj instanceof Object) { | ||
if (indexOf.call(seen, obj) >= 0) { | ||
this.log("Dropping circular data structure.", k, v, obj); | ||
return "[CIRCULAR DATA STRUCTURE]"; | ||
function serialize(obj, prefix, depth) { | ||
var k, pk, ret, v; | ||
ret = []; | ||
if (!depth) { depth = 0; } | ||
if (depth >= config('max_depth', 8)) { | ||
return encodeURIComponent(prefix) + '=[MAX DEPTH REACHED]'; | ||
} | ||
seen.push(obj); | ||
if (obj instanceof Array) { | ||
return obj.map((function(_this) { | ||
return function(v) { | ||
return _this._sanitize(v, seen); | ||
}; | ||
})(this)); | ||
} else { | ||
new_obj = {}; | ||
try { | ||
for (k in obj) { | ||
v = obj[k]; | ||
new_obj[k] = this._sanitize(v, seen); | ||
} | ||
} catch (_error) { | ||
e = _error; | ||
return { | ||
error: "Honeybadger was unable to read this object: " + String(e) | ||
}; | ||
for (k in obj) { | ||
v = obj[k]; | ||
if (v instanceof Function) { v = '[FUNC]' } | ||
if (obj.hasOwnProperty(k) && (k != null) && (v != null)) { | ||
pk = (prefix ? prefix + '[' + k + ']' : k); | ||
ret.push(typeof v === 'object' ? serialize(v, pk, depth+1) : encodeURIComponent(pk) + '=' + encodeURIComponent(v)); | ||
} | ||
return new_obj; | ||
} | ||
return ret.join('&'); | ||
} | ||
return obj; | ||
}; | ||
return Notice; | ||
function send(payload) { | ||
currentErr = currentPayload = null; | ||
})(); | ||
// Generated by CoffeeScript 1.9.3 | ||
var Client, Honeybadger, UncaughtError, currentError, currentNotice, ref, | ||
bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, | ||
hasProp = {}.hasOwnProperty, | ||
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; | ||
if (!config('api_key')) { | ||
log('Unable to send error report: no API key has been configured.'); | ||
return false; | ||
} | ||
ref = [null, null], currentError = ref[0], currentNotice = ref[1]; | ||
url = baseURL() + '/v1/notices/js.gif?' + serialize({notice: payload}) + | ||
'&api_key=' + config('api_key') + '&t=' + new Date().getTime(); | ||
Client = (function() { | ||
Client.prototype.version = '0.3.1'; | ||
request(url); | ||
function Client(options) { | ||
this._windowOnErrorHandler = bind(this._windowOnErrorHandler, this); | ||
this._domReady = bind(this._domReady, this); | ||
this.log = bind(this.log, this); | ||
this.log('Initializing honeybadger.js ' + this.version); | ||
if (options) { | ||
this.configure(options); | ||
return true; | ||
} | ||
} | ||
Client.prototype.log = function() { | ||
this.log.history = this.log.history || []; | ||
this.log.history.push(arguments); | ||
if (this.configuration.debug && window.console) { | ||
return console.log(Array.prototype.slice.call(arguments)); | ||
} | ||
}; | ||
function notify(err, generated) { | ||
if (config('disabled', false)) { return false; } | ||
if (!(err instanceof Object)) { return false; } | ||
Client.prototype.configure = function(options) { | ||
var args, i, k, len, ref1, v; | ||
if (options == null) { | ||
options = {}; | ||
} | ||
for (k in options) { | ||
v = options[k]; | ||
this.configuration[k] = v; | ||
} | ||
if (!this._configured && this.configuration.debug && window.console) { | ||
ref1 = this.log.history; | ||
for (i = 0, len = ref1.length; i < len; i++) { | ||
args = ref1[i]; | ||
console.log(Array.prototype.slice.call(args)); | ||
if (err instanceof Error) { | ||
var e = err; | ||
err = {name: e.name, message: e.message, stack: stackTrace(e)}; | ||
} | ||
} | ||
this._configured = true; | ||
return this; | ||
}; | ||
Client.prototype.configuration = new Configuration(); | ||
if (currentErrIs(err)) { | ||
// Skip the duplicate error. | ||
return false; | ||
} else if (currentPayload && loaded) { | ||
// This is a different error, send the old one now. | ||
send(currentPayload); | ||
} | ||
Client.prototype.context = {}; | ||
// Halt if err is empty. | ||
if (((function() { | ||
var k, results; | ||
results = []; | ||
for (k in err) { | ||
if (!{}.hasOwnProperty.call(err, k)) continue; | ||
results.push(k); | ||
} | ||
return results; | ||
})()).length === 0) { | ||
return false; | ||
} | ||
Client.prototype.resetContext = function(options) { | ||
if (options == null) { | ||
options = {}; | ||
} | ||
this.context = options instanceof Object ? options : {}; | ||
return this; | ||
}; | ||
if (generated) { | ||
err = merge(err, generated); | ||
} | ||
Client.prototype.setContext = function(options) { | ||
var k, v; | ||
if (options == null) { | ||
options = {}; | ||
} | ||
if (options instanceof Object) { | ||
for (k in options) { | ||
v = options[k]; | ||
this.context[k] = v; | ||
if (checkHandlers(self.beforeNotifyHandlers, err)) { | ||
return false; | ||
} | ||
} | ||
return this; | ||
}; | ||
Client.prototype.beforeNotifyHandlers = []; | ||
var payload = { | ||
notifier: NOTIFIER, | ||
error: { | ||
'class': err.name || 'Error', | ||
message: err.message, | ||
backtrace: err.stack, | ||
generator: err.generator, | ||
fingerprint: err.fingerprint | ||
}, | ||
request: { | ||
url: err.url || document.URL, | ||
component: err.component || config('component'), | ||
action: err.action || config('action'), | ||
context: merge(self.context, err.context), | ||
cgi_data: cgiData() | ||
}, | ||
server: { | ||
project_root: err.project_root || config('project_root', window.location.protocol + '//' + window.location.host), | ||
environment_name: err.environment || config('environment') | ||
} | ||
}; | ||
Client.prototype.beforeNotify = function(handler) { | ||
return this.beforeNotifyHandlers.push(handler); | ||
}; | ||
currentPayload = payload; | ||
currentErr = err; | ||
Client.prototype.notify = function(error, name, opts) { | ||
var generator, k, notice, ref1, ref2, ref3, ref4, stack, v; | ||
if (opts == null) { | ||
opts = {}; | ||
} | ||
if (!this._validConfig() || this.configuration.disabled === true) { | ||
return false; | ||
} | ||
ref1 = [void 0, void 0], stack = ref1[0], generator = ref1[1]; | ||
if (name instanceof Object) { | ||
opts = name; | ||
name = void 0; | ||
} else if (name != null) { | ||
opts['name'] = name; | ||
} | ||
if (error instanceof Object && (error.error != null)) { | ||
error = error.error; | ||
error['error'] = void 0; | ||
} | ||
if (error instanceof Error) { | ||
stack = this._stackTrace(error); | ||
opts['name'] || (opts['name'] = error.name); | ||
opts['message'] || (opts['message'] = error.message); | ||
} else if (typeof error === 'string') { | ||
opts['message'] = error; | ||
} else if (error instanceof Object) { | ||
for (k in error) { | ||
v = error[k]; | ||
opts[k] = v; | ||
if (loaded) { | ||
log('Defering notice.', err, payload); | ||
window.setTimeout(function(){ | ||
if (currentErrIs(err)) { | ||
send(payload); | ||
} | ||
}); | ||
} else { | ||
log('Queuing notice.', err, payload); | ||
queue.push(payload); | ||
} | ||
return err; | ||
} | ||
if (currentNotice) { | ||
if (error === currentError) { | ||
return; | ||
} else if (this._loaded) { | ||
this._send(currentNotice); | ||
} | ||
var preferCatch = true; | ||
// IE < 10 | ||
if (!window.atob) { preferCatch = false; } | ||
// See https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent | ||
if (window.ErrorEvent) { | ||
try { | ||
if ((new window.ErrorEvent('')).colno === 0) { | ||
preferCatch = false; | ||
} | ||
} catch(_e) {} | ||
} | ||
if (((function() { | ||
var results; | ||
results = []; | ||
for (k in opts) { | ||
if (!hasProp.call(opts, k)) continue; | ||
results.push(k); | ||
// wrap always returns the same function so that callbacks can be removed via | ||
// removeEventListener. | ||
function wrap(fn, force) { | ||
try { | ||
if (typeof fn !== 'function') { | ||
return fn; | ||
} | ||
if (!fn.___hb) { | ||
fn.___hb = function() { | ||
var onerror = config('onerror', true); | ||
// Don't catch if the browser is old or supports the new error | ||
// object and there is a window.onerror handler available instead. | ||
if ((preferCatch && (onerror || force)) || (force && !onerror)) { | ||
try { | ||
return fn.apply(this, arguments); | ||
} catch (e) { | ||
notify(e); | ||
throw(e); | ||
} | ||
} else { | ||
return fn.apply(this, arguments); | ||
} | ||
}; | ||
} | ||
return fn.___hb; | ||
} catch(_e) { | ||
return fn; | ||
} | ||
return results; | ||
})()).length === 0) { | ||
return false; | ||
} | ||
if (!stack) { | ||
ref2 = this._generateStackTrace(), stack = ref2[0], generator = ref2[1]; | ||
} | ||
ref3 = [stack, generator], opts['stack'] = ref3[0], opts['generator'] = ref3[1]; | ||
notice = this._buildNotice(opts); | ||
if (this._checkHandlers(this.beforeNotifyHandlers, notice)) { | ||
return false; | ||
} | ||
ref4 = [error, notice], currentError = ref4[0], currentNotice = ref4[1]; | ||
if (!this._loaded) { | ||
this.log('Queuing notice', notice); | ||
this._queue.push(notice); | ||
} else { | ||
this.log('Defering notice', notice); | ||
window.setTimeout((function(_this) { | ||
return function() { | ||
if (error === currentError) { | ||
return _this._send(notice); | ||
} | ||
}; | ||
})(this)); | ||
} | ||
return notice; | ||
}; | ||
Client.prototype.wrap = function(func) { | ||
var honeybadgerWrapper; | ||
return honeybadgerWrapper = function() { | ||
var e; | ||
try { | ||
return func.apply(this, arguments); | ||
} catch (_error) { | ||
e = _error; | ||
Honeybadger.notify(e); | ||
throw e; | ||
// Public API. | ||
self.notify = function(err, name, extra) { | ||
if (!err) { err = {}; } | ||
if (err instanceof Error) { | ||
var e = err; | ||
err = {name: e.name, message: e.message, stack: stackTrace(e)}; | ||
} | ||
}; | ||
}; | ||
Client.prototype.reset = function() { | ||
this.resetContext(); | ||
this.configuration.reset(); | ||
this._configured = false; | ||
return this; | ||
}; | ||
if (!(err instanceof Object)) { | ||
var m = String(err); | ||
err = {message: m}; | ||
} | ||
Client.prototype.install = function() { | ||
if (this.installed === true) { | ||
return; | ||
} | ||
if (window.onerror !== this._windowOnErrorHandler) { | ||
this.log('Installing window.onerror handler'); | ||
this._oldOnErrorHandler = window.onerror; | ||
window.onerror = this._windowOnErrorHandler; | ||
} | ||
if (this._loaded) { | ||
this.log('honeybadger.js ' + this.version + ' ready'); | ||
} else { | ||
this.log('Installing ready handler'); | ||
if (document.addEventListener) { | ||
document.addEventListener('DOMContentLoaded', this._domReady, true); | ||
window.addEventListener('load', this._domReady, true); | ||
} else { | ||
window.attachEvent('onload', this._domReady); | ||
if (name && !(name instanceof Object)) { | ||
var n = String(name); | ||
name = {name: n}; | ||
} | ||
} | ||
this._installed = true; | ||
return this; | ||
}; | ||
Client.prototype._queue = []; | ||
if (name) { | ||
err = merge(err, name); | ||
} | ||
if (extra instanceof Object) { | ||
err = merge(err, extra); | ||
} | ||
Client.prototype._loaded = document.readyState === 'complete'; | ||
return notify(err, generateStackTrace(err)); | ||
}; | ||
Client.prototype._configured = false; | ||
self.wrap = function(func) { | ||
return wrap(func, true); | ||
}; | ||
Client.prototype._domReady = function() { | ||
var notice, results; | ||
if (this._loaded) { | ||
return; | ||
} | ||
this._loaded = true; | ||
this.log('honeybadger.js ' + this.version + ' ready'); | ||
results = []; | ||
while (notice = this._queue.pop()) { | ||
results.push(this._send(notice)); | ||
} | ||
return results; | ||
}; | ||
self.setContext = function(context) { | ||
if (context instanceof Object) { | ||
self.context = merge(self.context, context); | ||
} | ||
return self; | ||
}; | ||
Client.prototype._generateStackTrace = function() { | ||
var e, stack; | ||
try { | ||
throw new Error(''); | ||
} catch (_error) { | ||
e = _error; | ||
if (stack = this._stackTrace(e)) { | ||
return [stack, 'throw']; | ||
self.resetContext = function(context) { | ||
if (context instanceof Object) { | ||
self.context = merge({}, context); | ||
} else { | ||
self.context = {}; | ||
} | ||
} | ||
return []; | ||
}; | ||
return self; | ||
}; | ||
Client.prototype._stackTrace = function(error) { | ||
return (error != null ? error.stacktrace : void 0) || (error != null ? error.stack : void 0) || null; | ||
}; | ||
Client.prototype._checkHandlers = function(handlers, notice) { | ||
var handler, i, len; | ||
for (i = 0, len = handlers.length; i < len; i++) { | ||
handler = handlers[i]; | ||
if (handler(notice) === false) { | ||
return true; | ||
self.configure = function(opts) { | ||
for (k in opts) { | ||
self[k] = opts[k]; | ||
} | ||
} | ||
return false; | ||
}; | ||
return self; | ||
}; | ||
Client.prototype._buildNotice = function(opts) { | ||
return new Notice({ | ||
log: this.log, | ||
stack: opts['stack'], | ||
generator: opts['generator'], | ||
message: opts['message'], | ||
name: opts['name'], | ||
fingerprint: opts['fingerprint'], | ||
context: opts['context'], | ||
component: opts['component'], | ||
action: opts['action'] | ||
}, this.configuration); | ||
}; | ||
self.beforeNotify = function(handler) { | ||
self.beforeNotifyHandlers.push(handler); | ||
return self; | ||
}; | ||
Client.prototype._send = function(notice) { | ||
var ref1; | ||
this.log('Sending notice', notice); | ||
ref1 = [null, null], currentError = ref1[0], currentNotice = ref1[1]; | ||
return this._sendRequest(notice.payload()); | ||
}; | ||
var indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; | ||
self.reset = function() { | ||
self.context = {}; | ||
self.beforeNotifyHandlers = []; | ||
for (k in self) { | ||
if (indexOf.call(defaultProps, k) == -1) { | ||
self[k] = undefined; | ||
} | ||
} | ||
return self; | ||
}; | ||
Client.prototype._validConfig = function() { | ||
var ref1; | ||
if (!this._configured) { | ||
return false; | ||
self.getVersion = function() { | ||
return VERSION; | ||
} | ||
if ((ref1 = this.configuration.api_key) != null ? ref1.match(/\S/) : void 0) { | ||
return true; | ||
} else { | ||
return false; | ||
// Install instrumentation. | ||
// This should happen once for the first factory call. | ||
function instrument(object, name, replacement) { | ||
if (installed) { return; } | ||
if (!object || !name || !replacement) { return; } | ||
var original = object[name]; | ||
object[name] = replacement(original); | ||
} | ||
}; | ||
Client.prototype._baseURL = function() { | ||
return 'http' + ((this.configuration.ssl && 's') || '') + '://' + this.configuration.host; | ||
}; | ||
var instrumentTimer = function(original) { | ||
// See https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout | ||
return function(func, delay) { | ||
if (func instanceof Function) { | ||
var args = Array.prototype.slice.call(arguments, 2); | ||
func = wrap(func); | ||
return original(function() { | ||
func.apply(null, args); | ||
}, delay); | ||
} else { | ||
return original(func, delay); | ||
} | ||
} | ||
}; | ||
instrument(window, 'setTimeout', instrumentTimer); | ||
instrument(window, 'setInterval', instrumentTimer); | ||
Client.prototype._sendRequest = function(data) { | ||
return this._xhrRequest(data) || this._imageRequest(data); | ||
}; | ||
// Event targets borrowed from bugsnag-js: | ||
// See https://github.com/bugsnag/bugsnag-js/blob/d55af916a4d3c7757f979d887f9533fe1a04cc93/src/bugsnag.js#L542 | ||
'EventTarget Window Node ApplicationCache AudioTrackList ChannelMergerNode CryptoOperation EventSource FileReader HTMLUnknownElement IDBDatabase IDBRequest IDBTransaction KeyOperation MediaController MessagePort ModalWindow Notification SVGElementInstance Screen TextTrack TextTrackCue TextTrackList WebSocket WebSocketWorker Worker XMLHttpRequest XMLHttpRequestEventTarget XMLHttpRequestUpload'.replace(/\w+/g, function (prop) { | ||
if (prototype = window[prop] && window[prop].prototype) { | ||
instrument(prototype, 'addEventListener', function(original) { | ||
// See https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener | ||
return function(type, listener, useCapture, wantsUntrusted) { | ||
try { | ||
if (listener && listener.handleEvent != null) { | ||
listener.handleEvent = wrap(listener.handleEvent); | ||
} | ||
} catch(e) { | ||
// Ignore 'Permission denied to access property "handleEvent"' errors. | ||
log(e); | ||
} | ||
return original.call(this, type, wrap(listener), useCapture, wantsUntrusted); | ||
}; | ||
}); | ||
instrument(prototype, 'removeEventListener', function(original) { | ||
return function(type, listener, useCapture, wantsUntrusted) { | ||
original.call(this, type, listener, useCapture, wantsUntrusted); | ||
return original.call(this, type, wrap(listener), useCapture, wantsUntrusted); | ||
}; | ||
}); | ||
} | ||
}); | ||
Client.prototype._imageRequest = function(data) { | ||
var endpoint, img, ref1, timeout, url; | ||
endpoint = this._baseURL() + '/v1/notices/js.gif'; | ||
url = endpoint + '?' + this._serialize({ | ||
api_key: this.configuration.api_key, | ||
notice: data, | ||
t: new Date().getTime() | ||
}); | ||
ref1 = [new Image(), null], img = ref1[0], timeout = ref1[1]; | ||
img.onabort = img.onerror = (function(_this) { | ||
return function() { | ||
if (timeout) { | ||
window.clearTimeout(timeout); | ||
instrument(window, 'onerror', function(original) { | ||
function onerror(msg, url, line, col, err) { | ||
if (currentErr) { return; } | ||
if (!config('onerror', true)) { return; } | ||
if (line === 0 && /Script error\.?/.test(msg)) { | ||
// See https://developer.mozilla.org/en/docs/Web/API/GlobalEventHandlers/onerror#Notes | ||
log('Ignoring cross-domain script error. Use CORS to enable tracking of these types of errors.'); | ||
return; | ||
} | ||
return _this.log('Request failed.', url, data); | ||
}; | ||
})(this); | ||
img.onload = (function(_this) { | ||
return function() { | ||
if (timeout) { | ||
return window.clearTimeout(timeout); | ||
log('Error caught by window.onerror'); | ||
if (err) { | ||
notify(err); | ||
return; | ||
} | ||
// simulate v8 stack | ||
stack = [msg, '\n at ? (', url || 'unknown', ':', line || 0, ':', col || 0, ')'].join(''); | ||
notify({ | ||
name: 'window.onerror', | ||
message: msg, | ||
stack: stack | ||
}); | ||
} | ||
// See https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror | ||
return function(msg, url, line, col, err) { | ||
onerror(msg, url, line, col, err); | ||
if (original instanceof Function) { | ||
return original.apply(this, arguments); | ||
} | ||
return false; | ||
}; | ||
})(this); | ||
img.src = url; | ||
if (this.configuration.timeout) { | ||
timeout = window.setTimeout(((function(_this) { | ||
return function() { | ||
img.src = ''; | ||
return _this.log('Request timed out.', url, data); | ||
}; | ||
})(this)), this.configuration.timeout); | ||
} | ||
return true; | ||
}; | ||
}); | ||
Client.prototype._xhrRequest = function(data) { | ||
var method, url, xhr; | ||
if (typeof XMLHttpRequest === 'undefined') { | ||
return false; | ||
} | ||
if (typeof JSON === 'undefined') { | ||
return false; | ||
} | ||
method = 'POST'; | ||
url = this._baseURL() + '/v1/notices/js?api_key=' + this.configuration.api_key; | ||
xhr = new XMLHttpRequest(); | ||
if ('withCredentials' in xhr) { | ||
xhr.open(method, url, true); | ||
xhr.setRequestHeader('Content-Type', 'application/json'); | ||
xhr.setRequestHeader('X-Api-Key', this.configuration.api_key); | ||
} else if (typeof XDomainRequest !== 'undefined') { | ||
xhr = new XDomainRequest(); | ||
xhr.open(method, url); | ||
} else { | ||
xhr = null; | ||
} | ||
if (xhr) { | ||
if (this.configuration.timeout) { | ||
xhr.timeout = this.configuration.timeout; | ||
} | ||
xhr.onerror = (function(_this) { | ||
return function() { | ||
return _this.log('Request failed.', data, xhr); | ||
}; | ||
})(this); | ||
xhr.ontimeout = (function(_this) { | ||
return function() { | ||
return _this.log('Request timed out.', data, xhr); | ||
}; | ||
})(this); | ||
xhr.onreadystatechange = (function(_this) { | ||
return function() { | ||
if (xhr.readyState === 4) { | ||
if (xhr.status === 201) { | ||
return _this.log('Request succeeded.', xhr.status, data, xhr); | ||
} else { | ||
return _this.log('Request rejected by server.', xhr.status, data, xhr); | ||
} | ||
} | ||
}; | ||
})(this); | ||
xhr.send(JSON.stringify(data)); | ||
return true; | ||
} | ||
return false; | ||
}; | ||
// End of instrumentation. | ||
installed = true; | ||
Client.prototype._serialize = function(obj, prefix) { | ||
var k, pk, ret, v; | ||
ret = []; | ||
for (k in obj) { | ||
v = obj[k]; | ||
if (obj.hasOwnProperty(k) && (k != null) && (v != null)) { | ||
pk = (prefix ? prefix + '[' + k + ']' : k); | ||
ret.push(typeof v === 'object' ? this._serialize(v, pk) : encodeURIComponent(pk) + '=' + encodeURIComponent(v)); | ||
} | ||
// Save original state for reset() | ||
for (k in self) { | ||
defaultProps.push(k); | ||
} | ||
return ret.join('&'); | ||
}; | ||
Client.prototype._windowOnErrorHandler = function(msg, url, line, col, error) { | ||
if (!currentNotice && this.configuration.onerror) { | ||
this.log('Error caught by window.onerror', msg, url, line, col, error); | ||
if (!error) { | ||
error = new UncaughtError(msg, url, line, col); | ||
// Initialization. | ||
log('Initializing honeybadger.js ' + VERSION); | ||
if (document.readyState === 'complete') { | ||
loaded = true; | ||
log('honeybadger.js ' + VERSION + ' ready'); | ||
} else { | ||
log('Installing ready handler'); | ||
var domReady = function() { | ||
loaded = true; | ||
log('honeybadger.js ' + VERSION + ' ready'); | ||
while (notice = queue.pop()) { | ||
send(notice); | ||
} | ||
}; | ||
if (document.addEventListener) { | ||
document.addEventListener('DOMContentLoaded', domReady, true); | ||
} else { | ||
window.attachEvent('onload', domReady); | ||
} | ||
this.notify(error); | ||
} | ||
if (this._oldOnErrorHandler) { | ||
return this._oldOnErrorHandler.apply(this, arguments); | ||
} | ||
return false; | ||
}; | ||
return Client; | ||
return self; | ||
}); | ||
})(); | ||
UncaughtError = (function(superClass) { | ||
extend(UncaughtError, superClass); | ||
function UncaughtError(message, url, line, column) { | ||
this.name = 'window.onerror'; | ||
this.message = message || 'An unknown error was caught by window.onerror.'; | ||
this.stack = [this.message, '\n at ? (', url || 'unknown', ':', line || 0, ':', column || 0, ')'].join(''); | ||
} | ||
return UncaughtError; | ||
})(Error); | ||
Honeybadger = new Client; | ||
Honeybadger.Client = Client; | ||
window.Honeybadger = Honeybadger; | ||
Honeybadger.install(); | ||
})(window); | ||
return factory; | ||
})); |
{ | ||
"name": "honeybadger-js", | ||
"description": "A JavaScript library for integrating apps with the Honeybadger Rails Error Notifier.", | ||
"version": "0.3.1", | ||
"version": "0.4.0", | ||
"homepage": "https://github.com/honeybadger-io/honeybadger-js", | ||
@@ -10,2 +10,3 @@ "author": { | ||
}, | ||
"main": "honeybadger.js", | ||
"repository": { | ||
@@ -22,4 +23,9 @@ "type": "git", | ||
"devDependencies": { | ||
"coffee-script": "~1.3.3", | ||
"connect": "~2.7.11", | ||
"grunt": "~0.4.5", | ||
"grunt-contrib-connect": "~0.1.2", | ||
"grunt-contrib-jasmine": "~0.5.2", | ||
"grunt-contrib-watch": "~0.2.0", | ||
"grunt-saucelabs": "~8.3.2", | ||
"grunt-shell": "~1.1.2", | ||
"phantomjs": "~1.9.0" | ||
@@ -26,0 +32,0 @@ }, |
356
README.md
@@ -1,2 +0,2 @@ | ||
# Honeybadger Client Side Javascript Library | ||
# Honeybadger Client-Side Javascript Library | ||
@@ -6,19 +6,19 @@ [![Build | ||
A JavaScript library for integrating apps with the :zap: [Honeybadger Rails Error Notifier](http://honeybadger.io). | ||
A client-side JavaScript library for integrating apps with the :zap: [Honeybadger Error Notifier](http://honeybadger.io). For server-side javascript, check out our [NodeJS library](https://github.com/honeybadger-io/honeybadger-node). | ||
## Upgrading | ||
## Getting Started | ||
It is recommended that you use our CDN, as outlined under | ||
[installation](#installation). The API is 100% backwards compatible, so no other | ||
code changes are required. | ||
### 1. Include the JS library | ||
*Note: 0.1 and 0.2 make significant improvements to error grouping. As a result, | ||
new errors may be grouped differently than old.* | ||
#### Globally | ||
## Installation | ||
Place the following code between the `<head></head>` tags of your page: | ||
```html | ||
<script src="//js.honeybadger.io/v0.3/honeybadger.min.js" type="text/javascript"></script> | ||
<script src="//js.honeybadger.io/v0.4/honeybadger.min.js" type="text/javascript" data-api_key="project api key" data-environment="production"></script> | ||
``` | ||
Honeybadger may also be configured via JavaScript: | ||
```javascript | ||
<script type="text/javascript"> | ||
@@ -32,33 +32,188 @@ Honeybadger.configure({ | ||
## Basic Usage | ||
#### Installing via Node.js | ||
To catch an error and notify Honeybadger: | ||
``` | ||
npm install honeybadger-js --save-dev | ||
``` | ||
#### Installing via Bower | ||
```sh | ||
bower install honeybadger --save-dev | ||
``` | ||
#### Browserify/Webpack (CommonJS) | ||
```sh | ||
var Honeybadger = require("path/to/honeybadger"); | ||
Honeybadger.configure({ | ||
api_key: 'project api key', | ||
environment: 'production' | ||
}); | ||
``` | ||
- See an [example browserify + honeybadger.js project](examples/browserify). | ||
- See an [example webpack + honeybadger.js project](examples/webpack). | ||
#### RequireJS (AMD) | ||
```sh | ||
requirejs(["path/to/honeybadger"], function(Honeybadger) { | ||
Honeybadger.configure({ | ||
api_key: 'project api key', | ||
environment: 'production' | ||
}); | ||
}); | ||
``` | ||
- See an [example requirejs + honeybadger.js project](examples/requirejs). | ||
### 2. Start reporting exceptions | ||
By default Honeybadger will report all uncaught exceptions automatically using our `window.onerror` handler. | ||
You can also manually notify Honeybadger of errors and other events in your application code: | ||
```javascript | ||
try { | ||
// ...error producing code... | ||
} catch(e) { | ||
Honeybadger.notify(e); | ||
} catch(error) { | ||
Honeybadger.notify(error); | ||
} | ||
``` | ||
JavaScript often uses generic class names -- such as `Error` -- which are uninformative and also cause unrelated errors to be grouped together. To get around this issue it's a good practice to send a custom error class when notifying Honeybadger: | ||
See the [full documentation](#honeybadgernotify-send-an-exception-to-honeybadger) for the `notify` method for more examples. | ||
## Advanced Configuration | ||
You can set configuration options by using the `Honeybadger.configure` function. All of the available options are shown below: | ||
```javascript | ||
Honeybadger.configure({ | ||
// Honeybadger API key (required) | ||
api_key: '', | ||
// Collector Host | ||
host: 'api.honeybadger.io', | ||
// Use SSL? | ||
ssl: true, | ||
// Project root | ||
project_root: 'http://my-app.com', | ||
// Environment | ||
environment: 'production', | ||
// Component (optional) | ||
component: '', | ||
// Action (optional) | ||
action: '', | ||
// Should unhandled (window.onerror) notifications be sent? | ||
onerror: true, | ||
// Disable notifications? | ||
disabled: false | ||
}); | ||
``` | ||
You can call `Honeybadger.configure` as many times as you like. The existing configuration data will be merged with any new data you provide. This is especially useful for changing the `action` and `component` values inside of single-page apps. | ||
### Configuring via data attributes | ||
The global Honeybadger instance may also be configured via data attributes on the script tag which loads honeybadger.js: | ||
```html | ||
<script src="honeybadger.js" type="text/javascript" data-component="pages" data-action="index" ...></script> | ||
``` | ||
## Public Interface | ||
### `Honeybadger.notify()`: Send an exception to Honeybadger | ||
If you've caught an exception and want to send it to Honeybadger, this is the method for you. | ||
#### Examples: | ||
```javascript | ||
try { | ||
// ...error producing code... | ||
} catch(e) { | ||
Honeybadger.notify(e, 'DescriptiveClass'); | ||
} catch(error) { | ||
Honeybadger.notify(error); | ||
} | ||
``` | ||
## Sending Custom Data | ||
JavaScript often uses generic class names -- such as `Error` -- which are uninformative and also cause unrelated errors to be grouped together. To get around this issue it's a good practice to send a custom error class when notifying Honeybadger: | ||
Honeybadger allows you to send custom data using | ||
`Honeybadger.setContext` And `Honeybadger.resetContext`: | ||
```javascript | ||
Honeybadger.notify(error, 'DescriptiveClass'); | ||
``` | ||
You can also set or override other optional data which is reported with the error: | ||
```javascript | ||
Honeybadger.notify(error, { | ||
message: 'My custom message', | ||
name: 'DescriptiveClass', | ||
component: 'badgers', | ||
action: 'show', | ||
context: { badgerId: 1 }, | ||
fingerprint: 'This unique string will group similar errors together', | ||
environment: 'production', | ||
project_root: 'https://www.example.com/' | ||
}); | ||
``` | ||
Finally, you can notify Honeybadger of anything, even if you don't have an error object: | ||
```javascript | ||
Honeybadger.notify('Badgers!'); | ||
Honeybadger.notify('Badgers!', { ... }); | ||
Honeybadger.notify('Badgers!', 'CustomClass'); | ||
Honeybadger.notify('Badgers!', 'CustomClass', { ... }); | ||
Honeybadger.notify({ | ||
message: 'Badgers!', | ||
name: 'CustomClass', | ||
... | ||
}); | ||
``` | ||
A stacktrace will be generated for you (when possible) if you do not provide an error object. | ||
--- | ||
### `Honeybadger.wrap()`: Wrap the given function in try/catch and report any exceptions | ||
It can be a pain to include try/catch blocks everywhere in your app. A slightly nicer option is to use `Honeybadger.wrap`. You pass it a function. It returns a new function which wraps your existing function in a try/catch block. | ||
#### Examples: | ||
```javascript | ||
Honeybadger.wrap(function(){ | ||
throw "oops"; | ||
})(); | ||
``` | ||
Note that `wrap` returns a function. This makes it easy to use with event handlers, as in the example below: | ||
```javascript | ||
$(document).on("click", "#myElement", Honeybadger.wrap(function(){ throw "oops"; })); | ||
``` | ||
--- | ||
### `Honeybadger.setContext()`: Set metadata to be sent if an exception occurs | ||
Javascript exceptions are pretty bare-bones. You probably have some additional data that could make them a lot easier to understand - perhaps the name of the current Angular view, or the id of the current user. This function lets you set context data that will be sent if an error should occur. | ||
You can call `setContext` as many times as you like. New context data will be merged with the existing data. | ||
#### Examples: | ||
```javascript | ||
// On load | ||
Honeybadger.setContext({ | ||
user_id: '<%= current_user.id %>' | ||
user_id: 123 | ||
}); | ||
@@ -71,31 +226,26 @@ | ||
// Honeybadger.context => { user_id: 1, backbone_view: 'tracks' } | ||
// The context now contains { user_id: 123, backbone_view: 'tracks' } | ||
``` | ||
--- | ||
Honeybadger.resetContext({ | ||
some_other_data: 'foo' | ||
}); | ||
### `Honeybadger.resetContext()`: Clear context metadata | ||
// Honeybadger.context == { some_other_data: 'foo' } | ||
``` | ||
If you've used `Honeybadger.setContext` to store context data, you can clear it with `Honeybadger.resetContext`. | ||
You can also add context to a specific exception by passing an | ||
associative array to the `notify` method. Global context will be | ||
merged locally: | ||
#### Example: | ||
```javascript | ||
Honeybadger.setContext({ | ||
user_id: '<%= current_user.id %>' | ||
// Set the context to {} | ||
Honeybadger.resetContext(); | ||
// Clear the context, then set it to `{ user_id: 123 }` | ||
Honeybadger.resetContext({ | ||
user_id: 123 | ||
}); | ||
``` | ||
try { | ||
// ...error producing code... | ||
} catch(e) { | ||
Honeybadger.notify(e, { context: { some_other_data: 'foo' } }); | ||
} | ||
--- | ||
// Honeybadger.context == { user_id: 1 } | ||
``` | ||
### `Honeybadger.beforeNotify()`: Add a callback to be run before an exception is reported | ||
## Notification handlers | ||
Passing a function to `Honeybadger.beforeNotify` will add the function | ||
@@ -106,2 +256,5 @@ to a list of before notify handlers. If the function includes a | ||
#### Examples | ||
```javascript | ||
@@ -111,7 +264,5 @@ Honeybadger.beforeNotify(function(notice) { | ||
}); | ||
``` | ||
To halt notification, return false from any `beforeNotify` handler: | ||
```javascript | ||
// To halt notification, return false from any `beforeNotify` handler: | ||
Honeybadger.beforeNotify(function(notice) { | ||
@@ -122,8 +273,6 @@ if (notice.class == 'MyCustomError') return false; | ||
### Notice Attributes | ||
The following notice attributes may be modified by your notification handlers: | ||
* stack - The stack trace | ||
* class - The exception class name | ||
* name - The exception class name | ||
* message - The error message | ||
@@ -138,2 +287,31 @@ * url - The current url | ||
--- | ||
### `Honeybadger.configure()`: Set configuration values | ||
The `configure` method takes an object containing config values. Its return value is unspecified. | ||
#### Examples: | ||
```javascript | ||
Honeybadger.configure({api_key: "adlkjfljk"}); | ||
``` | ||
--- | ||
### `Honeybadger.factory()`: create a new client instance. | ||
The `factory` method returns a new instance of Honeybadger which can be configured differently than the global/singleton instance. | ||
#### Examples: | ||
```javascript | ||
var other_hb = Honeybadger.factory({api_key: "zxcvbnm"}); | ||
other_hb.notify("This will go to an alternate project."); | ||
``` | ||
## Sourcemaps | ||
@@ -143,13 +321,23 @@ | ||
In order to make this work you must include a special comment at the bottom of your minified file which points to the corresponding sourcemap file. To see what this comment should look like, [see the comment at the bottom of honeybadger.js](https://js.honeybadger.io/v0.3/honeybadger.min.js). If you upload the original source files along with the sourcemap, Honeybadger will link to those files when displaying the stack trace. | ||
To do this, you'll add a special comment at the bottom of your minified JS. It tells us where to find your sourcemap. For example: | ||
All files must be publically accessible online so that Honeybadger's servers can download and parse them. For the full specification, see the [Source Map Revision 3 Proposal](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit). | ||
```js | ||
// ...minified code... | ||
//# sourceMappingURL=application.min.js.map | ||
``` | ||
The sourcemap URL needs to be a valid URL accessible to the public. | ||
For more information on sourcemaps, check out the [Source Map Revision 3 Proposal](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit). | ||
### Linking stack traces to source files | ||
Honeybadger supports an optional `sourceRoot` comment, which should point to the root path of the original source files online. For example: | ||
If you'd like to be able to jump from the Honeybadger backtrace to your unminified source file, just tell us where to find your unminified files using the `sourceRoot` option. | ||
`sourceRoot` is the root URL for your unminified source files. To set it, you can use another magic comment: | ||
```js | ||
// ...minified code... | ||
//# sourceMappingURL=application.min.js.map | ||
//# sourceRoot=https://sources.my-domain.com/src | ||
@@ -167,7 +355,7 @@ ``` | ||
To customize this setting *just* for Honeybadger, use `honeybadgerSourceRoot` instead. | ||
If providing the `sourceRoot` option fouls up other tools in your toolchain, you can alternatively use `honeybadgerSourceRoot`. | ||
#### Linking stack traces to GitHub | ||
#### Using GitHub | ||
If you're using the GitHub integration, you can also link to source files on GitHub by substituting a special `[PROJECT_ROOT]` token for the root of your GitHub repository: | ||
If you're using Honeybadger's GitHub integration, you can link to source files on GitHub by substituting a special `[PROJECT_ROOT]` token for the root of your GitHub repository: | ||
@@ -182,10 +370,7 @@ ```js | ||
## Unhandled errors via (window.onerror) | ||
## window.onerror | ||
By default, honeybadger.js does not track unhandled errors. This is | ||
because `window.onerror` is a very limited method of error handling, and | ||
does not usually yield useful information. It is our official | ||
recommendation to always use try/catch explicitly to notify Honeybadger. | ||
If you still want to automatically catch errors via `window.onerror`, | ||
you can set the `onerror` configuration option to true: | ||
Honeybadger.js automatically reports uncaught exceptions from window.onerror. To | ||
disable notifications for uncaught exceptions, set the `onerror` option to | ||
`false`. | ||
@@ -195,48 +380,7 @@ ```javascript | ||
api_key: 'project api key', | ||
onerror: true | ||
onerror: false | ||
}); | ||
``` | ||
## Configuration | ||
`Honeybadger.configure` may be called multiple times to set/update | ||
configuration options. Existing configuration will be merged. In most | ||
cases configuration will be set once, however the `action` and | ||
`component` options may change semi-frequently for client-side | ||
frameworks like Angular or Ember. | ||
```javascript | ||
Honeybadger.configure({ | ||
// Honeybadger API key (required) | ||
api_key: '', | ||
// Collector Host | ||
host: 'api.honeybadger.io', | ||
// Use SSL? | ||
ssl: true, | ||
// Project root | ||
project_root: 'http://my-app.com', | ||
// Environment | ||
environment: 'production', | ||
// Component (optional) | ||
component: '', | ||
// Action (optional) | ||
action: '', | ||
// Should unhandled (window.onerror) notifications be sent? | ||
onerror: false, | ||
// Disable notifications? | ||
disabled: false, | ||
// Timeout (in milliseconds) when making requests. | ||
timeout: false | ||
}); | ||
``` | ||
## Contributing | ||
@@ -252,7 +396,7 @@ | ||
To run the test suite, enter `make test` into the console. | ||
To run the test suite, enter `make test` into the console. | ||
### License | ||
The Honeybadger gem is MIT licensed. See the [MIT-LICENSE](https://raw.github.com/honeybadger-io/honeybadger-js/master/MIT-LICENSE) file in this repository for details. | ||
The Honeybadger gem is MIT licensed. See the [MIT-LICENSE](https://raw.github.com/honeybadger-io/honeybadger-js/master/MIT-LICENSE) file in this repository for details. | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
373588
32
9526
391
8
2