UNPKG

@gibraltarsoftware/loupe-typescript

Version:
734 lines 31.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.LoupeAgent = void 0; var StackTrace = __importStar(require("stacktrace-js")); var exception_1 = require("./exception"); var Header_1 = require("./Header"); var localStorageMessage_1 = require("./localStorageMessage"); var LogMessageSeverity_1 = require("./LogMessageSeverity"); var MethodSourceInfo_1 = require("./MethodSourceInfo"); var LoupeAgent = (function () { function LoupeAgent(window, document, clientPlatform) { var _a; this.window = window; this.document = document; this.clientPlatform = clientPlatform; this.propagateError = false; this.loupeAgentSessionIdHeader = 'loupe-agent-sessionId'; this.loupeSessionIdHeader = 'LoupeSessionId'; this.maxRequestSize = 204800; this.messageInterval = 10; this.exceptionCategory = 'Javascript.Exception'; this.loupeAgentSessionIdKey = 'LoupeAgentSessionId'; this.sequenceNumber = 0; this.messageStorage = []; this.storageAvailable = true; this.storageFull = false; this.corsOrigin = null; this.globalKeyList = []; this.headers = []; this.debounce = function (func, waitFor) { var timeout = 0; var debounced = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } clearTimeout(timeout); timeout = setTimeout(function () { return func.apply(void 0, args); }, waitFor); }; return debounced; }; if (typeof this.window !== 'undefined' && typeof ((_a = this.window) === null || _a === void 0 ? void 0 : _a.onerror) !== 'undefined') { this.existingOnError = this.window.onerror; this.setUpOnError(this.window); } this.storageAvailable = this.storageSupported(); this.setUpClientSessionId(); this.setUpSequenceNumber(); this.flushToServer(); } LoupeAgent.prototype.verbose = function (category, caption, description, parameters, exception, details, methodSourceInfo) { this.write(LogMessageSeverity_1.LogMessageSeverity.verbose, category, caption, description, parameters, exception, details, methodSourceInfo); }; LoupeAgent.prototype.information = function (category, caption, description, parameters, exception, details, methodSourceInfo) { this.write(LogMessageSeverity_1.LogMessageSeverity.information, category, caption, description, parameters, exception, details, methodSourceInfo); }; LoupeAgent.prototype.warning = function (category, caption, description, parameters, exception, details, methodSourceInfo) { this.write(LogMessageSeverity_1.LogMessageSeverity.warning, category, caption, description, parameters, exception, details, methodSourceInfo); }; LoupeAgent.prototype.error = function (category, caption, description, parameters, exception, details, methodSourceInfo) { this.write(LogMessageSeverity_1.LogMessageSeverity.error, category, caption, description, parameters, exception, details, methodSourceInfo); }; LoupeAgent.prototype.critical = function (category, caption, description, parameters, exception, details, methodSourceInfo) { this.write(LogMessageSeverity_1.LogMessageSeverity.critical, category, caption, description, parameters, exception, details, methodSourceInfo); }; LoupeAgent.prototype.recordException = function (exception, details, category) { var _this = this; var caption = exception.caption || exception.name; if (!category) { category = this.exceptionCategory; } if (exception.stack && typeof exception.stack === 'string') { this.createStackFromMessage(exception.stack).then(function (stack) { exception.stack = stack; _this.write(LogMessageSeverity_1.LogMessageSeverity.error, category, caption, exception.description, null, exception, details, null); }); } else { this.write(LogMessageSeverity_1.LogMessageSeverity.error, category, caption, exception.description, null, exception, details, null); } }; LoupeAgent.prototype.write = function (severity, category, caption, description, parameters, exception, details, methodSourceInfo) { this.getOrigin(); exception = this.sanitiseArgument(exception); details = this.sanitiseArgument(details); if (details && typeof details !== 'string') { details = JSON.stringify(details); } methodSourceInfo = this.sanitiseArgument(methodSourceInfo); if (methodSourceInfo && !(methodSourceInfo instanceof MethodSourceInfo_1.MethodSourceInfo)) { methodSourceInfo = this.buildMessageSourceInfo(methodSourceInfo); } this.createMessage(severity, category, caption, description, parameters, exception, details, methodSourceInfo); this.flushToServer(); }; LoupeAgent.prototype.flushToServer = function () { var _this = this; if ((this.storageAvailable && localStorage.length) || this.messageStorage.length) { setTimeout(function () { return _this.logMessageToServer(); }, this.messageInterval); } }; LoupeAgent.prototype.setSessionId = function (value) { this.sessionId = value; }; LoupeAgent.prototype.setLogServer = function (value, credentialsType) { if (credentialsType === void 0) { credentialsType = 'include'; } this.corsOrigin = value; this.credentialsType = credentialsType; }; LoupeAgent.prototype.addHeader = function (header) { if (header) { if (header.name && header.value) { if (header.name === this.loupeAgentSessionIdHeader) { this.consoleLog('Custom header cannot be named ' + this.loupeAgentSessionIdHeader); return; } if (header.name === this.loupeSessionIdHeader) { this.consoleLog('Custom header cannot be named ' + this.loupeSessionIdHeader); return; } this.headers.push(header); } else { this.consoleLog("addHeader failed. The header provided appears invalid as it doesn't have name & value"); } } else { this.consoleLog('addHeader failed. No header object provided'); } }; LoupeAgent.prototype.clientSessionHeader = function () { return new Header_1.Header(this.loupeAgentSessionIdHeader, this.agentSessionId); }; LoupeAgent.prototype.resetMessageInterval = function (interval) { var newInterval = interval || 10; if (newInterval < 10) { newInterval = 10; } if (newInterval < this.messageInterval) { this.messageInterval = newInterval; } }; LoupeAgent.prototype.storageSupported = function () { if (!this.window) { return false; } var testValue = '_loupe_storage_test_'; try { localStorage.setItem(testValue, testValue); localStorage.removeItem(testValue); return true; } catch (e) { return false; } }; LoupeAgent.prototype.sanitiseArgument = function (parameter) { if (typeof parameter === 'undefined') { return null; } return parameter; }; LoupeAgent.prototype.buildMessageSourceInfo = function (data) { return new MethodSourceInfo_1.MethodSourceInfo(data.file || null, data.method || null, data.line || null, data.column || null); }; LoupeAgent.prototype.setUpOnError = function (window) { var _this = this; var _a; if (typeof ((_a = this.window) === null || _a === void 0 ? void 0 : _a.onerror) === 'undefined') { this.consoleLog('Gibraltar Loupe JavaScript Logger: No onerror event; errors cannot be logged to Loupe'); return; } this.window.onerror = function (event, source, lineno, colno, error) { if (_this.existingOnError) { _this.existingOnError(event, source, lineno, colno, error); } setTimeout(function () { return _this.logError(event, source, lineno, colno, error); }, 10); return !_this.propagateError; }; }; LoupeAgent.prototype.getPlatform = function () { var _a, _b, _c, _d; if (!this.clientPlatform) { return null; } if (this.document) { this.clientPlatform.size = { height: ((_a = this.window) === null || _a === void 0 ? void 0 : _a.innerHeight) || ((_b = this.document) === null || _b === void 0 ? void 0 : _b.body.clientHeight), width: ((_c = this.window) === null || _c === void 0 ? void 0 : _c.innerWidth) || ((_d = this.document) === null || _d === void 0 ? void 0 : _d.body.clientWidth), }; } return this.clientPlatform; }; LoupeAgent.prototype.getStackTrace = function (error, errorMessage) { if (typeof error === 'undefined' || error === null || !error.stack) { return this.createStackFromMessage(errorMessage); } return Promise.resolve(this.createStackFromError(error)); }; LoupeAgent.prototype.createStackFromMessage = function (errorMessage) { var _this = this; if (StackTrace) { try { return StackTrace.fromError(new Error(errorMessage)).then(function (stack) { var notOurframes = _this.stripLoupeStackFrames(stack.reverse()); var notOurframeMessages = notOurframes.map(function (f) { return f.toString(); }); return notOurframeMessages; }); } catch (e) { } } return Promise.resolve([]); }; LoupeAgent.prototype.createStackFromError = function (error) { if (error.stack.substring(error.stack.length - 1) === '\n') { error.stack = error.stack.substring(0, error.stack.length - 1); } return error.stack.split('\n'); }; LoupeAgent.prototype.stripLoupeStackFrames = function (stack) { if (stack) { var userFramesStartPosition = this.userFramesStartAt(stack); if (userFramesStartPosition > 0) { stack = stack.slice(userFramesStartPosition); } } return stack; }; LoupeAgent.prototype.userFramesStartAt = function (stack) { var loupeMethods = ['logError', 'getStackTrace', 'createStackFromMessage', 'createStackTrace']; var position = 0; if (stack[0].toString().indexOf('Cannot access caller') > -1) { position++; } for (; position < loupeMethods.length; position++) { if (stack.length < position) { break; } var functionName = stack[position].functionName; if (!functionName) { functionName = stack[position].toString(); } if (functionName.indexOf(loupeMethods[position]) === -1) { break; } } return position; }; LoupeAgent.prototype.logError = function (msg, url, line, column, error) { var _this = this; var errorName = ''; if (error) { errorName = error.name || 'Exception'; } this.getStackTrace(error, msg).then(function (stack) { var exception = { cause: errorName, column: column, line: line, message: msg, stackTrace: stack, url: url, }; _this.createMessage(LogMessageSeverity_1.LogMessageSeverity.error, 'JavaScript', errorName, '', null, exception, null, null); _this.logMessageToServer(); }); }; LoupeAgent.prototype.isStorageQuotaReached = function (e) { if (e.name === 'QUOTA_EXCEEDED_ERR' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED' || e.name === 'QuotaExceededError') { this.storageFull = true; return true; } return false; }; LoupeAgent.prototype.setUpClientSessionId = function () { this.sessionId = this.generateUUID(); if (!this.storageAvailable) { this.agentSessionId = this.generateUUID(); return; } var currentClientSessionId = this.getClientSessionHeader(); if (currentClientSessionId) { this.agentSessionId = currentClientSessionId; } else { this.agentSessionId = this.generateUUID(); this.storeClientSessionId(this.agentSessionId); } }; LoupeAgent.prototype.storeClientSessionId = function (sessionIdToStore) { if (this.storageAvailable && !this.storageFull) { try { sessionStorage.setItem(this.loupeAgentSessionIdKey, sessionIdToStore); } catch (e) { if (this.isStorageQuotaReached(e)) { return; } this.consoleLog('Unable to store clientSessionId in session storage. ' + e.message); } } }; LoupeAgent.prototype.getClientSessionHeader = function () { try { var clientSessionId = sessionStorage.getItem(this.loupeAgentSessionIdKey); if (clientSessionId) { return clientSessionId; } } catch (e) { this.consoleLog('Unable to retrieve clientSessionId number from session storage. ' + e.message); } return null; }; LoupeAgent.prototype.setUpSequenceNumber = function () { var sequence = this.getSequenceNumber(); if (sequence === -1 && this.storageAvailable) { this.sequenceNumber = 0; } else { this.sequenceNumber = sequence; } }; LoupeAgent.prototype.getNextSequenceNumber = function () { var storedSequenceNumber; if (this.storageAvailable) { storedSequenceNumber = this.getSequenceNumber(); if (storedSequenceNumber < this.sequenceNumber) { storedSequenceNumber = this.sequenceNumber; } if (storedSequenceNumber !== -1) { storedSequenceNumber++; if (this.setSequenceNumber(storedSequenceNumber)) { this.sequenceNumber = storedSequenceNumber; return this.sequenceNumber; } } } this.sequenceNumber++; return this.sequenceNumber; }; LoupeAgent.prototype.getSequenceNumber = function () { if (this.storageAvailable) { try { var currentNumber = sessionStorage.getItem('LoupeSequenceNumber'); if (currentNumber) { return parseInt(currentNumber); } else { return 0; } } catch (e) { this.consoleLog('Unable to retrieve sequence number from session storage. ' + e.message); } } return -1; }; LoupeAgent.prototype.setSequenceNumber = function (sequenceNumber) { try { sessionStorage.setItem('LoupeSequenceNumber', sequenceNumber.toString()); return true; } catch (e) { if (this.isStorageQuotaReached(e)) { this.consoleLog('Unable to store sequence number as storage quote reached: ' + e.message); return false; } this.consoleLog('Unable to store sequence number: ' + e.message); return false; } }; LoupeAgent.prototype.createMessage = function (severity, category, caption, description, parameters, exception, details, methodSourceInfo) { var messageSequenceNumber = this.getNextSequenceNumber(); var timeStamp = this.createTimeStamp(); if (exception) { exception = this.createExceptionFromError(exception, null); } var message = new localStorageMessage_1.LocalStorageMessage(severity, category, caption, description, parameters, exception, details, methodSourceInfo, timeStamp, messageSequenceNumber, this.agentSessionId, this.sessionId); this.storeMessage(message); }; LoupeAgent.prototype.storeMessage = function (message) { if (this.storageAvailable && !this.storageFull) { try { localStorage.setItem('Loupe-message-' + this.generateUUID(), JSON.stringify(message)); } catch (e) { this.isStorageQuotaReached(e); this.consoleLog('Error occured trying to add item to localStorage: ' + e.message); this.messageStorage.push(message); } } else { if (this.messageStorage.length === 5000) { this.messageStorage.shift(); } this.messageStorage.push(message); } }; LoupeAgent.prototype.createExceptionFromError = function (error, cause) { var url = this.window ? this.window.location.href : ''; if (typeof error === 'string') { return new exception_1.Exception(cause || '', null, null, error, [], url); } if ('url' in error) { return error; } var stack = error.stackTrace || error.stack || null; if (stack && typeof stack === 'string') { stack = stack.split('\n'); } return new exception_1.Exception(cause || '', error.columnNumber || null, error.lineNumber || null, error.message, stack, url); }; LoupeAgent.prototype.createTimeStamp = function () { var now = new Date(); var tzo = -now.getTimezoneOffset(); var dif = tzo >= 0 ? '+' : '-'; var pad = function (num) { var norm = Math.abs(Math.floor(num)); return (norm < 10 ? '0' : '') + norm; }; return (now.getFullYear() + '-' + pad(now.getMonth() + 1) + '-' + pad(now.getDate()) + 'T' + pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds()) + '.' + pad(now.getMilliseconds()) + dif + pad(tzo / 60) + ':' + pad(tzo % 60)); }; LoupeAgent.prototype.generateUUID = function () { var d = Date.now(); var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = (d + Math.random() * 16) % 16 | 0; d = Math.floor(d / 16); return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16); }); return uuid; }; LoupeAgent.prototype.truncateDetails = function (storedData) { if (storedData.message.details) { var messageSizeWithoutDetails = storedData.size - storedData.message.details.length; if (messageSizeWithoutDetails < this.maxRequestSize) { var details = { message: 'User supplied details truncated as log message exceeded maximum size.' }; storedData.message.details = JSON.stringify(details); var messageSize = JSON.stringify(storedData); storedData.size = messageSize.length; } } return storedData; }; LoupeAgent.prototype.dropMessage = function (storedData) { this.removeMessagesFromStorage([storedData.key]); var droppedCaption = storedData.message.caption; var droppedDescription = storedData.message.description; if (droppedCaption.length + droppedDescription.length < this.maxRequestSize - 400) { this.createMessage(LogMessageSeverity_1.LogMessageSeverity.error, 'Loupe', 'Dropped message', 'Message was dropped as its size exceeded our max request size. Caption was {0} and description {1}', [droppedCaption, droppedDescription]); } else { if (droppedCaption.length < this.maxRequestSize - 400) { this.createMessage(LogMessageSeverity_1.LogMessageSeverity.error, 'Loupe', 'Dropped message', 'Message was dropped as its size exceeded our max request size. Caption was {0}', [droppedCaption]); } else { this.createMessage(LogMessageSeverity_1.LogMessageSeverity.error, 'Loupe', 'Dropped message', 'Message was dropped as its size exceeded our max request size.\nUnable to log caption or description as they exceed max request size'); } } }; LoupeAgent.prototype.overSizeMessage = function (storedData) { var messageTooLarge = false; if (storedData.size > this.maxRequestSize) { storedData = this.truncateDetails(storedData); if (storedData.size > this.maxRequestSize) { this.dropMessage(storedData); messageTooLarge = true; } } return messageTooLarge; }; LoupeAgent.prototype.messageSort = function (a, b) { var firstDate = new Date(a.message.timeStamp); var secondDate = new Date(b.message.timeStamp); if (firstDate > secondDate) { return -1; } if (firstDate < secondDate) { return 1; } return a.message.sequence - b.message.sequence; }; LoupeAgent.prototype.getMessagesToSend = function () { var messages = []; var keys = []; var moreMessagesInStorage = false; var messagesFromStorage = []; if (this.messageStorage.length) { messages = this.messageStorage.slice(); this.messageStorage.length = 0; } if (this.storageAvailable) { for (var i = 0; i < localStorage.length; i++) { var key = localStorage.key(i); if (key != null && key.indexOf('Loupe-message-') > -1) { if (this.globalKeyList.indexOf(key) === -1) { var message = localStorage.getItem(key); if (message != null) { messagesFromStorage.push({ key: localStorage.key(i), message: JSON.parse(message), size: message.length, }); } } } } } if (messagesFromStorage.length && messagesFromStorage.length > 1) { messagesFromStorage.sort(this.messageSort); } if (messagesFromStorage.length > 10) { moreMessagesInStorage = true; messagesFromStorage = messagesFromStorage.splice(0, 10); } if (this.messageInterval !== 10) { messagesFromStorage = messagesFromStorage.splice(0, 1); } var cumulativeSize = 0; for (var _i = 0, messagesFromStorage_1 = messagesFromStorage; _i < messagesFromStorage_1.length; _i++) { var msg = messagesFromStorage_1[_i]; if (this.overSizeMessage(msg)) { continue; } cumulativeSize += msg.size; if (cumulativeSize > this.maxRequestSize) { break; } messages.push(msg.message); if (msg.key) { keys.push(msg.key); } } if (keys.length) { Array.prototype.push.apply(this.globalKeyList, keys); } return { messages: messages, keys: keys, moreMessagesInStorage: moreMessagesInStorage }; }; LoupeAgent.prototype.removeKeysFromGlobalList = function (keys) { if (this.globalKeyList.length && keys) { var position = this.globalKeyList.indexOf(keys[0]); this.globalKeyList.splice(position, keys.length); } }; LoupeAgent.prototype.removeMessagesFromStorage = function (keys) { if (!keys) { return; } for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) { var key = keys_1[_i]; try { localStorage.removeItem(key); } catch (e) { this.consoleLog('Unable to remove message from localStorage: ' + e.message); } } }; LoupeAgent.prototype.setMessageInterval = function (callFailed) { if (!callFailed && this.messageInterval === 10) { return; } if (this.messageInterval < 10000) { if (callFailed) { this.messageInterval = this.messageInterval * 10; } else { this.messageInterval = this.messageInterval / 10; if (this.messageInterval < 10) { this.messageInterval = 10; } } return; } if (this.messageInterval === 10000) { if (callFailed) { this.messageInterval = 30000; } else { this.messageInterval = 1000; } return; } if (!callFailed && this.messageInterval === 30000) { this.messageInterval = 10000; return; } if (callFailed) { if (this.messageInterval < 960000) { this.messageInterval = this.messageInterval * 2; } } else { this.messageInterval = this.messageInterval / 2; } }; LoupeAgent.prototype.logMessageToServer = function () { var _this = this; var _a = this.getMessagesToSend(), messages = _a.messages, keys = _a.keys, moreMessagesInStorage = _a.moreMessagesInStorage; if (!messages.length) { return; } var logMessage = { logMessages: messages, session: { client: this.getPlatform(), currentAgentSessionId: this.agentSessionId, }, }; var updateMessageInterval = this.debounce(function () { return _this.setMessageInterval; }, 500); this.sendMessageToServer(logMessage, keys, moreMessagesInStorage, updateMessageInterval); }; LoupeAgent.prototype.afterRequest = function (callFailed, moreMessages, updateMessageInterval) { updateMessageInterval(callFailed); if (this.storageFull && !callFailed) { this.storageFull = false; } if (moreMessages) { this.flushToServer(); } }; LoupeAgent.prototype.requestSucceeded = function (keys, moreMessages, updateMessageInterval) { this.removeMessagesFromStorage(keys); this.afterRequest(false, moreMessages, updateMessageInterval); }; LoupeAgent.prototype.requestFailed = function (response, keys, moreMessages, updateMessageInterval) { var _a, _b; if (response.status === 0 || response.status === 401) { this.removeKeysFromGlobalList(keys); } else { this.removeMessagesFromStorage(keys); } this.consoleLog('Loupe JavaScript Logger: Failed to log to "' + ((_b = (_a = this.window) === null || _a === void 0 ? void 0 : _a.location) === null || _b === void 0 ? void 0 : _b.origin) + '/loupe/log"'); this.consoleLog(' Status: ' + response.status + ': ' + response.statusText); this.afterRequest(true, moreMessages, updateMessageInterval); }; LoupeAgent.prototype.getOrigin = function () { var _a, _b; var origin = this.corsOrigin || ((_b = (_a = this.window) === null || _a === void 0 ? void 0 : _a.location) === null || _b === void 0 ? void 0 : _b.origin); if (origin) { origin = this.stripTrailingSlash(origin); } else { throw new Error('Loupe JavaScript Logger: No origin found. Check you have called setLogServer'); } return origin; }; LoupeAgent.prototype.sendMessageToServer = function (logMessage, keys, moreMessages, updateMessageInterval) { var _this = this; try { var origin_1 = this.getOrigin(); var headers_1 = new Headers({ 'Content-type': 'application/json', }); headers_1.append(this.loupeAgentSessionIdHeader, this.agentSessionId); headers_1.append(this.loupeSessionIdHeader, this.sessionId); this.headers.forEach(function (header) { headers_1.append(header.name, header.value); }); var logDestination = "".concat(origin_1, "/loupe/log"); fetch(logDestination, { method: 'POST', mode: 'cors', credentials: this.credentialsType, headers: headers_1, body: JSON.stringify(logMessage), }).then(function (response) { if (response.status >= 200 && response.status <= 204) { _this.requestSucceeded(keys, moreMessages, updateMessageInterval); } else { _this.requestFailed(response, keys, moreMessages, updateMessageInterval); } }, function (e) { _this.consoleLog('Loupe JavaScript Logger: Exception while attempting to log', e); }); } catch (e) { this.consoleLog('Loupe JavaScript Logger: Exception while attempting to log', e); } }; LoupeAgent.prototype.stripTrailingSlash = function (origin) { return origin.replace(/\/$/, ''); }; LoupeAgent.prototype.consoleLog = function (msg, args) { if (console && typeof console.log === 'function') { console.log(msg, args || ''); } }; return LoupeAgent; }()); exports.LoupeAgent = LoupeAgent; //# sourceMappingURL=loupe.agent.js.map