traceView.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import {memo, useEffect, useRef, 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/organization';
  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 = useRef<HTMLDivElement>(null);
  26. const traceViewHeaderRef = useRef<HTMLDivElement>(null);
  27. const virtualScrollBarContainerRef = useRef<HTMLDivElement>(null);
  28. const minimapInteractiveRef = useRef<HTMLDivElement>(null);
  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. isEmbedded={!!props.isEmbedded}
  63. />
  64. );
  65. }}
  66. </Observer>
  67. );
  68. const {organization, waterfallModel, isEmbedded, performanceIssues} = props;
  69. if (!getTraceContext(waterfallModel.event)) {
  70. return (
  71. <EmptyStateWarning>
  72. <p>{t('There is no trace for this transaction')}</p>
  73. </EmptyStateWarning>
  74. );
  75. }
  76. if (
  77. (!waterfallModel.affectedSpanIds || !waterfallModel.affectedSpanIds.length) &&
  78. performanceIssues
  79. ) {
  80. const suspectSpans = performanceIssues.flatMap(issue => issue.suspect_spans);
  81. if (suspectSpans.length) {
  82. waterfallModel.affectedSpanIds = performanceIssues.flatMap(issue => [
  83. ...issue.suspect_spans,
  84. ...issue.span,
  85. ]);
  86. }
  87. }
  88. return (
  89. <SpanContext.Provider>
  90. <SpanContext.Consumer>
  91. {spanContextProps => (
  92. <DragManager interactiveLayerRef={minimapInteractiveRef}>
  93. {(dragProps: DragManagerChildrenProps) => (
  94. <Observer>
  95. {() => {
  96. const parsedTrace = waterfallModel.parsedTrace;
  97. return (
  98. <CursorGuideHandler.Provider
  99. interactiveLayerRef={minimapInteractiveRef}
  100. dragProps={dragProps}
  101. trace={parsedTrace}
  102. >
  103. <DividerHandlerManager.Provider interactiveLayerRef={traceViewRef}>
  104. <DividerHandlerManager.Consumer>
  105. {dividerHandlerChildrenProps => {
  106. return (
  107. <ScrollbarManager.Provider
  108. dividerPosition={
  109. dividerHandlerChildrenProps.dividerPosition
  110. }
  111. interactiveLayerRef={virtualScrollBarContainerRef}
  112. dragProps={dragProps}
  113. isEmbedded={isEmbedded}
  114. >
  115. {renderHeader(dragProps)}
  116. <Observer>
  117. {() => (
  118. <SpanTree
  119. traceViewRef={traceViewRef}
  120. traceViewHeaderRef={traceViewHeaderRef}
  121. dragProps={dragProps}
  122. organization={organization}
  123. waterfallModel={waterfallModel}
  124. filterSpans={waterfallModel.filterSpans}
  125. spans={waterfallModel.getWaterfall({
  126. viewStart: dragProps.viewWindowStart,
  127. viewEnd: dragProps.viewWindowEnd,
  128. })}
  129. focusedSpanIds={waterfallModel.focusedSpanIds}
  130. spanContextProps={spanContextProps}
  131. operationNameFilters={
  132. waterfallModel.operationNameFilters
  133. }
  134. isEmbedded={!!isEmbedded}
  135. />
  136. )}
  137. </Observer>
  138. </ScrollbarManager.Provider>
  139. );
  140. }}
  141. </DividerHandlerManager.Consumer>
  142. </DividerHandlerManager.Provider>
  143. </CursorGuideHandler.Provider>
  144. );
  145. }}
  146. </Observer>
  147. )}
  148. </DragManager>
  149. )}
  150. </SpanContext.Consumer>
  151. </SpanContext.Provider>
  152. );
  153. }
  154. export default memo(TraceView);