Key Operation to Metrics using USE_SPAN_METRIC feature flag (#3188)

* refactor: generic querybuilderWithFormula

* refactor: added generic datasource

* refactor: dynamic disabled in getQueryBuilderQueriesWithFormula

* refactor: generic legend for building query with formulas

* feat: added new TopOperationMetrics component for key operation

* refactor: added feature flag for key operation

* refactor: shifted types and fixed typos

* refactor: separated types and renamed file

* refactor: one on one mapping

* refactor: added clickable link for navigating to traces

* chore: separated types

* chore: removed unnecessary comments

* refactor: one on one mapping for DBCallQueries

* refactor: seperated types and one on one mapping for externalQueries

* refactor: separate type from metricsPagesQueriesFactory

* refactor: separated types and one on one mapping for overviewQueries

* refactor: remove the type inforcement from TopOperationQueries.ts

* refactor: one on one mapping in TopOperationQueries.ts

* refactor: one on one mapping and remove the unwanted code

* refactor: shifted logic of navigating to traces to utils

* refactor: separated renderColumnCell from the TopOperationMetric component

* refactor: generic tableRenderer

* refactor: made getTableColumnRenderer more generic

* chore: title is updated

---------

Co-authored-by: Palash Gupta <palashgdev@gmail.com>
This commit is contained in:
Rajat Dabade 2023-07-28 21:54:36 +05:30 committed by GitHub
parent 5c83d133a2
commit bc4a4edc7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 676 additions and 202 deletions

View File

@ -1,17 +1,19 @@
import { PANEL_TYPES } from 'constants/queryBuilder';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
export const getWidgetQueryBuilder = ( import { GetWidgetQueryBuilderProps } from './types';
query: Widgets['query'],
export const getWidgetQueryBuilder = ({
query,
title = '', title = '',
): Widgets => ({ panelTypes,
}: GetWidgetQueryBuilderProps): Widgets => ({
description: '', description: '',
id: v4(), id: v4(),
isStacked: false, isStacked: false,
nullZeroValues: '', nullZeroValues: '',
opacity: '0', opacity: '0',
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes,
query, query,
timePreferance: 'GLOBAL_TIME', timePreferance: 'GLOBAL_TIME',
title, title,

View File

@ -1,7 +1,11 @@
import { OPERATORS } from 'constants/queryBuilder'; import { OPERATORS } from 'constants/queryBuilder';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, QueryBuilderData } from 'types/common/queryBuilder'; import {
DataSource,
MetricAggregateOperator,
QueryBuilderData,
} from 'types/common/queryBuilder';
import { DataType, FORMULA, MetricsType, WidgetKeys } from '../constant'; import { DataType, FORMULA, MetricsType, WidgetKeys } from '../constant';
import { IServiceName } from '../Tabs/types'; import { IServiceName } from '../Tabs/types';
@ -44,13 +48,14 @@ export const databaseCallsRPS = ({
]; ];
const legends = [legend]; const legends = [legend];
const dataSource = DataSource.METRICS;
return getQueryBuilderQueries({ return getQueryBuilderQueries({
autocompleteData, autocompleteData,
groupBy, groupBy,
legends, legends,
filterItems, filterItems,
dataSource: DataSource.METRICS, dataSource,
}); });
}; };
@ -85,17 +90,36 @@ export const databaseCallsAvgDuration = ({
}, },
...tagFilterItems, ...tagFilterItems,
]; ];
const additionalItemsB = additionalItemsA;
return getQueryBuilderQuerieswithFormula({ const autocompleteData: BaseAutocompleteData[] = [
autocompleteDataA, autocompleteDataA,
autocompleteDataB, autocompleteDataB,
];
const additionalItems: TagFilterItem[][] = [
additionalItemsA, additionalItemsA,
additionalItemsB, additionalItemsA,
legend: '', ];
disabled: true,
expression: FORMULA.DATABASE_CALLS_AVG_DURATION, const legends = ['', ''];
legendFormula: 'Average Duration', const disabled = [true, true];
const legendFormula = 'Average Duration';
const expression = FORMULA.DATABASE_CALLS_AVG_DURATION;
const aggregateOperators = [
MetricAggregateOperator.SUM,
MetricAggregateOperator.SUM,
];
const dataSource = DataSource.METRICS;
return getQueryBuilderQuerieswithFormula({
autocompleteData,
additionalItems,
legends,
disabled,
expression,
legendFormula,
aggregateOperators,
dataSource,
}); });
}; };

View File

@ -1,10 +1,17 @@
import { OPERATORS } from 'constants/queryBuilder'; import { OPERATORS } from 'constants/queryBuilder';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, QueryBuilderData } from 'types/common/queryBuilder'; import {
DataSource,
MetricAggregateOperator,
QueryBuilderData,
} from 'types/common/queryBuilder';
import { DataType, FORMULA, MetricsType, WidgetKeys } from '../constant'; import { DataType, FORMULA, MetricsType, WidgetKeys } from '../constant';
import { IServiceName } from '../Tabs/types'; import {
ExternalCallDurationByAddressProps,
ExternalCallProps,
} from '../Tabs/types';
import { import {
getQueryBuilderQueries, getQueryBuilderQueries,
getQueryBuilderQuerieswithFormula, getQueryBuilderQuerieswithFormula,
@ -36,6 +43,7 @@ export const externalCallErrorPercent = ({
isColumn: true, isColumn: true,
type: null, type: null,
}; };
const additionalItemsA: TagFilterItem[] = [ const additionalItemsA: TagFilterItem[] = [
{ {
id: '', id: '',
@ -71,23 +79,38 @@ export const externalCallErrorPercent = ({
type: MetricsType.Resource, type: MetricsType.Resource,
}, },
op: OPERATORS.IN, op: OPERATORS.IN,
value: [`${servicename}`], value: [servicename],
}, },
...tagFilterItems, ...tagFilterItems,
]; ];
const legendFormula = legend; const legendFormula = legend;
const expression = FORMULA.ERROR_PERCENTAGE; const expression = FORMULA.ERROR_PERCENTAGE;
const disabled = true; const autocompleteData: BaseAutocompleteData[] = [
return getQueryBuilderQuerieswithFormula({
autocompleteDataA, autocompleteDataA,
autocompleteDataB, autocompleteDataB,
];
const additionalItems: TagFilterItem[][] = [
additionalItemsA, additionalItemsA,
additionalItemsB, additionalItemsB,
legend, ];
const legends = Array(2).fill(legend);
const aggregateOperators = Array(2).fill(MetricAggregateOperator.SUM);
const disabled = Array(2).fill(true);
const dataSource = DataSource.METRICS;
return getQueryBuilderQuerieswithFormula({
autocompleteData,
additionalItems,
legends,
groupBy, groupBy,
disabled, disabled,
expression, expression,
legendFormula, legendFormula,
aggregateOperators,
dataSource,
}); });
}; };
@ -107,10 +130,11 @@ export const externalCallDuration = ({
key: WidgetKeys.SignozExternalCallLatencyCount, key: WidgetKeys.SignozExternalCallLatencyCount,
type: null, type: null,
}; };
const expression = FORMULA.DATABASE_CALLS_AVG_DURATION; const expression = FORMULA.DATABASE_CALLS_AVG_DURATION;
const legendFormula = 'Average Duration'; const legendFormula = 'Average Duration';
const legend = ''; const legend = '';
const disabled = true; const disabled = Array(2).fill(true);
const additionalItemsA: TagFilterItem[] = [ const additionalItemsA: TagFilterItem[] = [
{ {
id: '', id: '',
@ -125,17 +149,29 @@ export const externalCallDuration = ({
}, },
...tagFilterItems, ...tagFilterItems,
]; ];
const additionalItemsB = additionalItemsA;
return getQueryBuilderQuerieswithFormula({ const autocompleteData: BaseAutocompleteData[] = [
autocompleteDataA, autocompleteDataA,
autocompleteDataB, autocompleteDataB,
];
const additionalItems: TagFilterItem[][] = [
additionalItemsA, additionalItemsA,
additionalItemsB, additionalItemsA,
legend, ];
const legends = Array(2).fill(legend);
const aggregateOperators = Array(2).fill(MetricAggregateOperator.SUM);
return getQueryBuilderQuerieswithFormula({
autocompleteData,
additionalItems,
legends,
disabled, disabled,
expression, expression,
legendFormula, legendFormula,
aggregateOperators,
dataSource: DataSource.METRICS,
}); });
}; };
@ -169,13 +205,15 @@ export const externalCallRpsByAddress = ({
], ],
]; ];
const legends: string[] = [legend]; const legends = [legend];
const dataSource = DataSource.METRICS;
return getQueryBuilderQueries({ return getQueryBuilderQueries({
autocompleteData, autocompleteData,
groupBy, groupBy,
legends, legends,
filterItems, filterItems,
dataSource: DataSource.METRICS, dataSource,
}); });
}; };
@ -198,7 +236,7 @@ export const externalCallDurationByAddress = ({
}; };
const expression = FORMULA.DATABASE_CALLS_AVG_DURATION; const expression = FORMULA.DATABASE_CALLS_AVG_DURATION;
const legendFormula = legend; const legendFormula = legend;
const disabled = true; const disabled = [true, true];
const additionalItemsA: TagFilterItem[] = [ const additionalItemsA: TagFilterItem[] = [
{ {
id: '', id: '',
@ -213,26 +251,30 @@ export const externalCallDurationByAddress = ({
}, },
...tagFilterItems, ...tagFilterItems,
]; ];
const additionalItemsB = additionalItemsA;
return getQueryBuilderQuerieswithFormula({ const autocompleteData: BaseAutocompleteData[] = [
autocompleteDataA, autocompleteDataA,
autocompleteDataB, autocompleteDataB,
];
const additionalItems: TagFilterItem[][] = [
additionalItemsA, additionalItemsA,
additionalItemsB, additionalItemsA,
legend, ];
const legends = Array(2).fill(legend);
const aggregateOperators = Array(2).fill(MetricAggregateOperator.SUM_RATE);
const dataSource = DataSource.METRICS;
return getQueryBuilderQuerieswithFormula({
autocompleteData,
additionalItems,
legends,
groupBy, groupBy,
disabled, disabled,
expression, expression,
legendFormula, legendFormula,
aggregateOperators,
dataSource,
}); });
}; };
interface ExternalCallDurationByAddressProps extends ExternalCallProps {
legend: string;
}
export interface ExternalCallProps {
servicename: IServiceName['servicename'];
tagFilterItems: TagFilterItem[];
}

View File

@ -1,20 +1,21 @@
import { import {
alphabet,
initialFormulaBuilderFormValues, initialFormulaBuilderFormValues,
initialQueryBuilderFormValuesMap, initialQueryBuilderFormValuesMap,
} from 'constants/queryBuilder'; } from 'constants/queryBuilder';
import getStep from 'lib/getStep'; import getStep from 'lib/getStep';
import store from 'store'; import store from 'store';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { import {
IBuilderQuery,
TagFilterItem,
} from 'types/api/queryBuilder/queryBuilderData';
import {
DataSource,
MetricAggregateOperator, MetricAggregateOperator,
QueryBuilderData, QueryBuilderData,
} from 'types/common/queryBuilder'; } from 'types/common/queryBuilder';
import {
BuilderQueriesProps,
BuilderQuerieswithFormulaProps,
} from '../Tabs/types';
export const getQueryBuilderQueries = ({ export const getQueryBuilderQueries = ({
autocompleteData, autocompleteData,
groupBy = [], groupBy = [],
@ -61,15 +62,15 @@ export const getQueryBuilderQueries = ({
}); });
export const getQueryBuilderQuerieswithFormula = ({ export const getQueryBuilderQuerieswithFormula = ({
autocompleteDataA, autocompleteData,
autocompleteDataB, additionalItems,
additionalItemsA, legends,
additionalItemsB,
legend,
groupBy = [], groupBy = [],
disabled, disabled,
expression, expression,
legendFormula, legendFormula,
aggregateOperators,
dataSource,
}: BuilderQuerieswithFormulaProps): QueryBuilderData => ({ }: BuilderQuerieswithFormulaProps): QueryBuilderData => ({
queryFormulas: [ queryFormulas: [
{ {
@ -78,17 +79,18 @@ export const getQueryBuilderQuerieswithFormula = ({
legend: legendFormula, legend: legendFormula,
}, },
], ],
queryData: [ queryData: autocompleteData.map((_, index) => ({
{
...initialQueryBuilderFormValuesMap.metrics, ...initialQueryBuilderFormValuesMap.metrics,
aggregateOperator: MetricAggregateOperator.SUM_RATE, aggregateOperator: aggregateOperators[index],
disabled, disabled: disabled[index],
groupBy, groupBy,
legend, legend: legends[index],
aggregateAttribute: autocompleteDataA, aggregateAttribute: autocompleteData[index],
queryName: alphabet[index],
expression: alphabet[index],
reduceTo: 'sum', reduceTo: 'sum',
filters: { filters: {
items: additionalItemsA, items: additionalItems[index],
op: 'AND', op: 'AND',
}, },
stepInterval: getStep({ stepInterval: getStep({
@ -96,48 +98,6 @@ export const getQueryBuilderQuerieswithFormula = ({
inputFormat: 'ns', inputFormat: 'ns',
start: store.getState().globalTime.minTime, start: store.getState().globalTime.minTime,
}), }),
}, dataSource,
{ })),
...initialQueryBuilderFormValuesMap.metrics,
aggregateOperator: MetricAggregateOperator.SUM_RATE,
disabled,
groupBy,
legend,
aggregateAttribute: autocompleteDataB,
queryName: 'B',
expression: 'B',
reduceTo: 'sum',
filters: {
items: additionalItemsB,
op: 'AND',
},
stepInterval: getStep({
end: store.getState().globalTime.maxTime,
inputFormat: 'ns',
start: store.getState().globalTime.minTime,
}),
},
],
}); });
interface BuilderQueriesProps {
autocompleteData: BaseAutocompleteData[];
groupBy?: BaseAutocompleteData[];
legends: string[];
filterItems: TagFilterItem[][];
aggregateOperator?: string[];
dataSource: DataSource;
queryNameAndExpression?: string[];
}
interface BuilderQuerieswithFormulaProps {
autocompleteDataA: BaseAutocompleteData;
autocompleteDataB: BaseAutocompleteData;
legend: string;
disabled: boolean;
groupBy?: BaseAutocompleteData[];
expression: string;
legendFormula: string;
additionalItemsA: TagFilterItem[];
additionalItemsB: TagFilterItem[];
}

View File

@ -1,7 +1,11 @@
import { OPERATORS } from 'constants/queryBuilder'; import { OPERATORS } from 'constants/queryBuilder';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, QueryBuilderData } from 'types/common/queryBuilder'; import {
DataSource,
MetricAggregateOperator,
QueryBuilderData,
} from 'types/common/queryBuilder';
import { import {
DataType, DataType,
@ -14,7 +18,7 @@ import {
QUERYNAME_AND_EXPRESSION, QUERYNAME_AND_EXPRESSION,
WidgetKeys, WidgetKeys,
} from '../constant'; } from '../constant';
import { IServiceName } from '../Tabs/types'; import { LatencyProps, OperationPerSecProps } from '../Tabs/types';
import { import {
getQueryBuilderQueries, getQueryBuilderQueries,
getQueryBuilderQuerieswithFormula, getQueryBuilderQuerieswithFormula,
@ -35,9 +39,7 @@ export const latency = ({
type: isSpanMetricEnable ? null : MetricsType.Tag, type: isSpanMetricEnable ? null : MetricsType.Tag,
}; };
const autocompleteData: BaseAutocompleteData[] = Array(3).fill( const autocompleteData = Array(3).fill(newAutoCompleteData);
newAutoCompleteData,
);
const filterItem: TagFilterItem[] = [ const filterItem: TagFilterItem[] = [
{ {
@ -65,17 +67,21 @@ export const latency = ({
...tagFilterItems, ...tagFilterItems,
]; ];
const filterItems: TagFilterItem[][] = Array(3).fill([...filterItem]); const filterItems = Array(3).fill([...filterItem]);
const legends = LATENCY_AGGREGATEOPERATOR;
const aggregateOperator = isSpanMetricEnable
? LATENCY_AGGREGATEOPERATOR_SPAN_METRICS
: LATENCY_AGGREGATEOPERATOR;
const dataSource = isSpanMetricEnable ? DataSource.METRICS : DataSource.TRACES;
const queryNameAndExpression = QUERYNAME_AND_EXPRESSION;
return getQueryBuilderQueries({ return getQueryBuilderQueries({
autocompleteData, autocompleteData,
legends: LATENCY_AGGREGATEOPERATOR, legends,
filterItems, filterItems,
aggregateOperator: isSpanMetricEnable aggregateOperator,
? LATENCY_AGGREGATEOPERATOR_SPAN_METRICS dataSource,
: LATENCY_AGGREGATEOPERATOR, queryNameAndExpression,
dataSource: isSpanMetricEnable ? DataSource.METRICS : DataSource.TRACES,
queryNameAndExpression: QUERYNAME_AND_EXPRESSION,
}); });
}; };
@ -121,11 +127,14 @@ export const operationPerSec = ({
], ],
]; ];
const legends = OPERATION_LEGENDS;
const dataSource = DataSource.METRICS;
return getQueryBuilderQueries({ return getQueryBuilderQueries({
autocompleteData, autocompleteData,
legends: OPERATION_LEGENDS, legends,
filterItems, filterItems,
dataSource: DataSource.METRICS, dataSource,
}); });
}; };
@ -146,6 +155,9 @@ export const errorPercentage = ({
isColumn: true, isColumn: true,
type: null, type: null,
}; };
const autocompleteData = [autocompleteDataA, autocompleteDataB];
const additionalItemsA: TagFilterItem[] = [ const additionalItemsA: TagFilterItem[] = [
{ {
id: '', id: '',
@ -209,27 +221,25 @@ export const errorPercentage = ({
...tagFilterItems, ...tagFilterItems,
]; ];
const additionalItems = [additionalItemsA, additionalItemsB];
const legends = [GraphTitle.ERROR_PERCENTAGE];
const disabled = [true, true];
const expression = FORMULA.ERROR_PERCENTAGE;
const legendFormula = GraphTitle.ERROR_PERCENTAGE;
const aggregateOperators = [
MetricAggregateOperator.SUM_RATE,
MetricAggregateOperator.SUM_RATE,
];
const dataSource = DataSource.METRICS;
return getQueryBuilderQuerieswithFormula({ return getQueryBuilderQuerieswithFormula({
autocompleteDataA, autocompleteData,
autocompleteDataB, additionalItems,
additionalItemsA, legends,
additionalItemsB, disabled,
legend: GraphTitle.ERROR_PERCENTAGE, expression,
disabled: true, legendFormula,
expression: FORMULA.ERROR_PERCENTAGE, aggregateOperators,
legendFormula: GraphTitle.ERROR_PERCENTAGE, dataSource,
}); });
}; };
export interface OperationPerSecProps {
servicename: IServiceName['servicename'];
tagFilterItems: TagFilterItem[];
topLevelOperations: string[];
}
export interface LatencyProps {
servicename: IServiceName['servicename'];
tagFilterItems: TagFilterItem[];
isSpanMetricEnable?: boolean;
topLevelOperationsRoute: string[];
}

View File

@ -0,0 +1,142 @@
import { OPERATORS } from 'constants/queryBuilder';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import {
DataSource,
MetricAggregateOperator,
QueryBuilderData,
} from 'types/common/queryBuilder';
import {
DataType,
GraphTitle,
KeyOperationTableHeader,
MetricsType,
WidgetKeys,
} from '../constant';
import { TopOperationQueryFactoryProps } from '../Tabs/types';
import { getQueryBuilderQuerieswithFormula } from './MetricsPageQueriesFactory';
export const topOperationQueries = ({
servicename,
}: TopOperationQueryFactoryProps): QueryBuilderData => {
const latencyAutoCompleteData: BaseAutocompleteData = {
key: WidgetKeys.Signoz_latency_bucket,
dataType: DataType.FLOAT64,
isColumn: true,
type: null,
};
const errorRateAutoCompleteData: BaseAutocompleteData = {
key: WidgetKeys.SignozCallsTotal,
dataType: DataType.FLOAT64,
isColumn: true,
type: null,
};
const numOfCallAutoCompleteData: BaseAutocompleteData = {
key: WidgetKeys.SignozLatencyCount,
dataType: DataType.FLOAT64,
isColumn: true,
type: null,
};
const latencyAndNumberOfCallAdditionalItems: TagFilterItem[] = [
{
id: '',
key: {
key: WidgetKeys.Service_name,
dataType: DataType.STRING,
isColumn: false,
type: MetricsType.Resource,
},
value: [servicename],
op: OPERATORS.IN,
},
];
const errorRateAdditionalItemsA: TagFilterItem[] = [
{
id: '',
key: {
dataType: DataType.STRING,
isColumn: false,
key: WidgetKeys.Service_name,
type: MetricsType.Resource,
},
op: OPERATORS.IN,
value: [servicename],
},
{
id: '',
key: {
dataType: DataType.INT64,
isColumn: false,
key: WidgetKeys.StatusCode,
type: MetricsType.Tag,
},
op: OPERATORS.IN,
value: ['STATUS_CODE_ERROR'],
},
];
const errorRateAdditionalItemsB = latencyAndNumberOfCallAdditionalItems;
const groupBy: BaseAutocompleteData[] = [
{
dataType: DataType.STRING,
isColumn: false,
key: WidgetKeys.Operation,
type: MetricsType.Tag,
},
];
const autocompleteData = [
latencyAutoCompleteData,
latencyAutoCompleteData,
latencyAutoCompleteData,
errorRateAutoCompleteData,
errorRateAutoCompleteData,
numOfCallAutoCompleteData,
];
const additionalItems = [
latencyAndNumberOfCallAdditionalItems,
latencyAndNumberOfCallAdditionalItems,
latencyAndNumberOfCallAdditionalItems,
errorRateAdditionalItemsA,
errorRateAdditionalItemsB,
latencyAndNumberOfCallAdditionalItems,
];
const disabled = [false, false, false, true, true, false];
const legends = [
KeyOperationTableHeader.P50,
KeyOperationTableHeader.P90,
KeyOperationTableHeader.P99,
KeyOperationTableHeader.ERROR_RATE,
KeyOperationTableHeader.ERROR_RATE,
KeyOperationTableHeader.NUM_OF_CALLS,
];
const aggregateOperators = [
MetricAggregateOperator.HIST_QUANTILE_50,
MetricAggregateOperator.HIST_QUANTILE_90,
MetricAggregateOperator.HIST_QUANTILE_99,
MetricAggregateOperator.SUM_RATE,
MetricAggregateOperator.SUM_RATE,
MetricAggregateOperator.SUM_RATE,
];
const expression = 'D*100/E';
const legendFormula = GraphTitle.ERROR_PERCENTAGE;
const dataSource = DataSource.METRICS;
return getQueryBuilderQuerieswithFormula({
autocompleteData,
additionalItems,
disabled,
legends,
aggregateOperators,
expression,
legendFormula,
dataSource,
groupBy,
});
};

View File

@ -1,4 +1,5 @@
import { Col } from 'antd'; import { Col } from 'antd';
import { PANEL_TYPES } from 'constants/queryBuilder';
import Graph from 'container/GridGraphLayout/Graph/'; import Graph from 'container/GridGraphLayout/Graph/';
import { import {
databaseCallsAvgDuration, databaseCallsAvgDuration,
@ -50,8 +51,8 @@ function DBCall(): JSX.Element {
const databaseCallsRPSWidget = useMemo( const databaseCallsRPSWidget = useMemo(
() => () =>
getWidgetQueryBuilder( getWidgetQueryBuilder({
{ query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
builder: databaseCallsRPS({ builder: databaseCallsRPS({
@ -62,14 +63,15 @@ function DBCall(): JSX.Element {
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
}, },
GraphTitle.DATABASE_CALLS_RPS, title: GraphTitle.DATABASE_CALLS_RPS,
), panelTypes: PANEL_TYPES.TIME_SERIES,
}),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
const databaseCallsAverageDurationWidget = useMemo( const databaseCallsAverageDurationWidget = useMemo(
() => () =>
getWidgetQueryBuilder( getWidgetQueryBuilder({
{ query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
builder: databaseCallsAvgDuration({ builder: databaseCallsAvgDuration({
@ -79,8 +81,9 @@ function DBCall(): JSX.Element {
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
}, },
GraphTitle.DATABASE_CALLS_AVG_DURATION, title: GraphTitle.DATABASE_CALLS_AVG_DURATION,
), panelTypes: PANEL_TYPES.TIME_SERIES,
}),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );

View File

@ -1,4 +1,5 @@
import { Col } from 'antd'; import { Col } from 'antd';
import { PANEL_TYPES } from 'constants/queryBuilder';
import Graph from 'container/GridGraphLayout/Graph/'; import Graph from 'container/GridGraphLayout/Graph/';
import { import {
externalCallDuration, externalCallDuration,
@ -41,8 +42,8 @@ function External(): JSX.Element {
const externalCallErrorWidget = useMemo( const externalCallErrorWidget = useMemo(
() => () =>
getWidgetQueryBuilder( getWidgetQueryBuilder({
{ query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
builder: externalCallErrorPercent({ builder: externalCallErrorPercent({
@ -53,8 +54,9 @@ function External(): JSX.Element {
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
}, },
GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE, title: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE,
), panelTypes: PANEL_TYPES.TIME_SERIES,
}),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
@ -65,8 +67,8 @@ function External(): JSX.Element {
const externalCallDurationWidget = useMemo( const externalCallDurationWidget = useMemo(
() => () =>
getWidgetQueryBuilder( getWidgetQueryBuilder({
{ query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
builder: externalCallDuration({ builder: externalCallDuration({
@ -76,15 +78,16 @@ function External(): JSX.Element {
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
}, },
GraphTitle.EXTERNAL_CALL_DURATION, title: GraphTitle.EXTERNAL_CALL_DURATION,
), panelTypes: PANEL_TYPES.TIME_SERIES,
}),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
const externalCallRPSWidget = useMemo( const externalCallRPSWidget = useMemo(
() => () =>
getWidgetQueryBuilder( getWidgetQueryBuilder({
{ query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
builder: externalCallRpsByAddress({ builder: externalCallRpsByAddress({
@ -95,15 +98,16 @@ function External(): JSX.Element {
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
}, },
GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS, title: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS,
), panelTypes: PANEL_TYPES.TIME_SERIES,
}),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
const externalCallDurationAddressWidget = useMemo( const externalCallDurationAddressWidget = useMemo(
() => () =>
getWidgetQueryBuilder( getWidgetQueryBuilder({
{ query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
builder: externalCallDurationByAddress({ builder: externalCallDurationByAddress({
@ -114,8 +118,9 @@ function External(): JSX.Element {
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
}, },
GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS, title: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS,
), panelTypes: PANEL_TYPES.TIME_SERIES,
}),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );

View File

@ -2,10 +2,13 @@ import getTopLevelOperations, {
ServiceDataProps, ServiceDataProps,
} from 'api/metrics/getTopLevelOperations'; } from 'api/metrics/getTopLevelOperations';
import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
import { FeatureKeys } from 'constants/features';
import { QueryParams } from 'constants/query'; import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
import ROUTES from 'constants/routes'; import ROUTES from 'constants/routes';
import { routeConfig } from 'container/SideNav/config'; import { routeConfig } from 'container/SideNav/config';
import { getQueryString } from 'container/SideNav/helper'; import { getQueryString } from 'container/SideNav/helper';
import useFeatureFlag from 'hooks/useFeatureFlag';
import useResourceAttribute from 'hooks/useResourceAttribute'; import useResourceAttribute from 'hooks/useResourceAttribute';
import { import {
convertRawQueriesToTraceSelectedTags, convertRawQueriesToTraceSelectedTags,
@ -29,10 +32,11 @@ import {
errorPercentage, errorPercentage,
operationPerSec, operationPerSec,
} from '../MetricsPageQueries/OverviewQueries'; } from '../MetricsPageQueries/OverviewQueries';
import { Col, Row } from '../styles'; import { Card, Col, Row } from '../styles';
import ServiceOverview from './Overview/ServiceOverview'; import ServiceOverview from './Overview/ServiceOverview';
import TopLevelOperation from './Overview/TopLevelOperations'; import TopLevelOperation from './Overview/TopLevelOperations';
import TopOperation from './Overview/TopOperation'; import TopOperation from './Overview/TopOperation';
import TopOperationMetrics from './Overview/TopOperationMetrics';
import { Button } from './styles'; import { Button } from './styles';
import { IServiceName } from './types'; import { IServiceName } from './types';
import { import {
@ -53,6 +57,8 @@ function Application(): JSX.Element {
() => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [], () => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [],
[queries], [queries],
); );
const isSpanMetricEnabled = useFeatureFlag(FeatureKeys.USE_SPAN_METRICS)
?.active;
const handleSetTimeStamp = useCallback((selectTime: number) => { const handleSetTimeStamp = useCallback((selectTime: number) => {
setSelectedTimeStamp(selectTime); setSelectedTimeStamp(selectTime);
@ -104,8 +110,8 @@ function Application(): JSX.Element {
const operationPerSecWidget = useMemo( const operationPerSecWidget = useMemo(
() => () =>
getWidgetQueryBuilder( getWidgetQueryBuilder({
{ query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
builder: operationPerSec({ builder: operationPerSec({
@ -116,15 +122,16 @@ function Application(): JSX.Element {
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
}, },
GraphTitle.RATE_PER_OPS, title: GraphTitle.RATE_PER_OPS,
), panelTypes: PANEL_TYPES.TIME_SERIES,
}),
[servicename, tagFilterItems, topLevelOperationsRoute], [servicename, tagFilterItems, topLevelOperationsRoute],
); );
const errorPercentageWidget = useMemo( const errorPercentageWidget = useMemo(
() => () =>
getWidgetQueryBuilder( getWidgetQueryBuilder({
{ query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
builder: errorPercentage({ builder: errorPercentage({
@ -135,8 +142,9 @@ function Application(): JSX.Element {
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
}, },
GraphTitle.ERROR_PERCENTAGE, title: GraphTitle.ERROR_PERCENTAGE,
), panelTypes: PANEL_TYPES.TIME_SERIES,
}),
[servicename, tagFilterItems, topLevelOperationsRoute], [servicename, tagFilterItems, topLevelOperationsRoute],
); );
@ -239,7 +247,9 @@ function Application(): JSX.Element {
</Col> </Col>
<Col span={12}> <Col span={12}>
<TopOperation /> <Card>
{isSpanMetricEnabled ? <TopOperationMetrics /> : <TopOperation />}
</Card>
</Col> </Col>
</Row> </Row>
</> </>

View File

@ -1,4 +1,5 @@
import { FeatureKeys } from 'constants/features'; import { FeatureKeys } from 'constants/features';
import { PANEL_TYPES } from 'constants/queryBuilder';
import Graph from 'container/GridGraphLayout/Graph/'; import Graph from 'container/GridGraphLayout/Graph/';
import { GraphTitle } from 'container/MetricsApplication/constant'; import { GraphTitle } from 'container/MetricsApplication/constant';
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory'; import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
@ -31,8 +32,8 @@ function ServiceOverview({
const latencyWidget = useMemo( const latencyWidget = useMemo(
() => () =>
getWidgetQueryBuilder( getWidgetQueryBuilder({
{ query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
builder: latency({ builder: latency({
@ -44,8 +45,9 @@ function ServiceOverview({
clickhouse_sql: [], clickhouse_sql: [],
id: uuid(), id: uuid(),
}, },
GraphTitle.LATENCY, title: GraphTitle.LATENCY,
), panelTypes: PANEL_TYPES.TIME_SERIES,
}),
[servicename, tagFilterItems, isSpanMetricEnable, topLevelOperationsRoute], [servicename, tagFilterItems, isSpanMetricEnable, topLevelOperationsRoute],
); );

View File

@ -0,0 +1,39 @@
import { Tooltip, Typography } from 'antd';
import { navigateToTrace } from 'container/MetricsApplication/utils';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
function ColumnWithLink({
servicename,
minTime,
maxTime,
selectedTraceTags,
record,
}: LinkColumnProps): JSX.Element {
const text = record.toString();
const handleOnClick = (operation: string) => (): void => {
navigateToTrace({
servicename,
operation,
minTime,
maxTime,
selectedTraceTags,
});
};
return (
<Tooltip placement="topLeft" title={text}>
<Typography.Link onClick={handleOnClick(text)}>{text}</Typography.Link>
</Tooltip>
);
}
interface LinkColumnProps {
servicename: string;
minTime: number;
maxTime: number;
selectedTraceTags: string;
record: RowData;
}
export default ColumnWithLink;

View File

@ -0,0 +1,11 @@
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { ReactNode } from 'react';
import { TableRendererProps } from '../../types';
export const getTableColumnRenderer = ({
columnName,
renderFunction,
}: TableRendererProps): Record<string, (record: RowData) => ReactNode> => ({
[columnName]: renderFunction,
});

View File

@ -1,6 +1,5 @@
import getTopOperations from 'api/metrics/getTopOperations'; import getTopOperations from 'api/metrics/getTopOperations';
import Spinner from 'components/Spinner'; import Spinner from 'components/Spinner';
import { Card } from 'container/MetricsApplication/styles';
import TopOperationsTable from 'container/MetricsApplication/TopOperationsTable'; import TopOperationsTable from 'container/MetricsApplication/TopOperationsTable';
import useResourceAttribute from 'hooks/useResourceAttribute'; import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils'; import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
@ -35,11 +34,13 @@ function TopOperation(): JSX.Element {
}), }),
}); });
const topOperationData = data || [];
return ( return (
<Card> <>
{isLoading && <Spinner size="large" tip="Loading..." height="40vh" />} {isLoading && <Spinner size="large" tip="Loading..." height="40vh" />}
{!isLoading && <TopOperationsTable data={data || []} />} {!isLoading && <TopOperationsTable data={topOperationData} />}
</Card> </>
); );
} }

View File

@ -0,0 +1,122 @@
import { PANEL_TYPES } from 'constants/queryBuilder';
import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory';
import { topOperationQueries } from 'container/MetricsApplication/MetricsPageQueries/TopOperationQueries';
import { QueryTable } from 'container/QueryTable';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { isEmpty } from 'lodash-es';
import { ReactNode, useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { AppState } from 'store/reducers';
import { EQueryType } from 'types/common/dashboard';
import { GlobalReducer } from 'types/reducer/globalTime';
import { v4 as uuid } from 'uuid';
import { IServiceName } from '../types';
import { title } from './config';
import ColumnWithLink from './TableRenderer/ColumnWithLink';
import { getTableColumnRenderer } from './TableRenderer/TableColumnRenderer';
function TopOperationMetrics(): JSX.Element {
const { servicename } = useParams<IServiceName>();
const [errorMessage, setErrorMessage] = useState<string | undefined>('');
const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector<
AppState,
GlobalReducer
>((state) => state.globalTime);
const { queries } = useResourceAttribute();
const selectedTraceTags = JSON.stringify(
convertRawQueriesToTraceSelectedTags(queries) || [],
);
const keyOperationWidget = useMemo(
() =>
getWidgetQueryBuilder({
query: {
queryType: EQueryType.QUERY_BUILDER,
promql: [],
builder: topOperationQueries({
servicename,
}),
clickhouse_sql: [],
id: uuid(),
},
panelTypes: PANEL_TYPES.TABLE,
}),
[servicename],
);
const updatedQuery = useStepInterval(keyOperationWidget.query);
const isEmptyWidget = useMemo(
() => keyOperationWidget.id === 'empty' || isEmpty(keyOperationWidget),
[keyOperationWidget],
);
const { data, isLoading } = useGetQueryRange(
{
selectedTime: keyOperationWidget?.timePreferance,
graphType: keyOperationWidget?.panelTypes,
query: updatedQuery,
globalSelectedInterval,
variables: getDashboardVariables(),
},
{
queryKey: [
`GetMetricsQueryRange-${keyOperationWidget?.timePreferance}-${globalSelectedInterval}-${keyOperationWidget?.id}`,
keyOperationWidget,
maxTime,
minTime,
globalSelectedInterval,
],
keepPreviousData: true,
enabled: !isEmptyWidget,
refetchOnMount: false,
onError: (error) => {
setErrorMessage(error.message);
},
},
);
const queryTableData = data?.payload.data.newResult.data.result || [];
const renderColumnCell = useMemo(
() =>
getTableColumnRenderer({
columnName: 'operation',
renderFunction: (record: RowData): ReactNode => (
<ColumnWithLink
servicename={servicename}
minTime={minTime}
maxTime={maxTime}
selectedTraceTags={selectedTraceTags}
record={record}
/>
),
}),
[servicename, minTime, maxTime, selectedTraceTags],
);
if (errorMessage) {
return <div>{errorMessage}</div>;
}
return (
<QueryTable
title={title}
query={updatedQuery}
queryTableData={queryTableData}
loading={isLoading}
renderColumnCell={renderColumnCell}
/>
);
}
export default TopOperationMetrics;

View File

@ -0,0 +1 @@
export const title = (): string => 'Key Operations';

View File

@ -1,3 +1,62 @@
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { ReactNode } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder';
export interface IServiceName { export interface IServiceName {
servicename: string; servicename: string;
} }
export interface TopOperationQueryFactoryProps {
servicename: IServiceName['servicename'];
}
export interface ExternalCallDurationByAddressProps extends ExternalCallProps {
legend: string;
}
export interface ExternalCallProps {
servicename: IServiceName['servicename'];
tagFilterItems: TagFilterItem[];
}
export interface BuilderQueriesProps {
autocompleteData: BaseAutocompleteData[];
groupBy?: BaseAutocompleteData[];
legends: string[];
filterItems: TagFilterItem[][];
aggregateOperator?: string[];
dataSource: DataSource;
queryNameAndExpression?: string[];
}
export interface BuilderQuerieswithFormulaProps {
autocompleteData: BaseAutocompleteData[];
legends: string[];
disabled: boolean[];
groupBy?: BaseAutocompleteData[];
expression: string;
legendFormula: string;
additionalItems: TagFilterItem[][];
aggregateOperators: MetricAggregateOperator[];
dataSource: DataSource;
}
export interface OperationPerSecProps {
servicename: IServiceName['servicename'];
tagFilterItems: TagFilterItem[];
topLevelOperations: string[];
}
export interface LatencyProps {
servicename: IServiceName['servicename'];
tagFilterItems: TagFilterItem[];
isSpanMetricEnable?: boolean;
topLevelOperationsRoute: string[];
}
export interface TableRendererProps {
columnName: string;
renderFunction: (record: RowData) => ReactNode;
}

View File

@ -1,17 +1,14 @@
import { Tooltip, Typography } from 'antd'; import { Tooltip, Typography } from 'antd';
import { ColumnsType } from 'antd/lib/table'; import { ColumnsType } from 'antd/lib/table';
import { ResizeTable } from 'components/ResizeTable'; import { ResizeTable } from 'components/ResizeTable';
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import useResourceAttribute from 'hooks/useResourceAttribute'; import useResourceAttribute from 'hooks/useResourceAttribute';
import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils'; import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils';
import history from 'lib/history';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import { getErrorRate } from './utils'; import { getErrorRate, navigateToTrace } from './utils';
function TopOperationsTable(props: TopOperationsTableProps): JSX.Element { function TopOperationsTable(props: TopOperationsTableProps): JSX.Element {
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>( const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
@ -28,16 +25,15 @@ function TopOperationsTable(props: TopOperationsTableProps): JSX.Element {
const params = useParams<{ servicename: string }>(); const params = useParams<{ servicename: string }>();
const handleOnClick = (operation: string): void => { const handleOnClick = (operation: string): void => {
const urlParams = new URLSearchParams();
const { servicename } = params; const { servicename } = params;
urlParams.set(QueryParams.startTime, (minTime / 1000000).toString());
urlParams.set(QueryParams.endTime, (maxTime / 1000000).toString());
history.push( navigateToTrace({
`${ servicename,
ROUTES.TRACE operation,
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"operation":["${operation}"]}&filterToFetchData=["duration","status","serviceName","operation"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false,"operation":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"],"operation":["${operation}"]}&spanAggregateCurrentPage=1`, minTime,
); maxTime,
selectedTraceTags,
});
}; };
const columns: ColumnsType<TopOperationList> = [ const columns: ColumnsType<TopOperationList> = [

View File

@ -28,6 +28,14 @@ export enum GraphTitle {
EXTERNAL_CALL_DURATION_BY_ADDRESS = 'External Call duration(by Address)', EXTERNAL_CALL_DURATION_BY_ADDRESS = 'External Call duration(by Address)',
} }
export enum KeyOperationTableHeader {
P50 = 'P50',
P90 = 'P90',
P99 = 'P99',
NUM_OF_CALLS = 'Number of Calls',
ERROR_RATE = 'Error Rate',
}
export enum DataType { export enum DataType {
STRING = 'string', STRING = 'string',
FLOAT64 = 'float64', FLOAT64 = 'float64',

View File

@ -0,0 +1,15 @@
import { Widgets } from 'types/api/dashboard/getAll';
export interface GetWidgetQueryBuilderProps {
query: Widgets['query'];
title?: string;
panelTypes: Widgets['panelTypes'];
}
export interface NavigateToTraceProps {
servicename: string;
operation: string;
minTime: number;
maxTime: number;
selectedTraceTags: string;
}

View File

@ -1,4 +1,26 @@
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import history from 'lib/history';
import { TopOperationList } from './TopOperationsTable'; import { TopOperationList } from './TopOperationsTable';
import { NavigateToTraceProps } from './types';
export const getErrorRate = (list: TopOperationList): number => export const getErrorRate = (list: TopOperationList): number =>
(list.errorCount / list.numCalls) * 100; (list.errorCount / list.numCalls) * 100;
export const navigateToTrace = ({
servicename,
operation,
minTime,
maxTime,
selectedTraceTags,
}: NavigateToTraceProps): void => {
const urlParams = new URLSearchParams();
urlParams.set(QueryParams.startTime, (minTime / 1000000).toString());
urlParams.set(QueryParams.endTime, (maxTime / 1000000).toString());
history.push(
`${
ROUTES.TRACE
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"operation":["${operation}"]}&filterToFetchData=["duration","status","serviceName","operation"]&spanAggregateCurrentPage=1&selectedTags=${selectedTraceTags}&&isFilterExclude={"serviceName":false,"operation":false}&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"],"operation":["${operation}"]}&spanAggregateCurrentPage=1`,
);
};