utils.tsx 4.4 KB

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