123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- import type {Location, Query} from 'history';
- import pick from 'lodash/pick';
- import {DEFAULT_RELATIVE_PERIODS} from 'sentry/constants';
- import {t} from 'sentry/locale';
- import {defined} from 'sentry/utils';
- import EventView from 'sentry/utils/discover/eventView';
- import {isAggregateField} from 'sentry/utils/discover/fields';
- import type {SpanSlug} from 'sentry/utils/performance/suspectSpans/types';
- import {decodeScalar} from 'sentry/utils/queryString';
- import {MutableSearch} from 'sentry/utils/tokenizeSearch';
- import type {DomainView} from 'sentry/views/insights/pages/useFilters';
- import {getTransactionSummaryBaseUrl} from 'sentry/views/performance/transactionSummary/utils';
- import type {SpanSort, SpanSortOption} from './types';
- import {SpanSortOthers, SpanSortPercentiles} from './types';
- export function generateSpansRoute({
- orgSlug,
- view,
- }: {
- orgSlug: string;
- view?: DomainView;
- }): string {
- return `${getTransactionSummaryBaseUrl(orgSlug, view)}/spans/`;
- }
- export function spansRouteWithQuery({
- orgSlug,
- transaction,
- projectID,
- query,
- view,
- }: {
- orgSlug: string;
- query: Query;
- transaction: string;
- projectID?: string | string[];
- view?: DomainView;
- }) {
- const pathname = generateSpansRoute({
- orgSlug,
- view,
- });
- return {
- pathname,
- query: {
- transaction,
- project: projectID,
- environment: query.environment,
- statsPeriod: query.statsPeriod,
- start: query.start,
- end: query.end,
- query: query.query,
- },
- };
- }
- export const SPAN_RETENTION_DAYS = 30;
- export const SPAN_RELATIVE_PERIODS = pick(DEFAULT_RELATIVE_PERIODS, [
- '1h',
- '24h',
- '7d',
- '14d',
- '30d',
- ]);
- export const SPAN_SORT_OPTIONS: SpanSortOption[] = [
- {
- prefix: t('Sort By'),
- label: t('Total Self Time'),
- field: SpanSortOthers.SUM_EXCLUSIVE_TIME,
- },
- {
- prefix: t('Sort By'),
- label: t('Average Count'),
- field: SpanSortOthers.AVG_OCCURRENCE,
- },
- {
- prefix: t('Sort By'),
- label: t('p50 Self Time'),
- field: SpanSortPercentiles.P50_EXCLUSIVE_TIME,
- },
- {
- prefix: t('Sort By'),
- label: t('p75 Self Time'),
- field: SpanSortPercentiles.P75_EXCLUSIVE_TIME,
- },
- {
- prefix: t('Sort By'),
- label: t('p95 Self Time'),
- field: SpanSortPercentiles.P95_EXCLUSIVE_TIME,
- },
- {
- prefix: t('Sort By'),
- label: t('p99 Self Time'),
- field: SpanSortPercentiles.P99_EXCLUSIVE_TIME,
- },
- ];
- const DEFAULT_SORT = SpanSortOthers.SUM_EXCLUSIVE_TIME;
- function getSuspectSpanSort(sort: string): SpanSortOption {
- const selected = SPAN_SORT_OPTIONS.find(option => option.field === sort);
- if (selected) {
- return selected;
- }
- return SPAN_SORT_OPTIONS.find(option => option.field === DEFAULT_SORT)!;
- }
- export function getSuspectSpanSortFromLocation(
- location: Location,
- sortKey: string = 'sort'
- ): SpanSortOption {
- const sort = decodeScalar(location?.query?.[sortKey]) ?? DEFAULT_SORT;
- return getSuspectSpanSort(sort);
- }
- export function getSuspectSpanSortFromEventView(eventView: EventView): SpanSortOption {
- const sort = eventView.sorts.length ? eventView.sorts[0].field : DEFAULT_SORT;
- return getSuspectSpanSort(sort);
- }
- export function parseSpanSlug(spanSlug: string | undefined): SpanSlug | undefined {
- if (!defined(spanSlug)) {
- return undefined;
- }
- const delimiterPos = spanSlug.lastIndexOf(':');
- if (delimiterPos < 0) {
- return undefined;
- }
- const op = spanSlug.slice(0, delimiterPos);
- const group = spanSlug.slice(delimiterPos + 1);
- return {op, group};
- }
- export function generateSpansEventView({
- location,
- transactionName,
- }: {
- location: Location;
- transactionName: string;
- }): EventView {
- const query = decodeScalar(location.query.query, '');
- const conditions = new MutableSearch(query);
- conditions.setFilterValues('event.type', ['transaction']);
- conditions.setFilterValues('transaction', [transactionName]);
- Object.keys(conditions.filters).forEach(field => {
- if (isAggregateField(field)) {
- conditions.removeFilter(field);
- }
- });
- const eventView = EventView.fromNewQueryWithLocation(
- {
- id: undefined,
- version: 2,
- name: transactionName,
- fields: [
- ...Object.values(SpanSortOthers),
- ...Object.values(SpanSortPercentiles),
- 'trace',
- ],
- query: conditions.formatString(),
- projects: [],
- },
- location
- );
- const sort = getSuspectSpanSortFromLocation(location);
- return eventView.withSorts([{field: sort.field, kind: 'desc'}]);
- }
- /**
- * For the totals view, we want to get some transaction level stats like
- * the number of transactions and the sum of the transaction duration.
- * This requires the removal of any aggregate conditions as they can result
- * in unexpected empty responses.
- */
- export function getTotalsView(eventView: EventView): EventView {
- const totalsView = eventView.withColumns([
- {kind: 'function', function: ['count', '', undefined, undefined]},
- {kind: 'function', function: ['sum', 'transaction.duration', undefined, undefined]},
- ]);
- const conditions = new MutableSearch(eventView.query);
- // filter out any aggregate conditions
- Object.keys(conditions.filters).forEach(field => {
- if (isAggregateField(field)) {
- conditions.removeFilter(field);
- }
- });
- totalsView.query = conditions.formatString();
- return totalsView;
- }
- export const SPAN_SORT_TO_FIELDS: Record<SpanSort, string[]> = {
- [SpanSortOthers.SUM_EXCLUSIVE_TIME]: [
- 'percentileArray(spans_exclusive_time, 0.75)',
- 'count()',
- 'count_unique(id)',
- 'sumArray(spans_exclusive_time)',
- ],
- [SpanSortOthers.AVG_OCCURRENCE]: [
- 'percentileArray(spans_exclusive_time, 0.75)',
- 'count()',
- 'count_unique(id)',
- 'equation|count() / count_unique(id)',
- 'sumArray(spans_exclusive_time)',
- ],
- [SpanSortPercentiles.P50_EXCLUSIVE_TIME]: [
- 'percentileArray(spans_exclusive_time, 0.5)',
- 'count()',
- 'count_unique(id)',
- 'sumArray(spans_exclusive_time)',
- ],
- [SpanSortPercentiles.P75_EXCLUSIVE_TIME]: [
- 'percentileArray(spans_exclusive_time, 0.75)',
- 'count()',
- 'count_unique(id)',
- 'sumArray(spans_exclusive_time)',
- ],
- [SpanSortPercentiles.P95_EXCLUSIVE_TIME]: [
- 'percentileArray(spans_exclusive_time, 0.95)',
- 'count()',
- 'count_unique(id)',
- 'sumArray(spans_exclusive_time)',
- ],
- [SpanSortPercentiles.P99_EXCLUSIVE_TIME]: [
- 'percentileArray(spans_exclusive_time, 0.99)',
- 'count()',
- 'count_unique(id)',
- 'sumArray(spans_exclusive_time)',
- ],
- };
- export function getExclusiveTimeDisplayedValue(value: string): string {
- return value.replace('exclusive', 'self');
- }
|