From 06ed1c5b2be5faf460ef4da8e74bdb1cd96d4a51 Mon Sep 17 00:00:00 2001 From: Ricardo Seromenho Date: Thu, 8 Jun 2017 03:05:37 +0100 Subject: [PATCH] Store offline errors had a failure being sent to server new option allowOfflineStorage --- src/raven.js | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/src/raven.js b/src/raven.js index 53026c1d8c26..7395a475d4a4 100644 --- a/src/raven.js +++ b/src/raven.js @@ -26,6 +26,7 @@ var _window = typeof window !== 'undefined' ? window var _document = _window.document; var _navigator = _window.navigator; +var offlineStorageKey = 'raven-js'; function keepOriginalCallback(original, callback) { return isFunction(callback) ? @@ -177,6 +178,16 @@ Raven.prototype = { TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors; + // If payload offline storage + if (globalOptions.allowOfflineStorage) { + // Process queue on start + this.processOfflineQueue(); + + // Add event listener on online or custom event to trigger offline queue sending + window.addEventListener(globalOptions.onlineEventName || 'online', this.processOfflineQueue); + } + + // return for chaining return self; }, @@ -708,6 +719,51 @@ Raven.prototype = { (_document.head || _document.body).appendChild(script); }, + /** + * Stores a passed data payload or the last one on local storage + */ + storePayloadOffline: function(data) { + var self = this; + try { + var queue = JSON.parse(localStorage.getItem(offlineStorageKey)) || []; + queue.push(data || this._lastData); + localStorage.setItem(offlineStorageKey, JSON.stringify(queue)); + } catch (e) { + self._logDebug('error', 'Raven failed to store payload offline: ', e); + } + }, + + /** + * Process the queue if the browser is online + */ + processOfflineQueue: function() { + var self = this; + + // Let's stop here if there's no connection + if (!navigator.onLine) { + return; + } + + try { + // Get the queue + var queue = JSON.parse(localStorage.getItem(offlineStorageKey)) || []; + + // Store an empty queue. If processing these one fails they get back to the queue + localStorage.setItem(offlineStorageKey, JSON.stringify([])); + + queue.forEach(function processOfflinePayload(data) { + // Avoid duplication verification for offline stored + // as they may try multiple times to be processed + data.extra.storedOffline = true; + + // Try to process it again + self._sendProcessedPayload(data); + }); + } catch (e) { + self._logDebug('error', 'Raven transport failed to store offline: ', e); + } + }, + /**** Private functions ****/ _ignoreNextOnError: function () { var self = this; @@ -1595,7 +1651,7 @@ Raven.prototype = { // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback, // but this would require copying an un-truncated copy of the data packet, which can be // arbitrarily deep (extra_data) -- could be worthwhile? will revisit - if (!this._globalOptions.allowDuplicates && this._isRepeatData(data)) { + if (!this._globalOptions.allowDuplicates && this._isRepeatData(data) && !this._globalOptions.allowOfflineStorage && !data.extra.storedOffline) { this._logDebug('warn', 'Raven dropped repeat event: ', data); return; } @@ -1642,6 +1698,11 @@ Raven.prototype = { onError: function failure(error) { self._logDebug('error', 'Raven transport failed to send: ', error); + // Store the error on local storage to try to send it later + if (globalOptions.allowOfflineStorage) { + self.storePayloadOffline(); + } + if (error.request) { self._setBackoffState(error.request); }