spanTree.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import {Component, createRef, Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import * as DividerHandlerManager from 'app/components/events/interfaces/spans/dividerHandlerManager';
  4. import {
  5. OrphanTreeDepth,
  6. TreeDepthType,
  7. } from 'app/components/events/interfaces/spans/types';
  8. import {EventTransaction} from 'app/types/event';
  9. import SpanGroup from './spanGroup';
  10. import {
  11. boundsGenerator,
  12. DiffSpanType,
  13. diffTransactions,
  14. getSpanID,
  15. isOrphanDiffSpan,
  16. SpanChildrenLookupType,
  17. SpanGeneratedBoundsType,
  18. } from './utils';
  19. type RenderedSpanTree = {
  20. spanTree: JSX.Element | null;
  21. nextSpanNumber: number;
  22. // numOfSpansOutOfViewAbove: number;
  23. // numOfFilteredSpansAbove: number;
  24. };
  25. type Props = {
  26. baselineEvent: EventTransaction;
  27. regressionEvent: EventTransaction;
  28. };
  29. class SpanTree extends Component<Props> {
  30. traceViewRef = createRef<HTMLDivElement>();
  31. renderSpan({
  32. span,
  33. childSpans,
  34. spanNumber,
  35. treeDepth,
  36. continuingTreeDepths,
  37. isLast,
  38. isRoot,
  39. generateBounds,
  40. }: {
  41. span: Readonly<DiffSpanType>;
  42. childSpans: SpanChildrenLookupType;
  43. spanNumber: number;
  44. treeDepth: number;
  45. continuingTreeDepths: Array<TreeDepthType>;
  46. isLast: boolean;
  47. isRoot: boolean;
  48. generateBounds: (span: DiffSpanType) => SpanGeneratedBoundsType;
  49. }): RenderedSpanTree {
  50. const spanChildren: Array<DiffSpanType> = childSpans?.[getSpanID(span)] ?? [];
  51. // Mark descendents as being rendered. This is to address potential recursion issues due to malformed data.
  52. // For example if a span has a span_id that's identical to its parent_span_id.
  53. childSpans = {
  54. ...childSpans,
  55. };
  56. delete childSpans[getSpanID(span)];
  57. type AccType = {
  58. renderedSpanChildren: Array<JSX.Element>;
  59. nextSpanNumber: number;
  60. };
  61. const treeDepthEntry = isOrphanDiffSpan(span)
  62. ? ({type: 'orphan', depth: treeDepth} as OrphanTreeDepth)
  63. : treeDepth;
  64. const treeArr = isLast
  65. ? continuingTreeDepths
  66. : [...continuingTreeDepths, treeDepthEntry];
  67. const reduced: AccType = spanChildren.reduce(
  68. (acc: AccType, spanChild, index) => {
  69. const key = `${getSpanID(spanChild)}`;
  70. const results = this.renderSpan({
  71. spanNumber: acc.nextSpanNumber,
  72. isLast: index + 1 === spanChildren.length,
  73. isRoot: false,
  74. span: spanChild,
  75. childSpans,
  76. continuingTreeDepths: treeArr,
  77. treeDepth: treeDepth + 1,
  78. generateBounds,
  79. });
  80. acc.renderedSpanChildren.push(<Fragment key={key}>{results.spanTree}</Fragment>);
  81. acc.nextSpanNumber = results.nextSpanNumber;
  82. return acc;
  83. },
  84. {
  85. renderedSpanChildren: [],
  86. nextSpanNumber: spanNumber + 1,
  87. }
  88. );
  89. const spanTree = (
  90. <Fragment>
  91. <SpanGroup
  92. spanNumber={spanNumber}
  93. span={span}
  94. renderedSpanChildren={reduced.renderedSpanChildren}
  95. treeDepth={treeDepth}
  96. continuingTreeDepths={continuingTreeDepths}
  97. isRoot={isRoot}
  98. isLast={isLast}
  99. numOfSpanChildren={spanChildren.length}
  100. generateBounds={generateBounds}
  101. />
  102. </Fragment>
  103. );
  104. return {
  105. nextSpanNumber: reduced.nextSpanNumber,
  106. spanTree,
  107. };
  108. }
  109. renderRootSpans(): RenderedSpanTree {
  110. const {baselineEvent, regressionEvent} = this.props;
  111. const comparisonReport = diffTransactions({
  112. baselineEvent,
  113. regressionEvent,
  114. });
  115. const {rootSpans, childSpans} = comparisonReport;
  116. const generateBounds = boundsGenerator(rootSpans);
  117. let nextSpanNumber = 1;
  118. const spanTree = (
  119. <Fragment key="root-spans-tree">
  120. {rootSpans.map((rootSpan, index) => {
  121. const renderedRootSpan = this.renderSpan({
  122. isLast: index + 1 === rootSpans.length,
  123. isRoot: true,
  124. span: rootSpan,
  125. childSpans,
  126. spanNumber: nextSpanNumber,
  127. treeDepth: 0,
  128. continuingTreeDepths: [],
  129. generateBounds,
  130. });
  131. nextSpanNumber = renderedRootSpan.nextSpanNumber;
  132. return <Fragment key={String(index)}>{renderedRootSpan.spanTree}</Fragment>;
  133. })}
  134. </Fragment>
  135. );
  136. return {
  137. spanTree,
  138. nextSpanNumber,
  139. };
  140. }
  141. render() {
  142. const {spanTree} = this.renderRootSpans();
  143. return (
  144. <DividerHandlerManager.Provider interactiveLayerRef={this.traceViewRef}>
  145. <TraceViewContainer ref={this.traceViewRef}>{spanTree}</TraceViewContainer>
  146. </DividerHandlerManager.Provider>
  147. );
  148. }
  149. }
  150. const TraceViewContainer = styled('div')`
  151. overflow-x: hidden;
  152. border-bottom-left-radius: 3px;
  153. border-bottom-right-radius: 3px;
  154. `;
  155. export default SpanTree;