|
|
@@ -1,4 +1,4 @@
|
|
|
-/*! Raven.js 1.1.18 (8ad15bc) | github.com/getsentry/raven-js */
|
|
|
+/*! Raven.js 1.3.0 (768fdca) | github.com/getsentry/raven-js */
|
|
|
|
|
|
/*
|
|
|
* Includes TraceKit
|
|
|
@@ -21,7 +21,8 @@ var TraceKit = {
|
|
|
remoteFetching: false,
|
|
|
collectWindowErrors: true,
|
|
|
// 3 lines before, the offending line, 3 lines after
|
|
|
- linesOfContext: 7
|
|
|
+ linesOfContext: 7,
|
|
|
+ debug: false
|
|
|
};
|
|
|
|
|
|
// global reference to slice
|
|
|
@@ -29,23 +30,11 @@ var _slice = [].slice;
|
|
|
var UNKNOWN_FUNCTION = '?';
|
|
|
|
|
|
|
|
|
-/**
|
|
|
- * TraceKit.wrap: Wrap any function in a TraceKit reporter
|
|
|
- * Example: func = TraceKit.wrap(func);
|
|
|
- *
|
|
|
- * @param {Function} func Function to be wrapped
|
|
|
- * @return {Function} The wrapped func
|
|
|
- */
|
|
|
-TraceKit.wrap = function traceKitWrapper(func) {
|
|
|
- function wrapped() {
|
|
|
- try {
|
|
|
- return func.apply(this, arguments);
|
|
|
- } catch (e) {
|
|
|
- TraceKit.report(e);
|
|
|
- throw e;
|
|
|
- }
|
|
|
- }
|
|
|
- return wrapped;
|
|
|
+function getLocationHref() {
|
|
|
+ if (typeof document === 'undefined')
|
|
|
+ return '';
|
|
|
+
|
|
|
+ return document.location.href;
|
|
|
};
|
|
|
|
|
|
/**
|
|
|
@@ -181,7 +170,7 @@ TraceKit.report = (function reportModuleWrapper() {
|
|
|
location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line);
|
|
|
stack = {
|
|
|
'message': message,
|
|
|
- 'url': document.location.href,
|
|
|
+ 'url': getLocationHref(),
|
|
|
'stack': [location]
|
|
|
};
|
|
|
notifyHandlers(stack, true);
|
|
|
@@ -319,8 +308,7 @@ TraceKit.report = (function reportModuleWrapper() {
|
|
|
*
|
|
|
*/
|
|
|
TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
- var debug = false,
|
|
|
- sourceCache = {};
|
|
|
+ var sourceCache = {};
|
|
|
|
|
|
/**
|
|
|
* Attempts to retrieve source code via XMLHttpRequest, which is used
|
|
|
@@ -362,7 +350,9 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
// URL needs to be able to fetched within the acceptable domain. Otherwise,
|
|
|
// cross-domain errors will be triggered.
|
|
|
var source = '';
|
|
|
- if (url.indexOf(document.domain) !== -1) {
|
|
|
+ var domain = '';
|
|
|
+ try { domain = document.domain; } catch (e) {}
|
|
|
+ if (url.indexOf(domain) !== -1) {
|
|
|
source = loadSource(url);
|
|
|
}
|
|
|
sourceCache[url] = source ? source.split('\n') : [];
|
|
|
@@ -524,6 +514,9 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
* the url, line, and column number of the defined function.
|
|
|
*/
|
|
|
function findSourceByFunctionBody(func) {
|
|
|
+ if (typeof document === 'undefined')
|
|
|
+ return;
|
|
|
+
|
|
|
var urls = [window.location.href],
|
|
|
scripts = document.getElementsByTagName('script'),
|
|
|
body,
|
|
|
@@ -627,12 +620,11 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
* @return {?Object.<string, *>} Stack trace information.
|
|
|
*/
|
|
|
function computeStackTraceFromStackProp(ex) {
|
|
|
- if (!ex.stack) {
|
|
|
- return null;
|
|
|
- }
|
|
|
+ if (isUndefined(ex.stack) || !ex.stack) return;
|
|
|
|
|
|
- var chrome = /^\s*at (.*?) ?\(?((?:file|https?|chrome-extension):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
|
|
|
+ var chrome = /^\s*at (.*?) ?\(?((?:(?:file|https?|chrome-extension):.*?)|<anonymous>):(\d+)(?::(\d+))?\)?\s*$/i,
|
|
|
gecko = /^\s*(.*?)(?:\((.*?)\))?@((?:file|https?|chrome).*?):(\d+)(?::(\d+))?\s*$/i,
|
|
|
+ winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|http|https):.*?):(\d+)(?::(\d+))?\)?\s*$/i,
|
|
|
lines = ex.stack.split('\n'),
|
|
|
stack = [],
|
|
|
parts,
|
|
|
@@ -655,6 +647,13 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
'line': +parts[3],
|
|
|
'column': parts[4] ? +parts[4] : null
|
|
|
};
|
|
|
+ } else if ((parts = winjs.exec(lines[i]))) {
|
|
|
+ element = {
|
|
|
+ 'url': parts[2],
|
|
|
+ 'func': parts[1] || UNKNOWN_FUNCTION,
|
|
|
+ 'line': +parts[3],
|
|
|
+ 'column': parts[4] ? +parts[4] : null
|
|
|
+ };
|
|
|
} else {
|
|
|
continue;
|
|
|
}
|
|
|
@@ -686,7 +685,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
return {
|
|
|
'name': ex.name,
|
|
|
'message': ex.message,
|
|
|
- 'url': document.location.href,
|
|
|
+ 'url': getLocationHref(),
|
|
|
'stack': stack
|
|
|
};
|
|
|
}
|
|
|
@@ -702,6 +701,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
// else to it because Opera is not very good at providing it
|
|
|
// reliably in other circumstances.
|
|
|
var stacktrace = ex.stacktrace;
|
|
|
+ if (isUndefined(ex.stacktrace) || !ex.stacktrace) return;
|
|
|
|
|
|
var testRE = / line (\d+), column (\d+) in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\) in (.*):\s*$/i,
|
|
|
lines = stacktrace.split('\n'),
|
|
|
@@ -742,7 +742,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
return {
|
|
|
'name': ex.name,
|
|
|
'message': ex.message,
|
|
|
- 'url': document.location.href,
|
|
|
+ 'url': getLocationHref(),
|
|
|
'stack': stack
|
|
|
};
|
|
|
}
|
|
|
@@ -852,7 +852,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
return {
|
|
|
'name': ex.name,
|
|
|
'message': lines[0],
|
|
|
- 'url': document.location.href,
|
|
|
+ 'url': getLocationHref(),
|
|
|
'stack': stack
|
|
|
};
|
|
|
}
|
|
|
@@ -951,6 +951,12 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
item.func = parts[1];
|
|
|
}
|
|
|
|
|
|
+ if (typeof item.func === 'undefined') {
|
|
|
+ try {
|
|
|
+ item.func = parts.input.substring(0, parts.input.indexOf('{'));
|
|
|
+ } catch (e) { }
|
|
|
+ }
|
|
|
+
|
|
|
if ((source = findSourceByFunctionBody(curr))) {
|
|
|
item.url = source.url;
|
|
|
item.line = source.line;
|
|
|
@@ -983,7 +989,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
var result = {
|
|
|
'name': ex.name,
|
|
|
'message': ex.message,
|
|
|
- 'url': document.location.href,
|
|
|
+ 'url': getLocationHref(),
|
|
|
'stack': stack
|
|
|
};
|
|
|
augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description);
|
|
|
@@ -1008,7 +1014,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
return stack;
|
|
|
}
|
|
|
} catch (e) {
|
|
|
- if (debug) {
|
|
|
+ if (TraceKit.debug) {
|
|
|
throw e;
|
|
|
}
|
|
|
}
|
|
|
@@ -1019,7 +1025,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
return stack;
|
|
|
}
|
|
|
} catch (e) {
|
|
|
- if (debug) {
|
|
|
+ if (TraceKit.debug) {
|
|
|
throw e;
|
|
|
}
|
|
|
}
|
|
|
@@ -1030,7 +1036,7 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
return stack;
|
|
|
}
|
|
|
} catch (e) {
|
|
|
- if (debug) {
|
|
|
+ if (TraceKit.debug) {
|
|
|
throw e;
|
|
|
}
|
|
|
}
|
|
|
@@ -1041,12 +1047,16 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
return stack;
|
|
|
}
|
|
|
} catch (e) {
|
|
|
- if (debug) {
|
|
|
+ if (TraceKit.debug) {
|
|
|
throw e;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return {};
|
|
|
+ return {
|
|
|
+ 'name': ex.name,
|
|
|
+ 'message': ex.message,
|
|
|
+ 'url': getLocationHref()
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement;
|
|
|
@@ -1064,38 +1074,45 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() {
|
|
|
// since JSON is required to encode the payload
|
|
|
var _Raven = window.Raven,
|
|
|
hasJSON = !!(typeof JSON === 'object' && JSON.stringify),
|
|
|
+ // Raven can run in contexts where there's no document (react-native)
|
|
|
+ hasDocument = typeof document !== 'undefined',
|
|
|
lastCapturedException,
|
|
|
lastEventId,
|
|
|
globalServer,
|
|
|
- globalUser,
|
|
|
globalKey,
|
|
|
globalProject,
|
|
|
+ globalContext = {},
|
|
|
globalOptions = {
|
|
|
logger: 'javascript',
|
|
|
ignoreErrors: [],
|
|
|
ignoreUrls: [],
|
|
|
whitelistUrls: [],
|
|
|
includePaths: [],
|
|
|
+ crossOrigin: 'anonymous',
|
|
|
collectWindowErrors: true,
|
|
|
- tags: {},
|
|
|
- maxMessageLength: 100,
|
|
|
- extra: {}
|
|
|
+ maxMessageLength: 100
|
|
|
},
|
|
|
- authQueryString,
|
|
|
isRavenInstalled = false,
|
|
|
-
|
|
|
objectPrototype = Object.prototype,
|
|
|
+ // capture references to window.console *and* all its methods first
|
|
|
+ // before the console plugin has a chance to monkey patch
|
|
|
+ originalConsole = window.console || {},
|
|
|
+ originalConsoleMethods = {},
|
|
|
+ plugins = [],
|
|
|
startTime = now();
|
|
|
|
|
|
+for (var method in originalConsole) {
|
|
|
+ originalConsoleMethods[method] = originalConsole[method];
|
|
|
+}
|
|
|
/*
|
|
|
* The core Raven singleton
|
|
|
*
|
|
|
* @this {Raven}
|
|
|
*/
|
|
|
var Raven = {
|
|
|
- VERSION: '1.1.18',
|
|
|
+ VERSION: '1.3.0',
|
|
|
|
|
|
- debug: true,
|
|
|
+ debug: false,
|
|
|
|
|
|
/*
|
|
|
* Allow multiple versions of Raven to be installed.
|
|
|
@@ -1129,7 +1146,12 @@ var Raven = {
|
|
|
// merge in options
|
|
|
if (options) {
|
|
|
each(options, function(key, value){
|
|
|
- globalOptions[key] = value;
|
|
|
+ // tags and extra are special and need to be put into context
|
|
|
+ if (key == 'tags' || key == 'extra') {
|
|
|
+ globalContext[key] = value;
|
|
|
+ } else {
|
|
|
+ globalOptions[key] = value;
|
|
|
+ }
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -1166,8 +1188,6 @@ var Raven = {
|
|
|
|
|
|
TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors;
|
|
|
|
|
|
- setAuthQueryString();
|
|
|
-
|
|
|
// return for chaining
|
|
|
return Raven;
|
|
|
},
|
|
|
@@ -1183,6 +1203,12 @@ var Raven = {
|
|
|
install: function() {
|
|
|
if (isSetup() && !isRavenInstalled) {
|
|
|
TraceKit.report.subscribe(handleStackInfo);
|
|
|
+
|
|
|
+ // Install all of the plugins
|
|
|
+ each(plugins, function(_, plugin) {
|
|
|
+ plugin();
|
|
|
+ });
|
|
|
+
|
|
|
isRavenInstalled = true;
|
|
|
}
|
|
|
|
|
|
@@ -1261,6 +1287,7 @@ var Raven = {
|
|
|
wrapped[property] = func[property];
|
|
|
}
|
|
|
}
|
|
|
+ wrapped.prototype = func.prototype;
|
|
|
|
|
|
// Signal that this function has been wrapped already
|
|
|
// for both debugging and to prevent it to being wrapped twice
|
|
|
@@ -1302,7 +1329,8 @@ var Raven = {
|
|
|
// raises an exception different from the one we asked to
|
|
|
// report on.
|
|
|
try {
|
|
|
- TraceKit.report(ex, options);
|
|
|
+ var stack = TraceKit.computeStackTrace(ex);
|
|
|
+ handleStackInfo(stack, options);
|
|
|
} catch(ex1) {
|
|
|
if(ex !== ex1) {
|
|
|
throw ex1;
|
|
|
@@ -1337,6 +1365,12 @@ var Raven = {
|
|
|
return Raven;
|
|
|
},
|
|
|
|
|
|
+ addPlugin: function(plugin) {
|
|
|
+ plugins.push(plugin);
|
|
|
+ if (isRavenInstalled) plugin();
|
|
|
+ return Raven;
|
|
|
+ },
|
|
|
+
|
|
|
/*
|
|
|
* Set/clear a user to be sent along with the payload.
|
|
|
*
|
|
|
@@ -1344,47 +1378,110 @@ var Raven = {
|
|
|
* @return {Raven}
|
|
|
*/
|
|
|
setUserContext: function(user) {
|
|
|
- globalUser = user;
|
|
|
+ // Intentionally do not merge here since that's an unexpected behavior.
|
|
|
+ globalContext.user = user;
|
|
|
|
|
|
return Raven;
|
|
|
},
|
|
|
|
|
|
/*
|
|
|
- * Set extra attributes to be sent along with the payload.
|
|
|
+ * Merge extra attributes to be sent along with the payload.
|
|
|
*
|
|
|
* @param {object} extra An object representing extra data [optional]
|
|
|
* @return {Raven}
|
|
|
*/
|
|
|
setExtraContext: function(extra) {
|
|
|
- globalOptions.extra = extra || {};
|
|
|
+ mergeContext('extra', extra);
|
|
|
|
|
|
return Raven;
|
|
|
},
|
|
|
|
|
|
/*
|
|
|
- * Set tags to be sent along with the payload.
|
|
|
+ * Merge tags to be sent along with the payload.
|
|
|
*
|
|
|
* @param {object} tags An object representing tags [optional]
|
|
|
* @return {Raven}
|
|
|
*/
|
|
|
setTagsContext: function(tags) {
|
|
|
- globalOptions.tags = tags || {};
|
|
|
+ mergeContext('tags', tags);
|
|
|
|
|
|
return Raven;
|
|
|
},
|
|
|
|
|
|
+ /*
|
|
|
+ * Clear all of the context.
|
|
|
+ *
|
|
|
+ * @return {Raven}
|
|
|
+ */
|
|
|
+ clearContext: function() {
|
|
|
+ globalContext = {};
|
|
|
+
|
|
|
+ return Raven;
|
|
|
+ },
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Get a copy of the current context. This cannot be mutated.
|
|
|
+ *
|
|
|
+ * @return {object} copy of context
|
|
|
+ */
|
|
|
+ getContext: function() {
|
|
|
+ // lol javascript
|
|
|
+ return JSON.parse(JSON.stringify(globalContext));
|
|
|
+ },
|
|
|
+
|
|
|
/*
|
|
|
* Set release version of application
|
|
|
*
|
|
|
* @param {string} release Typically something like a git SHA to identify version
|
|
|
* @return {Raven}
|
|
|
*/
|
|
|
- setReleaseContext: function(release) {
|
|
|
+ setRelease: function(release) {
|
|
|
globalOptions.release = release;
|
|
|
|
|
|
return Raven;
|
|
|
},
|
|
|
|
|
|
+ /*
|
|
|
+ * Set the dataCallback option
|
|
|
+ *
|
|
|
+ * @param {function} callback The callback to run which allows the
|
|
|
+ * data blob to be mutated before sending
|
|
|
+ * @return {Raven}
|
|
|
+ */
|
|
|
+ setDataCallback: function(callback) {
|
|
|
+ globalOptions.dataCallback = callback;
|
|
|
+
|
|
|
+ return Raven;
|
|
|
+ },
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Set the shouldSendCallback option
|
|
|
+ *
|
|
|
+ * @param {function} callback The callback to run which allows
|
|
|
+ * introspecting the blob before sending
|
|
|
+ * @return {Raven}
|
|
|
+ */
|
|
|
+ setShouldSendCallback: function(callback) {
|
|
|
+ globalOptions.shouldSendCallback = callback;
|
|
|
+
|
|
|
+ return Raven;
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Override the default HTTP transport mechanism that transmits data
|
|
|
+ * to the Sentry server.
|
|
|
+ *
|
|
|
+ * @param {function} transport Function invoked instead of the default
|
|
|
+ * `makeRequest` handler.
|
|
|
+ *
|
|
|
+ * @return {Raven}
|
|
|
+ */
|
|
|
+ setTransport: function(transport) {
|
|
|
+ globalOptions.transport = transport;
|
|
|
+
|
|
|
+ return Raven;
|
|
|
+ },
|
|
|
+
|
|
|
/*
|
|
|
* Get the latest raw exception that was captured by Raven.
|
|
|
*
|
|
|
@@ -1413,41 +1510,47 @@ var Raven = {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
-Raven.setUser = Raven.setUserContext; // To be deprecated
|
|
|
+// Deprecations
|
|
|
+Raven.setUser = Raven.setUserContext;
|
|
|
+Raven.setReleaseContext = Raven.setRelease;
|
|
|
|
|
|
function triggerEvent(eventType, options) {
|
|
|
- var event, key;
|
|
|
+ // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it
|
|
|
+ var evt, key;
|
|
|
+
|
|
|
+ if (!hasDocument)
|
|
|
+ return;
|
|
|
|
|
|
options = options || {};
|
|
|
|
|
|
eventType = 'raven' + eventType.substr(0,1).toUpperCase() + eventType.substr(1);
|
|
|
|
|
|
if (document.createEvent) {
|
|
|
- event = document.createEvent('HTMLEvents');
|
|
|
- event.initEvent(eventType, true, true);
|
|
|
+ evt = document.createEvent('HTMLEvents');
|
|
|
+ evt.initEvent(eventType, true, true);
|
|
|
} else {
|
|
|
- event = document.createEventObject();
|
|
|
- event.eventType = eventType;
|
|
|
+ evt = document.createEventObject();
|
|
|
+ evt.eventType = eventType;
|
|
|
}
|
|
|
|
|
|
for (key in options) if (hasKey(options, key)) {
|
|
|
- event[key] = options[key];
|
|
|
+ evt[key] = options[key];
|
|
|
}
|
|
|
|
|
|
if (document.createEvent) {
|
|
|
// IE9 if standards
|
|
|
- document.dispatchEvent(event);
|
|
|
+ document.dispatchEvent(evt);
|
|
|
} else {
|
|
|
// IE8 regardless of Quirks or Standards
|
|
|
// IE9 if quirks
|
|
|
try {
|
|
|
- document.fireEvent('on' + event.eventType.toLowerCase(), event);
|
|
|
+ document.fireEvent('on' + evt.eventType.toLowerCase(), evt);
|
|
|
} catch(e) {}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
var dsnKeys = 'source protocol user pass host port path'.split(' '),
|
|
|
- dsnPattern = /^(?:(\w+):)?\/\/(\w+)(:\w+)?@([\w\.-]+)(?::(\d+))?(\/.*)/;
|
|
|
+ dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/;
|
|
|
|
|
|
function RavenConfigError(message) {
|
|
|
this.name = 'RavenConfigError';
|
|
|
@@ -1475,7 +1578,7 @@ function parseDSN(str) {
|
|
|
}
|
|
|
|
|
|
function isUndefined(what) {
|
|
|
- return typeof what === 'undefined';
|
|
|
+ return what === void 0;
|
|
|
}
|
|
|
|
|
|
function isFunction(what) {
|
|
|
@@ -1483,7 +1586,7 @@ function isFunction(what) {
|
|
|
}
|
|
|
|
|
|
function isString(what) {
|
|
|
- return typeof what === 'string';
|
|
|
+ return objectPrototype.toString.call(what) === '[object String]';
|
|
|
}
|
|
|
|
|
|
function isObject(what) {
|
|
|
@@ -1533,15 +1636,6 @@ function each(obj, callback) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-function setAuthQueryString() {
|
|
|
- authQueryString =
|
|
|
- '?sentry_version=4' +
|
|
|
- '&sentry_client=raven-js/' + Raven.VERSION +
|
|
|
- '&sentry_key=' + globalKey;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
function handleStackInfo(stackInfo, options) {
|
|
|
var frames = [];
|
|
|
|
|
|
@@ -1588,7 +1682,7 @@ function normalizeFrame(frame) {
|
|
|
|
|
|
normalized.in_app = !( // determine if an exception came from outside of our app
|
|
|
// first we check the global includePaths list.
|
|
|
- !globalOptions.includePaths.test(normalized.filename) ||
|
|
|
+ (!!globalOptions.includePaths.test && !globalOptions.includePaths.test(normalized.filename)) ||
|
|
|
// Now we check for fun, if the function name is Raven or TraceKit
|
|
|
/(Raven|TraceKit)\./.test(normalized['function']) ||
|
|
|
// finally, we do a last ditch effort and check for raven.min.js
|
|
|
@@ -1638,20 +1732,12 @@ function extractContextFromFrame(frame) {
|
|
|
}
|
|
|
|
|
|
function processException(type, message, fileurl, lineno, frames, options) {
|
|
|
- var stacktrace, label, i;
|
|
|
+ var stacktrace, i, fullMessage;
|
|
|
|
|
|
- // In some instances message is not actually a string, no idea why,
|
|
|
- // so we want to always coerce it to one.
|
|
|
- message += '';
|
|
|
-
|
|
|
- // Sometimes an exception is getting logged in Sentry as
|
|
|
- // <no message value>
|
|
|
- // This can only mean that the message was falsey since this value
|
|
|
- // is hardcoded into Sentry itself.
|
|
|
- // At this point, if the message is falsey, we bail since it's useless
|
|
|
- if (type === 'Error' && !message) return;
|
|
|
+ if (!!globalOptions.ignoreErrors.test && globalOptions.ignoreErrors.test(message)) return;
|
|
|
|
|
|
- if (globalOptions.ignoreErrors.test(message)) return;
|
|
|
+ message += '';
|
|
|
+ fullMessage = type + ': ' + message;
|
|
|
|
|
|
if (frames && frames.length) {
|
|
|
fileurl = frames[0].filename || fileurl;
|
|
|
@@ -1669,26 +1755,22 @@ function processException(type, message, fileurl, lineno, frames, options) {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- // Truncate the message to a max of characters
|
|
|
- message = truncate(message, globalOptions.maxMessageLength);
|
|
|
-
|
|
|
- if (globalOptions.ignoreUrls && globalOptions.ignoreUrls.test(fileurl)) return;
|
|
|
- if (globalOptions.whitelistUrls && !globalOptions.whitelistUrls.test(fileurl)) return;
|
|
|
-
|
|
|
- label = lineno ? message + ' at ' + lineno : message;
|
|
|
+ if (!!globalOptions.ignoreUrls.test && globalOptions.ignoreUrls.test(fileurl)) return;
|
|
|
+ if (!!globalOptions.whitelistUrls.test && !globalOptions.whitelistUrls.test(fileurl)) return;
|
|
|
|
|
|
// Fire away!
|
|
|
send(
|
|
|
objectMerge({
|
|
|
// sentry.interfaces.Exception
|
|
|
exception: {
|
|
|
- type: type,
|
|
|
- value: message
|
|
|
+ values: [{
|
|
|
+ type: type,
|
|
|
+ value: message,
|
|
|
+ stacktrace: stacktrace
|
|
|
+ }]
|
|
|
},
|
|
|
- // sentry.interfaces.Stacktrace
|
|
|
- stacktrace: stacktrace,
|
|
|
culprit: fileurl,
|
|
|
- message: label
|
|
|
+ message: fullMessage
|
|
|
}, options)
|
|
|
);
|
|
|
}
|
|
|
@@ -1707,58 +1789,83 @@ function truncate(str, max) {
|
|
|
return str.length <= max ? str : str.substr(0, max) + '\u2026';
|
|
|
}
|
|
|
|
|
|
+function trimPacket(data) {
|
|
|
+ // For now, we only want to truncate the two different messages
|
|
|
+ // but this could/should be expanded to just trim everything
|
|
|
+ var max = globalOptions.maxMessageLength;
|
|
|
+ data.message = truncate(data.message, max);
|
|
|
+ if (data.exception) {
|
|
|
+ var exception = data.exception.values[0];
|
|
|
+ exception.value = truncate(exception.value, max);
|
|
|
+ }
|
|
|
+
|
|
|
+ return data;
|
|
|
+}
|
|
|
+
|
|
|
function now() {
|
|
|
return +new Date();
|
|
|
}
|
|
|
|
|
|
function getHttpData() {
|
|
|
- var http = {
|
|
|
- url: document.location.href,
|
|
|
+ if (!hasDocument || !document.location || !document.location.href) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var httpData = {
|
|
|
headers: {
|
|
|
'User-Agent': navigator.userAgent
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ httpData.url = document.location.href;
|
|
|
+
|
|
|
if (document.referrer) {
|
|
|
- http.headers.Referer = document.referrer;
|
|
|
+ httpData.headers.Referer = document.referrer;
|
|
|
}
|
|
|
|
|
|
- return http;
|
|
|
+ return httpData;
|
|
|
}
|
|
|
|
|
|
function send(data) {
|
|
|
- if (!isSetup()) return;
|
|
|
-
|
|
|
- data = objectMerge({
|
|
|
+ var baseData = {
|
|
|
project: globalProject,
|
|
|
logger: globalOptions.logger,
|
|
|
- platform: 'javascript',
|
|
|
- // sentry.interfaces.Http
|
|
|
- request: getHttpData()
|
|
|
- }, data);
|
|
|
+ platform: 'javascript'
|
|
|
+ }, httpData = getHttpData();
|
|
|
+
|
|
|
+ if (httpData) {
|
|
|
+ baseData.request = httpData;
|
|
|
+ }
|
|
|
+
|
|
|
+ data = objectMerge(baseData, data);
|
|
|
|
|
|
// Merge in the tags and extra separately since objectMerge doesn't handle a deep merge
|
|
|
- data.tags = objectMerge(objectMerge({}, globalOptions.tags), data.tags);
|
|
|
- data.extra = objectMerge(objectMerge({}, globalOptions.extra), data.extra);
|
|
|
+ data.tags = objectMerge(objectMerge({}, globalContext.tags), data.tags);
|
|
|
+ data.extra = objectMerge(objectMerge({}, globalContext.extra), data.extra);
|
|
|
|
|
|
// Send along our own collected metadata with extra
|
|
|
- data.extra = objectMerge({
|
|
|
- 'session:duration': now() - startTime
|
|
|
- }, data.extra);
|
|
|
+ data.extra['session:duration'] = now() - startTime;
|
|
|
|
|
|
// If there are no tags/extra, strip the key from the payload alltogther.
|
|
|
if (isEmptyObject(data.tags)) delete data.tags;
|
|
|
|
|
|
- if (globalUser) {
|
|
|
+ if (globalContext.user) {
|
|
|
// sentry.interfaces.User
|
|
|
- data.user = globalUser;
|
|
|
+ data.user = globalContext.user;
|
|
|
}
|
|
|
|
|
|
- // Include the release iff it's defined in globalOptions
|
|
|
+ // Include the release if it's defined in globalOptions
|
|
|
if (globalOptions.release) data.release = globalOptions.release;
|
|
|
+ // Include server_name if it's defined in globalOptions
|
|
|
+ if (globalOptions.serverName) data.server_name = globalOptions.serverName;
|
|
|
|
|
|
if (isFunction(globalOptions.dataCallback)) {
|
|
|
- data = globalOptions.dataCallback(data);
|
|
|
+ data = globalOptions.dataCallback(data) || data;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Why??????????
|
|
|
+ if (!data || isEmptyObject(data)) {
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
// Check if the request should be filtered or not
|
|
|
@@ -1771,34 +1878,68 @@ function send(data) {
|
|
|
// Set lastEventId after we know the error should actually be sent
|
|
|
lastEventId = data.event_id || (data.event_id = uuid4());
|
|
|
|
|
|
- makeRequest(data);
|
|
|
+ // Try and clean up the packet before sending by truncating long values
|
|
|
+ data = trimPacket(data);
|
|
|
+
|
|
|
+ logDebug('debug', 'Raven about to send:', data);
|
|
|
+
|
|
|
+ if (!isSetup()) return;
|
|
|
+
|
|
|
+ (globalOptions.transport || makeRequest)({
|
|
|
+ url: globalServer,
|
|
|
+ auth: {
|
|
|
+ sentry_version: '7',
|
|
|
+ sentry_client: 'raven-js/' + Raven.VERSION,
|
|
|
+ sentry_key: globalKey
|
|
|
+ },
|
|
|
+ data: data,
|
|
|
+ options: globalOptions,
|
|
|
+ onSuccess: function success() {
|
|
|
+ triggerEvent('success', {
|
|
|
+ data: data,
|
|
|
+ src: globalServer
|
|
|
+ });
|
|
|
+ },
|
|
|
+ onError: function failure() {
|
|
|
+ triggerEvent('failure', {
|
|
|
+ data: data,
|
|
|
+ src: globalServer
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
+function makeRequest(opts) {
|
|
|
+ // Tack on sentry_data to auth options, which get urlencoded
|
|
|
+ opts.auth.sentry_data = JSON.stringify(opts.data);
|
|
|
|
|
|
-function makeRequest(data) {
|
|
|
- var img = new Image(),
|
|
|
- src = globalServer + authQueryString + '&sentry_data=' + encodeURIComponent(JSON.stringify(data));
|
|
|
+ var img = newImage(),
|
|
|
+ src = opts.url + '?' + urlencode(opts.auth),
|
|
|
+ crossOrigin = opts.options.crossOrigin;
|
|
|
|
|
|
- img.crossOrigin = 'anonymous';
|
|
|
- img.onload = function success() {
|
|
|
- triggerEvent('success', {
|
|
|
- data: data,
|
|
|
- src: src
|
|
|
- });
|
|
|
- };
|
|
|
- img.onerror = img.onabort = function failure() {
|
|
|
- triggerEvent('failure', {
|
|
|
- data: data,
|
|
|
- src: src
|
|
|
- });
|
|
|
- };
|
|
|
+ if (crossOrigin || crossOrigin === '') {
|
|
|
+ img.crossOrigin = crossOrigin;
|
|
|
+ }
|
|
|
+ img.onload = opts.onSuccess;
|
|
|
+ img.onerror = img.onabort = opts.onError;
|
|
|
img.src = src;
|
|
|
}
|
|
|
|
|
|
+// Note: this is shitty, but I can't figure out how to get
|
|
|
+// sinon to stub document.createElement without breaking everything
|
|
|
+// so this wrapper is just so I can stub it for tests.
|
|
|
+function newImage() {
|
|
|
+ return document.createElement('img');
|
|
|
+}
|
|
|
+
|
|
|
+var ravenNotConfiguredError;
|
|
|
+
|
|
|
function isSetup() {
|
|
|
if (!hasJSON) return false; // needs JSON support
|
|
|
if (!globalServer) {
|
|
|
- logDebug('error', 'Error: Raven has not been configured.');
|
|
|
+ if (!ravenNotConfiguredError)
|
|
|
+ logDebug('error', 'Error: Raven has not been configured.');
|
|
|
+ ravenNotConfiguredError = true;
|
|
|
return false;
|
|
|
}
|
|
|
return true;
|
|
|
@@ -1826,18 +1967,44 @@ function joinRegExp(patterns) {
|
|
|
return new RegExp(sources.join('|'), 'i');
|
|
|
}
|
|
|
|
|
|
-// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
|
|
|
function uuid4() {
|
|
|
- return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
|
- var r = Math.random()*16|0,
|
|
|
- v = c == 'x' ? r : (r&0x3|0x8);
|
|
|
- return v.toString(16);
|
|
|
- });
|
|
|
+ var crypto = window.crypto || window.msCrypto;
|
|
|
+
|
|
|
+ if (!isUndefined(crypto) && crypto.getRandomValues) {
|
|
|
+ // Use window.crypto API if available
|
|
|
+ var arr = new Uint16Array(8);
|
|
|
+ crypto.getRandomValues(arr);
|
|
|
+
|
|
|
+ // set 4 in byte 7
|
|
|
+ arr[3] = arr[3] & 0xFFF | 0x4000;
|
|
|
+ // set 2 most significant bits of byte 9 to '10'
|
|
|
+ arr[4] = arr[4] & 0x3FFF | 0x8000;
|
|
|
+
|
|
|
+ var pad = function(num) {
|
|
|
+ var v = num.toString(16);
|
|
|
+ while (v.length < 4) {
|
|
|
+ v = '0' + v;
|
|
|
+ }
|
|
|
+ return v;
|
|
|
+ };
|
|
|
+
|
|
|
+ return (pad(arr[0]) + pad(arr[1]) + pad(arr[2]) + pad(arr[3]) + pad(arr[4]) +
|
|
|
+ pad(arr[5]) + pad(arr[6]) + pad(arr[7]));
|
|
|
+ } else {
|
|
|
+ // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523
|
|
|
+ return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
|
+ var r = Math.random()*16|0,
|
|
|
+ v = c == 'x' ? r : (r&0x3|0x8);
|
|
|
+ return v.toString(16);
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-function logDebug(level, message) {
|
|
|
- if (window.console && console[level] && Raven.debug) {
|
|
|
- console[level](message);
|
|
|
+function logDebug(level) {
|
|
|
+ if (originalConsoleMethods[level] && Raven.debug) {
|
|
|
+ // _slice is coming from vendor/TraceKit/tracekit.js
|
|
|
+ // so it's accessible globally
|
|
|
+ originalConsoleMethods[level].apply(originalConsole, _slice.call(arguments, 1));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -1848,12 +2015,32 @@ function afterLoad() {
|
|
|
Raven.config(RavenConfig.dsn, RavenConfig.config).install();
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+function urlencode(o) {
|
|
|
+ var pairs = [];
|
|
|
+ each(o, function(key, value) {
|
|
|
+ pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
|
|
|
+ });
|
|
|
+ return pairs.join('&');
|
|
|
+}
|
|
|
+
|
|
|
+function mergeContext(key, context) {
|
|
|
+ if (isUndefined(context)) {
|
|
|
+ delete globalContext[key];
|
|
|
+ } else {
|
|
|
+ globalContext[key] = objectMerge(globalContext[key] || {}, context);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
afterLoad();
|
|
|
|
|
|
+// This is being exposed no matter what because there are too many weird
|
|
|
+// usecases for how people use Raven. If this is really a problem, I'm sorry.
|
|
|
+window.Raven = Raven;
|
|
|
+
|
|
|
// Expose Raven to the world
|
|
|
if (typeof define === 'function' && define.amd) {
|
|
|
// AMD
|
|
|
- window.Raven = Raven;
|
|
|
define('raven', [], function() {
|
|
|
return Raven;
|
|
|
});
|
|
|
@@ -1863,9 +2050,6 @@ if (typeof define === 'function' && define.amd) {
|
|
|
} else if (typeof exports === 'object') {
|
|
|
// CommonJS
|
|
|
exports = Raven;
|
|
|
-} else {
|
|
|
- // Everything else
|
|
|
- window.Raven = Raven;
|
|
|
}
|
|
|
|
|
|
-})(window);
|
|
|
+})(typeof window !== 'undefined' ? window : this);
|