Browse Source

feat(starfish): Add p95 charts with breakdowns in tabs (#51806)

![Screenshot 2023-06-28 at 1 50 35
PM](https://github.com/getsentry/sentry/assets/63818634/de5aa24b-0d8f-4fa0-ba8c-08ee0373be18)
Shruthi 1 year ago
parent
commit
d85ca6d3fa

+ 2 - 2
static/app/views/starfish/colours.tsx

@@ -1,6 +1,6 @@
 import {CHART_PALETTE} from 'sentry/constants/chartPalette';
 
-export const THROUGHPUT_COLOR = CHART_PALETTE[0][0];
+export const THROUGHPUT_COLOR = CHART_PALETTE[3][3];
 export const P50_COLOR = CHART_PALETTE[3][1];
-export const P95_COLOR = CHART_PALETTE[3][3];
+export const P95_COLOR = CHART_PALETTE[0][0];
 export const ERRORS_COLOR = CHART_PALETTE[5][3];

+ 8 - 43
static/app/views/starfish/views/webServiceView/endpointOverview/index.tsx

@@ -29,7 +29,7 @@ import usePageFilters from 'sentry/utils/usePageFilters';
 import withApi from 'sentry/utils/withApi';
 import {normalizeUrl} from 'sentry/utils/withDomainRequired';
 import {SidebarSpacer} from 'sentry/views/performance/transactionSummary/utils';
-import {ERRORS_COLOR, P95_COLOR, THROUGHPUT_COLOR} from 'sentry/views/starfish/colours';
+import {ERRORS_COLOR, THROUGHPUT_COLOR} from 'sentry/views/starfish/colours';
 import Chart from 'sentry/views/starfish/components/chart';
 import {TransactionSamplesTable} from 'sentry/views/starfish/components/samplesTable/transactionSamplesTable';
 import {ModuleName} from 'sentry/views/starfish/types';
@@ -38,8 +38,8 @@ import {getDateConditions} from 'sentry/views/starfish/utils/getDateConditions';
 import SpansTable from 'sentry/views/starfish/views/spans/spansTable';
 import {DataTitles} from 'sentry/views/starfish/views/spans/types';
 import IssuesTable from 'sentry/views/starfish/views/webServiceView/endpointOverview/issuesTable';
+import {ServiceDurationChartContainer} from 'sentry/views/starfish/views/webServiceView/serviceDurationChartContainer';
 import {ServiceTimeSpentBreakdown} from 'sentry/views/starfish/views/webServiceView/serviceTimeSpentBreakdown';
-import {SpanGroupBreakdownContainer} from 'sentry/views/starfish/views/webServiceView/spanGroupBreakdownContainer';
 
 const SPANS_TABLE_LIMIT = 5;
 
@@ -121,7 +121,7 @@ export default function EndpointOverview() {
         start={eventView.start}
         end={eventView.end}
         organization={organization}
-        yAxis={['tps()', 'p95(transaction.duration)', 'http_error_count()']}
+        yAxis={['tps()', 'http_error_count()']}
         dataset={DiscoverDatasets.METRICS}
       >
         {({loading, results}) => {
@@ -167,44 +167,6 @@ export default function EndpointOverview() {
                 }}
               />
               <SidebarSpacer />
-              <Header>
-                <ChartLabel>{DataTitles.p95}</ChartLabel>
-              </Header>
-              <ChartSummaryValue
-                isLoading={isTotalsLoading}
-                value={
-                  defined(totals)
-                    ? tooltipFormatterUsingAggregateOutputType(
-                        totals.data[0]['p95(transaction.duration)'] as number,
-                        'duration'
-                      )
-                    : undefined
-                }
-              />
-              <Chart
-                statsPeriod={(statsPeriod as string) ?? '24h'}
-                height={80}
-                data={[results[1]]}
-                start=""
-                end=""
-                loading={loading}
-                utc={false}
-                isLineChart
-                definedAxisTicks={2}
-                disableXAxis
-                chartColors={[P95_COLOR]}
-                grid={{
-                  left: '8px',
-                  right: '0',
-                  top: '8px',
-                  bottom: '0',
-                }}
-                tooltipFormatterOptions={{
-                  valueFormatter: value =>
-                    tooltipFormatterUsingAggregateOutputType(value, 'duration'),
-                }}
-              />
-              <SidebarSpacer />
               <Header>
                 <ChartLabel>{DataTitles.errorCount}</ChartLabel>
               </Header>
@@ -222,7 +184,7 @@ export default function EndpointOverview() {
               <Chart
                 statsPeriod={eventView.statsPeriod}
                 height={80}
-                data={[results[2]]}
+                data={[results[1]]}
                 start={eventView.start as string}
                 end={eventView.end as string}
                 loading={loading}
@@ -292,7 +254,10 @@ export default function EndpointOverview() {
 
           <Layout.Main>
             <StyledRow minSize={200}>
-              <SpanGroupBreakdownContainer transaction={transaction as string} />
+              <ServiceDurationChartContainer
+                transaction={transaction as string}
+                transactionMethod={method}
+              />
             </StyledRow>
             <SegmentedControlContainer>
               <SegmentedControl

+ 175 - 0
static/app/views/starfish/views/webServiceView/serviceDurationChartContainer.tsx

@@ -0,0 +1,175 @@
+import {useState} from 'react';
+import styled from '@emotion/styled';
+
+import {Button} from 'sentry/components/button';
+import ButtonBar from 'sentry/components/buttonBar';
+import {getInterval} from 'sentry/components/charts/utils';
+import {t} from 'sentry/locale';
+import {space} from 'sentry/styles/space';
+import {Series, SeriesDataUnit} from 'sentry/types/echarts';
+import {tooltipFormatterUsingAggregateOutputType} from 'sentry/utils/discover/charts';
+import EventView from 'sentry/utils/discover/eventView';
+import {DiscoverDatasets} from 'sentry/utils/discover/types';
+import usePageFilters from 'sentry/utils/usePageFilters';
+import {P95_COLOR} from 'sentry/views/starfish/colours';
+import Chart, {useSynchronizeCharts} from 'sentry/views/starfish/components/chart';
+import MiniChartPanel from 'sentry/views/starfish/components/miniChartPanel';
+import {SpanMetricsFields} from 'sentry/views/starfish/types';
+import {useEventsStatsQuery} from 'sentry/views/starfish/utils/useEventsStatsQuery';
+import {NULL_SPAN_CATEGORY} from 'sentry/views/starfish/views/webServiceView/spanGroupBreakdownContainer';
+
+const {SPAN_SELF_TIME} = SpanMetricsFields;
+
+type Props = {
+  transaction?: string;
+  transactionMethod?: string;
+};
+
+export function ServiceDurationChartContainer({transaction, transactionMethod}: Props) {
+  const pageFilter = usePageFilters();
+  const {selection} = pageFilter;
+
+  const [selectedSeries, setSelectedSeries] = useState('Overall');
+
+  const serviceEventView = EventView.fromSavedQuery({
+    name: '',
+    fields: [`p95(transaction.duration)`],
+    yAxis: [`p95(transaction.duration)`],
+    query: `transaction.op:http.server ${
+      transaction ? `transaction:${transaction}` : ''
+    } ${transactionMethod ? `http.method:${transactionMethod}` : ''}`,
+    dataset: DiscoverDatasets.METRICS,
+    start: selection.datetime.start ?? undefined,
+    end: selection.datetime.end ?? undefined,
+    range: selection.datetime.period ?? undefined,
+    projects: selection.projects,
+    version: 2,
+    interval: getInterval(selection.datetime, 'low'),
+  });
+
+  const {
+    isLoading: isServiceDurationLoading,
+    data: serviceDuration,
+    isError,
+  } = useEventsStatsQuery({
+    eventView: serviceEventView,
+    enabled: true,
+    referrer: 'starfish-web-service.p95-duration-timeseries',
+    initialData: {},
+  });
+
+  const durationSeries: {[category: string]: Series} = {};
+
+  durationSeries.Overall = {
+    seriesName: 'Overall',
+    data:
+      serviceDuration?.data?.map(datum => {
+        return {name: datum[0] * 1000, value: datum[1][0].count} as SeriesDataUnit;
+      }) ?? [],
+  };
+
+  const categoryEventView = EventView.fromSavedQuery({
+    name: '',
+    fields: [`p95(${SPAN_SELF_TIME})`, `sum(${SPAN_SELF_TIME})`, 'span.category'],
+    yAxis: [`p95(${SPAN_SELF_TIME})`],
+    query: `transaction.op:http.server ${
+      transaction ? `transaction:${transaction}` : ''
+    } ${transactionMethod ? `transaction.method:${transactionMethod}` : ''}`,
+    dataset: DiscoverDatasets.SPANS_METRICS,
+    start: selection.datetime.start ?? undefined,
+    end: selection.datetime.end ?? undefined,
+    range: selection.datetime.period ?? undefined,
+    orderby: '-sum_span_self_time',
+    projects: selection.projects,
+    version: 2,
+    topEvents: '4',
+    interval: getInterval(selection.datetime, 'low'),
+  });
+
+  const {
+    isLoading: isCategoryDurationLoading,
+    data: categoryDuration,
+    isError: isCategoryDurationError,
+  } = useEventsStatsQuery({
+    eventView: categoryEventView,
+    enabled: true,
+    referrer: 'starfish-web-service.p95-duration-category-breakdown',
+    initialData: {},
+  });
+
+  const orderArray: any[] = [];
+  if (!isCategoryDurationError && !isCategoryDurationLoading) {
+    Object.keys(categoryDuration).forEach(key => {
+      const seriesData = categoryDuration?.[key];
+      const label = key === '' ? NULL_SPAN_CATEGORY : key;
+      orderArray.push([label, seriesData.order]);
+      durationSeries[label] = {
+        seriesName: label,
+        data:
+          seriesData?.data?.map(datum => {
+            return {name: datum[0] * 1000, value: datum[1][0].count} as SeriesDataUnit;
+          }) ?? [],
+      };
+    });
+  }
+
+  const tabOrder = [t('Overall')];
+  orderArray.sort((a, b) => a[1] - b[1]).forEach(val => tabOrder.push(val[0]));
+
+  useSynchronizeCharts();
+
+  return (
+    <MiniChartPanel title="Duration (P95)">
+      <MinWidthButtonBar gap={1}>
+        {tabOrder.map(label => {
+          return (
+            <Button
+              type="button"
+              size="sm"
+              onClick={() => {
+                setSelectedSeries(label);
+              }}
+              key={label}
+              priority={selectedSeries === label ? 'primary' : undefined}
+            >
+              {label}
+            </Button>
+          );
+        })}
+      </MinWidthButtonBar>
+      <Chart
+        statsPeriod="24h"
+        data={[durationSeries[selectedSeries]]}
+        start=""
+        end=""
+        errored={isError}
+        loading={
+          selectedSeries === 'Overall'
+            ? isServiceDurationLoading
+            : isCategoryDurationLoading
+        }
+        utc={false}
+        grid={{
+          left: '0',
+          right: '0',
+          top: '8px',
+          bottom: '0',
+        }}
+        height={175}
+        chartColors={[P95_COLOR]}
+        isLineChart
+        definedAxisTicks={6}
+        aggregateOutputFormat="duration"
+        tooltipFormatterOptions={{
+          valueFormatter: value =>
+            tooltipFormatterUsingAggregateOutputType(value, 'duration'),
+        }}
+      />
+    </MiniChartPanel>
+  );
+}
+
+const MinWidthButtonBar = styled(ButtonBar)`
+  width: min-content;
+  margin-bottom: ${space(1)};
+`;

+ 4 - 3
static/app/views/starfish/views/webServiceView/starfishView.tsx

@@ -21,12 +21,13 @@ import {DiscoverDatasets} from 'sentry/utils/discover/types';
 import {MutableSearch} from 'sentry/utils/tokenizeSearch';
 import usePageFilters from 'sentry/utils/usePageFilters';
 import withApi from 'sentry/utils/withApi';
+import {THROUGHPUT_COLOR} from 'sentry/views/starfish/colours';
 import Chart, {useSynchronizeCharts} from 'sentry/views/starfish/components/chart';
 import MiniChartPanel from 'sentry/views/starfish/components/miniChartPanel';
 import formatThroughput from 'sentry/views/starfish/utils/chartValueFormatters/formatThroughput';
 import {DataTitles} from 'sentry/views/starfish/views/spans/types';
+import {ServiceDurationChartContainer} from 'sentry/views/starfish/views/webServiceView/serviceDurationChartContainer';
 import {ServiceTimeSpentBreakdown} from 'sentry/views/starfish/views/webServiceView/serviceTimeSpentBreakdown';
-import {SpanGroupBreakdownContainer} from 'sentry/views/starfish/views/webServiceView/spanGroupBreakdownContainer';
 
 import EndpointList from './endpointList';
 
@@ -104,7 +105,7 @@ export function StarfishView(props: BasePerformanceViewProps) {
                   definedAxisTicks={2}
                   stacked
                   isLineChart
-                  chartColors={theme.charts.getColorPalette(2)}
+                  chartColors={[THROUGHPUT_COLOR]}
                   tooltipFormatterOptions={{
                     valueFormatter: value => formatThroughput(value),
                   }}
@@ -145,7 +146,7 @@ export function StarfishView(props: BasePerformanceViewProps) {
       <StyledRow minSize={200}>
         <ChartsContainer>
           <ChartsContainerItem>
-            <SpanGroupBreakdownContainer />
+            <ServiceDurationChartContainer />
           </ChartsContainerItem>
           <ChartsContainerItem2>{renderCharts()}</ChartsContainerItem2>
           <ChartsContainerItem3>