flamegraph.tsx 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import React, {useEffect, useState} from 'react';
  2. import {Client} from 'sentry/api';
  3. import Alert from 'sentry/components/alert';
  4. import LoadingIndicator from 'sentry/components/loadingIndicator';
  5. import {Flamegraph} from 'sentry/components/profiling/flamegraph';
  6. import {FullScreenFlamegraphContainer} from 'sentry/components/profiling/fullScreenFlamegraphContainer';
  7. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  8. import {IconFlag} from 'sentry/icons';
  9. import {t} from 'sentry/locale';
  10. import {Organization, Project} from 'sentry/types';
  11. import {Trace} from 'sentry/types/profiling/core';
  12. import {FlamegraphPreferencesProvider} from 'sentry/utils/profiling/flamegraph/flamegraphPreferencesProvider';
  13. import {FlamegraphThemeProvider} from 'sentry/utils/profiling/flamegraph/flamegraphThemeProvider';
  14. import {importProfile, ProfileGroup} from 'sentry/utils/profiling/profile/importProfile';
  15. import {Profile} from 'sentry/utils/profiling/profile/profile';
  16. import useApi from 'sentry/utils/useApi';
  17. import useOrganization from 'sentry/utils/useOrganization';
  18. type RequestState = 'initial' | 'loading' | 'resolved' | 'errored';
  19. function fetchFlamegraphs(
  20. api: Client,
  21. eventId: string,
  22. projectId: Project['id'],
  23. organization: Organization
  24. ): Promise<ProfileGroup> {
  25. return api
  26. .requestPromise(
  27. `/projects/${organization.slug}/${projectId}/profiling/profiles/${eventId}/`,
  28. {
  29. method: 'GET',
  30. includeAllArgs: true,
  31. }
  32. )
  33. .then(([data]) => importProfile(data, eventId));
  34. }
  35. const LoadingGroup: ProfileGroup = {
  36. name: 'Loading',
  37. traceID: '',
  38. activeProfileIndex: 0,
  39. profiles: [Profile.Empty()],
  40. };
  41. interface FlamegraphViewProps {
  42. location: Location;
  43. params: {
  44. eventId?: Trace['id'];
  45. projectId?: Project['id'];
  46. };
  47. }
  48. function FlamegraphView(props: FlamegraphViewProps): React.ReactElement {
  49. const api = useApi();
  50. const organization = useOrganization();
  51. const [profiles, setProfiles] = useState<ProfileGroup | null>(null);
  52. const [requestState, setRequestState] = useState<RequestState>('initial');
  53. useEffect(() => {
  54. document.scrollingElement?.scrollTo(0, 0);
  55. }, []);
  56. useEffect(() => {
  57. if (!props.params.eventId || !props.params.projectId) {
  58. return;
  59. }
  60. api.clear();
  61. setRequestState('loading');
  62. fetchFlamegraphs(api, props.params.eventId, props.params.projectId, organization)
  63. .then(importedFlamegraphs => {
  64. setProfiles(importedFlamegraphs);
  65. setRequestState('resolved');
  66. })
  67. .catch(() => setRequestState('errored'));
  68. }, [props.params.eventId, props.params.projectId, api, organization]);
  69. return (
  70. <SentryDocumentTitle title={t('Profiling')} orgSlug={organization.slug}>
  71. <FlamegraphPreferencesProvider>
  72. <FlamegraphThemeProvider>
  73. <FullScreenFlamegraphContainer>
  74. {requestState === 'errored' ? (
  75. <Alert type="error" icon={<IconFlag size="md" />}>
  76. {t('Unable to load profiles')}
  77. </Alert>
  78. ) : requestState === 'loading' ? (
  79. <React.Fragment>
  80. <Flamegraph profiles={LoadingGroup} />
  81. <LoadingIndicator />
  82. </React.Fragment>
  83. ) : requestState === 'resolved' && profiles ? (
  84. <Flamegraph profiles={profiles} />
  85. ) : null}
  86. </FullScreenFlamegraphContainer>
  87. </FlamegraphThemeProvider>
  88. </FlamegraphPreferencesProvider>
  89. </SentryDocumentTitle>
  90. );
  91. }
  92. export default FlamegraphView;