utils.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. import type {Location, LocationDescriptorObject} from 'history';
  2. import {PAGE_URL_PARAM} from 'sentry/constants/pageFilters';
  3. import type {DateString} from 'sentry/types/core';
  4. import type {Organization} from 'sentry/types/organization';
  5. import {getTimeStampFromTableDateField} from 'sentry/utils/dates';
  6. import type {
  7. EventLite,
  8. TraceError,
  9. TraceFull,
  10. TraceFullDetailed,
  11. TraceSplitResults,
  12. } from 'sentry/utils/performance/quickTrace/types';
  13. import {isTraceSplitResult, reduceTrace} from 'sentry/utils/performance/quickTrace/utils';
  14. import normalizeUrl from 'sentry/utils/url/normalizeUrl';
  15. import type {DomainView} from 'sentry/views/insights/pages/useFilters';
  16. import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree';
  17. import {getPerformanceBaseUrl} from 'sentry/views/performance/utils';
  18. import {DEFAULT_TRACE_ROWS_LIMIT} from './limitExceededMessage';
  19. import type {TraceInfo} from './types';
  20. export function getTraceDetailsUrl({
  21. organization,
  22. traceSlug,
  23. dateSelection,
  24. timestamp,
  25. spanId,
  26. eventId,
  27. targetId,
  28. demo,
  29. location,
  30. source,
  31. view,
  32. }: {
  33. // @TODO add a type for dateSelection
  34. dateSelection;
  35. location: Location;
  36. organization: Organization;
  37. traceSlug: string;
  38. demo?: string;
  39. eventId?: string;
  40. source?: string;
  41. spanId?: string;
  42. // targetId represents the span id of the transaction. It will replace eventId once all links
  43. // to trace view are updated to use spand ids of transactions instead of event ids.
  44. targetId?: string;
  45. timestamp?: string | number;
  46. view?: DomainView;
  47. }): LocationDescriptorObject {
  48. const performanceBaseUrl = getPerformanceBaseUrl(organization.slug, view);
  49. const queryParams: Record<string, string | number | undefined | DateString | string[]> =
  50. {
  51. ...location.query,
  52. statsPeriod: dateSelection.statsPeriod,
  53. [PAGE_URL_PARAM.PAGE_START]: dateSelection.start,
  54. [PAGE_URL_PARAM.PAGE_END]: dateSelection.end,
  55. };
  56. if (shouldForceRouteToOldView(organization, timestamp)) {
  57. return {
  58. pathname: normalizeUrl(`${performanceBaseUrl}/trace/${traceSlug}/`),
  59. query: queryParams,
  60. };
  61. }
  62. if (organization.features.includes('trace-view-v1')) {
  63. if (spanId) {
  64. const path: TraceTree.NodePath[] = [`span-${spanId}`, `txn-${targetId ?? eventId}`];
  65. queryParams.node = path;
  66. }
  67. return {
  68. pathname: normalizeUrl(`${performanceBaseUrl}/trace/${traceSlug}/`),
  69. query: {
  70. ...queryParams,
  71. timestamp: getTimeStampFromTableDateField(timestamp),
  72. eventId,
  73. targetId,
  74. demo,
  75. source,
  76. },
  77. };
  78. }
  79. if (organization.features.includes('trace-view-load-more')) {
  80. queryParams.limit = DEFAULT_TRACE_ROWS_LIMIT;
  81. }
  82. return {
  83. pathname: normalizeUrl(`${performanceBaseUrl}/trace/${traceSlug}/`),
  84. query: queryParams,
  85. };
  86. }
  87. /**
  88. * Single tenant, on-premise etc. users may not have span extraction enabled.
  89. *
  90. * This code can be removed at the time we're sure all STs have rolled out span extraction.
  91. */
  92. export function shouldForceRouteToOldView(
  93. organization: Organization,
  94. timestamp: string | number | undefined
  95. ) {
  96. const usableTimestamp = getTimeStampFromTableDateField(timestamp);
  97. if (!usableTimestamp) {
  98. // Timestamps must always be provided for the new view, if it doesn't exist, fall back to the old view.
  99. return true;
  100. }
  101. return (
  102. organization.extraOptions?.traces.checkSpanExtractionDate &&
  103. organization.extraOptions?.traces.spansExtractionDate > usableTimestamp
  104. );
  105. }
  106. function transactionVisitor() {
  107. return (accumulator: TraceInfo, event: TraceFullDetailed) => {
  108. for (const error of event.errors ?? []) {
  109. accumulator.errors.add(error.event_id);
  110. }
  111. for (const performanceIssue of event.performance_issues ?? []) {
  112. accumulator.performanceIssues.add(performanceIssue.event_id);
  113. }
  114. accumulator.transactions.add(event.event_id);
  115. accumulator.projects.add(event.project_slug);
  116. accumulator.startTimestamp = Math.min(
  117. accumulator.startTimestamp,
  118. event.start_timestamp
  119. );
  120. accumulator.endTimestamp = Math.max(accumulator.endTimestamp, event.timestamp);
  121. accumulator.maxGeneration = Math.max(accumulator.maxGeneration, event.generation);
  122. return accumulator;
  123. };
  124. }
  125. export function hasTraceData(
  126. traces: TraceFullDetailed[] | null | undefined,
  127. orphanErrors: TraceError[] | undefined
  128. ): boolean {
  129. return Boolean(
  130. (traces && traces.length > 0) || (orphanErrors && orphanErrors.length > 0)
  131. );
  132. }
  133. export function getTraceSplitResults<U extends TraceFullDetailed | TraceFull | EventLite>(
  134. trace: TraceSplitResults<U> | U[],
  135. organization: Organization
  136. ) {
  137. let transactions: U[] | undefined;
  138. let orphanErrors: TraceError[] | undefined;
  139. if (
  140. trace &&
  141. organization.features.includes('performance-tracing-without-performance') &&
  142. isTraceSplitResult<TraceSplitResults<U>, U[]>(trace)
  143. ) {
  144. orphanErrors = trace.orphan_errors;
  145. transactions = trace.transactions;
  146. }
  147. return {transactions, orphanErrors};
  148. }
  149. export function getTraceInfo(
  150. traces: TraceFullDetailed[] = [],
  151. orphanErrors: TraceError[] = []
  152. ) {
  153. const initial = {
  154. projects: new Set<string>(),
  155. errors: new Set<string>(),
  156. performanceIssues: new Set<string>(),
  157. transactions: new Set<string>(),
  158. startTimestamp: Number.MAX_SAFE_INTEGER,
  159. endTimestamp: 0,
  160. maxGeneration: 0,
  161. trailingOrphansCount: 0,
  162. };
  163. const transactionsInfo = traces.reduce(
  164. (info: TraceInfo, trace: TraceFullDetailed) =>
  165. reduceTrace<TraceInfo>(trace, transactionVisitor(), info),
  166. initial
  167. );
  168. // Accumulate orphan error information.
  169. return orphanErrors.reduce((accumulator: TraceInfo, event: TraceError) => {
  170. accumulator.errors.add(event.event_id);
  171. accumulator.trailingOrphansCount++;
  172. if (event.timestamp) {
  173. accumulator.startTimestamp = Math.min(accumulator.startTimestamp, event.timestamp);
  174. accumulator.endTimestamp = Math.max(accumulator.endTimestamp, event.timestamp);
  175. }
  176. return accumulator;
  177. }, transactionsInfo);
  178. }
  179. export function shortenErrorTitle(title: string): string {
  180. return title.split(':')[0];
  181. }
  182. export function isRootTransaction(trace: TraceFullDetailed): boolean {
  183. // Root transactions has no parent_span_id
  184. return trace.parent_span_id === null;
  185. }