diff --git a/pkg/tsdb/tempo/traceql/metrics.go b/pkg/tsdb/tempo/traceql/metrics.go index 7ed97e963a5..ec057c80cca 100644 --- a/pkg/tsdb/tempo/traceql/metrics.go +++ b/pkg/tsdb/tempo/traceql/metrics.go @@ -67,13 +67,6 @@ func TransformInstantMetricsResponse(query *dataquery.TempoQuery, resp tempopb.Q for i, series := range resp.Series { name, labels := transformLabelsAndGetName(series.Labels) - labelKeys := make([]string, 0, len(labels)) - labelFields := make([]*data.Field, 0, len(labels)) - for key := range labels { - labelKeys = append(labelKeys, key) - labelFields = append(labelFields, data.NewField(key, nil, []string{})) - } - timeField := data.NewField("time", nil, []time.Time{}) valueField := data.NewField("value", labels, []float64{}) valueField.Config = &data.FieldConfig{ @@ -83,17 +76,13 @@ func TransformInstantMetricsResponse(query *dataquery.TempoQuery, resp tempopb.Q frame := &data.Frame{ RefID: name, Name: name, - Fields: append([]*data.Field{timeField}, append(labelFields, valueField)...), + Fields: append([]*data.Field{timeField}, valueField), Meta: &data.FrameMeta{ PreferredVisualization: data.VisTypeTable, }, } - labelValues := make([]interface{}, len(labels)) - for idx, key := range labelKeys { - labelValues[idx] = strings.Trim(labels[key], "\"") - } - row := append([]interface{}{time.Now()}, append(labelValues, series.GetValue())...) + row := append([]interface{}{time.Now()}, series.GetValue()) frame.AppendRow(row...) frames[i] = frame diff --git a/pkg/tsdb/tempo/traceql/metrics_test.go b/pkg/tsdb/tempo/traceql/metrics_test.go index d0dc1196008..0cf956f88a2 100644 --- a/pkg/tsdb/tempo/traceql/metrics_test.go +++ b/pkg/tsdb/tempo/traceql/metrics_test.go @@ -117,12 +117,6 @@ func TestTransformInstantMetricsResponse(t *testing.T) { resp := tempopb.QueryInstantResponse{ Series: []*tempopb.InstantSeries{ { - Labels: []v1.KeyValue{ - { - Key: "label", - Value: &v1.AnyValue{Value: &v1.AnyValue_StringValue{StringValue: "value"}}, - }, - }, Value: 123.45, PromLabels: "label=\"value\"", }, @@ -134,22 +128,14 @@ func TestTransformInstantMetricsResponse(t *testing.T) { assert.Len(t, frames, 1) frame := frames[0] - assert.Equal(t, "value", frame.RefID) - assert.Equal(t, "value", frame.Name) - assert.Len(t, frame.Fields, 3) + assert.Len(t, frame.Fields, 2) timeField := frame.Fields[0] assert.Equal(t, "time", timeField.Name) assert.Equal(t, 1, timeField.Len()) assert.IsType(t, time.Time{}, timeField.At(0)) - labelField := frame.Fields[1] - assert.Equal(t, "label", labelField.Name) - assert.Equal(t, 1, labelField.Len()) - assert.IsType(t, "", labelField.At(0)) - assert.Equal(t, "value", labelField.At(0)) - - valueField := frame.Fields[2] + valueField := frame.Fields[1] assert.Equal(t, "value", valueField.Name) assert.Equal(t, 1, valueField.Len()) assert.IsType(t, 0.0, valueField.At(0)) diff --git a/public/app/plugins/datasource/tempo/QueryField.tsx b/public/app/plugins/datasource/tempo/QueryField.tsx index 9d6dc8efcaf..d64fcba919e 100644 --- a/public/app/plugins/datasource/tempo/QueryField.tsx +++ b/public/app/plugins/datasource/tempo/QueryField.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/css'; import { PureComponent } from 'react'; -import { QueryEditorProps, SelectableValue } from '@grafana/data'; +import { CoreApp, QueryEditorProps, SelectableValue } from '@grafana/data'; import { config, reportInteraction } from '@grafana/runtime'; import { Button, @@ -68,6 +68,7 @@ class TempoQueryFieldComponent extends PureComponent { render() { const { query, onChange, datasource, app } = this.props; + const isAlerting = app === CoreApp.UnifiedAlerting; const graphDatasourceUid = datasource.serviceMap?.datasourceUid; @@ -114,41 +115,43 @@ class TempoQueryFieldComponent extends PureComponent { /> - - - - - options={queryTypeOptions} - value={query.queryType} - onChange={(v) => { - reportInteraction('grafana_traces_query_type_changed', { - datasourceType: 'tempo', - app: app ?? '', - grafana_version: config.buildInfo.version, - newQueryType: v, - previousQueryType: query.queryType ?? '', - }); + {!isAlerting && ( + + + + + options={queryTypeOptions} + value={query.queryType} + onChange={(v) => { + reportInteraction('grafana_traces_query_type_changed', { + datasourceType: 'tempo', + app: app ?? '', + grafana_version: config.buildInfo.version, + newQueryType: v, + previousQueryType: query.queryType ?? '', + }); - this.onClearResults(); - onChange({ - ...query, - queryType: v, - }); - }} - size="md" - /> - - - - + this.onClearResults(); + onChange({ + ...query, + queryType: v, + }); + }} + size="md" + /> + + + + + )} {query.queryType === 'traceqlSearch' && ( {error ? ( diff --git a/public/app/plugins/datasource/tempo/plugin.json b/public/app/plugins/datasource/tempo/plugin.json index e1ab2453392..92ab0a54fd6 100644 --- a/public/app/plugins/datasource/tempo/plugin.json +++ b/public/app/plugins/datasource/tempo/plugin.json @@ -6,7 +6,7 @@ "executable": "gpx_tempo", "metrics": true, - "alerting": false, + "alerting": true, "annotations": false, "logs": false, "streaming": false, diff --git a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx index f80f9986f93..72d0e9890d8 100644 --- a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx +++ b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx @@ -77,6 +77,7 @@ export function QueryEditor(props: Props) { onChange={props.onChange} searchStreaming={props.datasource.isStreamingSearchEnabled() ?? false} metricsStreaming={props.datasource.isStreamingMetricsEnabled() ?? false} + app={props.app} /> diff --git a/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx b/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx index 7552565acc1..c591455d578 100644 --- a/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx +++ b/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import * as React from 'react'; import { useToggle } from 'react-use'; -import { GrafanaTheme2 } from '@grafana/data'; +import { CoreApp, GrafanaTheme2 } from '@grafana/data'; import { EditorField, EditorRow } from '@grafana/plugin-ui'; import { AutoSizeInput, RadioButtonGroup, useStyles2 } from '@grafana/ui'; @@ -16,6 +16,7 @@ interface Props { query: Partial & TempoQuery; searchStreaming: boolean; metricsStreaming: boolean; + app?: CoreApp; } /** @@ -31,162 +32,174 @@ const parseIntWithFallback = (val: string, fallback: number) => { return isNaN(parsed) ? fallback : parsed; }; -export const TempoQueryBuilderOptions = React.memo(({ onChange, query, searchStreaming, metricsStreaming }) => { - const styles = useStyles2(getStyles); - const [isOpen, toggleOpen] = useToggle(false); +export const TempoQueryBuilderOptions = React.memo( + ({ onChange, query, searchStreaming, metricsStreaming, app }) => { + const styles = useStyles2(getStyles); + const [isOpen, toggleOpen] = useToggle(false); + const isAlerting = app === CoreApp.UnifiedAlerting; - if (!query.hasOwnProperty('limit')) { - query.limit = DEFAULT_LIMIT; - } + if (!query.hasOwnProperty('limit')) { + query.limit = DEFAULT_LIMIT; + } - if (!query.hasOwnProperty('tableType')) { - query.tableType = SearchTableType.Traces; - } + if (!query.hasOwnProperty('tableType')) { + query.tableType = SearchTableType.Traces; + } - if (!query.hasOwnProperty('metricsQueryType')) { - query.metricsQueryType = MetricsQueryType.Range; - } + if (!query.hasOwnProperty('metricsQueryType')) { + query.metricsQueryType = MetricsQueryType.Range; + } - const onLimitChange = (e: React.FormEvent) => { - onChange({ ...query, limit: parseIntWithFallback(e.currentTarget.value, DEFAULT_LIMIT) }); - }; - const onSpssChange = (e: React.FormEvent) => { - onChange({ ...query, spss: parseIntWithFallback(e.currentTarget.value, DEFAULT_SPSS) }); - }; - const onTableTypeChange = (val: SearchTableType) => { - onChange({ ...query, tableType: val }); - }; - const onMetricsQueryTypeChange = (val: MetricsQueryType) => { - onChange({ ...query, metricsQueryType: val }); - }; - const onStepChange = (e: React.FormEvent) => { - onChange({ ...query, step: e.currentTarget.value }); - }; + if (isAlerting && query.metricsQueryType === MetricsQueryType.Range) { + onChange({ ...query, metricsQueryType: MetricsQueryType.Instant }); + } - // There's a bug in Tempo which causes the exemplars param to be ignored. It's commented out for now. + const onLimitChange = (e: React.FormEvent) => { + onChange({ ...query, limit: parseIntWithFallback(e.currentTarget.value, DEFAULT_LIMIT) }); + }; + const onSpssChange = (e: React.FormEvent) => { + onChange({ ...query, spss: parseIntWithFallback(e.currentTarget.value, DEFAULT_SPSS) }); + }; + const onTableTypeChange = (val: SearchTableType) => { + onChange({ ...query, tableType: val }); + }; + const onMetricsQueryTypeChange = (val: MetricsQueryType) => { + onChange({ ...query, metricsQueryType: val }); + }; + const onStepChange = (e: React.FormEvent) => { + onChange({ ...query, step: e.currentTarget.value }); + }; - // const onExemplarsChange = (e: React.FormEvent) => { - // const exemplars = parseInt(e.currentTarget.value, 10); - // if (!isNaN(exemplars) && exemplars >= 0) { - // onChange({ ...query, exemplars }); - // } else { - // onChange({ ...query, exemplars: undefined }); - // } - // }; + // There's a bug in Tempo which causes the exemplars param to be ignored. It's commented out for now. - const collapsedSearchOptions = [ - `Limit: ${query.limit || DEFAULT_LIMIT}`, - `Spans Limit: ${query.spss || DEFAULT_SPSS}`, - `Table Format: ${query.tableType === SearchTableType.Traces ? 'Traces' : 'Spans'}`, - '|', - `Streaming: ${searchStreaming ? 'Enabled' : 'Disabled'}`, - ]; + // const onExemplarsChange = (e: React.FormEvent) => { + // const exemplars = parseInt(e.currentTarget.value, 10); + // if (!isNaN(exemplars) && exemplars >= 0) { + // onChange({ ...query, exemplars }); + // } else { + // onChange({ ...query, exemplars: undefined }); + // } + // }; - const collapsedMetricsOptions = [ - `Step: ${query.step || 'auto'}`, - `Type: ${query.metricsQueryType === MetricsQueryType.Range ? 'Range' : 'Instant'}`, - '|', - `Streaming: ${metricsStreaming ? 'Enabled' : 'Disabled'}`, - // `Exemplars: ${query.exemplars !== undefined ? query.exemplars : 'auto'}`, - ]; + const collapsedSearchOptions = [ + `Limit: ${query.limit || DEFAULT_LIMIT}`, + `Spans Limit: ${query.spss || DEFAULT_SPSS}`, + `Table Format: ${query.tableType === SearchTableType.Traces ? 'Traces' : 'Spans'}`, + '|', + `Streaming: ${searchStreaming ? 'Enabled' : 'Disabled'}`, + ]; - return ( - -
- - - - - - - - - - - } tooltipInteractive> -
{searchStreaming ? 'Enabled' : 'Disabled'}
-
-
+ const collapsedMetricsOptions = [ + `Step: ${query.step || 'auto'}`, + `Type: ${query.metricsQueryType === MetricsQueryType.Range ? 'Range' : 'Instant'}`, + '|', + `Streaming: ${metricsStreaming ? 'Enabled' : 'Disabled'}`, + // `Exemplars: ${query.exemplars !== undefined ? query.exemplars : 'auto'}`, + ]; - - +
+ {!isAlerting && ( + + + + + + + + + + + } tooltipInteractive> +
{searchStreaming ? 'Enabled' : 'Disabled'}
+
+
+ )} + + - - - - - + {!isAlerting && ( + + + + )} + + + - } tooltipInteractive> -
{metricsStreaming ? 'Enabled' : 'Disabled'}
-
- {/**/} - {/* */} - {/**/} -
-
- - ); -}); + } tooltipInteractive> +
{metricsStreaming ? 'Enabled' : 'Disabled'}
+
+ {/**/} + {/* */} + {/*
*/} +
+
+
+ ); + } +); const StreamingTooltip = () => { return (