From dfe2af9559eafc8942ad2d87a110ee3c14a415d9 Mon Sep 17 00:00:00 2001 From: Joey <90795735+joey-grafana@users.noreply.github.com> Date: Mon, 24 Mar 2025 09:17:32 +0000 Subject: [PATCH] Tempo: Remove aggregate by (#98474) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remove aggregate by * Update betterer * Run toggles test * Add back group by in schema so it can be checked for * Show error if group by * Update error message * Allow user to remove the group by from the query * Fix assertion --------- Co-authored-by: Piotr Jamróz --- .betterer.results | 3 - .../feature-toggles/index.md | 1 - .../src/types/featureToggles.gen.ts | 4 - .../dataquery/x/TempoDataQuery_types.gen.ts | 2 +- pkg/services/featuremgmt/registry.go | 7 - pkg/services/featuremgmt/toggles_gen.csv | 1 - pkg/services/featuremgmt/toggles_gen.go | 4 - pkg/services/featuremgmt/toggles_gen.json | 3 +- .../kinds/dataquery/types_dataquery_gen.go | 2 +- .../SearchTraceQLEditor/AggregateByAlert.tsx | 20 ++ .../SearchTraceQLEditor/GroupByField.test.tsx | 162 ---------- .../SearchTraceQLEditor/GroupByField.tsx | 193 ----------- .../TraceQLSearch.test.tsx | 28 +- .../SearchTraceQLEditor/TraceQLSearch.tsx | 20 +- .../plugins/datasource/tempo/dataquery.cue | 2 +- .../plugins/datasource/tempo/dataquery.gen.ts | 2 +- .../datasource/tempo/datasource.test.ts | 26 -- .../plugins/datasource/tempo/datasource.ts | 138 +------- .../tempo/language_provider.test.ts | 32 -- .../datasource/tempo/language_provider.ts | 12 - .../datasource/tempo/metricsSummary.test.ts | 300 ------------------ .../datasource/tempo/metricsSummary.ts | 273 ---------------- public/app/plugins/datasource/tempo/utils.ts | 1 - 23 files changed, 61 insertions(+), 1175 deletions(-) create mode 100644 public/app/plugins/datasource/tempo/SearchTraceQLEditor/AggregateByAlert.tsx delete mode 100644 public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.test.tsx delete mode 100644 public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.tsx delete mode 100644 public/app/plugins/datasource/tempo/metricsSummary.test.ts delete mode 100644 public/app/plugins/datasource/tempo/metricsSummary.ts diff --git a/.betterer.results b/.betterer.results index 95e276339e3..053ef6bfd02 100644 --- a/.betterer.results +++ b/.betterer.results @@ -6557,9 +6557,6 @@ exports[`better eslint`] = { "public/app/plugins/datasource/tempo/SearchTraceQLEditor/DurationInput.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], - "public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.tsx:5381": [ - [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] - ], "public/app/plugins/datasource/tempo/SearchTraceQLEditor/SearchField.tsx:5381": [ [0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"] ], diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index bf962a6c350..84bc6c9d3b3 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -150,7 +150,6 @@ Experimental features might be changed or removed without prior notice. | `lokiPredefinedOperations` | Adds predefined query operations to Loki query editor | | `awsDatasourcesTempCredentials` | Support temporary security credentials in AWS plugins for Grafana Cloud customers | | `mlExpressions` | Enable support for Machine Learning in server-side expressions | -| `metricsSummary` | Enables metrics summary queries in the Tempo data source | | `datasourceAPIServers` | Expose some datasources as apiservers. | | `provisioning` | Next generation provisioning... and git | | `permissionsFilterRemoveSubquery` | Alternative permission filter implementation that does not use subqueries for fetching the dashboard folder | diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 40dc24a523d..46f4da08a67 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -271,10 +271,6 @@ export interface FeatureToggles { */ traceQLStreaming?: boolean; /** - * Enables metrics summary queries in the Tempo data source - */ - metricsSummary?: boolean; - /** * Expose some datasources as apiservers. */ datasourceAPIServers?: boolean; diff --git a/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts index 9a279f1653b..342f399cd17 100644 --- a/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/tempo/dataquery/x/TempoDataQuery_types.gen.ts @@ -19,7 +19,7 @@ export interface TempoQuery extends common.DataQuery { exemplars?: number; filters: Array; /** - * Filters that are used to query the metrics summary + * deprecated Filters that are used to query the metrics summary */ groupBy?: Array; /** diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index 320342ff7e2..89f2d6f8220 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -441,13 +441,6 @@ var ( Owner: grafanaObservabilityTracesAndProfilingSquad, Expression: "false", }, - { - Name: "metricsSummary", - Description: "Enables metrics summary queries in the Tempo data source", - Stage: FeatureStageExperimental, - FrontendOnly: true, - Owner: grafanaObservabilityTracesAndProfilingSquad, - }, { Name: "datasourceAPIServers", Description: "Expose some datasources as apiservers.", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index b86059bd2be..62bd4844236 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -58,7 +58,6 @@ awsDatasourcesTempCredentials,experimental,@grafana/aws-datasources,false,false, transformationsRedesign,GA,@grafana/observability-metrics,false,false,true mlExpressions,experimental,@grafana/alerting-squad,false,false,false traceQLStreaming,GA,@grafana/observability-traces-and-profiling,false,false,true -metricsSummary,experimental,@grafana/observability-traces-and-profiling,false,false,true datasourceAPIServers,experimental,@grafana/grafana-app-platform-squad,false,true,false grafanaAPIServerWithExperimentalAPIs,experimental,@grafana/grafana-app-platform-squad,true,true,false provisioning,experimental,@grafana/grafana-app-platform-squad,false,true,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 7207be7a8a2..b1d5e756e5b 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -243,10 +243,6 @@ const ( // Enables response streaming of TraceQL queries of the Tempo data source FlagTraceQLStreaming = "traceQLStreaming" - // FlagMetricsSummary - // Enables metrics summary queries in the Tempo data source - FlagMetricsSummary = "metricsSummary" - // FlagDatasourceAPIServers // Expose some datasources as apiservers. FlagDatasourceAPIServers = "datasourceAPIServers" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 3f65b33ffc0..1d0e357c378 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -2768,7 +2768,8 @@ "metadata": { "name": "metricsSummary", "resourceVersion": "1718727528075", - "creationTimestamp": "2023-08-28T14:02:12Z" + "creationTimestamp": "2023-08-28T14:02:12Z", + "deletionTimestamp": "2024-11-25T10:47:18Z" }, "spec": { "description": "Enables metrics summary queries in the Tempo data source", diff --git a/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go b/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go index 874c4161741..457ed0c9baa 100644 --- a/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go +++ b/pkg/tsdb/tempo/kinds/dataquery/types_dataquery_gen.go @@ -48,7 +48,7 @@ type TempoQuery struct { // Defines the maximum number of spans per spanset that are returned from Tempo Spss *int64 `json:"spss,omitempty"` Filters []TraceqlFilter `json:"filters"` - // Filters that are used to query the metrics summary + // deprecated Filters that are used to query the metrics summary GroupBy []TraceqlFilter `json:"groupBy,omitempty"` // The type of the table that is used to display the search results TableType *SearchTableType `json:"tableType,omitempty"` diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/AggregateByAlert.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/AggregateByAlert.tsx new file mode 100644 index 00000000000..ccb979569df --- /dev/null +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/AggregateByAlert.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +import { Alert, Button } from '@grafana/ui'; + +import { TempoQuery } from '../dataquery.gen'; + +export function AggregateByAlert({ + query, + onChange, +}: { + query: TempoQuery; + onChange?: () => void; +}): React.ReactNode | null { + return query.groupBy ? ( + + The aggregate by feature has been removed. We recommend using Traces Drildown app instead.   + + + ) : null; +} diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.test.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.test.tsx deleted file mode 100644 index 8a35d0905c5..00000000000 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.test.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; -import { useState } from 'react'; - -import { TraceqlSearchScope } from '../dataquery.gen'; -import { TempoDatasource } from '../datasource'; -import TempoLanguageProvider from '../language_provider'; -import { initTemplateSrv } from '../test/test_utils'; -import { TempoQuery } from '../types'; - -import { GroupByField } from './GroupByField'; - -describe('GroupByField', () => { - let user: ReturnType; - - const datasource: TempoDatasource = { - search: { - filters: [], - }, - } as unknown as TempoDatasource; - - const lp = new TempoLanguageProvider(datasource); - datasource.languageProvider = lp; - - let query: TempoQuery = { - refId: 'A', - queryType: 'traceqlSearch', - query: '', - filters: [], - groupBy: [{ id: 'group-by-id', scope: TraceqlSearchScope.Span, tag: 'component' }], - }; - - const onChange = (q: TempoQuery) => { - query = q; - }; - - jest.spyOn(lp, 'getMetricsSummaryTags').mockReturnValue(['component', 'http.method', 'http.status_code']); - jest.spyOn(lp, 'getTags').mockReturnValue(['component', 'http.method', 'http.status_code']); - - beforeEach(() => { - jest.useFakeTimers(); - // Need to use delay: null here to work with fakeTimers - // see https://github.com/testing-library/user-event/issues/833 - user = userEvent.setup({ delay: null }); - - initTemplateSrv([{ name: 'templateVariable1' }, { name: 'templateVariable2' }], {}); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - it('should only show add/remove tag when necessary', async () => { - const GroupByWithProps = () => { - const [query, setQuery] = useState({ - refId: 'A', - queryType: 'traceqlSearch', - key: 'Q-595a9bbc-2a25-49a7-9249-a52a0a475d83-0', - filters: [], - groupBy: [{ id: 'group-by-id', scope: TraceqlSearchScope.Span }], - }); - return ( - setQuery(q)} - isTagsLoading={false} - /> - ); - }; - render(); - expect(screen.queryAllByLabelText('Add tag').length).toBe(0); // not filled in the default tag, so no need to add another one - expect(screen.queryAllByLabelText(/Remove tag/).length).toBe(0); // mot filled in the default tag, so no values to remove - expect(screen.getAllByText('Select tag').length).toBe(1); - - await user.click(screen.getByText('Select tag')); - jest.advanceTimersByTime(1000); - await user.click(screen.getByText('http.method')); - jest.advanceTimersByTime(1000); - expect(screen.getAllByLabelText('Add tag').length).toBe(1); - expect(screen.getAllByLabelText(/Remove tag/).length).toBe(1); - - await user.click(screen.getByLabelText('Add tag')); - jest.advanceTimersByTime(1000); - expect(screen.queryAllByLabelText('Add tag').length).toBe(0); // not filled in the new tag, so no need to add another one - expect(screen.getAllByLabelText(/Remove tag/).length).toBe(2); // one for each tag - - await user.click(screen.getAllByLabelText(/Remove tag/)[1]); - jest.advanceTimersByTime(1000); - expect(screen.queryAllByLabelText('Add tag').length).toBe(1); // filled in the default tag, so can add another one - expect(screen.queryAllByLabelText(/Remove tag/).length).toBe(1); // filled in the default tag, so can remove values - - await user.click(screen.getAllByLabelText(/Remove tag/)[0]); - jest.advanceTimersByTime(1000); - expect(screen.queryAllByLabelText('Add tag').length).toBe(0); // not filled in the default tag, so no need to add another one - expect(screen.queryAllByLabelText(/Remove tag/).length).toBe(0); // mot filled in the default tag, so no values to remove - }); - - it('should update scope when new value is selected in scope input', async () => { - const { container } = render( - - ); - - const scopeSelect = container.querySelector(`input[aria-label="Select scope for filter 1"]`); - expect(scopeSelect).not.toBeNull(); - expect(scopeSelect).toBeInTheDocument(); - - if (scopeSelect) { - await user.click(scopeSelect); - jest.advanceTimersByTime(1000); - const resourceScope = await screen.findByText('resource'); - await user.click(resourceScope); - const groupByFilter = query.groupBy?.find((f) => f.id === 'group-by-id'); - expect(groupByFilter).not.toBeNull(); - expect(groupByFilter?.scope).toBe('resource'); - expect(groupByFilter?.tag).toBe(''); - } - }); - - it('should update tag when new value is selected in tag input', async () => { - const { container } = render( - - ); - - const tagSelect = container.querySelector(`input[aria-label="Select tag for filter 1"]`); - expect(tagSelect).not.toBeNull(); - expect(tagSelect).toBeInTheDocument(); - - if (tagSelect) { - await user.click(tagSelect); - jest.advanceTimersByTime(1000); - const tag = await screen.findByText('http.method'); - await user.click(tag); - const groupByFilter = query.groupBy?.find((f) => f.id === 'group-by-id'); - expect(groupByFilter).not.toBeNull(); - expect(groupByFilter?.tag).toBe('http.method'); - } - }); - - it('should allow selecting template variables', async () => { - const { container } = render( - - ); - - const tagSelect = container.querySelector(`input[aria-label="Select tag for filter 1"]`); - expect(tagSelect).not.toBeNull(); - expect(tagSelect).toBeInTheDocument(); - - if (tagSelect) { - await user.click(tagSelect); - jest.advanceTimersByTime(1000); - expect(await screen.findByText('$templateVariable1')).toBeInTheDocument(); - expect(await screen.findByText('$templateVariable2')).toBeInTheDocument(); - } - }); -}); diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.tsx deleted file mode 100644 index 14134fec7ec..00000000000 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/GroupByField.tsx +++ /dev/null @@ -1,193 +0,0 @@ -import { css } from '@emotion/css'; -import { useEffect, useMemo, useState } from 'react'; -import { v4 as uuidv4 } from 'uuid'; - -import { GrafanaTheme2 } from '@grafana/data'; -import { AccessoryButton } from '@grafana/plugin-ui'; -import { Alert, HorizontalGroup, InputActionMeta, Select, useStyles2 } from '@grafana/ui'; - -import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen'; -import { TempoDatasource } from '../datasource'; -import { OPTIONS_LIMIT } from '../language_provider'; -import { TempoQuery } from '../types'; - -import InlineSearchField from './InlineSearchField'; -import { withTemplateVariableOptions } from './SearchField'; -import { replaceAt } from './utils'; - -interface Props { - datasource: TempoDatasource; - onChange: (value: TempoQuery) => void; - query: Partial & TempoQuery; - isTagsLoading: boolean; - addVariablesToOptions?: boolean; -} - -export const GroupByField = (props: Props) => { - const { datasource, onChange, query, isTagsLoading, addVariablesToOptions } = props; - const styles = useStyles2(getStyles); - const generateId = () => uuidv4().slice(0, 8); - const [tagQuery, setTagQuery] = useState(''); - - useEffect(() => { - if (!query.groupBy || query.groupBy.length === 0) { - onChange({ - ...query, - groupBy: [ - { - id: generateId(), - scope: TraceqlSearchScope.Span, - }, - ], - }); - } - }, [onChange, query]); - - const tagOptions = useMemo( - () => (f: TraceqlFilter) => { - const tags = datasource!.languageProvider.getMetricsSummaryTags(f.scope); - if (tagQuery.length === 0) { - return tags.slice(0, OPTIONS_LIMIT); - } - - const queryLowerCase = tagQuery.toLowerCase(); - return tags.filter((tag) => tag.toLowerCase().includes(queryLowerCase)).slice(0, OPTIONS_LIMIT); - }, - [datasource, tagQuery] - ); - - const addFilter = () => { - updateFilter({ - id: generateId(), - scope: TraceqlSearchScope.Span, - }); - }; - - const removeFilter = (filter: TraceqlFilter) => { - onChange({ ...query, groupBy: query.groupBy?.filter((f) => f.id !== filter.id) }); - }; - - const updateFilter = (filter: TraceqlFilter) => { - const copy = { ...query }; - copy.groupBy ||= []; - const indexOfFilter = copy.groupBy.findIndex((f) => f.id === filter.id); - if (indexOfFilter >= 0) { - copy.groupBy = replaceAt(copy.groupBy, indexOfFilter, filter); - } else { - copy.groupBy.push(filter); - } - onChange(copy); - }; - - const scopeOptions = Object.values(TraceqlSearchScope) - .filter((s) => { - // only add scope if it has tags - return datasource.languageProvider.getTags(s).length > 0; - }) - .map((t) => ({ label: t, value: t })); - - return ( - - <> - {query.groupBy?.map((f, i) => { - const tags = tagOptions(f) - ?.concat(f.tag !== undefined && f.tag !== '' && !tagOptions(f)?.includes(f.tag) ? [f.tag] : []) - .map((t) => ({ - label: t, - value: t, - })); - return ( -
- - { - updateFilter({ ...f, tag: v?.value }); - }} - options={addVariablesToOptions ? withTemplateVariableOptions(tags) : tags} - onInputChange={(value: string, { action }: InputActionMeta) => { - if (action === 'input-change') { - setTagQuery(value); - } - }} - onCloseMenu={() => setTagQuery('')} - placeholder="Select tag" - value={f.tag || ''} - /> - {(f.tag || (query.groupBy?.length ?? 0) > 1) && ( - removeFilter(f)} - tooltip="Remove tag" - title={`Remove tag for filter ${i + 1}`} - variant="secondary" - /> - )} - {f.tag && i === (query.groupBy?.length ?? 0) - 1 && ( - - addFilter()} - tooltip="Add tag" - variant="secondary" - /> - - )} - -
- ); - })} - {query.groupBy && query.groupBy.length > 0 && query.groupBy[0].tag && ( - - The aggregate by feature is deprecated. We recommend using Explore Traces instead. If you want to write your - own TraceQL queries to replicate this API, please check - - this page - - . - - )} - -
- ); -}; - -const getStyles = (theme: GrafanaTheme2) => ({ - addTag: css({ - marginLeft: theme.spacing(1), - }), - notice: css({ - width: '500px', - marginTop: theme.spacing(0.75), - }), - noticeLink: css({ - color: theme.colors.text.link, - textDecoration: 'underline', - marginLeft: '5px', - }), -}); diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.test.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.test.tsx index 69ee73631f7..5957dfd90d2 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.test.tsx +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.test.tsx @@ -2,8 +2,6 @@ import { act, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { useState } from 'react'; -import { config } from '@grafana/runtime'; - import { TraceqlSearchScope } from '../dataquery.gen'; import { TempoDatasource } from '../datasource'; import TempoLanguageProvider from '../language_provider'; @@ -238,26 +236,28 @@ describe('TraceQLSearch', () => { }); }); - it('should not render group by when feature toggle is not enabled', async () => { - await waitFor(() => { + it('should not render group by alert when query does not contain group by', async () => { + await act(async () => { render( ); - const groupBy = screen.queryByText('Aggregate by'); - expect(groupBy).toBeNull(); - expect(groupBy).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: 'Remove aggregate by from this query' })).not.toBeInTheDocument(); }); }); - it('should render group by when feature toggle enabled', async () => { - config.featureToggles.metricsSummary = true; - await waitFor(() => { + it('should render group by alert when query contains group by', async () => { + const onChange = jest.fn(); + await waitFor(async () => { render( - + ); - const groupBy = screen.queryByText('Aggregate by'); - expect(groupBy).not.toBeNull(); - expect(groupBy).toBeInTheDocument(); + const button = screen.queryByRole('button', { name: 'Remove aggregate by from this query' }); + expect(button).toBeInTheDocument(); }); }); }); diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx index 7ef9bd88303..fee68d5c771 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx @@ -13,8 +13,8 @@ import { TempoQueryBuilderOptions } from '../traceql/TempoQueryBuilderOptions'; import { traceqlGrammar } from '../traceql/traceql'; import { TempoQuery } from '../types'; +import { AggregateByAlert } from './AggregateByAlert'; import DurationInput from './DurationInput'; -import { GroupByField } from './GroupByField'; import InlineSearchField from './InlineSearchField'; import SearchField from './SearchField'; import TagsInput from './TagsInput'; @@ -237,15 +237,15 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa addVariablesToOptions={addVariablesToOptions} /> - {config.featureToggles.metricsSummary && ( - - )} + { + delete query.groupBy; + onChange({ + ...query, + }); + }} + />
diff --git a/public/app/plugins/datasource/tempo/dataquery.cue b/public/app/plugins/datasource/tempo/dataquery.cue index ef5a4526dbb..c87f5eddb9e 100644 --- a/public/app/plugins/datasource/tempo/dataquery.cue +++ b/public/app/plugins/datasource/tempo/dataquery.cue @@ -47,7 +47,7 @@ composableKinds: DataQuery: { // Defines the maximum number of spans per spanset that are returned from Tempo spss?: int64 filters: [...#TraceqlFilter] - // Filters that are used to query the metrics summary + // deprecated Filters that are used to query the metrics summary groupBy?: [...#TraceqlFilter] // The type of the table that is used to display the search results tableType?: #SearchTableType diff --git a/public/app/plugins/datasource/tempo/dataquery.gen.ts b/public/app/plugins/datasource/tempo/dataquery.gen.ts index 757960330c8..f55757b5a47 100644 --- a/public/app/plugins/datasource/tempo/dataquery.gen.ts +++ b/public/app/plugins/datasource/tempo/dataquery.gen.ts @@ -17,7 +17,7 @@ export interface TempoQuery extends common.DataQuery { exemplars?: number; filters: Array; /** - * Filters that are used to query the metrics summary + * deprecated Filters that are used to query the metrics summary */ groupBy?: Array; /** diff --git a/public/app/plugins/datasource/tempo/datasource.test.ts b/public/app/plugins/datasource/tempo/datasource.test.ts index c51e27fdec8..cc5683941e8 100644 --- a/public/app/plugins/datasource/tempo/datasource.test.ts +++ b/public/app/plugins/datasource/tempo/datasource.test.ts @@ -172,16 +172,6 @@ describe('Tempo data source', () => { valueType: 'string', }, ], - groupBy: [ - { - id: 'groupBy1', - tag: '$interpolationVar', - }, - { - id: 'groupBy2', - tag: '$interpolationVar', - }, - ], }; } let templateSrv: TemplateSrv; @@ -214,8 +204,6 @@ describe('Tempo data source', () => { expect(queries[0].filters[0].value).toBe(textWithPipe); expect(queries[0].filters[1].value).toBe(text); expect(queries[0].filters[1].tag).toBe(text); - expect(queries[0].groupBy?.[0].tag).toBe(text); - expect(queries[0].groupBy?.[1].tag).toBe(text); }); it('when applying template variables', async () => { @@ -228,8 +216,6 @@ describe('Tempo data source', () => { expect(resp.filters[0].value).toBe(textWithPipe); expect(resp.filters[1].value).toBe(scopedText); expect(resp.filters[1].tag).toBe(scopedText); - expect(resp.groupBy?.[0].tag).toBe(scopedText); - expect(resp.groupBy?.[1].tag).toBe(scopedText); }); it('when serviceMapQuery is an array', async () => { @@ -352,18 +338,6 @@ describe('Tempo data source', () => { expect(edgesFrame.meta?.preferredVisualisationType).toBe('nodeGraph'); }); - it('should format metrics summary query correctly', () => { - const ds = new TempoDatasource(defaultSettings, {} as TemplateSrv); - const queryGroupBy = [ - { id: '1', scope: TraceqlSearchScope.Unscoped, tag: 'component' }, - { id: '2', scope: TraceqlSearchScope.Span, tag: 'name' }, - { id: '3', scope: TraceqlSearchScope.Resource, tag: 'service.name' }, - { id: '4', scope: TraceqlSearchScope.Intrinsic, tag: 'kind' }, - ]; - const groupBy = ds.formatGroupBy(queryGroupBy); - expect(groupBy).toEqual('.component, span.name, resource.service.name, kind'); - }); - describe('test the testDatasource function', () => { it('should return a success msg if response.ok is true', async () => { mockObservable = () => of({ ok: true }); diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index de07cc54e45..0791334f1c1 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -53,7 +53,6 @@ import { totalsMetric, } from './graphTransform'; import TempoLanguageProvider from './language_provider'; -import { createTableFrameFromMetricsSummaryQuery, emptyResponse, MetricsSummary } from './metricsSummary'; import { enhanceTraceQlMetricsResponse, formatTraceQLResponse, @@ -405,19 +404,18 @@ export class TempoDatasource extends DataSourceWithBackend this.hasGroupBy(t)); - if (target) { - const appliedQuery = this.applyVariables(target, options.scopedVars); - const queryFromFilters = this.languageProvider.generateQueryFromFilters(appliedQuery.filters); - subQueries.push(this.handleMetricsSummaryQuery(appliedQuery, queryFromFilters, options)); - } - } + if (targets.traceqlSearch[0].groupBy) { + return of({ + error: { + message: + 'The aggregate by query is deprecated. Please remove the current query and create a new one. Alternatively, you can use Traces Drilldown.', + }, + data: [], + }); + } - const traceqlSearchTargets = config.featureToggles.metricsSummary - ? targets.traceqlSearch.filter((t) => !this.hasGroupBy(t)) - : targets.traceqlSearch; + try { + const traceqlSearchTargets = targets.traceqlSearch; if (traceqlSearchTargets.length > 0) { const appliedQuery = this.applyVariables(traceqlSearchTargets[0], options.scopedVars); const queryFromFilters = this.languageProvider.generateQueryFromFilters(appliedQuery.filters); @@ -552,17 +550,6 @@ export class TempoDatasource extends DataSourceWithBackend { - const updatedFilter = { - ...filter, - tag: this.templateSrv.replace(filter.tag ?? '', scopedVars), - }; - - return updatedFilter; - }); - } - return { ...expandedQuery, query: this.templateSrv.replace(query.query ?? '', scopedVars, VariableFormatID.Pipe), @@ -572,22 +559,6 @@ export class TempoDatasource extends DataSourceWithBackend { - return groupBy - ?.filter((f) => f.tag) - .map((f) => { - if (f.scope === TraceqlSearchScope.Unscoped) { - return `.${f.tag}`; - } - return f.scope !== TraceqlSearchScope.Intrinsic ? `${f.scope}.${f.tag}` : f.tag; - }) - .join(', '); - }; - - hasGroupBy = (query: TempoQuery) => { - return query.groupBy?.find((gb) => gb.tag); - }; - /** * Handles the simplest of the queries where we have just a trace id and return trace data for it. * @param options @@ -731,93 +702,6 @@ export class TempoDatasource extends DataSourceWithBackend) => { - reportInteraction('grafana_traces_metrics_summary_queried', { - datasourceType: 'tempo', - app: options.app ?? '', - grafana_version: config.buildInfo.version, - filterCount: target.groupBy?.length ?? 0, - }); - - if (query === '{}') { - return of({ - error: { - message: - 'Please ensure you do not have an empty query. This is so filters are applied and the metrics summary is not generated from all spans.', - }, - data: emptyResponse, - }); - } - - const startTime = performance.now(); - const groupBy = target.groupBy ? this.formatGroupBy(target.groupBy) : ''; - return this._request('/api/metrics/summary', { - q: query, - groupBy, - start: options.range.from.unix(), - end: options.range.to.unix(), - }).pipe( - map((response) => { - if (!response.data.summaries) { - reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { - success: false, - streaming: false, - latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond - query: query ?? '', - error: getErrorMessage(`No summary data for '${groupBy}'.`), - }); - return { - error: { - message: getErrorMessage(`No summary data for '${groupBy}'.`), - }, - data: emptyResponse, - }; - } - // Check if any of the results have series data as older versions of Tempo placed the series data in a different structure - const hasSeries = response.data.summaries.some((summary: MetricsSummary) => summary.series.length > 0); - if (!hasSeries) { - reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { - success: false, - streaming: false, - latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond - query: query ?? '', - error: getErrorMessage(`No series data. Ensure you are using an up to date version of Tempo`), - }); - return { - error: { - message: getErrorMessage(`No series data. Ensure you are using an up to date version of Tempo`), - }, - data: emptyResponse, - }; - } - reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { - success: true, - streaming: false, - latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond - query: query ?? '', - }); - return { - data: createTableFrameFromMetricsSummaryQuery(response.data.summaries, query, this.instanceSettings), - }; - }), - catchError((error) => { - reportTempoQueryMetrics('grafana_traces_metrics_summary_response', options, { - success: false, - streaming: false, - latencyMs: Math.round(performance.now() - startTime), // rounded to nearest millisecond - query: query ?? '', - error: getErrorMessage(error.data.message), - statusCode: error.status, - statusText: error.statusText, - }); - return of({ - error: { message: getErrorMessage(error.data.message) }, - data: emptyResponse, - }); - }) - ); - }; - // This function can probably be simplified by avoiding passing both `targets` and `query`, // since `query` is built from `targets`, if you look at how this function is currently called handleStreamingQuery( diff --git a/public/app/plugins/datasource/tempo/language_provider.test.ts b/public/app/plugins/datasource/tempo/language_provider.test.ts index 7aff5eb7b18..9a91c94176a 100644 --- a/public/app/plugins/datasource/tempo/language_provider.test.ts +++ b/public/app/plugins/datasource/tempo/language_provider.test.ts @@ -8,38 +8,6 @@ import { intrinsics } from './traceql/traceql'; import { Scope } from './types'; describe('Language_provider', () => { - describe('should get correct metrics summary tags', () => { - it('for API v1 tags', async () => { - const lp = setup(v1Tags); - const tags = lp.getMetricsSummaryTags(); - expect(tags).toEqual(['bar', 'foo']); - }); - - it('for API v2 intrinsic tags', async () => { - const lp = setup(undefined, v2Tags); - const tags = lp.getMetricsSummaryTags(TraceqlSearchScope.Intrinsic); - expect(tags).toEqual(uniq(['duration', 'kind', 'name', 'status'].concat(intrinsics))); - }); - - it('for API v2 resource tags', async () => { - const lp = setup(undefined, v2Tags); - const tags = lp.getMetricsSummaryTags(TraceqlSearchScope.Resource); - expect(tags).toEqual(['cluster', 'container']); - }); - - it('for API v2 span tags', async () => { - const lp = setup(undefined, v2Tags); - const tags = lp.getMetricsSummaryTags(TraceqlSearchScope.Span); - expect(tags).toEqual(['db']); - }); - - it('for API v2 unscoped tags', async () => { - const lp = setup(undefined, v2Tags); - const tags = lp.getMetricsSummaryTags(TraceqlSearchScope.Unscoped); - expect(tags).toEqual(['cluster', 'container', 'db']); - }); - }); - describe('should get correct tags', () => { it('for API v1 tags', async () => { const lp = setup(v1Tags); diff --git a/public/app/plugins/datasource/tempo/language_provider.ts b/public/app/plugins/datasource/tempo/language_provider.ts index 63639645ef1..e8c4d202dde 100644 --- a/public/app/plugins/datasource/tempo/language_provider.ts +++ b/public/app/plugins/datasource/tempo/language_provider.ts @@ -97,18 +97,6 @@ export default class TempoLanguageProvider extends LanguageProvider { return []; }; - getMetricsSummaryTags = (scope?: TraceqlSearchScope) => { - if (this.tagsV2 && scope) { - if (scope === TraceqlSearchScope.Unscoped) { - return getUnscopedTags(this.tagsV2); - } - return getTagsByScope(this.tagsV2, scope); - } else if (this.tagsV1) { - return this.tagsV1; - } - return []; - }; - getTraceqlAutocompleteTags = (scope?: string) => { if (this.tagsV2) { if (!scope) { diff --git a/public/app/plugins/datasource/tempo/metricsSummary.test.ts b/public/app/plugins/datasource/tempo/metricsSummary.test.ts deleted file mode 100644 index 15b351e564e..00000000000 --- a/public/app/plugins/datasource/tempo/metricsSummary.test.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { defaultSettings } from './datasource.test'; -import { - createTableFrameFromMetricsSummaryQuery, - emptyResponse, - getConfigQuery, - transformToMetricsData, -} from './metricsSummary'; - -describe('MetricsSummary', () => { - describe('createTableFrameFromMetricsSummaryQuery', () => { - it('should return emptyResponse when state is LoadingState.Error', () => { - const result = createTableFrameFromMetricsSummaryQuery([], '', defaultSettings); - expect(result).toEqual(emptyResponse); - }); - - it('should return correctly when state is LoadingState.Done', () => { - const data = [ - { - spanCount: '10', - errorSpanCount: '1', - p50: '1', - p90: '2', - p95: '3', - p99: '4', - series: [ - { - key: 'span.http.status_code', - value: { - type: 3, - n: 208, - }, - }, - { - key: 'temperature', - value: { - type: 4, - f: 38.1, - }, - }, - ], - }, - ]; - const result = createTableFrameFromMetricsSummaryQuery( - data, - '{name="HTTP POST - post"} | by(resource.service.name)', - defaultSettings - ); - expect(result).toMatchInlineSnapshot(` - [ - { - "fields": [ - { - "config": { - "displayNameFromDS": "span.http.status_code", - "links": [ - { - "internal": { - "datasourceName": "tempo", - "datasourceUid": "gdev-tempo", - "query": { - "query": "{name="HTTP POST - post" && span.http.status_code=\${__data.fields["span.http.status_code"]} && temperature=\${__data.fields["temperature"]}} | by(resource.service.name)", - "queryType": "traceql", - }, - }, - "title": "Query in explore", - "url": "", - }, - ], - "noValue": "", - }, - "name": "span.http.status_code", - "type": "string", - "values": [ - 208, - ], - }, - { - "config": { - "displayNameFromDS": "temperature", - "links": [ - { - "internal": { - "datasourceName": "tempo", - "datasourceUid": "gdev-tempo", - "query": { - "query": "{name="HTTP POST - post" && span.http.status_code=\${__data.fields["span.http.status_code"]} && temperature=\${__data.fields["temperature"]}} | by(resource.service.name)", - "queryType": "traceql", - }, - }, - "title": "Query in explore", - "url": "", - }, - ], - "noValue": "", - }, - "name": "temperature", - "type": "string", - "values": [ - 38.1, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "Span count", - }, - "name": "spanCount", - "type": "number", - "values": [ - 10, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "Error", - "unit": "percent", - }, - "name": "errorPercentage", - "type": "number", - "values": [ - 10, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "p50", - "unit": "ns", - }, - "name": "p50", - "type": "number", - "values": [ - 1, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "p90", - "unit": "ns", - }, - "name": "p90", - "type": "number", - "values": [ - 2, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "p95", - "unit": "ns", - }, - "name": "p95", - "type": "number", - "values": [ - 3, - ], - }, - { - "config": { - "custom": { - "width": 150, - }, - "displayNameFromDS": "p99", - "unit": "ns", - }, - "name": "p99", - "type": "number", - "values": [ - 4, - ], - }, - ], - "length": 1, - "meta": { - "preferredVisualisationType": "table", - }, - "name": "Metrics Summary", - "refId": "metrics-summary", - }, - ] - `); - }); - - it('transformToMetricsData should return correctly', () => { - const data = { - spanCount: '10', - errorSpanCount: '1', - p50: '1', - p90: '2', - p95: '3', - p99: '4', - series, - }; - const result = transformToMetricsData(data); - expect(result).toMatchInlineSnapshot(` - { - "contains_sink": "true", - "errorPercentage": 10, - "p50": 1, - "p90": 2, - "p95": 3, - "p99": 4, - "room": "kitchen", - "span.http.status_code": 208, - "spanCount": 10, - "spanKind": "server", - "spanStatus": "ok", - "temperature": 38.1, - "window_open": "8h", - } - `); - }); - - it('getConfigQuery should return correctly for empty target query', () => { - const result = getConfigQuery(series, '{}'); - expect(result).toEqual( - '{span.http.status_code=${__data.fields["span.http.status_code"]} && temperature=${__data.fields["temperature"]} && room="${__data.fields["room"]}" && contains_sink="${__data.fields["contains_sink"]}" && window_open="${__data.fields["window_open"]}" && spanStatus=${__data.fields["spanStatus"]} && spanKind=${__data.fields["spanKind"]}}' - ); - }); - - it('getConfigQuery should return correctly for target query', () => { - const result = getConfigQuery(series, '{name="HTTP POST - post"} | by(resource.service.name)'); - expect(result).toEqual( - '{name="HTTP POST - post" && span.http.status_code=${__data.fields["span.http.status_code"]} && temperature=${__data.fields["temperature"]} && room="${__data.fields["room"]}" && contains_sink="${__data.fields["contains_sink"]}" && window_open="${__data.fields["window_open"]}" && spanStatus=${__data.fields["spanStatus"]} && spanKind=${__data.fields["spanKind"]}} | by(resource.service.name)' - ); - }); - - it('getConfigQuery should return correctly for target query without brackets', () => { - const result = getConfigQuery(series, 'by(resource.service.name)'); - expect(result).toEqual( - '{span.http.status_code=${__data.fields["span.http.status_code"]} && temperature=${__data.fields["temperature"]} && room="${__data.fields["room"]}" && contains_sink="${__data.fields["contains_sink"]}" && window_open="${__data.fields["window_open"]}" && spanStatus=${__data.fields["spanStatus"]} && spanKind=${__data.fields["spanKind"]}} | by(resource.service.name)' - ); - }); - }); -}); - -const series = [ - { - key: 'span.http.status_code', - value: { - type: 3, - n: 208, - }, - }, - { - key: 'temperature', - value: { - type: 4, - f: 38.1, - }, - }, - { - key: 'room', - value: { - type: 5, - s: 'kitchen', - }, - }, - { - key: 'contains_sink', - value: { - type: 6, - b: 'true', - }, - }, - { - key: 'window_open', - value: { - type: 7, - d: '8h', - }, - }, - { - key: 'spanStatus', - value: { - type: 8, - status: 1, - }, - }, - { - key: 'spanKind', - value: { - type: 9, - kind: 3, - }, - }, -]; diff --git a/public/app/plugins/datasource/tempo/metricsSummary.ts b/public/app/plugins/datasource/tempo/metricsSummary.ts deleted file mode 100644 index 9aebb32853c..00000000000 --- a/public/app/plugins/datasource/tempo/metricsSummary.ts +++ /dev/null @@ -1,273 +0,0 @@ -import { - createDataFrame, - DataSourceInstanceSettings, - FieldDTO, - FieldType, - MutableDataFrame, - sortDataFrame, -} from '@grafana/data'; - -export type MetricsSummary = { - spanCount: string; - errorSpanCount?: string; - p50: string; - p90: string; - p95: string; - p99: string; - series: Series[]; -}; - -type Series = { - key: string; - value: { - type: number; - n?: number; - f?: number; - s?: string; - b?: string; - d?: string; - status?: number; - kind?: number; - }; -}; - -type MetricsData = { - spanCount: number; - errorPercentage: number | string; - p50: number; - p90: number; - p95: number; - p99: number; - [key: string]: string | number; -}; - -export function createTableFrameFromMetricsSummaryQuery( - data: MetricsSummary[], - targetQuery: string, - instanceSettings: DataSourceInstanceSettings -) { - let frame; - - if (!data.length) { - return emptyResponse; - } - - const dynamicMetrics: Record = {}; - data.forEach((res: MetricsSummary) => { - const configQuery = getConfigQuery(res.series, targetQuery); - res.series.forEach((series: Series) => { - dynamicMetrics[series.key] = { - name: `${series.key}`, - type: FieldType.string, - config: getConfig(series, configQuery, instanceSettings), - values: [], - }; - }); - }); - - frame = createDataFrame({ - name: 'Metrics Summary', - refId: 'metrics-summary', - fields: [ - ...Object.values(dynamicMetrics).sort((a, b) => a.name.localeCompare(b.name)), - { - name: 'spanCount', - type: FieldType.number, - config: { displayNameFromDS: 'Span count', custom: { width: 150 } }, - }, - { - name: 'errorPercentage', - type: FieldType.number, - config: { displayNameFromDS: 'Error', unit: 'percent', custom: { width: 150 } }, - }, - getPercentileRow('p50'), - getPercentileRow('p90'), - getPercentileRow('p95'), - getPercentileRow('p99'), - ], - meta: { - preferredVisualisationType: 'table', - }, - }); - - const metricsData = data.map(transformToMetricsData); - frame.length = metricsData.length; - for (const trace of metricsData) { - for (const field of frame.fields) { - field.values.push(trace[field.name]); - } - } - frame = sortDataFrame(frame, 0); - - return [frame]; -} - -export const transformToMetricsData = (data: MetricsSummary) => { - const errorPercentage = data.errorSpanCount - ? (getNumberForMetric(data.errorSpanCount) / getNumberForMetric(data.spanCount)) * 100 - : '0%'; - - const metricsData: MetricsData = { - spanCount: getNumberForMetric(data.spanCount), - errorPercentage, - p50: getNumberForMetric(data.p50), - p90: getNumberForMetric(data.p90), - p95: getNumberForMetric(data.p95), - p99: getNumberForMetric(data.p99), - }; - - data.series.forEach((series: Series) => { - metricsData[`${series.key}`] = getMetricValue(series) || ''; - }); - - return metricsData; -}; - -export const getConfigQuery = (series: Series[], targetQuery: string) => { - const queryParts = series.map((x: Series) => { - const isNumber = x.value.type === 3 || x.value.type === 4; - const isIntrinsic = x.value.type === 8 || x.value.type === 9; - const surround = isNumber || isIntrinsic ? '' : '"'; - return `${x.key}=${surround}` + '${__data.fields["' + x.key + '"]}' + `${surround}`; - }); - - let configQuery = ''; - const closingBracketIndex = targetQuery.indexOf('}'); - - if (closingBracketIndex !== -1) { - const queryAfterClosingBracket = targetQuery.substring(closingBracketIndex + 1); - configQuery = targetQuery.substring(0, closingBracketIndex); - if (queryParts.length > 0) { - configQuery += targetQuery.replace(/\s/g, '').includes('{}') ? '' : ' && '; - configQuery += `${queryParts.join(' && ')}`; - configQuery += `}`; - } - configQuery += `${queryAfterClosingBracket}`; - } else { - configQuery = `{${queryParts.join(' && ')}} | ${targetQuery}`; - } - - return configQuery; -}; - -const getConfig = (series: Series, query: string, instanceSettings: DataSourceInstanceSettings) => { - const commonConfig = { - displayNameFromDS: series.key, - noValue: '', - links: [ - { - title: 'Query in explore', - url: '', - internal: { - datasourceUid: instanceSettings.uid, - datasourceName: instanceSettings.name, - query: { - query, - queryType: 'traceql', - }, - }, - }, - ], - }; - - if (series.value.type === 7) { - return { - ...commonConfig, - unit: 'ns', - }; - } - return { ...commonConfig }; -}; - -const NO_VALUE = ''; - -const getMetricValue = (series: Series) => { - if (!series.value.type) { - return NO_VALUE; - } - - switch (series.value.type) { - case 3: - return series.value.n; - case 4: - return series.value.f; - case 5: - return series.value.s; - case 6: - return series.value.b; - case 7: - return series.value.d; - case 8: - return getSpanStatusCode(series.value.status); - case 9: - return getSpanKind(series.value.kind); - default: - return NO_VALUE; - } -}; - -// Values set according to Tempo enum: https://github.com/grafana/tempo/blob/main/pkg/traceql/enum_statics.go -const getSpanStatusCode = (statusCode: number | undefined) => { - if (!statusCode) { - return NO_VALUE; - } - - switch (statusCode) { - case 0: - return 'error'; - case 1: - return 'ok'; - default: - return 'unset'; - } -}; - -// Values set according to Tempo enum: https://github.com/grafana/tempo/blob/main/pkg/traceql/enum_statics.go -const getSpanKind = (kind: number | undefined) => { - if (!kind) { - return NO_VALUE; - } - - switch (kind) { - case 1: - return 'internal'; - case 2: - return 'client'; - case 3: - return 'server'; - case 4: - return 'producer'; - case 5: - return 'consumer'; - default: - return 'unspecified'; - } -}; - -const getPercentileRow = (name: string) => { - return { - name: name, - type: FieldType.number, - config: { - displayNameFromDS: name, - unit: 'ns', - custom: { - width: 150, - }, - }, - }; -}; - -const getNumberForMetric = (metric: string) => { - const number = parseInt(metric, 10); - return isNaN(number) ? 0 : number; -}; - -export const emptyResponse = new MutableDataFrame({ - name: 'Metrics Summary', - refId: 'metrics-summary', - fields: [], - meta: { - preferredVisualisationType: 'table', - }, -}); diff --git a/public/app/plugins/datasource/tempo/utils.ts b/public/app/plugins/datasource/tempo/utils.ts index 8bc5c2addd5..5c772239ff5 100644 --- a/public/app/plugins/datasource/tempo/utils.ts +++ b/public/app/plugins/datasource/tempo/utils.ts @@ -91,7 +91,6 @@ export const migrateFromSearchToTraceQLSearch = (query: TempoQuery) => { const migratedQuery: TempoQuery = { datasource: query.datasource, filters, - groupBy: query.groupBy, limit: query.limit, query: query.query, queryType: 'traceqlSearch',