useReplayPerfData.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import {useEffect, useState} from 'react';
  2. import type {TraceFullDetailed} from 'sentry/utils/performance/quickTrace/types';
  3. import type ReplayReader from 'sentry/utils/replays/replayReader';
  4. import type {
  5. LargestContentfulPaintFrame,
  6. PaintFrame,
  7. ReplayFrame,
  8. } from 'sentry/utils/replays/types';
  9. import {
  10. useFetchTransactions,
  11. useTransactionData,
  12. } from 'sentry/views/replays/detail/trace/replayTransactionContext';
  13. interface IndentedTraceDetailed {
  14. indent: number;
  15. trace: TraceFullDetailed;
  16. }
  17. export type FlattenedTrace = IndentedTraceDetailed[];
  18. export interface ReplayTraceRow {
  19. durationMs: number;
  20. flattenedTraces: FlattenedTrace[];
  21. lcpFrames: LargestContentfulPaintFrame[];
  22. offsetMs: number;
  23. paintFrames: PaintFrame[];
  24. replayFrame: ReplayFrame;
  25. timestampMs: number;
  26. traces: TraceFullDetailed[];
  27. }
  28. interface Props {
  29. replay: ReplayReader | null;
  30. }
  31. function mapTraces(indent: number, traces: TraceFullDetailed[]) {
  32. return traces.flatMap(trace => {
  33. return [
  34. {
  35. indent,
  36. trace,
  37. },
  38. ...mapTraces(indent + 1, trace.children),
  39. ];
  40. });
  41. }
  42. export default function useReplayPerfData({replay}: Props) {
  43. const [data, setData] = useState<ReplayTraceRow[]>([]);
  44. const {
  45. state: {didInit: _didInit, errors: errors, isFetching: isFetching, traces = []},
  46. eventView: _eventView,
  47. } = useTransactionData();
  48. useFetchTransactions();
  49. useEffect(() => {
  50. if (!replay) {
  51. return;
  52. }
  53. const frames = replay.getPerfFrames();
  54. const rows = frames.map((thisFrame, i): ReplayTraceRow => {
  55. const nextFrame = frames[i + 1] as ReplayFrame | undefined;
  56. const isWithinThisAndNextFrame = (frame: ReplayFrame) => {
  57. return (
  58. frame.timestampMs > thisFrame.timestampMs &&
  59. (nextFrame === undefined || frame.timestampMs < nextFrame.timestampMs)
  60. );
  61. };
  62. const lcpFrames = replay.getLPCFrames().filter(isWithinThisAndNextFrame);
  63. const paintFrames = replay.getPaintFrames().filter(isWithinThisAndNextFrame);
  64. const tracesAfterThis = traces.filter(
  65. trace => trace.timestamp * 1000 >= thisFrame.timestampMs
  66. );
  67. const relatedTraces = nextFrame
  68. ? tracesAfterThis.filter(trace => trace.timestamp * 1000 < nextFrame.timestampMs)
  69. : tracesAfterThis;
  70. const flattenedTraces = relatedTraces.map(trace => mapTraces(0, [trace]));
  71. return {
  72. durationMs: nextFrame ? nextFrame.timestampMs - thisFrame.timestampMs : 0,
  73. lcpFrames,
  74. offsetMs: thisFrame.offsetMs,
  75. paintFrames,
  76. replayFrame: thisFrame,
  77. timestampMs: thisFrame.timestampMs,
  78. traces: relatedTraces,
  79. flattenedTraces,
  80. };
  81. });
  82. setData(rows);
  83. }, [replay, traces]);
  84. return {
  85. data,
  86. errors,
  87. isFetching,
  88. };
  89. }