traceView.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import {createRef, memo, useEffect, useState} from 'react';
  2. import {Observer} from 'mobx-react';
  3. import EmptyStateWarning from 'sentry/components/emptyStateWarning';
  4. import {t} from 'sentry/locale';
  5. import type {Organization} from 'sentry/types';
  6. import type {TracePerformanceIssue} from 'sentry/utils/performance/quickTrace/types';
  7. import * as CursorGuideHandler from './cursorGuideHandler';
  8. import * as DividerHandlerManager from './dividerHandlerManager';
  9. import type {DragManagerChildrenProps} from './dragManager';
  10. import DragManager from './dragManager';
  11. import TraceViewHeader from './header';
  12. import * as ScrollbarManager from './scrollbarManager';
  13. import * as SpanContext from './spanContext';
  14. import SpanTree from './spanTree';
  15. import {getTraceContext} from './utils';
  16. import type WaterfallModel from './waterfallModel';
  17. type Props = {
  18. organization: Organization;
  19. waterfallModel: WaterfallModel;
  20. isAggregate?: boolean;
  21. isEmbedded?: boolean;
  22. performanceIssues?: TracePerformanceIssue[];
  23. };
  24. function TraceView(props: Props) {
  25. const traceViewRef = createRef<HTMLDivElement>();
  26. const traceViewHeaderRef = createRef<HTMLDivElement>();
  27. const virtualScrollBarContainerRef = createRef<HTMLDivElement>();
  28. const minimapInteractiveRef = createRef<HTMLDivElement>();
  29. const [isMounted, setIsMounted] = useState(false);
  30. // Since this component is memoized, we need this hook here.
  31. // renderHeader performs some expensive calculations and so we only want to render once, hence why we memoize.
  32. // However, the virtualScrollbar will not be visible unless we have this effect here. This is a bit of a hack that will
  33. // cause the component to rerender by setting the isMounted state, so we will re-render only once the scrollbar ref is present.
  34. useEffect(() => {
  35. if (virtualScrollBarContainerRef.current && !isMounted) {
  36. setIsMounted(true);
  37. }
  38. }, [virtualScrollBarContainerRef, isMounted]);
  39. const renderHeader = (dragProps: DragManagerChildrenProps) => (
  40. <Observer>
  41. {() => {
  42. const {waterfallModel} = props;
  43. return (
  44. <TraceViewHeader
  45. traceViewHeaderRef={traceViewHeaderRef}
  46. organization={props.organization}
  47. minimapInteractiveRef={minimapInteractiveRef}
  48. dragProps={dragProps}
  49. trace={waterfallModel.parsedTrace}
  50. event={waterfallModel.event}
  51. virtualScrollBarContainerRef={virtualScrollBarContainerRef}
  52. operationNameFilters={waterfallModel.operationNameFilters}
  53. rootSpan={waterfallModel.rootSpan.span}
  54. spans={waterfallModel.getWaterfall({
  55. viewStart: 0,
  56. viewEnd: 1,
  57. })}
  58. generateBounds={waterfallModel.generateBounds({
  59. viewStart: 0,
  60. viewEnd: 1,
  61. })}
  62. />
  63. );
  64. }}
  65. </Observer>
  66. );
  67. const {organization, waterfallModel, isEmbedded, performanceIssues} = props;
  68. if (!getTraceContext(waterfallModel.event)) {
  69. return (
  70. <EmptyStateWarning>
  71. <p>{t('There is no trace for this transaction')}</p>
  72. </EmptyStateWarning>
  73. );
  74. }
  75. if (
  76. (!waterfallModel.affectedSpanIds || !waterfallModel.affectedSpanIds.length) &&
  77. performanceIssues
  78. ) {
  79. const suspectSpans = performanceIssues.flatMap(issue => issue.suspect_spans);
  80. if (suspectSpans.length) {
  81. waterfallModel.affectedSpanIds = performanceIssues.flatMap(issue => [
  82. ...issue.suspect_spans,
  83. ...issue.span,
  84. ]);
  85. }
  86. }
  87. return (
  88. <SpanContext.Provider>
  89. <SpanContext.Consumer>
  90. {spanContextProps => (
  91. <DragManager interactiveLayerRef={minimapInteractiveRef}>
  92. {(dragProps: DragManagerChildrenProps) => (
  93. <Observer>
  94. {() => {
  95. const parsedTrace = waterfallModel.parsedTrace;
  96. return (
  97. <CursorGuideHandler.Provider
  98. interactiveLayerRef={minimapInteractiveRef}
  99. dragProps={dragProps}
  100. trace={parsedTrace}
  101. >
  102. <DividerHandlerManager.Provider interactiveLayerRef={traceViewRef}>
  103. <DividerHandlerManager.Consumer>
  104. {dividerHandlerChildrenProps => {
  105. return (
  106. <ScrollbarManager.Provider
  107. dividerPosition={
  108. dividerHandlerChildrenProps.dividerPosition
  109. }
  110. interactiveLayerRef={virtualScrollBarContainerRef}
  111. dragProps={dragProps}
  112. isEmbedded={isEmbedded}
  113. >
  114. {renderHeader(dragProps)}
  115. <Observer>
  116. {() => (
  117. <SpanTree
  118. traceViewRef={traceViewRef}
  119. traceViewHeaderRef={traceViewHeaderRef}
  120. dragProps={dragProps}
  121. organization={organization}
  122. waterfallModel={waterfallModel}
  123. filterSpans={waterfallModel.filterSpans}
  124. spans={waterfallModel.getWaterfall({
  125. viewStart: dragProps.viewWindowStart,
  126. viewEnd: dragProps.viewWindowEnd,
  127. })}
  128. focusedSpanIds={waterfallModel.focusedSpanIds}
  129. spanContextProps={spanContextProps}
  130. operationNameFilters={
  131. waterfallModel.operationNameFilters
  132. }
  133. />
  134. )}
  135. </Observer>
  136. </ScrollbarManager.Provider>
  137. );
  138. }}
  139. </DividerHandlerManager.Consumer>
  140. </DividerHandlerManager.Provider>
  141. </CursorGuideHandler.Provider>
  142. );
  143. }}
  144. </Observer>
  145. )}
  146. </DragManager>
  147. )}
  148. </SpanContext.Consumer>
  149. </SpanContext.Provider>
  150. );
  151. }
  152. export default memo(TraceView);