utils.tsx 5.8 KB

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