useTraceOnLoad.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import {useLayoutEffect, useRef, useState} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import type {Client} from 'sentry/api';
  4. import type {Organization} from 'sentry/types/organization';
  5. import useApi from 'sentry/utils/useApi';
  6. import useOrganization from 'sentry/utils/useOrganization';
  7. import {TraceTree} from './traceModels/traceTree';
  8. import type {TracePreferencesState} from './traceState/tracePreferences';
  9. import {useTraceState} from './traceState/traceStateProvider';
  10. import {isTransactionNode} from './traceGuards';
  11. import type {TraceReducerState} from './traceState';
  12. import type {useTraceScrollToPath} from './useTraceScrollToPath';
  13. // If a trace has less than 3 transactions, we automatically expand all transactions.
  14. // We do this as the tree is otherwise likely to be very small and not very useful.
  15. const AUTO_EXPAND_TRANSACTION_THRESHOLD = 3;
  16. async function maybeAutoExpandTrace(
  17. tree: TraceTree,
  18. options: {
  19. api: Client;
  20. organization: Organization;
  21. preferences: Pick<TracePreferencesState, 'autogroup' | 'missing_instrumentation'>;
  22. }
  23. ): Promise<TraceTree> {
  24. const transactions = TraceTree.FindAll(tree.root, node => isTransactionNode(node));
  25. if (transactions.length >= AUTO_EXPAND_TRANSACTION_THRESHOLD) {
  26. return tree;
  27. }
  28. const promises: Promise<any>[] = [];
  29. for (const transaction of transactions) {
  30. promises.push(tree.zoom(transaction, true, options));
  31. }
  32. await Promise.allSettled(promises).catch(_e => {
  33. Sentry.withScope(scope => {
  34. scope.setFingerprint(['trace-auto-expand']);
  35. Sentry.captureMessage('Failed to auto expand trace with low transaction count');
  36. });
  37. });
  38. return tree;
  39. }
  40. type UseTraceScrollToEventOnLoadOptions = {
  41. onTraceLoad: () => void;
  42. pathToNodeOrEventId: ReturnType<typeof useTraceScrollToPath>['current'];
  43. tree: TraceTree;
  44. };
  45. export function useTraceOnLoad(
  46. options: UseTraceScrollToEventOnLoadOptions
  47. ): 'success' | 'error' | 'pending' | 'idle' {
  48. const api = useApi();
  49. const organization = useOrganization();
  50. const initializedRef = useRef<boolean>(false);
  51. const {tree, pathToNodeOrEventId, onTraceLoad} = options;
  52. const [status, setStatus] = useState<'success' | 'error' | 'pending' | 'idle'>('idle');
  53. const traceState = useTraceState();
  54. const traceStateRef = useRef<TraceReducerState>(traceState);
  55. traceStateRef.current = traceState;
  56. const traceStatePreferencesRef = useRef<
  57. Pick<TraceReducerState['preferences'], 'autogroup' | 'missing_instrumentation'>
  58. >(traceState.preferences);
  59. traceStatePreferencesRef.current = traceState.preferences;
  60. useLayoutEffect(() => {
  61. if (initializedRef.current) {
  62. return undefined;
  63. }
  64. if (tree.type !== 'trace') {
  65. return undefined;
  66. }
  67. let cancel = false;
  68. function cleanup() {
  69. cancel = true;
  70. }
  71. setStatus('pending');
  72. initializedRef.current = true;
  73. const expandOptions = {
  74. api,
  75. organization,
  76. preferences: traceStatePreferencesRef.current,
  77. };
  78. // Node path has higher specificity than eventId. If neither are provided, we check if the
  79. // trace should be automatically expanded
  80. const promise = pathToNodeOrEventId?.path
  81. ? TraceTree.ExpandToPath(tree, pathToNodeOrEventId.path, expandOptions)
  82. : pathToNodeOrEventId?.eventId
  83. ? TraceTree.ExpandToEventID(tree, pathToNodeOrEventId.eventId, expandOptions)
  84. : maybeAutoExpandTrace(tree, expandOptions);
  85. promise
  86. .then(() => {
  87. setStatus('success');
  88. if (cancel) {
  89. return;
  90. }
  91. onTraceLoad();
  92. })
  93. .catch(() => setStatus('error'));
  94. return cleanup;
  95. }, [tree, api, onTraceLoad, organization, pathToNodeOrEventId]);
  96. return status;
  97. }