Browse Source

feat(perf) Add user misery chart per transaction (#36086)

Fixes PERF-1270
Dominik Buszowiecki 2 years ago
parent
commit
27bb605405

+ 16 - 0
static/app/views/performance/transactionSummary/transactionOverview/charts.tsx

@@ -29,6 +29,7 @@ import DurationChart from './durationChart';
 import DurationPercentileChart from './durationPercentileChart';
 import LatencyChart from './latencyChart';
 import TrendChart from './trendChart';
+import UserMiseryChart from './userMiseryChart';
 import VitalsChart from './vitalsChart';
 
 export enum DisplayModes {
@@ -37,6 +38,7 @@ export enum DisplayModes {
   LATENCY = 'latency',
   TREND = 'trend',
   VITALS = 'vitals',
+  USER_MISERY = 'usermisery',
 }
 
 function generateDisplayOptions(
@@ -49,6 +51,7 @@ function generateDisplayOptions(
       {value: DisplayModes.LATENCY, label: t('Duration Distribution')},
       {value: DisplayModes.TREND, label: t('Trends')},
       {value: DisplayModes.VITALS, label: t('Web Vitals')},
+      {value: DisplayModes.USER_MISERY, label: t('User Misery')},
     ];
   }
 
@@ -229,6 +232,19 @@ function TransactionSummaryCharts({
             withoutZerofill={withoutZerofill}
           />
         )}
+        {display === DisplayModes.USER_MISERY && (
+          <UserMiseryChart
+            organization={organization}
+            query={eventView.query}
+            queryExtra={releaseQueryExtra}
+            project={eventView.project}
+            environment={eventView.environment}
+            start={eventView.start}
+            end={eventView.end}
+            statsPeriod={eventView.statsPeriod}
+            withoutZerofill={withoutZerofill}
+          />
+        )}
       </ChartContainer>
 
       <ChartControls>

+ 114 - 0
static/app/views/performance/transactionSummary/transactionOverview/userMiseryChart/index.tsx

@@ -0,0 +1,114 @@
+import {Fragment} from 'react';
+import {withRouter, WithRouterProps} from 'react-router';
+import {Location, Query} from 'history';
+
+import EventsRequest from 'sentry/components/charts/eventsRequest';
+import {HeaderTitleLegend} from 'sentry/components/charts/styles';
+import {getInterval} from 'sentry/components/charts/utils';
+import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
+import QuestionTooltip from 'sentry/components/questionTooltip';
+import {t} from 'sentry/locale';
+import {Organization, OrganizationSummary} from 'sentry/types';
+import {Series} from 'sentry/types/echarts';
+import {getUtcToLocalDateObject} from 'sentry/utils/dates';
+import {useMEPSettingContext} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
+import useApi from 'sentry/utils/useApi';
+import {getTermHelp, PERFORMANCE_TERM} from 'sentry/views/performance/data';
+import {getMEPQueryParams} from 'sentry/views/performance/landing/widgets/utils';
+import {DurationChart} from 'sentry/views/performance/landing/widgets/widgets/singleFieldAreaWidget';
+import {ViewProps} from 'sentry/views/performance/types';
+
+type Props = WithRouterProps &
+  ViewProps & {
+    location: Location;
+    organization: OrganizationSummary;
+    queryExtra: Query;
+    withoutZerofill: boolean;
+  };
+
+/**
+ * Fetch and render an area chart that shows user misery over a period
+ */
+function UserMiseryChart({
+  project,
+  environment,
+  location,
+  organization,
+  query,
+  statsPeriod,
+  withoutZerofill,
+  start: propsStart,
+  end: propsEnd,
+}: Props) {
+  const api = useApi();
+  const mepContext = useMEPSettingContext();
+
+  const start = propsStart ? getUtcToLocalDateObject(propsStart) : null;
+  const end = propsEnd ? getUtcToLocalDateObject(propsEnd) : null;
+  const utc = normalizeDateTimeParams(location.query).utc === 'true';
+  const period = statsPeriod;
+
+  const datetimeSelection = {start, end, period};
+
+  const requestCommonProps = {
+    api,
+    start,
+    end,
+    project,
+    environment,
+    query,
+    period,
+    interval: getInterval(datetimeSelection, 'high'),
+  };
+
+  const header = (
+    <HeaderTitleLegend>
+      {t('User Misery')}
+      <QuestionTooltip
+        size="sm"
+        position="top"
+        title={t(getTermHelp(organization as Organization, PERFORMANCE_TERM.USER_MISERY))}
+      />
+    </HeaderTitleLegend>
+  );
+
+  const yAxis = 'user_misery()';
+
+  return (
+    <Fragment>
+      {header}
+      <EventsRequest
+        {...requestCommonProps}
+        organization={organization}
+        showLoading={false}
+        includePrevious={false}
+        yAxis={yAxis}
+        partial
+        withoutZerofill={withoutZerofill}
+        referrer="api.performance.transaction-summary.user-misery-chart"
+        queryExtras={getMEPQueryParams(mepContext)}
+      >
+        {({loading, reloading, timeseriesData}) => {
+          const data: Series[] = timeseriesData?.[0]
+            ? [{...timeseriesData[0], seriesName: yAxis}]
+            : [];
+          return (
+            <DurationChart
+              grid={{left: '10px', right: '10px', top: '40px', bottom: '0px'}}
+              data={data}
+              statsPeriod={statsPeriod}
+              loading={loading || reloading}
+              disableMultiAxis
+              definedAxisTicks={4}
+              start={start}
+              end={end}
+              utc={utc}
+            />
+          );
+        }}
+      </EventsRequest>
+    </Fragment>
+  );
+}
+
+export default withRouter(UserMiseryChart);