mirror of
https://github.com/grafana/grafana.git
synced 2024-11-24 09:50:29 -06:00
Tempo: Merge Trace ID and TraceQL tabs (#60180)
* Remove TraceID tab when TraceQL is enabled. Use TraceQL editor to query for trace IDs by checking whether the content is an hex only string * Highlight valid trace IDs in traceql editor * Update trace and span links to use TraceQL tab when feature flag is enabled * Remove traceqlEditor feature flag. * Remove traceId query type from Tempo and replace it with traceQl
This commit is contained in:
parent
75a11e92b2
commit
209b1848b8
@ -79,7 +79,6 @@ Alpha features might be changed or removed without prior notice.
|
||||
| `logRequestsInstrumentedAsUnknown` | Logs the path for requests that are instrumented as unknown |
|
||||
| `dataConnectionsConsole` | Enables a new top-level page called Connections. This page is an experiment that provides a better experience when you install and configure data sources and other plugins. |
|
||||
| `topnav` | New top nav and page layouts |
|
||||
| `traceqlEditor` | Show the TraceQL editor in the explore page |
|
||||
| `flameGraph` | Show the flame graph |
|
||||
| `cloudWatchCrossAccountQuerying` | Use cross-account querying in CloudWatch datasource |
|
||||
| `redshiftAsyncQueryDataSupport` | Enable async query data support for Redshift |
|
||||
|
@ -66,7 +66,6 @@ export interface FeatureToggles {
|
||||
topnav?: boolean;
|
||||
grpcServer?: boolean;
|
||||
entityStore?: boolean;
|
||||
traceqlEditor?: boolean;
|
||||
flameGraph?: boolean;
|
||||
cloudWatchCrossAccountQuerying?: boolean;
|
||||
redshiftAsyncQueryDataSupport?: boolean;
|
||||
|
@ -287,11 +287,6 @@ var (
|
||||
State: FeatureStateAlpha,
|
||||
RequiresDevMode: true,
|
||||
},
|
||||
{
|
||||
Name: "traceqlEditor",
|
||||
Description: "Show the TraceQL editor in the explore page",
|
||||
State: FeatureStateAlpha,
|
||||
},
|
||||
{
|
||||
Name: "flameGraph",
|
||||
Description: "Show the flame graph",
|
||||
|
@ -207,10 +207,6 @@ const (
|
||||
// SQL-based entity store (requires storage flag also)
|
||||
FlagEntityStore = "entityStore"
|
||||
|
||||
// FlagTraceqlEditor
|
||||
// Show the TraceQL editor in the explore page
|
||||
FlagTraceqlEditor = "traceqlEditor"
|
||||
|
||||
// FlagFlameGraph
|
||||
// Show the flame graph
|
||||
FlagFlameGraph = "flameGraph"
|
||||
|
@ -354,7 +354,7 @@ function getDataLinks(options: ExemplarTraceIdDestination): DataLink[] {
|
||||
title: options.urlDisplayLabel || `Query with ${dsSettings?.name}`,
|
||||
url: '',
|
||||
internal: {
|
||||
query: { query: '${__value.raw}', queryType: 'traceId' },
|
||||
query: { query: '${__value.raw}', queryType: 'traceql' },
|
||||
datasourceUid: options.datasourceUid,
|
||||
datasourceName: dsSettings?.name ?? 'Data source not found',
|
||||
},
|
||||
|
@ -3,13 +3,12 @@ import React from 'react';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
|
||||
import { QueryEditorProps, SelectableValue } from '@grafana/data';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { reportInteraction } from '@grafana/runtime';
|
||||
import {
|
||||
FileDropzone,
|
||||
InlineField,
|
||||
InlineFieldRow,
|
||||
InlineLabel,
|
||||
QueryField,
|
||||
RadioButtonGroup,
|
||||
Themeable2,
|
||||
withTheme2,
|
||||
@ -28,7 +27,7 @@ import { getDS } from './utils';
|
||||
|
||||
interface Props extends QueryEditorProps<TempoDatasource, TempoQuery>, Themeable2 {}
|
||||
|
||||
const DEFAULT_QUERY_TYPE: TempoQueryType = 'traceId';
|
||||
const DEFAULT_QUERY_TYPE: TempoQueryType = 'traceql';
|
||||
|
||||
class TempoQueryFieldComponent extends React.PureComponent<Props> {
|
||||
constructor(props: Props) {
|
||||
@ -77,8 +76,8 @@ class TempoQueryFieldComponent extends React.PureComponent<Props> {
|
||||
|
||||
const graphDatasourceUid = datasource.serviceMap?.datasourceUid;
|
||||
|
||||
const queryTypeOptions: Array<SelectableValue<TempoQueryType>> = [
|
||||
{ value: 'traceId', label: 'TraceID' },
|
||||
let queryTypeOptions: Array<SelectableValue<TempoQueryType>> = [
|
||||
{ value: 'traceql', label: 'TraceQL' },
|
||||
{ value: 'upload', label: 'JSON File' },
|
||||
{ value: 'serviceMap', label: 'Service Graph' },
|
||||
];
|
||||
@ -97,10 +96,6 @@ class TempoQueryFieldComponent extends React.PureComponent<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
if (config.featureToggles.traceqlEditor) {
|
||||
queryTypeOptions.push({ value: 'traceql', label: 'TraceQL' });
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<InlineFieldRow>
|
||||
@ -155,27 +150,6 @@ class TempoQueryFieldComponent extends React.PureComponent<Props> {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{query.queryType === 'traceId' && (
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Trace ID" labelWidth={14} grow>
|
||||
<QueryField
|
||||
query={query.query}
|
||||
onChange={(val) => {
|
||||
onChange({
|
||||
...query,
|
||||
query: val,
|
||||
queryType: 'traceId',
|
||||
linkedQuery: undefined,
|
||||
});
|
||||
}}
|
||||
onBlur={this.props.onBlur}
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
placeholder={'Enter a Trace ID (run with Shift+Enter)'}
|
||||
portalOrigin="tempo"
|
||||
/>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
)}
|
||||
{query.queryType === 'serviceMap' && (
|
||||
<ServiceGraphSection graphDatasourceUid={graphDatasourceUid} query={query} onChange={onChange} />
|
||||
)}
|
||||
|
@ -51,7 +51,7 @@ describe('Tempo data source', () => {
|
||||
it('returns empty response when traceId is empty', async () => {
|
||||
const ds = new TempoDatasource(defaultSettings);
|
||||
const response = await lastValueFrom(
|
||||
ds.query({ targets: [{ refId: 'refid1', queryType: 'traceId', query: '' } as Partial<TempoQuery>] } as any),
|
||||
ds.query({ targets: [{ refId: 'refid1', queryType: 'traceql', query: '' } as Partial<TempoQuery>] } as any),
|
||||
{ defaultValue: 'empty' }
|
||||
);
|
||||
expect(response).toBe('empty');
|
||||
@ -61,7 +61,7 @@ describe('Tempo data source', () => {
|
||||
function getQuery(): TempoQuery {
|
||||
return {
|
||||
refId: 'x',
|
||||
queryType: 'traceId',
|
||||
queryType: 'traceql',
|
||||
linkedQuery: {
|
||||
refId: 'linked',
|
||||
expr: '{instance="$interpolationVar"}',
|
||||
@ -388,7 +388,7 @@ describe('Tempo data source', () => {
|
||||
raw: { from: '15m', to: 'now' },
|
||||
},
|
||||
},
|
||||
[{ refId: 'refid1', queryType: 'traceId', query: '' } as TempoQuery]
|
||||
[{ refId: 'refid1', queryType: 'traceql', query: '' } as TempoQuery]
|
||||
);
|
||||
|
||||
expect(request.range.from.unix()).toBe(dateTime(new Date(2022, 8, 13, 15, 58, 0, 0)).unix());
|
||||
@ -417,7 +417,7 @@ describe('Tempo data source', () => {
|
||||
raw: { from: '15m', to: 'now' },
|
||||
},
|
||||
},
|
||||
[{ refId: 'refid1', queryType: 'traceId', query: '' } as TempoQuery]
|
||||
[{ refId: 'refid1', queryType: 'traceql', query: '' } as TempoQuery]
|
||||
);
|
||||
|
||||
expect(request.range.from.unix()).toBe(dateTime(0).unix());
|
||||
|
@ -95,7 +95,7 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
|
||||
query(options: DataQueryRequest<TempoQuery>): Observable<DataQueryResponse> {
|
||||
const subQueries: Array<Observable<DataQueryResponse>> = [];
|
||||
const filteredTargets = options.targets.filter((target) => !target.hide);
|
||||
const targets: { [type: string]: TempoQuery[] } = groupBy(filteredTargets, (t) => t.queryType || 'traceId');
|
||||
const targets: { [type: string]: TempoQuery[] } = groupBy(filteredTargets, (t) => t.queryType || 'traceql');
|
||||
|
||||
if (targets.clear) {
|
||||
return of({ data: [], state: LoadingState.Done });
|
||||
@ -175,28 +175,42 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
|
||||
}
|
||||
if (targets.traceql?.length) {
|
||||
try {
|
||||
reportInteraction('grafana_traces_traceql_queried', {
|
||||
datasourceType: 'tempo',
|
||||
app: options.app ?? '',
|
||||
});
|
||||
const queryValue = targets.traceql[0].query;
|
||||
const hexOnlyRegex = /^[0-9A-Fa-f]*$/;
|
||||
// Check whether this is a trace ID or traceQL query by checking if it only contains hex characters
|
||||
if (queryValue.trim().match(hexOnlyRegex)) {
|
||||
// There's only hex characters so let's assume that this is a trace ID
|
||||
reportInteraction('grafana_traces_traceql_traceID_queried', {
|
||||
datasourceType: 'tempo',
|
||||
app: options.app ?? '',
|
||||
query: queryValue ?? '',
|
||||
});
|
||||
|
||||
subQueries.push(
|
||||
this._request('/api/search', {
|
||||
q: targets.traceql[0].query,
|
||||
limit: options.targets[0].limit,
|
||||
start: options.range.from.unix(),
|
||||
end: options.range.to.unix(),
|
||||
}).pipe(
|
||||
map((response) => {
|
||||
return {
|
||||
data: createTableFrameFromTraceQlQuery(response.data.traces, this.instanceSettings),
|
||||
};
|
||||
}),
|
||||
catchError((error) => {
|
||||
return of({ error: { message: error.data.message }, data: [] });
|
||||
})
|
||||
)
|
||||
);
|
||||
subQueries.push(this.handleTraceIdQuery(options, targets.traceql));
|
||||
} else {
|
||||
reportInteraction('grafana_traces_traceql_queried', {
|
||||
datasourceType: 'tempo',
|
||||
app: options.app ?? '',
|
||||
query: queryValue ?? '',
|
||||
});
|
||||
subQueries.push(
|
||||
this._request('/api/search', {
|
||||
q: queryValue,
|
||||
limit: options.targets[0].limit,
|
||||
start: options.range.from.unix(),
|
||||
end: options.range.to.unix(),
|
||||
}).pipe(
|
||||
map((response) => {
|
||||
return {
|
||||
data: createTableFrameFromTraceQlQuery(response.data.traces, this.instanceSettings),
|
||||
};
|
||||
}),
|
||||
catchError((error) => {
|
||||
return of({ error: { message: error.data.message }, data: [] });
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
return of({ error: { message: error instanceof Error ? error.message : 'Unknown error occurred' }, data: [] });
|
||||
}
|
||||
@ -250,16 +264,6 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
|
||||
}
|
||||
}
|
||||
|
||||
if (targets.traceId?.length > 0) {
|
||||
reportInteraction('grafana_traces_traceID_queried', {
|
||||
datasourceType: 'tempo',
|
||||
app: options.app ?? '',
|
||||
query: targets.traceId[0].query ?? '',
|
||||
});
|
||||
|
||||
subQueries.push(this.handleTraceIdQuery(options, targets.traceId));
|
||||
}
|
||||
|
||||
return merge(...subQueries);
|
||||
}
|
||||
|
||||
|
@ -567,7 +567,7 @@ export function createTableFrameFromSearch(data: TraceSearchMetadata[], instance
|
||||
datasourceName: instanceSettings.name,
|
||||
query: {
|
||||
query: '${__value.raw}',
|
||||
queryType: 'traceId',
|
||||
queryType: 'traceql',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -639,7 +639,7 @@ export function createTableFrameFromTraceQlQuery(
|
||||
datasourceName: instanceSettings.name,
|
||||
query: {
|
||||
query: '${__value.raw}',
|
||||
queryType: 'traceId',
|
||||
queryType: 'traceql',
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -719,7 +719,7 @@ const traceSubFrame = (
|
||||
datasourceName: instanceSettings.name,
|
||||
query: {
|
||||
query: '${__data.fields.traceIdHidden}',
|
||||
queryType: 'traceId',
|
||||
queryType: 'traceql',
|
||||
},
|
||||
panelsState: {
|
||||
trace: {
|
||||
|
@ -34,7 +34,7 @@ export function QueryEditor(props: Props) {
|
||||
</a>
|
||||
</InlineLabel>
|
||||
<TraceQLEditor
|
||||
placeholder="Enter a TraceQL query (run with Shift+Enter)"
|
||||
placeholder="Enter a TraceQL query or trace ID (run with Shift+Enter)"
|
||||
value={query.query}
|
||||
onChange={onEditorChange}
|
||||
datasource={props.datasource}
|
||||
|
@ -53,6 +53,9 @@ export const language = {
|
||||
// labels
|
||||
[/[a-z_.][\w./_-]*(?=\s*(=|!=|>|<|>=|<=|=~|!~))/, 'tag'],
|
||||
|
||||
//trace ID
|
||||
[/^\s*[0-9A-Fa-f]+\s*$/, 'tag'],
|
||||
|
||||
// all keywords have the same color
|
||||
[
|
||||
/[a-zA-Z_.]\w*/,
|
||||
|
@ -37,7 +37,7 @@ export interface TempoJsonData extends DataSourceJsonData {
|
||||
}
|
||||
|
||||
// search = Loki search, nativeSearch = Tempo search for backwards compatibility
|
||||
export type TempoQueryType = 'traceql' | 'search' | 'traceId' | 'serviceMap' | 'upload' | 'nativeSearch' | 'clear';
|
||||
export type TempoQueryType = 'traceql' | 'search' | 'serviceMap' | 'upload' | 'nativeSearch' | 'clear';
|
||||
|
||||
export interface TempoQuery extends DataQuery {
|
||||
query: string;
|
||||
|
Loading…
Reference in New Issue
Block a user