From bc4a4edc7f8ac5d2b1bbcca642927df1cc37c9af Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Fri, 28 Jul 2023 21:54:36 +0530 Subject: [PATCH] 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 --- .../MetricsApplication.factory.ts | 12 +- .../MetricsPageQueries/DBCallQueries.ts | 42 ++++-- .../MetricsPageQueries/ExternalQueries.ts | 96 ++++++++---- .../MetricsPageQueriesFactory.ts | 104 ++++--------- .../MetricsPageQueries/OverviewQueries.ts | 80 +++++----- .../MetricsPageQueries/TopOperationQueries.ts | 142 ++++++++++++++++++ .../MetricsApplication/Tabs/DBCall.tsx | 19 ++- .../MetricsApplication/Tabs/External.tsx | 37 +++-- .../MetricsApplication/Tabs/Overview.tsx | 30 ++-- .../Tabs/Overview/ServiceOverview.tsx | 10 +- .../Overview/TableRenderer/ColumnWithLink.tsx | 39 +++++ .../TableRenderer/TableColumnRenderer.tsx | 11 ++ .../Tabs/Overview/TopOperation.tsx | 9 +- .../Tabs/Overview/TopOperationMetrics.tsx | 122 +++++++++++++++ .../Tabs/Overview/config.ts | 1 + .../MetricsApplication/Tabs/types.ts | 59 ++++++++ .../MetricsApplication/TopOperationsTable.tsx | 20 +-- .../container/MetricsApplication/constant.ts | 8 + .../src/container/MetricsApplication/types.ts | 15 ++ .../src/container/MetricsApplication/utils.ts | 22 +++ 20 files changed, 676 insertions(+), 202 deletions(-) create mode 100644 frontend/src/container/MetricsApplication/MetricsPageQueries/TopOperationQueries.ts create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/TableRenderer/ColumnWithLink.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/TableRenderer/TableColumnRenderer.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/TopOperationMetrics.tsx create mode 100644 frontend/src/container/MetricsApplication/Tabs/Overview/config.ts create mode 100644 frontend/src/container/MetricsApplication/types.ts diff --git a/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts b/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts index 880d9edba9..26400a7541 100644 --- a/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts +++ b/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts @@ -1,17 +1,19 @@ -import { PANEL_TYPES } from 'constants/queryBuilder'; import { Widgets } from 'types/api/dashboard/getAll'; import { v4 } from 'uuid'; -export const getWidgetQueryBuilder = ( - query: Widgets['query'], +import { GetWidgetQueryBuilderProps } from './types'; + +export const getWidgetQueryBuilder = ({ + query, title = '', -): Widgets => ({ + panelTypes, +}: GetWidgetQueryBuilderProps): Widgets => ({ description: '', id: v4(), isStacked: false, nullZeroValues: '', opacity: '0', - panelTypes: PANEL_TYPES.TIME_SERIES, + panelTypes, query, timePreferance: 'GLOBAL_TIME', title, diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/DBCallQueries.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/DBCallQueries.ts index c1896b6884..72b9703cdd 100644 --- a/frontend/src/container/MetricsApplication/MetricsPageQueries/DBCallQueries.ts +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/DBCallQueries.ts @@ -1,7 +1,11 @@ import { OPERATORS } from 'constants/queryBuilder'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; 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 { IServiceName } from '../Tabs/types'; @@ -44,13 +48,14 @@ export const databaseCallsRPS = ({ ]; const legends = [legend]; + const dataSource = DataSource.METRICS; return getQueryBuilderQueries({ autocompleteData, groupBy, legends, filterItems, - dataSource: DataSource.METRICS, + dataSource, }); }; @@ -85,17 +90,36 @@ export const databaseCallsAvgDuration = ({ }, ...tagFilterItems, ]; - const additionalItemsB = additionalItemsA; - return getQueryBuilderQuerieswithFormula({ + const autocompleteData: BaseAutocompleteData[] = [ autocompleteDataA, autocompleteDataB, + ]; + + const additionalItems: TagFilterItem[][] = [ additionalItemsA, - additionalItemsB, - legend: '', - disabled: true, - expression: FORMULA.DATABASE_CALLS_AVG_DURATION, - legendFormula: 'Average Duration', + additionalItemsA, + ]; + + const legends = ['', '']; + 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, }); }; diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts index 1d375f2e53..b140882c46 100644 --- a/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/ExternalQueries.ts @@ -1,10 +1,17 @@ import { OPERATORS } from 'constants/queryBuilder'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; 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 { IServiceName } from '../Tabs/types'; +import { + ExternalCallDurationByAddressProps, + ExternalCallProps, +} from '../Tabs/types'; import { getQueryBuilderQueries, getQueryBuilderQuerieswithFormula, @@ -36,6 +43,7 @@ export const externalCallErrorPercent = ({ isColumn: true, type: null, }; + const additionalItemsA: TagFilterItem[] = [ { id: '', @@ -71,23 +79,38 @@ export const externalCallErrorPercent = ({ type: MetricsType.Resource, }, op: OPERATORS.IN, - value: [`${servicename}`], + value: [servicename], }, ...tagFilterItems, ]; + const legendFormula = legend; const expression = FORMULA.ERROR_PERCENTAGE; - const disabled = true; - return getQueryBuilderQuerieswithFormula({ + const autocompleteData: BaseAutocompleteData[] = [ autocompleteDataA, autocompleteDataB, + ]; + + const additionalItems: TagFilterItem[][] = [ additionalItemsA, 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, disabled, expression, legendFormula, + aggregateOperators, + dataSource, }); }; @@ -107,10 +130,11 @@ export const externalCallDuration = ({ key: WidgetKeys.SignozExternalCallLatencyCount, type: null, }; + const expression = FORMULA.DATABASE_CALLS_AVG_DURATION; const legendFormula = 'Average Duration'; const legend = ''; - const disabled = true; + const disabled = Array(2).fill(true); const additionalItemsA: TagFilterItem[] = [ { id: '', @@ -125,17 +149,29 @@ export const externalCallDuration = ({ }, ...tagFilterItems, ]; - const additionalItemsB = additionalItemsA; - return getQueryBuilderQuerieswithFormula({ + const autocompleteData: BaseAutocompleteData[] = [ autocompleteDataA, autocompleteDataB, + ]; + + const additionalItems: TagFilterItem[][] = [ additionalItemsA, - additionalItemsB, - legend, + additionalItemsA, + ]; + + const legends = Array(2).fill(legend); + const aggregateOperators = Array(2).fill(MetricAggregateOperator.SUM); + + return getQueryBuilderQuerieswithFormula({ + autocompleteData, + additionalItems, + legends, disabled, expression, 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({ autocompleteData, groupBy, legends, filterItems, - dataSource: DataSource.METRICS, + dataSource, }); }; @@ -198,7 +236,7 @@ export const externalCallDurationByAddress = ({ }; const expression = FORMULA.DATABASE_CALLS_AVG_DURATION; const legendFormula = legend; - const disabled = true; + const disabled = [true, true]; const additionalItemsA: TagFilterItem[] = [ { id: '', @@ -213,26 +251,30 @@ export const externalCallDurationByAddress = ({ }, ...tagFilterItems, ]; - const additionalItemsB = additionalItemsA; - return getQueryBuilderQuerieswithFormula({ + const autocompleteData: BaseAutocompleteData[] = [ autocompleteDataA, autocompleteDataB, + ]; + + const additionalItems: TagFilterItem[][] = [ additionalItemsA, - additionalItemsB, - legend, + additionalItemsA, + ]; + + const legends = Array(2).fill(legend); + const aggregateOperators = Array(2).fill(MetricAggregateOperator.SUM_RATE); + const dataSource = DataSource.METRICS; + + return getQueryBuilderQuerieswithFormula({ + autocompleteData, + additionalItems, + legends, groupBy, disabled, expression, legendFormula, + aggregateOperators, + dataSource, }); }; - -interface ExternalCallDurationByAddressProps extends ExternalCallProps { - legend: string; -} - -export interface ExternalCallProps { - servicename: IServiceName['servicename']; - tagFilterItems: TagFilterItem[]; -} diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/MetricsPageQueriesFactory.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/MetricsPageQueriesFactory.ts index 58f535cd04..2412dfce47 100644 --- a/frontend/src/container/MetricsApplication/MetricsPageQueries/MetricsPageQueriesFactory.ts +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/MetricsPageQueriesFactory.ts @@ -1,20 +1,21 @@ import { + alphabet, initialFormulaBuilderFormValues, initialQueryBuilderFormValuesMap, } from 'constants/queryBuilder'; import getStep from 'lib/getStep'; import store from 'store'; -import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { - IBuilderQuery, - TagFilterItem, -} from 'types/api/queryBuilder/queryBuilderData'; -import { - DataSource, MetricAggregateOperator, QueryBuilderData, } from 'types/common/queryBuilder'; +import { + BuilderQueriesProps, + BuilderQuerieswithFormulaProps, +} from '../Tabs/types'; + export const getQueryBuilderQueries = ({ autocompleteData, groupBy = [], @@ -61,15 +62,15 @@ export const getQueryBuilderQueries = ({ }); export const getQueryBuilderQuerieswithFormula = ({ - autocompleteDataA, - autocompleteDataB, - additionalItemsA, - additionalItemsB, - legend, + autocompleteData, + additionalItems, + legends, groupBy = [], disabled, expression, legendFormula, + aggregateOperators, + dataSource, }: BuilderQuerieswithFormulaProps): QueryBuilderData => ({ queryFormulas: [ { @@ -78,66 +79,25 @@ export const getQueryBuilderQuerieswithFormula = ({ legend: legendFormula, }, ], - queryData: [ - { - ...initialQueryBuilderFormValuesMap.metrics, - aggregateOperator: MetricAggregateOperator.SUM_RATE, - disabled, - groupBy, - legend, - aggregateAttribute: autocompleteDataA, - reduceTo: 'sum', - filters: { - items: additionalItemsA, - op: 'AND', - }, - stepInterval: getStep({ - end: store.getState().globalTime.maxTime, - inputFormat: 'ns', - start: store.getState().globalTime.minTime, - }), + queryData: autocompleteData.map((_, index) => ({ + ...initialQueryBuilderFormValuesMap.metrics, + aggregateOperator: aggregateOperators[index], + disabled: disabled[index], + groupBy, + legend: legends[index], + aggregateAttribute: autocompleteData[index], + queryName: alphabet[index], + expression: alphabet[index], + reduceTo: 'sum', + filters: { + items: additionalItems[index], + op: 'AND', }, - { - ...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, - }), - }, - ], + stepInterval: getStep({ + end: store.getState().globalTime.maxTime, + inputFormat: 'ns', + start: store.getState().globalTime.minTime, + }), + dataSource, + })), }); - -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[]; -} diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts index ec2d7b9272..e1137f4cfc 100644 --- a/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/OverviewQueries.ts @@ -1,7 +1,11 @@ import { OPERATORS } from 'constants/queryBuilder'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; -import { DataSource, QueryBuilderData } from 'types/common/queryBuilder'; +import { + DataSource, + MetricAggregateOperator, + QueryBuilderData, +} from 'types/common/queryBuilder'; import { DataType, @@ -14,7 +18,7 @@ import { QUERYNAME_AND_EXPRESSION, WidgetKeys, } from '../constant'; -import { IServiceName } from '../Tabs/types'; +import { LatencyProps, OperationPerSecProps } from '../Tabs/types'; import { getQueryBuilderQueries, getQueryBuilderQuerieswithFormula, @@ -35,9 +39,7 @@ export const latency = ({ type: isSpanMetricEnable ? null : MetricsType.Tag, }; - const autocompleteData: BaseAutocompleteData[] = Array(3).fill( - newAutoCompleteData, - ); + const autocompleteData = Array(3).fill(newAutoCompleteData); const filterItem: TagFilterItem[] = [ { @@ -65,17 +67,21 @@ export const latency = ({ ...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({ autocompleteData, - legends: LATENCY_AGGREGATEOPERATOR, + legends, filterItems, - aggregateOperator: isSpanMetricEnable - ? LATENCY_AGGREGATEOPERATOR_SPAN_METRICS - : LATENCY_AGGREGATEOPERATOR, - dataSource: isSpanMetricEnable ? DataSource.METRICS : DataSource.TRACES, - queryNameAndExpression: QUERYNAME_AND_EXPRESSION, + aggregateOperator, + dataSource, + queryNameAndExpression, }); }; @@ -121,11 +127,14 @@ export const operationPerSec = ({ ], ]; + const legends = OPERATION_LEGENDS; + const dataSource = DataSource.METRICS; + return getQueryBuilderQueries({ autocompleteData, - legends: OPERATION_LEGENDS, + legends, filterItems, - dataSource: DataSource.METRICS, + dataSource, }); }; @@ -146,6 +155,9 @@ export const errorPercentage = ({ isColumn: true, type: null, }; + + const autocompleteData = [autocompleteDataA, autocompleteDataB]; + const additionalItemsA: TagFilterItem[] = [ { id: '', @@ -209,27 +221,25 @@ export const errorPercentage = ({ ...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({ - autocompleteDataA, - autocompleteDataB, - additionalItemsA, - additionalItemsB, - legend: GraphTitle.ERROR_PERCENTAGE, - disabled: true, - expression: FORMULA.ERROR_PERCENTAGE, - legendFormula: GraphTitle.ERROR_PERCENTAGE, + autocompleteData, + additionalItems, + legends, + disabled, + expression, + legendFormula, + aggregateOperators, + dataSource, }); }; - -export interface OperationPerSecProps { - servicename: IServiceName['servicename']; - tagFilterItems: TagFilterItem[]; - topLevelOperations: string[]; -} - -export interface LatencyProps { - servicename: IServiceName['servicename']; - tagFilterItems: TagFilterItem[]; - isSpanMetricEnable?: boolean; - topLevelOperationsRoute: string[]; -} diff --git a/frontend/src/container/MetricsApplication/MetricsPageQueries/TopOperationQueries.ts b/frontend/src/container/MetricsApplication/MetricsPageQueries/TopOperationQueries.ts new file mode 100644 index 0000000000..6f75d9666d --- /dev/null +++ b/frontend/src/container/MetricsApplication/MetricsPageQueries/TopOperationQueries.ts @@ -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, + }); +}; diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx index e2fd6d240f..678d271d11 100644 --- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx @@ -1,4 +1,5 @@ import { Col } from 'antd'; +import { PANEL_TYPES } from 'constants/queryBuilder'; import Graph from 'container/GridGraphLayout/Graph/'; import { databaseCallsAvgDuration, @@ -50,8 +51,8 @@ function DBCall(): JSX.Element { const databaseCallsRPSWidget = useMemo( () => - getWidgetQueryBuilder( - { + getWidgetQueryBuilder({ + query: { queryType: EQueryType.QUERY_BUILDER, promql: [], builder: databaseCallsRPS({ @@ -62,14 +63,15 @@ function DBCall(): JSX.Element { clickhouse_sql: [], id: uuid(), }, - GraphTitle.DATABASE_CALLS_RPS, - ), + title: GraphTitle.DATABASE_CALLS_RPS, + panelTypes: PANEL_TYPES.TIME_SERIES, + }), [servicename, tagFilterItems], ); const databaseCallsAverageDurationWidget = useMemo( () => - getWidgetQueryBuilder( - { + getWidgetQueryBuilder({ + query: { queryType: EQueryType.QUERY_BUILDER, promql: [], builder: databaseCallsAvgDuration({ @@ -79,8 +81,9 @@ function DBCall(): JSX.Element { clickhouse_sql: [], id: uuid(), }, - GraphTitle.DATABASE_CALLS_AVG_DURATION, - ), + title: GraphTitle.DATABASE_CALLS_AVG_DURATION, + panelTypes: PANEL_TYPES.TIME_SERIES, + }), [servicename, tagFilterItems], ); diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx index ab1e99f430..6595a11808 100644 --- a/frontend/src/container/MetricsApplication/Tabs/External.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx @@ -1,4 +1,5 @@ import { Col } from 'antd'; +import { PANEL_TYPES } from 'constants/queryBuilder'; import Graph from 'container/GridGraphLayout/Graph/'; import { externalCallDuration, @@ -41,8 +42,8 @@ function External(): JSX.Element { const externalCallErrorWidget = useMemo( () => - getWidgetQueryBuilder( - { + getWidgetQueryBuilder({ + query: { queryType: EQueryType.QUERY_BUILDER, promql: [], builder: externalCallErrorPercent({ @@ -53,8 +54,9 @@ function External(): JSX.Element { clickhouse_sql: [], id: uuid(), }, - GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE, - ), + title: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE, + panelTypes: PANEL_TYPES.TIME_SERIES, + }), [servicename, tagFilterItems], ); @@ -65,8 +67,8 @@ function External(): JSX.Element { const externalCallDurationWidget = useMemo( () => - getWidgetQueryBuilder( - { + getWidgetQueryBuilder({ + query: { queryType: EQueryType.QUERY_BUILDER, promql: [], builder: externalCallDuration({ @@ -76,15 +78,16 @@ function External(): JSX.Element { clickhouse_sql: [], id: uuid(), }, - GraphTitle.EXTERNAL_CALL_DURATION, - ), + title: GraphTitle.EXTERNAL_CALL_DURATION, + panelTypes: PANEL_TYPES.TIME_SERIES, + }), [servicename, tagFilterItems], ); const externalCallRPSWidget = useMemo( () => - getWidgetQueryBuilder( - { + getWidgetQueryBuilder({ + query: { queryType: EQueryType.QUERY_BUILDER, promql: [], builder: externalCallRpsByAddress({ @@ -95,15 +98,16 @@ function External(): JSX.Element { clickhouse_sql: [], id: uuid(), }, - GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS, - ), + title: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS, + panelTypes: PANEL_TYPES.TIME_SERIES, + }), [servicename, tagFilterItems], ); const externalCallDurationAddressWidget = useMemo( () => - getWidgetQueryBuilder( - { + getWidgetQueryBuilder({ + query: { queryType: EQueryType.QUERY_BUILDER, promql: [], builder: externalCallDurationByAddress({ @@ -114,8 +118,9 @@ function External(): JSX.Element { clickhouse_sql: [], id: uuid(), }, - GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS, - ), + title: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS, + panelTypes: PANEL_TYPES.TIME_SERIES, + }), [servicename, tagFilterItems], ); diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index 9d30b624f2..bb34eb78d7 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -2,10 +2,13 @@ import getTopLevelOperations, { ServiceDataProps, } from 'api/metrics/getTopLevelOperations'; import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js'; +import { FeatureKeys } from 'constants/features'; import { QueryParams } from 'constants/query'; +import { PANEL_TYPES } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; import { routeConfig } from 'container/SideNav/config'; import { getQueryString } from 'container/SideNav/helper'; +import useFeatureFlag from 'hooks/useFeatureFlag'; import useResourceAttribute from 'hooks/useResourceAttribute'; import { convertRawQueriesToTraceSelectedTags, @@ -29,10 +32,11 @@ import { errorPercentage, operationPerSec, } from '../MetricsPageQueries/OverviewQueries'; -import { Col, Row } from '../styles'; +import { Card, Col, Row } from '../styles'; import ServiceOverview from './Overview/ServiceOverview'; import TopLevelOperation from './Overview/TopLevelOperations'; import TopOperation from './Overview/TopOperation'; +import TopOperationMetrics from './Overview/TopOperationMetrics'; import { Button } from './styles'; import { IServiceName } from './types'; import { @@ -53,6 +57,8 @@ function Application(): JSX.Element { () => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [], [queries], ); + const isSpanMetricEnabled = useFeatureFlag(FeatureKeys.USE_SPAN_METRICS) + ?.active; const handleSetTimeStamp = useCallback((selectTime: number) => { setSelectedTimeStamp(selectTime); @@ -104,8 +110,8 @@ function Application(): JSX.Element { const operationPerSecWidget = useMemo( () => - getWidgetQueryBuilder( - { + getWidgetQueryBuilder({ + query: { queryType: EQueryType.QUERY_BUILDER, promql: [], builder: operationPerSec({ @@ -116,15 +122,16 @@ function Application(): JSX.Element { clickhouse_sql: [], id: uuid(), }, - GraphTitle.RATE_PER_OPS, - ), + title: GraphTitle.RATE_PER_OPS, + panelTypes: PANEL_TYPES.TIME_SERIES, + }), [servicename, tagFilterItems, topLevelOperationsRoute], ); const errorPercentageWidget = useMemo( () => - getWidgetQueryBuilder( - { + getWidgetQueryBuilder({ + query: { queryType: EQueryType.QUERY_BUILDER, promql: [], builder: errorPercentage({ @@ -135,8 +142,9 @@ function Application(): JSX.Element { clickhouse_sql: [], id: uuid(), }, - GraphTitle.ERROR_PERCENTAGE, - ), + title: GraphTitle.ERROR_PERCENTAGE, + panelTypes: PANEL_TYPES.TIME_SERIES, + }), [servicename, tagFilterItems, topLevelOperationsRoute], ); @@ -239,7 +247,9 @@ function Application(): JSX.Element { - + + {isSpanMetricEnabled ? : } + diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx index 4fb5d4f024..28895d2909 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ServiceOverview.tsx @@ -1,4 +1,5 @@ import { FeatureKeys } from 'constants/features'; +import { PANEL_TYPES } from 'constants/queryBuilder'; import Graph from 'container/GridGraphLayout/Graph/'; import { GraphTitle } from 'container/MetricsApplication/constant'; import { getWidgetQueryBuilder } from 'container/MetricsApplication/MetricsApplication.factory'; @@ -31,8 +32,8 @@ function ServiceOverview({ const latencyWidget = useMemo( () => - getWidgetQueryBuilder( - { + getWidgetQueryBuilder({ + query: { queryType: EQueryType.QUERY_BUILDER, promql: [], builder: latency({ @@ -44,8 +45,9 @@ function ServiceOverview({ clickhouse_sql: [], id: uuid(), }, - GraphTitle.LATENCY, - ), + title: GraphTitle.LATENCY, + panelTypes: PANEL_TYPES.TIME_SERIES, + }), [servicename, tagFilterItems, isSpanMetricEnable, topLevelOperationsRoute], ); diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/TableRenderer/ColumnWithLink.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/TableRenderer/ColumnWithLink.tsx new file mode 100644 index 0000000000..99230f3a75 --- /dev/null +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/TableRenderer/ColumnWithLink.tsx @@ -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 ( + + {text} + + ); +} + +interface LinkColumnProps { + servicename: string; + minTime: number; + maxTime: number; + selectedTraceTags: string; + record: RowData; +} + +export default ColumnWithLink; diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/TableRenderer/TableColumnRenderer.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/TableRenderer/TableColumnRenderer.tsx new file mode 100644 index 0000000000..1720ce9be0 --- /dev/null +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/TableRenderer/TableColumnRenderer.tsx @@ -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 ReactNode> => ({ + [columnName]: renderFunction, +}); diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperation.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperation.tsx index 183ec20e7a..8acd032065 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperation.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperation.tsx @@ -1,6 +1,5 @@ import getTopOperations from 'api/metrics/getTopOperations'; import Spinner from 'components/Spinner'; -import { Card } from 'container/MetricsApplication/styles'; import TopOperationsTable from 'container/MetricsApplication/TopOperationsTable'; import useResourceAttribute from 'hooks/useResourceAttribute'; import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils'; @@ -35,11 +34,13 @@ function TopOperation(): JSX.Element { }), }); + const topOperationData = data || []; + return ( - + <> {isLoading && } - {!isLoading && } - + {!isLoading && } + ); } diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperationMetrics.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperationMetrics.tsx new file mode 100644 index 0000000000..d205bfbd83 --- /dev/null +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/TopOperationMetrics.tsx @@ -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(); + + const [errorMessage, setErrorMessage] = useState(''); + 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 => ( + + ), + }), + [servicename, minTime, maxTime, selectedTraceTags], + ); + + if (errorMessage) { + return
{errorMessage}
; + } + + return ( + + ); +} + +export default TopOperationMetrics; diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/config.ts b/frontend/src/container/MetricsApplication/Tabs/Overview/config.ts new file mode 100644 index 0000000000..03bd1e7d2c --- /dev/null +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/config.ts @@ -0,0 +1 @@ +export const title = (): string => 'Key Operations'; diff --git a/frontend/src/container/MetricsApplication/Tabs/types.ts b/frontend/src/container/MetricsApplication/Tabs/types.ts index 2d60b132ee..971df2c705 100644 --- a/frontend/src/container/MetricsApplication/Tabs/types.ts +++ b/frontend/src/container/MetricsApplication/Tabs/types.ts @@ -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 { 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; +} diff --git a/frontend/src/container/MetricsApplication/TopOperationsTable.tsx b/frontend/src/container/MetricsApplication/TopOperationsTable.tsx index 739ef8af0e..e753a965a5 100644 --- a/frontend/src/container/MetricsApplication/TopOperationsTable.tsx +++ b/frontend/src/container/MetricsApplication/TopOperationsTable.tsx @@ -1,17 +1,14 @@ import { Tooltip, Typography } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import { ResizeTable } from 'components/ResizeTable'; -import { QueryParams } from 'constants/query'; -import ROUTES from 'constants/routes'; import useResourceAttribute from 'hooks/useResourceAttribute'; import { convertRawQueriesToTraceSelectedTags } from 'hooks/useResourceAttribute/utils'; -import history from 'lib/history'; import { useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; import { AppState } from 'store/reducers'; import { GlobalReducer } from 'types/reducer/globalTime'; -import { getErrorRate } from './utils'; +import { getErrorRate, navigateToTrace } from './utils'; function TopOperationsTable(props: TopOperationsTableProps): JSX.Element { const { minTime, maxTime } = useSelector( @@ -28,16 +25,15 @@ function TopOperationsTable(props: TopOperationsTableProps): JSX.Element { const params = useParams<{ servicename: string }>(); const handleOnClick = (operation: string): void => { - const urlParams = new URLSearchParams(); const { servicename } = params; - 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`, - ); + navigateToTrace({ + servicename, + operation, + minTime, + maxTime, + selectedTraceTags, + }); }; const columns: ColumnsType = [ diff --git a/frontend/src/container/MetricsApplication/constant.ts b/frontend/src/container/MetricsApplication/constant.ts index 0e917cce47..8b1564df93 100644 --- a/frontend/src/container/MetricsApplication/constant.ts +++ b/frontend/src/container/MetricsApplication/constant.ts @@ -28,6 +28,14 @@ export enum GraphTitle { 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 { STRING = 'string', FLOAT64 = 'float64', diff --git a/frontend/src/container/MetricsApplication/types.ts b/frontend/src/container/MetricsApplication/types.ts new file mode 100644 index 0000000000..86acab2cf0 --- /dev/null +++ b/frontend/src/container/MetricsApplication/types.ts @@ -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; +} diff --git a/frontend/src/container/MetricsApplication/utils.ts b/frontend/src/container/MetricsApplication/utils.ts index a27242718c..543e792d77 100644 --- a/frontend/src/container/MetricsApplication/utils.ts +++ b/frontend/src/container/MetricsApplication/utils.ts @@ -1,4 +1,26 @@ +import { QueryParams } from 'constants/query'; +import ROUTES from 'constants/routes'; +import history from 'lib/history'; + import { TopOperationList } from './TopOperationsTable'; +import { NavigateToTraceProps } from './types'; export const getErrorRate = (list: TopOperationList): number => (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`, + ); +};