utils.tsx 4.5 KB

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