useReplayPerfData.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  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<Map<ReplayFrame, ReplayTraceRow>>(new Map());
  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 collection = new Map<ReplayFrame, ReplayTraceRow>();
  54. const frames = replay.getChapterFrames();
  55. frames.forEach((thisFrame, i) => {
  56. const nextFrame = frames[i + 1] as ReplayFrame | undefined;
  57. const isWithinThisAndNextFrame = (frame: ReplayFrame) => {
  58. return (
  59. frame.timestampMs > thisFrame.timestampMs &&
  60. (nextFrame === undefined || frame.timestampMs < nextFrame.timestampMs)
  61. );
  62. };
  63. const lcpFrames = replay.getLPCFrames().filter(isWithinThisAndNextFrame);
  64. const paintFrames = replay.getPaintFrames().filter(isWithinThisAndNextFrame);
  65. const tracesAfterThis = traces.filter(
  66. trace => trace.timestamp * 1000 >= thisFrame.timestampMs
  67. );
  68. const relatedTraces = nextFrame
  69. ? tracesAfterThis.filter(trace => trace.timestamp * 1000 < nextFrame.timestampMs)
  70. : tracesAfterThis;
  71. const flattenedTraces = relatedTraces.map(trace => mapTraces(0, [trace]));
  72. collection.set(thisFrame, {
  73. durationMs: nextFrame ? nextFrame.timestampMs - thisFrame.timestampMs : 0,
  74. lcpFrames,
  75. offsetMs: thisFrame.offsetMs,
  76. paintFrames,
  77. replayFrame: thisFrame,
  78. timestampMs: thisFrame.timestampMs,
  79. traces: relatedTraces,
  80. flattenedTraces,
  81. });
  82. });
  83. setData(collection);
  84. }, [replay, traces]);
  85. return {
  86. data,
  87. errors,
  88. isFetching,
  89. };
  90. }