utils.tsx 4.4 KB

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