@platformos/pos-cli
Version:
Manage your platformOS application
631 lines • 26.4 kB
JavaScript
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