utils.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import type {Location, LocationDescriptorObject} from 'history';
  2. import {PAGE_URL_PARAM} from 'sentry/constants/pageFilters';
  3. import type {Organization, OrganizationSummary} from 'sentry/types';
  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/withDomainRequired';
  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. demo,
  24. location,
  25. source,
  26. }: {
  27. dateSelection;
  28. location: Location;
  29. organization: Pick<OrganizationSummary, 'slug' | 'features'>;
  30. traceSlug: string;
  31. demo?: string;
  32. eventId?: string;
  33. source?: string;
  34. spanId?: string;
  35. timestamp?: string | number;
  36. }): LocationDescriptorObject {
  37. const {start, end, statsPeriod} = dateSelection;
  38. const queryParams = {
  39. ...location.query,
  40. statsPeriod,
  41. [PAGE_URL_PARAM.PAGE_START]: start,
  42. [PAGE_URL_PARAM.PAGE_END]: end,
  43. };
  44. if (organization.features.includes('trace-view-v1')) {
  45. if (spanId) {
  46. queryParams.node = [`span-${spanId}`, `txn-${eventId}`];
  47. }
  48. return {
  49. pathname: normalizeUrl(
  50. `/organizations/${organization.slug}/performance/trace/${traceSlug}/`
  51. ),
  52. query: {
  53. ...queryParams,
  54. timestamp: getTimeStampFromTableDateField(timestamp),
  55. eventId,
  56. demo,
  57. source,
  58. },
  59. };
  60. }
  61. if (organization.features.includes('trace-view-load-more')) {
  62. queryParams.limit = DEFAULT_TRACE_ROWS_LIMIT;
  63. }
  64. return {
  65. pathname: normalizeUrl(
  66. `/organizations/${organization.slug}/performance/trace/${traceSlug}/`
  67. ),
  68. query: queryParams,
  69. };
  70. }
  71. function transactionVisitor() {
  72. return (accumulator: TraceInfo, event: TraceFullDetailed) => {
  73. for (const error of event.errors ?? []) {
  74. accumulator.errors.add(error.event_id);
  75. }
  76. for (const performanceIssue of event.performance_issues ?? []) {
  77. accumulator.performanceIssues.add(performanceIssue.event_id);
  78. }
  79. accumulator.transactions.add(event.event_id);
  80. accumulator.projects.add(event.project_slug);
  81. accumulator.startTimestamp = Math.min(
  82. accumulator.startTimestamp,
  83. event.start_timestamp
  84. );
  85. accumulator.endTimestamp = Math.max(accumulator.endTimestamp, event.timestamp);
  86. accumulator.maxGeneration = Math.max(accumulator.maxGeneration, event.generation);
  87. return accumulator;
  88. };
  89. }
  90. export function hasTraceData(
  91. traces: TraceFullDetailed[] | null | undefined,
  92. orphanErrors: TraceError[] | undefined
  93. ): boolean {
  94. return Boolean(
  95. (traces && traces.length > 0) || (orphanErrors && orphanErrors.length > 0)
  96. );
  97. }
  98. export function getTraceSplitResults<U extends TraceFullDetailed | TraceFull | EventLite>(
  99. trace: TraceSplitResults<U> | U[],
  100. organization: Organization
  101. ) {
  102. let transactions: U[] | undefined;
  103. let orphanErrors: TraceError[] | undefined;
  104. if (
  105. trace &&
  106. organization.features.includes('performance-tracing-without-performance') &&
  107. isTraceSplitResult<TraceSplitResults<U>, U[]>(trace)
  108. ) {
  109. orphanErrors = trace.orphan_errors;
  110. transactions = trace.transactions;
  111. }
  112. return {transactions, orphanErrors};
  113. }
  114. export function getTraceInfo(
  115. traces: TraceFullDetailed[] = [],
  116. orphanErrors: TraceError[] = []
  117. ) {
  118. const initial = {
  119. projects: new Set<string>(),
  120. errors: new Set<string>(),
  121. performanceIssues: new Set<string>(),
  122. transactions: new Set<string>(),
  123. startTimestamp: Number.MAX_SAFE_INTEGER,
  124. endTimestamp: 0,
  125. maxGeneration: 0,
  126. trailingOrphansCount: 0,
  127. };
  128. const transactionsInfo = traces.reduce(
  129. (info: TraceInfo, trace: TraceFullDetailed) =>
  130. reduceTrace<TraceInfo>(trace, transactionVisitor(), info),
  131. initial
  132. );
  133. // Accumulate orphan error information.
  134. return orphanErrors.reduce((accumulator: TraceInfo, event: TraceError) => {
  135. accumulator.errors.add(event.event_id);
  136. accumulator.trailingOrphansCount++;
  137. if (event.timestamp) {
  138. accumulator.startTimestamp = Math.min(accumulator.startTimestamp, event.timestamp);
  139. accumulator.endTimestamp = Math.max(accumulator.endTimestamp, event.timestamp);
  140. }
  141. return accumulator;
  142. }, transactionsInfo);
  143. }
  144. export function shortenErrorTitle(title: string): string {
  145. return title.split(':')[0];
  146. }
  147. export function isRootTransaction(trace: TraceFullDetailed): boolean {
  148. // Root transactions has no parent_span_id
  149. return trace.parent_span_id === null;
  150. }