UNPKG

@sentry/core

Version:
284 lines (232 loc) 7.71 kB
Object.defineProperty(exports, '__esModule', { value: true }); const utils = require('@sentry/utils'); const hub = require('../hub.js'); const dynamicSamplingContext = require('./dynamicSamplingContext.js'); const span = require('./span.js'); /** JSDoc */ class Transaction extends span.Span { /** * The reference to the current hub. */ /** * This constructor should never be called manually. Those instrumenting tracing should use * `Sentry.startTransaction()`, and internal methods should use `hub.startTransaction()`. * @internal * @hideconstructor * @hidden */ constructor(transactionContext, hub$1) { super(transactionContext); // We need to delete description since it's set by the Span class constructor // but not needed for transactions. delete this.description; this._measurements = {}; this._contexts = {}; this._hub = hub$1 || hub.getCurrentHub(); this._name = transactionContext.name || ''; this.metadata = { source: 'custom', ...transactionContext.metadata, spanMetadata: {}, }; this._trimEnd = transactionContext.trimEnd; // this is because transactions are also spans, and spans have a transaction pointer this.transaction = this; // If Dynamic Sampling Context is provided during the creation of the transaction, we freeze it as it usually means // there is incoming Dynamic Sampling Context. (Either through an incoming request, a baggage meta-tag, or other means) const incomingDynamicSamplingContext = this.metadata.dynamicSamplingContext; if (incomingDynamicSamplingContext) { // We shallow copy this in case anything writes to the original reference of the passed in `dynamicSamplingContext` this._frozenDynamicSamplingContext = { ...incomingDynamicSamplingContext }; } } /** Getter for `name` property */ get name() { return this._name; } /** Setter for `name` property, which also sets `source` as custom */ set name(newName) { this.setName(newName); } /** * JSDoc */ setName(name, source = 'custom') { this._name = name; this.metadata.source = source; } /** * Attaches SpanRecorder to the span itself * @param maxlen maximum number of spans that can be recorded */ initSpanRecorder(maxlen = 1000) { if (!this.spanRecorder) { this.spanRecorder = new span.SpanRecorder(maxlen); } this.spanRecorder.add(this); } /** * @inheritDoc */ setContext(key, context) { if (context === null) { // eslint-disable-next-line @typescript-eslint/no-dynamic-delete delete this._contexts[key]; } else { this._contexts[key] = context; } } /** * @inheritDoc */ setMeasurement(name, value, unit = '') { this._measurements[name] = { value, unit }; } /** * @inheritDoc */ setMetadata(newMetadata) { this.metadata = { ...this.metadata, ...newMetadata }; } /** * @inheritDoc */ finish(endTimestamp) { const transaction = this._finishTransaction(endTimestamp); if (!transaction) { return undefined; } return this._hub.captureEvent(transaction); } /** * @inheritDoc */ toContext() { const spanContext = super.toContext(); return utils.dropUndefinedKeys({ ...spanContext, name: this.name, trimEnd: this._trimEnd, }); } /** * @inheritDoc */ updateWithContext(transactionContext) { super.updateWithContext(transactionContext); this.name = transactionContext.name || ''; this._trimEnd = transactionContext.trimEnd; return this; } /** * @inheritdoc * * @experimental */ getDynamicSamplingContext() { if (this._frozenDynamicSamplingContext) { return this._frozenDynamicSamplingContext; } const hub$1 = this._hub || hub.getCurrentHub(); const client = hub$1.getClient(); if (!client) return {}; const scope = hub$1.getScope(); const dsc = dynamicSamplingContext.getDynamicSamplingContextFromClient(this.traceId, client, scope); const maybeSampleRate = this.metadata.sampleRate; if (maybeSampleRate !== undefined) { dsc.sample_rate = `${maybeSampleRate}`; } // We don't want to have a transaction name in the DSC if the source is "url" because URLs might contain PII const source = this.metadata.source; if (source && source !== 'url') { dsc.transaction = this.name; } if (this.sampled !== undefined) { dsc.sampled = String(this.sampled); } // Uncomment if we want to make DSC immutable // this._frozenDynamicSamplingContext = dsc; return dsc; } /** * Override the current hub with a new one. * Used if you want another hub to finish the transaction. * * @internal */ setHub(hub) { this._hub = hub; } /** * Finish the transaction & prepare the event to send to Sentry. */ _finishTransaction(endTimestamp) { // This transaction is already finished, so we should not flush it again. if (this.endTimestamp !== undefined) { return undefined; } if (!this.name) { (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.warn('Transaction has no name, falling back to `<unlabeled transaction>`.'); this.name = '<unlabeled transaction>'; } // just sets the end timestamp super.finish(endTimestamp); const client = this._hub.getClient(); if (client && client.emit) { client.emit('finishTransaction', this); } if (this.sampled !== true) { // At this point if `sampled !== true` we want to discard the transaction. (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.log('[Tracing] Discarding transaction because its trace was not chosen to be sampled.'); if (client) { client.recordDroppedEvent('sample_rate', 'transaction'); } return undefined; } const finishedSpans = this.spanRecorder ? this.spanRecorder.spans.filter(s => s !== this && s.endTimestamp) : []; if (this._trimEnd && finishedSpans.length > 0) { this.endTimestamp = finishedSpans.reduce((prev, current) => { if (prev.endTimestamp && current.endTimestamp) { return prev.endTimestamp > current.endTimestamp ? prev : current; } return prev; }).endTimestamp; } const metadata = this.metadata; const transaction = { contexts: { ...this._contexts, // We don't want to override trace context trace: this.getTraceContext(), }, spans: finishedSpans, start_timestamp: this.startTimestamp, tags: this.tags, timestamp: this.endTimestamp, transaction: this.name, type: 'transaction', sdkProcessingMetadata: { ...metadata, dynamicSamplingContext: this.getDynamicSamplingContext(), }, ...(metadata.source && { transaction_info: { source: metadata.source, }, }), }; const hasMeasurements = Object.keys(this._measurements).length > 0; if (hasMeasurements) { (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.log( '[Measurements] Adding measurements to transaction', JSON.stringify(this._measurements, undefined, 2), ); transaction.measurements = this._measurements; } (typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__) && utils.logger.log(`[Tracing] Finishing ${this.op} transaction: ${this.name}.`); return transaction; } } exports.Transaction = Transaction; //# sourceMappingURL=transaction.js.map