utils.tsx 4.5 KB

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