utils.tsx 5.7 KB

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