Skip to content

Commit

Permalink
ref(insights): Refactor Resources page charts (#81976)
Browse files Browse the repository at this point in the history
Makes two important changes:

- Extract `DurationChart`, `ThroughputChart`, and `AssetSizeChart`
components. These are re-used on the landing page and the asset details
page. This also brings this module more in-line with how other insights
are structured
- use `useSpanMetricsSeries` instead of `useSpansQuery`. This is a
better way to load data, since it returns `meta` and other good
information. Again, better consistency with other Insights modules
  • Loading branch information
gggritso authored Dec 11, 2024
1 parent be6706b commit a5ca1be
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 244 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {tct} from 'sentry/locale';
import type {Series} from 'sentry/types/echarts';
import {formatBytesBase2} from 'sentry/utils/bytes/formatBytesBase2';
import getDynamicText from 'sentry/utils/getDynamicText';
import {DATA_TYPE} from 'sentry/views/insights/browser/resources/settings';
import {AVG_COLOR} from 'sentry/views/insights/colors';
import Chart, {ChartType} from 'sentry/views/insights/common/components/chart';
import ChartPanel from 'sentry/views/insights/common/components/chartPanel';
import {DataTitles} from 'sentry/views/insights/common/views/spans/types';

interface Props {
isLoading: boolean;
series: Series[];
error?: Error | null;
}

export function AssetSizeChart({series, isLoading}: Props) {
return (
<ChartPanel title={tct('Average [dataType] Size', {dataType: DATA_TYPE})}>
<Chart
height={160}
aggregateOutputFormat="size"
data={series}
loading={isLoading}
chartColors={[AVG_COLOR]}
type={ChartType.LINE}
definedAxisTicks={4}
tooltipFormatterOptions={{
valueFormatter: bytes =>
getDynamicText({
value: formatBytesBase2(bytes),
fixed: 'xx KiB',
}),
nameFormatter: name => DataTitles[name],
}}
/>
</ChartPanel>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type {Series} from 'sentry/types/echarts';
import {AVG_COLOR} from 'sentry/views/insights/colors';
import Chart, {ChartType} from 'sentry/views/insights/common/components/chart';
import ChartPanel from 'sentry/views/insights/common/components/chartPanel';
import {getDurationChartTitle} from 'sentry/views/insights/common/views/spans/types';
import {CHART_HEIGHT} from 'sentry/views/insights/database/settings';

interface Props {
isLoading: boolean;
series: Series[];
error?: Error | null;
}

export function DurationChart({series, isLoading, error}: Props) {
return (
<ChartPanel title={getDurationChartTitle('resource')}>
<Chart
height={CHART_HEIGHT}
grid={{
left: '0',
right: '0',
top: '8px',
bottom: '0',
}}
data={series}
loading={isLoading}
error={error}
chartColors={[AVG_COLOR]}
type={ChartType.LINE}
/>
</ChartPanel>
);
}
Original file line number Diff line number Diff line change
@@ -1,217 +1,58 @@
import styled from '@emotion/styled';

import {getInterval} from 'sentry/components/charts/utils';
import {space} from 'sentry/styles/space';
import type {PageFilters} from 'sentry/types/core';
import EventView from 'sentry/utils/discover/eventView';
import {DiscoverDatasets} from 'sentry/utils/discover/types';
import {formatRate} from 'sentry/utils/formatters';
import {EMPTY_OPTION_VALUE} from 'sentry/utils/tokenizeSearch';
import usePageFilters from 'sentry/utils/usePageFilters';
import {AVG_COLOR, THROUGHPUT_COLOR} from 'sentry/views/insights/colors';
import Chart, {
ChartType,
useSynchronizeCharts,
} from 'sentry/views/insights/common/components/chart';
import ChartPanel from 'sentry/views/insights/common/components/chartPanel';
import {useSpansQuery} from 'sentry/views/insights/common/queries/useSpansQuery';
import {STARFISH_CHART_INTERVAL_FIDELITY} from 'sentry/views/insights/common/utils/constants';
import {
getDurationChartTitle,
getThroughputChartTitle,
} from 'sentry/views/insights/common/views/spans/types';
import {EMPTY_OPTION_VALUE, MutableSearch} from 'sentry/utils/tokenizeSearch';
import {useSynchronizeCharts} from 'sentry/views/insights/common/components/chart';
import {useSpanMetricsSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries';
import type {ModuleFilters} from 'sentry/views/insights/common/views/spans/useModuleFilters';
import {ModuleName, SpanMetricsField} from 'sentry/views/insights/types';
import {SpanMetricsField} from 'sentry/views/insights/types';

import {RESOURCE_THROUGHPUT_UNIT} from '../../settings';
import {DurationChart} from './durationChart';
import {ThroughputChart} from './throughputChart';

const {SPAN_SELF_TIME, SPAN_DESCRIPTION, SPAN_DOMAIN} = SpanMetricsField;

const CHART_HEIGHT = 140;

type Props = {
appliedFilters: ModuleFilters;
extraQuery?: string[];
};

type ChartProps = {
filters: ModuleFilters;
title: string;
extraQuery?: string[];
};

export function ResourceLandingPageCharts({appliedFilters, extraQuery}: Props) {
const moduleName = ModuleName.RESOURCE;
const {selection} = usePageFilters();

const eventView = getEventView(selection, appliedFilters);
let query: string = buildDiscoverQueryConditions(appliedFilters);

if (extraQuery) {
eventView.query += ` ${extraQuery.join(' ')}`;
query += ` ${extraQuery.join(' ')}`;
}

const {isPending} = useSpansQuery({
eventView,
initialData: [],
referrer: 'api.starfish.span-time-charts',
});
const {data, isPending, error} = useSpanMetricsSeries(
{
search: new MutableSearch(query),
yAxis: ['spm()', `avg(${SPAN_SELF_TIME})`],
},
'api.starfish.span-time-charts'
);

useSynchronizeCharts(1, !isPending);

return (
<ChartsContainer>
<ChartsContainerItem>
<ThroughputChart
title={getThroughputChartTitle(moduleName, RESOURCE_THROUGHPUT_UNIT)}
filters={appliedFilters}
extraQuery={extraQuery}
/>
<ThroughputChart series={data['spm()']} isLoading={isPending} error={error} />
</ChartsContainerItem>

<ChartsContainerItem>
<DurationChart
title={getDurationChartTitle(moduleName)}
filters={appliedFilters}
extraQuery={extraQuery}
series={[data[`avg(${SPAN_SELF_TIME})`]]}
isLoading={isPending}
error={error}
/>
</ChartsContainerItem>
</ChartsContainer>
);
}

function ThroughputChart({title, filters, extraQuery}: ChartProps): JSX.Element {
const pageFilters = usePageFilters();
const eventView = getEventView(pageFilters.selection, filters);
if (extraQuery) {
eventView.query += ` ${extraQuery.join(' ')}`;
}

const label = 'Requests';
const {isPending, data} = useSpansQuery<
{
'avg(span.self_time)': number;
interval: number;
'spm()': number;
}[]
>({
eventView,
initialData: [],
referrer: 'api.starfish.span-time-charts',
});
const dataByGroup = {[label]: data};

const throughputTimeSeries = Object.keys(dataByGroup).map(groupName => {
const groupData = dataByGroup[groupName];

return {
seriesName: label ?? 'Throughput',
data: (groupData ?? []).map(datum => ({
value: datum['spm()'],
name: datum.interval,
})),
};
});

return (
<ChartPanel title={title}>
<Chart
height={CHART_HEIGHT}
data={throughputTimeSeries}
loading={isPending}
grid={{
left: '0',
right: '0',
top: '8px',
bottom: '0',
}}
definedAxisTicks={4}
aggregateOutputFormat="rate"
rateUnit={RESOURCE_THROUGHPUT_UNIT}
stacked
type={ChartType.LINE}
chartColors={[THROUGHPUT_COLOR]}
tooltipFormatterOptions={{
valueFormatter: value => formatRate(value, RESOURCE_THROUGHPUT_UNIT),
}}
/>
</ChartPanel>
);
}

function DurationChart({title, filters, extraQuery}: ChartProps): JSX.Element {
const pageFilters = usePageFilters();
const eventView = getEventView(pageFilters.selection, filters);
if (extraQuery) {
eventView.query += ` ${extraQuery.join(' ')}`;
}

const label = `avg(${SPAN_SELF_TIME})`;

const {isPending, data} = useSpansQuery<
{
'avg(span.self_time)': number;
interval: number;
'spm()': number;
}[]
>({
eventView,
initialData: [],
referrer: 'api.starfish.span-time-charts',
});
const dataByGroup = {[label]: data};

const avgSeries = Object.keys(dataByGroup).map(groupName => {
const groupData = dataByGroup[groupName];

return {
seriesName: label,
data: (groupData ?? []).map(datum => ({
value: datum[`avg(${SPAN_SELF_TIME})`],
name: datum.interval,
})),
};
});

return (
<ChartPanel title={title}>
<Chart
height={CHART_HEIGHT}
data={[...avgSeries]}
loading={isPending}
grid={{
left: '0',
right: '0',
top: '8px',
bottom: '0',
}}
definedAxisTicks={4}
stacked
type={ChartType.LINE}
chartColors={[AVG_COLOR]}
/>
</ChartPanel>
);
}

const SPAN_FILTER_KEYS = ['span_operation', SPAN_DOMAIN, 'action'];

const getEventView = (pageFilters: PageFilters, appliedFilters: ModuleFilters) => {
const query = buildDiscoverQueryConditions(appliedFilters);

return EventView.fromNewQueryWithPageFilters(
{
name: '',
fields: [''],
yAxis: ['spm()', `avg(${SPAN_SELF_TIME})`],
query,
dataset: DiscoverDatasets.SPANS_METRICS,
interval: getInterval(pageFilters.datetime, STARFISH_CHART_INTERVAL_FIDELITY),
version: 2,
},
pageFilters
);
};

const buildDiscoverQueryConditions = (appliedFilters: ModuleFilters) => {
const result = Object.keys(appliedFilters)
.filter(key => SPAN_FILTER_KEYS.includes(key))
Expand Down
Loading

0 comments on commit a5ca1be

Please sign in to comment.