UNPKG

@platformos/pos-cli

Version:

Manage your platformOS application

631 lines 26.4 kB
Object.defineProperty(exports, "__esModule", { value: true }); var tslib_1 = require("tslib"); var types_1 = require("@sentry/types"); var utils_1 = require("@sentry/utils"); var global = utils_1.getGlobalObject(); var defaultTracingOrigins = ['localhost', /^\//]; /** * Tracing Integration */ var Tracing = /** @class */ (function () { /** * Constructor for Tracing * * @param _options TracingOptions */ function Tracing(_options) { /** * @inheritDoc */ this.name = Tracing.id; this._emitOptionsWarning = false; if (global.performance) { global.performance.mark('sentry-tracing-init'); } var defaults = { discardBackgroundSpans: true, idleTimeout: 500, maxTransactionDuration: 600, shouldCreateSpanForRequest: function (url) { var origins = (_options && _options.tracingOrigins) || defaultTracingOrigins; return (origins.some(function (origin) { return utils_1.isMatchingPattern(url, origin); }) && !utils_1.isMatchingPattern(url, 'sentry_key')); }, startTransactionOnLocationChange: true, traceFetch: true, traceXHR: true, tracesSampleRate: 1, tracingOrigins: defaultTracingOrigins, }; // NOTE: Logger doesn't work in contructors, as it's initialized after integrations instances if (!_options || !Array.isArray(_options.tracingOrigins) || _options.tracingOrigins.length === 0) { this._emitOptionsWarning = true; } Tracing.options = tslib_1.__assign({}, defaults, _options); } /** * @inheritDoc */ Tracing.prototype.setupOnce = function (addGlobalEventProcessor, getCurrentHub) { Tracing._getCurrentHub = getCurrentHub; if (this._emitOptionsWarning) { utils_1.logger.warn('[Tracing] You need to define `tracingOrigins` in the options. Set an array of urls or patterns to trace.'); utils_1.logger.warn("[Tracing] We added a reasonable default for you: " + defaultTracingOrigins); } if (!Tracing._isEnabled()) { return; } // Starting our inital pageload transaction if (global.location && global.location.href) { // `${global.location.href}` will be used a temp transaction name Tracing.startIdleTransaction(global.location.href, { op: 'pageload', sampled: true, }); } this._setupXHRTracing(); this._setupFetchTracing(); this._setupHistory(); this._setupErrorHandling(); this._setupBackgroundTabDetection(); Tracing._pingHeartbeat(); // This EventProcessor makes sure that the transaction is not longer than maxTransactionDuration addGlobalEventProcessor(function (event) { var self = getCurrentHub().getIntegration(Tracing); if (!self) { return event; } if (Tracing._isEnabled()) { var isOutdatedTransaction = event.timestamp && event.start_timestamp && (event.timestamp - event.start_timestamp > Tracing.options.maxTransactionDuration || event.timestamp - event.start_timestamp < 0); if (Tracing.options.maxTransactionDuration !== 0 && event.type === 'transaction' && isOutdatedTransaction) { utils_1.logger.log('[Tracing] Discarded transaction since it maxed out maxTransactionDuration'); return null; } } return event; }); }; /** * Pings the heartbeat */ Tracing._pingHeartbeat = function () { Tracing._heartbeatTimer = setTimeout(function () { Tracing._beat(); }, 5000); }; /** * Checks when entries of Tracing._activities are not changing for 3 beats. If this occurs we finish the transaction * */ Tracing._beat = function () { clearTimeout(Tracing._heartbeatTimer); var keys = Object.keys(Tracing._activities); if (keys.length) { var heartbeatString = keys.reduce(function (prev, current) { return prev + current; }); if (heartbeatString === Tracing._prevHeartbeatString) { Tracing._heartbeatCounter++; } else { Tracing._heartbeatCounter = 0; } if (Tracing._heartbeatCounter >= 3) { if (Tracing._activeTransaction) { utils_1.logger.log("[Tracing] Heartbeat safeguard kicked in, finishing transaction since activities content hasn't changed for 3 beats"); Tracing._activeTransaction.setStatus(types_1.SpanStatus.DeadlineExceeded); Tracing._activeTransaction.setTag('heartbeat', 'failed'); Tracing.finishIdleTransaction(); } } Tracing._prevHeartbeatString = heartbeatString; } Tracing._pingHeartbeat(); }; /** * Discards active transactions if tab moves to background */ Tracing.prototype._setupBackgroundTabDetection = function () { if (Tracing.options.discardBackgroundSpans && global.document) { document.addEventListener('visibilitychange', function () { if (document.hidden && Tracing._activeTransaction) { utils_1.logger.log('[Tracing] Discarded active transaction incl. activities since tab moved to the background'); Tracing._resetActiveTransaction(); } }); } }; /** * Unsets the current active transaction + activities */ Tracing._resetActiveTransaction = function () { Tracing._activeTransaction = undefined; Tracing._activities = {}; }; /** * Registers to History API to detect navigation changes */ Tracing.prototype._setupHistory = function () { if (Tracing.options.startTransactionOnLocationChange) { utils_1.addInstrumentationHandler({ callback: historyCallback, type: 'history', }); } }; /** * Attaches to fetch to add sentry-trace header + creating spans */ Tracing.prototype._setupFetchTracing = function () { if (Tracing.options.traceFetch && utils_1.supportsNativeFetch()) { utils_1.addInstrumentationHandler({ callback: fetchCallback, type: 'fetch', }); } }; /** * Attaches to XHR to add sentry-trace header + creating spans */ Tracing.prototype._setupXHRTracing = function () { if (Tracing.options.traceXHR) { utils_1.addInstrumentationHandler({ callback: xhrCallback, type: 'xhr', }); } }; /** * Configures global error listeners */ Tracing.prototype._setupErrorHandling = function () { // tslint:disable-next-line: completed-docs function errorCallback() { if (Tracing._activeTransaction) { /** * If an error or unhandled promise occurs, we mark the active transaction as failed */ utils_1.logger.log("[Tracing] Global error occured, setting status in transaction: " + types_1.SpanStatus.InternalError); Tracing._activeTransaction.setStatus(types_1.SpanStatus.InternalError); } } utils_1.addInstrumentationHandler({ callback: errorCallback, type: 'error', }); utils_1.addInstrumentationHandler({ callback: errorCallback, type: 'unhandledrejection', }); }; /** * Is tracing enabled */ Tracing._isEnabled = function () { if (Tracing._enabled !== undefined) { return Tracing._enabled; } // This happens only in test cases where the integration isn't initalized properly // tslint:disable-next-line: strict-type-predicates if (!Tracing.options || typeof Tracing.options.tracesSampleRate !== 'number') { return false; } Tracing._enabled = Math.random() > Tracing.options.tracesSampleRate ? false : true; return Tracing._enabled; }; /** * Starts a Transaction waiting for activity idle to finish */ Tracing.startIdleTransaction = function (name, spanContext) { if (!Tracing._isEnabled()) { // Tracing is not enabled return undefined; } // If we already have an active transaction it means one of two things // a) The user did rapid navigation changes and didn't wait until the transaction was finished // b) A activity wasn't popped correctly and therefore the transaction is stalling Tracing.finishIdleTransaction(); utils_1.logger.log('[Tracing] startIdleTransaction, name:', name); var _getCurrentHub = Tracing._getCurrentHub; if (!_getCurrentHub) { return undefined; } var hub = _getCurrentHub(); if (!hub) { return undefined; } var span = hub.startSpan(tslib_1.__assign({}, spanContext, { transaction: name }), true); Tracing._activeTransaction = span; // We need to do this workaround here and not use configureScope // Reason being at the time we start the inital transaction we do not have a client bound on the hub yet // therefore configureScope wouldn't be executed and we would miss setting the transaction // tslint:disable-next-line: no-unsafe-any hub.getScope().setSpan(span); // The reason we do this here is because of cached responses // If we start and transaction without an activity it would never finish since there is no activity var id = Tracing.pushActivity('idleTransactionStarted'); setTimeout(function () { Tracing.popActivity(id); }, (Tracing.options && Tracing.options.idleTimeout) || 100); return span; }; /** * Update transaction * @deprecated */ Tracing.updateTransactionName = function (name) { utils_1.logger.log('[Tracing] DEPRECATED, use Sentry.configureScope => scope.setTransaction instead', name); var _getCurrentHub = Tracing._getCurrentHub; if (_getCurrentHub) { var hub = _getCurrentHub(); if (hub) { hub.configureScope(function (scope) { scope.setTransaction(name); }); } } }; /** * Finshes the current active transaction */ Tracing.finishIdleTransaction = function () { var active = Tracing._activeTransaction; if (active) { Tracing._addPerformanceEntries(active); utils_1.logger.log('[Tracing] finishIdleTransaction', active.transaction); active.finish(/*trimEnd*/ true); Tracing._resetActiveTransaction(); } }; /** * This uses `performance.getEntries()` to add additional spans to the active transaction. * Also, we update our timings since we consider the timings in this API to be more correct than our manual * measurements. * * @param transactionSpan The transaction span */ Tracing._addPerformanceEntries = function (transactionSpan) { if (!global.performance) { // Gatekeeper if performance API not available return; } utils_1.logger.log('[Tracing] Adding & adjusting spans using Performance API'); var timeOrigin = Tracing._msToSec(performance.timeOrigin); // tslint:disable-next-line: completed-docs function addSpan(span) { if (transactionSpan.spanRecorder) { transactionSpan.spanRecorder.finishSpan(span); } } // tslint:disable-next-line: completed-docs function addPerformanceNavigationTiming(parent, entry, event) { var span = parent.child({ description: event, op: 'browser', }); span.startTimestamp = timeOrigin + Tracing._msToSec(entry[event + "Start"]); span.timestamp = timeOrigin + Tracing._msToSec(entry[event + "End"]); addSpan(span); } // tslint:disable-next-line: completed-docs function addRequest(parent, entry) { var request = parent.child({ description: 'request', op: 'browser', }); request.startTimestamp = timeOrigin + Tracing._msToSec(entry.requestStart); request.timestamp = timeOrigin + Tracing._msToSec(entry.responseEnd); addSpan(request); var response = parent.child({ description: 'response', op: 'browser', }); response.startTimestamp = timeOrigin + Tracing._msToSec(entry.responseStart); response.timestamp = timeOrigin + Tracing._msToSec(entry.responseEnd); addSpan(response); } var entryScriptSrc; if (global.document) { // tslint:disable-next-line: prefer-for-of for (var i = 0; i < document.scripts.length; i++) { // We go through all scripts on the page and look for 'data-entry' // We remember the name and measure the time between this script finished loading and // our mark 'sentry-tracing-init' if (document.scripts[i].dataset.entry === 'true') { entryScriptSrc = document.scripts[i].src; break; } } } var entryScriptStartEndTime; var tracingInitMarkStartTime; // tslint:disable: no-unsafe-any performance .getEntries() .slice(Tracing._performanceCursor) .forEach(function (entry) { var startTime = Tracing._msToSec(entry.startTime); var duration = Tracing._msToSec(entry.duration); if (transactionSpan.op === 'navigation' && timeOrigin + startTime < transactionSpan.startTimestamp) { return; } switch (entry.entryType) { case 'navigation': addPerformanceNavigationTiming(transactionSpan, entry, 'unloadEvent'); addPerformanceNavigationTiming(transactionSpan, entry, 'domContentLoadedEvent'); addPerformanceNavigationTiming(transactionSpan, entry, 'loadEvent'); addPerformanceNavigationTiming(transactionSpan, entry, 'connect'); addPerformanceNavigationTiming(transactionSpan, entry, 'domainLookup'); addRequest(transactionSpan, entry); break; case 'mark': case 'paint': case 'measure': var mark = transactionSpan.child({ description: entry.entryType + " " + entry.name, op: 'mark', }); mark.startTimestamp = timeOrigin + startTime; mark.timestamp = mark.startTimestamp + duration; if (tracingInitMarkStartTime === undefined && entry.name === 'sentry-tracing-init') { tracingInitMarkStartTime = mark.startTimestamp; } addSpan(mark); break; case 'resource': var resourceName_1 = entry.name.replace(window.location.origin, ''); if (entry.initiatorType === 'xmlhttprequest' || entry.initiatorType === 'fetch') { // We need to update existing spans with new timing info if (transactionSpan.spanRecorder) { transactionSpan.spanRecorder.finishedSpans.map(function (finishedSpan) { if (finishedSpan.description && finishedSpan.description.indexOf(resourceName_1) !== -1) { finishedSpan.startTimestamp = timeOrigin + startTime; finishedSpan.timestamp = finishedSpan.startTimestamp + duration; } }); } } else { var resource = transactionSpan.child({ description: entry.initiatorType + " " + resourceName_1, op: "resource", }); resource.startTimestamp = timeOrigin + startTime; resource.timestamp = resource.startTimestamp + duration; // We remember the entry script end time to calculate the difference to the first init mark if (entryScriptStartEndTime === undefined && (entryScriptSrc || '').includes(resourceName_1)) { entryScriptStartEndTime = resource.timestamp; } addSpan(resource); } break; default: // Ignore other entry types. } }); if (entryScriptStartEndTime !== undefined && tracingInitMarkStartTime !== undefined) { var evaluation = transactionSpan.child({ description: 'evaluation', op: "script", }); evaluation.startTimestamp = entryScriptStartEndTime; evaluation.timestamp = tracingInitMarkStartTime; addSpan(evaluation); } Tracing._performanceCursor = Math.max(performance.getEntries().length - 1, 0); // tslint:enable: no-unsafe-any }; /** * Sets the status of the current active transaction (if there is one) */ Tracing.setTransactionStatus = function (status) { var active = Tracing._activeTransaction; if (active) { utils_1.logger.log('[Tracing] setTransactionStatus', status); active.setStatus(status); } }; /** * Converts from milliseconds to seconds * @param time time in ms */ Tracing._msToSec = function (time) { return time / 1000; }; /** * Starts tracking for a specifc activity * * @param name Name of the activity, can be any string (Only used internally to identify the activity) * @param spanContext If provided a Span with the SpanContext will be created. * @param options _autoPopAfter_ | Time in ms, if provided the activity will be popped automatically after this timeout. This can be helpful in cases where you cannot gurantee your application knows the state and calls `popActivity` for sure. */ Tracing.pushActivity = function (name, spanContext, options) { if (!Tracing._isEnabled()) { // Tracing is not enabled return 0; } if (!Tracing._activeTransaction) { utils_1.logger.log("[Tracing] Not pushing activity " + name + " since there is no active transaction"); return 0; } // We want to clear the timeout also here since we push a new activity clearTimeout(Tracing._debounce); var _getCurrentHub = Tracing._getCurrentHub; if (spanContext && _getCurrentHub) { var hub = _getCurrentHub(); if (hub) { var span = hub.startSpan(spanContext); Tracing._activities[Tracing._currentIndex] = { name: name, span: span, }; } } else { Tracing._activities[Tracing._currentIndex] = { name: name, }; } utils_1.logger.log("[Tracing] pushActivity: " + name + "#" + Tracing._currentIndex); utils_1.logger.log('[Tracing] activies count', Object.keys(Tracing._activities).length); if (options && typeof options.autoPopAfter === 'number') { utils_1.logger.log("[Tracing] auto pop of: " + name + "#" + Tracing._currentIndex + " in " + options.autoPopAfter + "ms"); var index_1 = Tracing._currentIndex; setTimeout(function () { Tracing.popActivity(index_1, { autoPop: true, status: types_1.SpanStatus.DeadlineExceeded, }); }, options.autoPopAfter); } return Tracing._currentIndex++; }; /** * Removes activity and finishes the span in case there is one */ Tracing.popActivity = function (id, spanData) { // The !id is on purpose to also fail with 0 // Since 0 is returned by push activity in case tracing is not enabled // or there is no active transaction if (!Tracing._isEnabled() || !id) { // Tracing is not enabled return; } var activity = Tracing._activities[id]; if (activity) { utils_1.logger.log("[Tracing] popActivity " + activity.name + "#" + id); var span_1 = activity.span; if (span_1) { if (spanData) { Object.keys(spanData).forEach(function (key) { span_1.setData(key, spanData[key]); if (key === 'status_code') { span_1.setHttpStatus(spanData[key]); } if (key === 'status') { span_1.setStatus(spanData[key]); } }); } span_1.finish(); } // tslint:disable-next-line: no-dynamic-delete delete Tracing._activities[id]; } var count = Object.keys(Tracing._activities).length; clearTimeout(Tracing._debounce); utils_1.logger.log('[Tracing] activies count', count); if (count === 0 && Tracing._activeTransaction) { var timeout = Tracing.options && Tracing.options.idleTimeout; utils_1.logger.log("[Tracing] Flushing Transaction in " + timeout + "ms"); Tracing._debounce = setTimeout(function () { Tracing.finishIdleTransaction(); }, timeout); } }; /** * @inheritDoc */ Tracing.id = 'Tracing'; Tracing._currentIndex = 1; Tracing._activities = {}; Tracing._debounce = 0; Tracing._performanceCursor = 0; Tracing._heartbeatTimer = 0; Tracing._heartbeatCounter = 0; return Tracing; }()); exports.Tracing = Tracing; /** * Creates breadcrumbs from XHR API calls */ function xhrCallback(handlerData) { if (!Tracing.options.traceXHR) { return; } // tslint:disable-next-line: no-unsafe-any if (!handlerData || !handlerData.xhr || !handlerData.xhr.__sentry_xhr__) { return; } // tslint:disable: no-unsafe-any var xhr = handlerData.xhr.__sentry_xhr__; if (!Tracing.options.shouldCreateSpanForRequest(xhr.url)) { return; } // We only capture complete, non-sentry requests if (handlerData.xhr.__sentry_own_request__) { return; } if (handlerData.endTimestamp && handlerData.xhr.__sentry_xhr_activity_id__) { Tracing.popActivity(handlerData.xhr.__sentry_xhr_activity_id__, handlerData.xhr.__sentry_xhr__); return; } handlerData.xhr.__sentry_xhr_activity_id__ = Tracing.pushActivity('xhr', { data: tslib_1.__assign({}, xhr.data, { type: 'xhr' }), description: xhr.method + " " + xhr.url, op: 'http', }); // Adding the trace header to the span var activity = Tracing._activities[handlerData.xhr.__sentry_xhr_activity_id__]; if (activity) { var span = activity.span; if (span && handlerData.xhr.setRequestHeader) { handlerData.xhr.setRequestHeader('sentry-trace', span.toTraceparent()); } } // tslint:enable: no-unsafe-any } /** * Creates breadcrumbs from fetch API calls */ function fetchCallback(handlerData) { // tslint:disable: no-unsafe-any if (!Tracing.options.traceFetch) { return; } if (!Tracing.options.shouldCreateSpanForRequest(handlerData.fetchData.url)) { return; } if (handlerData.endTimestamp && handlerData.fetchData.__activity) { Tracing.popActivity(handlerData.fetchData.__activity, handlerData.fetchData); } else { handlerData.fetchData.__activity = Tracing.pushActivity('fetch', { data: tslib_1.__assign({}, handlerData.fetchData, { type: 'fetch' }), description: handlerData.fetchData.method + " " + handlerData.fetchData.url, op: 'http', }); var activity = Tracing._activities[handlerData.fetchData.__activity]; if (activity) { var span = activity.span; if (span) { var options = (handlerData.args[1] = handlerData.args[1] || {}); if (options.headers) { if (Array.isArray(options.headers)) { options.headers = tslib_1.__spread(options.headers, [{ 'sentry-trace': span.toTraceparent() }]); } else { options.headers = tslib_1.__assign({}, options.headers, { 'sentry-trace': span.toTraceparent() }); } } else { options.headers = { 'sentry-trace': span.toTraceparent() }; } } } } // tslint:enable: no-unsafe-any } /** * Creates transaction from navigation changes */ function historyCallback(_) { if (Tracing.options.startTransactionOnLocationChange && global && global.location) { Tracing.startIdleTransaction(global.location.href, { op: 'navigation', sampled: true, }); } } //# sourceMappingURL=tracing.js.map