瀏覽代碼

Update raven.js

Thibaut 10 年之前
父節點
當前提交
3b68eb02e9
共有 1 個文件被更改,包括 336 次插入152 次删除
  1. 336 152
      assets/javascripts/vendor/raven.js

+ 336 - 152
assets/javascripts/vendor/raven.js

@@ -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);