transaction.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. Object.defineProperty(exports, '__esModule', { value: true });
  2. const utils = require('@sentry/utils');
  3. const debugBuild = require('../debug-build.js');
  4. const hub = require('../hub.js');
  5. const metricSummary = require('../metrics/metric-summary.js');
  6. const semanticAttributes = require('../semanticAttributes.js');
  7. const spanUtils = require('../utils/spanUtils.js');
  8. const dynamicSamplingContext = require('./dynamicSamplingContext.js');
  9. const span = require('./span.js');
  10. const trace = require('./trace.js');
  11. /** JSDoc */
  12. class Transaction extends span.Span {
  13. /**
  14. * The reference to the current hub.
  15. */
  16. // DO NOT yet remove this property, it is used in a hack for v7 backwards compatibility.
  17. /**
  18. * This constructor should never be called manually. Those instrumenting tracing should use
  19. * `Sentry.startTransaction()`, and internal methods should use `hub.startTransaction()`.
  20. * @internal
  21. * @hideconstructor
  22. * @hidden
  23. *
  24. * @deprecated Transactions will be removed in v8. Use spans instead.
  25. */
  26. constructor(transactionContext, hub$1) {
  27. super(transactionContext);
  28. this._measurements = {};
  29. this._contexts = {};
  30. // eslint-disable-next-line deprecation/deprecation
  31. this._hub = hub$1 || hub.getCurrentHub();
  32. this._name = transactionContext.name || '';
  33. this._metadata = {
  34. // eslint-disable-next-line deprecation/deprecation
  35. ...transactionContext.metadata,
  36. };
  37. this._trimEnd = transactionContext.trimEnd;
  38. // this is because transactions are also spans, and spans have a transaction pointer
  39. // TODO (v8): Replace this with another way to set the root span
  40. // eslint-disable-next-line deprecation/deprecation
  41. this.transaction = this;
  42. // If Dynamic Sampling Context is provided during the creation of the transaction, we freeze it as it usually means
  43. // there is incoming Dynamic Sampling Context. (Either through an incoming request, a baggage meta-tag, or other means)
  44. const incomingDynamicSamplingContext = this._metadata.dynamicSamplingContext;
  45. if (incomingDynamicSamplingContext) {
  46. // We shallow copy this in case anything writes to the original reference of the passed in `dynamicSamplingContext`
  47. this._frozenDynamicSamplingContext = { ...incomingDynamicSamplingContext };
  48. }
  49. }
  50. // This sadly conflicts with the getter/setter ordering :(
  51. /* eslint-disable @typescript-eslint/member-ordering */
  52. /**
  53. * Getter for `name` property.
  54. * @deprecated Use `spanToJSON(span).description` instead.
  55. */
  56. get name() {
  57. return this._name;
  58. }
  59. /**
  60. * Setter for `name` property, which also sets `source` as custom.
  61. * @deprecated Use `updateName()` and `setMetadata()` instead.
  62. */
  63. set name(newName) {
  64. // eslint-disable-next-line deprecation/deprecation
  65. this.setName(newName);
  66. }
  67. /**
  68. * Get the metadata for this transaction.
  69. * @deprecated Use `spanGetMetadata(transaction)` instead.
  70. */
  71. get metadata() {
  72. // We merge attributes in for backwards compatibility
  73. return {
  74. // Defaults
  75. // eslint-disable-next-line deprecation/deprecation
  76. source: 'custom',
  77. spanMetadata: {},
  78. // Legacy metadata
  79. ...this._metadata,
  80. // From attributes
  81. ...(this._attributes[semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] && {
  82. source: this._attributes[semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] ,
  83. }),
  84. ...(this._attributes[semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE] && {
  85. sampleRate: this._attributes[semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE] ,
  86. }),
  87. };
  88. }
  89. /**
  90. * Update the metadata for this transaction.
  91. * @deprecated Use `spanGetMetadata(transaction)` instead.
  92. */
  93. set metadata(metadata) {
  94. this._metadata = metadata;
  95. }
  96. /* eslint-enable @typescript-eslint/member-ordering */
  97. /**
  98. * Setter for `name` property, which also sets `source` on the metadata.
  99. *
  100. * @deprecated Use `.updateName()` and `.setAttribute()` instead.
  101. */
  102. setName(name, source = 'custom') {
  103. this._name = name;
  104. this.setAttribute(semanticAttributes.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source);
  105. }
  106. /** @inheritdoc */
  107. updateName(name) {
  108. this._name = name;
  109. return this;
  110. }
  111. /**
  112. * Attaches SpanRecorder to the span itself
  113. * @param maxlen maximum number of spans that can be recorded
  114. */
  115. initSpanRecorder(maxlen = 1000) {
  116. // eslint-disable-next-line deprecation/deprecation
  117. if (!this.spanRecorder) {
  118. // eslint-disable-next-line deprecation/deprecation
  119. this.spanRecorder = new span.SpanRecorder(maxlen);
  120. }
  121. // eslint-disable-next-line deprecation/deprecation
  122. this.spanRecorder.add(this);
  123. }
  124. /**
  125. * Set the context of a transaction event.
  126. * @deprecated Use either `.setAttribute()`, or set the context on the scope before creating the transaction.
  127. */
  128. setContext(key, context) {
  129. if (context === null) {
  130. // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
  131. delete this._contexts[key];
  132. } else {
  133. this._contexts[key] = context;
  134. }
  135. }
  136. /**
  137. * @inheritDoc
  138. *
  139. * @deprecated Use top-level `setMeasurement()` instead.
  140. */
  141. setMeasurement(name, value, unit = '') {
  142. this._measurements[name] = { value, unit };
  143. }
  144. /**
  145. * Store metadata on this transaction.
  146. * @deprecated Use attributes or store data on the scope instead.
  147. */
  148. setMetadata(newMetadata) {
  149. this._metadata = { ...this._metadata, ...newMetadata };
  150. }
  151. /**
  152. * @inheritDoc
  153. */
  154. end(endTimestamp) {
  155. const timestampInS = spanUtils.spanTimeInputToSeconds(endTimestamp);
  156. const transaction = this._finishTransaction(timestampInS);
  157. if (!transaction) {
  158. return undefined;
  159. }
  160. // eslint-disable-next-line deprecation/deprecation
  161. return this._hub.captureEvent(transaction);
  162. }
  163. /**
  164. * @inheritDoc
  165. */
  166. toContext() {
  167. // eslint-disable-next-line deprecation/deprecation
  168. const spanContext = super.toContext();
  169. return utils.dropUndefinedKeys({
  170. ...spanContext,
  171. name: this._name,
  172. trimEnd: this._trimEnd,
  173. });
  174. }
  175. /**
  176. * @inheritDoc
  177. */
  178. updateWithContext(transactionContext) {
  179. // eslint-disable-next-line deprecation/deprecation
  180. super.updateWithContext(transactionContext);
  181. this._name = transactionContext.name || '';
  182. this._trimEnd = transactionContext.trimEnd;
  183. return this;
  184. }
  185. /**
  186. * @inheritdoc
  187. *
  188. * @experimental
  189. *
  190. * @deprecated Use top-level `getDynamicSamplingContextFromSpan` instead.
  191. */
  192. getDynamicSamplingContext() {
  193. return dynamicSamplingContext.getDynamicSamplingContextFromSpan(this);
  194. }
  195. /**
  196. * Override the current hub with a new one.
  197. * Used if you want another hub to finish the transaction.
  198. *
  199. * @internal
  200. */
  201. setHub(hub) {
  202. this._hub = hub;
  203. }
  204. /**
  205. * Finish the transaction & prepare the event to send to Sentry.
  206. */
  207. _finishTransaction(endTimestamp) {
  208. // This transaction is already finished, so we should not flush it again.
  209. if (this._endTime !== undefined) {
  210. return undefined;
  211. }
  212. if (!this._name) {
  213. debugBuild.DEBUG_BUILD && utils.logger.warn('Transaction has no name, falling back to `<unlabeled transaction>`.');
  214. this._name = '<unlabeled transaction>';
  215. }
  216. // just sets the end timestamp
  217. super.end(endTimestamp);
  218. // eslint-disable-next-line deprecation/deprecation
  219. const client = this._hub.getClient();
  220. if (client && client.emit) {
  221. client.emit('finishTransaction', this);
  222. }
  223. if (this._sampled !== true) {
  224. // At this point if `sampled !== true` we want to discard the transaction.
  225. debugBuild.DEBUG_BUILD && utils.logger.log('[Tracing] Discarding transaction because its trace was not chosen to be sampled.');
  226. if (client) {
  227. client.recordDroppedEvent('sample_rate', 'transaction');
  228. }
  229. return undefined;
  230. }
  231. // eslint-disable-next-line deprecation/deprecation
  232. const finishedSpans = this.spanRecorder
  233. ? // eslint-disable-next-line deprecation/deprecation
  234. this.spanRecorder.spans.filter(span => span !== this && spanUtils.spanToJSON(span).timestamp)
  235. : [];
  236. if (this._trimEnd && finishedSpans.length > 0) {
  237. const endTimes = finishedSpans.map(span => spanUtils.spanToJSON(span).timestamp).filter(Boolean) ;
  238. this._endTime = endTimes.reduce((prev, current) => {
  239. return prev > current ? prev : current;
  240. });
  241. }
  242. const { scope: capturedSpanScope, isolationScope: capturedSpanIsolationScope } = trace.getCapturedScopesOnSpan(this);
  243. // eslint-disable-next-line deprecation/deprecation
  244. const { metadata } = this;
  245. // eslint-disable-next-line deprecation/deprecation
  246. const { source } = metadata;
  247. const transaction = {
  248. contexts: {
  249. ...this._contexts,
  250. // We don't want to override trace context
  251. trace: spanUtils.spanToTraceContext(this),
  252. },
  253. // TODO: Pass spans serialized via `spanToJSON()` here instead in v8.
  254. spans: finishedSpans,
  255. start_timestamp: this._startTime,
  256. // eslint-disable-next-line deprecation/deprecation
  257. tags: this.tags,
  258. timestamp: this._endTime,
  259. transaction: this._name,
  260. type: 'transaction',
  261. sdkProcessingMetadata: {
  262. ...metadata,
  263. capturedSpanScope,
  264. capturedSpanIsolationScope,
  265. dynamicSamplingContext: dynamicSamplingContext.getDynamicSamplingContextFromSpan(this),
  266. },
  267. _metrics_summary: metricSummary.getMetricSummaryJsonForSpan(this),
  268. ...(source && {
  269. transaction_info: {
  270. source,
  271. },
  272. }),
  273. };
  274. const hasMeasurements = Object.keys(this._measurements).length > 0;
  275. if (hasMeasurements) {
  276. debugBuild.DEBUG_BUILD &&
  277. utils.logger.log(
  278. '[Measurements] Adding measurements to transaction',
  279. JSON.stringify(this._measurements, undefined, 2),
  280. );
  281. transaction.measurements = this._measurements;
  282. }
  283. // eslint-disable-next-line deprecation/deprecation
  284. debugBuild.DEBUG_BUILD && utils.logger.log(`[Tracing] Finishing ${this.op} transaction: ${this._name}.`);
  285. return transaction;
  286. }
  287. }
  288. exports.Transaction = Transaction;
  289. //# sourceMappingURL=transaction.js.map