Files
grafana/public/app/plugins/datasource/tempo/QueryEditor/ServiceGraphSection.tsx
Brendan O'Handley eedcd7d5b1 Prometheus Datasource: Improve Prom query variable editor (#58292)
* add prom query var editor with tests and migrations

* fix migration, now query not expr

* fix label_values migration

* remove comments

* fix label_values() variables order

* update UI and use more clear language

* fix tests

* use null coalescing operators

* allow users to query label values with label and metric if they have not set there flavor and version

* use enums instead of numbers for readability

* fix label&metrics switch

* update type in qv editor

* reuse datasource function to get all label names, getLabelNames(), prev named getTagKeys()

* use getLabelNames in the query var editor

* make label_values() variables label and metric more readable in the migration

* fix tooltip for label_values to remove API reference

* clean up tooltips and allow newlines in query_result function

* change function wording and exprType to query type/qryType for readability

* update prometheus query variable docs

* Update public/app/plugins/datasource/prometheus/components/VariableQueryEditor.tsx

Co-authored-by: Galen Kistler <109082771+gtk-grafana@users.noreply.github.com>

---------

Co-authored-by: Galen Kistler <109082771+gtk-grafana@users.noreply.github.com>
2023-02-09 15:35:36 -05:00

137 lines
4.3 KiB
TypeScript

import { css } from '@emotion/css';
import React, { useEffect, useState } from 'react';
import useAsync from 'react-use/lib/useAsync';
import { GrafanaTheme2 } from '@grafana/data';
import { Alert, InlineField, InlineFieldRow, useStyles2 } from '@grafana/ui';
import { AdHocFilter } from '../../../../features/variables/adhoc/picker/AdHocFilter';
import { AdHocVariableFilter } from '../../../../features/variables/types';
import { PrometheusDatasource } from '../../prometheus/datasource';
import { TempoQuery } from '../types';
import { getDS } from './utils';
export function ServiceGraphSection({
graphDatasourceUid,
query,
onChange,
}: {
graphDatasourceUid?: string;
query: TempoQuery;
onChange: (value: TempoQuery) => void;
}) {
const styles = useStyles2(getStyles);
const dsState = useAsync(() => getDS(graphDatasourceUid), [graphDatasourceUid]);
// Check if service graph metrics are being collected. If not, displays a warning
const [hasKeys, setHasKeys] = useState<boolean | undefined>(undefined);
useEffect(() => {
async function fn(ds: PrometheusDatasource) {
const keys = await ds.getLabelNames({
series: [
'traces_service_graph_request_server_seconds_sum',
'traces_service_graph_request_total',
'traces_service_graph_request_failed_total',
],
});
setHasKeys(Boolean(keys.length));
}
if (!dsState.loading && dsState.value) {
fn(dsState.value as PrometheusDatasource);
}
}, [dsState]);
if (dsState.loading) {
return null;
}
const ds = dsState.value as PrometheusDatasource;
if (!graphDatasourceUid) {
return <div className="text-warning">Please set up a service graph datasource in the datasource settings.</div>;
}
if (graphDatasourceUid && !ds) {
return (
<div className="text-warning">
Service graph datasource is configured but the data source no longer exists. Please configure existing data
source to use the service graph functionality.
</div>
);
}
const filters = queryToFilter(query.serviceMapQuery || '');
return (
<div>
<InlineFieldRow>
<InlineField label="Filter" labelWidth={14} grow>
<AdHocFilter
datasource={{ uid: graphDatasourceUid }}
filters={filters}
getTagKeysOptions={{
series: ['traces_service_graph_request_total', 'traces_spanmetrics_calls_total'],
}}
addFilter={(filter: AdHocVariableFilter) => {
onChange({
...query,
serviceMapQuery: filtersToQuery([...filters, filter]),
});
}}
removeFilter={(index: number) => {
const newFilters = [...filters];
newFilters.splice(index, 1);
onChange({ ...query, serviceMapQuery: filtersToQuery(newFilters) });
}}
changeFilter={(index: number, filter: AdHocVariableFilter) => {
const newFilters = [...filters];
newFilters.splice(index, 1, filter);
onChange({ ...query, serviceMapQuery: filtersToQuery(newFilters) });
}}
/>
</InlineField>
</InlineFieldRow>
{hasKeys === false ? (
<Alert title="No service graph data found" severity="info" className={styles.alert}>
Please ensure that service graph metrics are set up correctly according to the{' '}
<a
target="_blank"
rel="noreferrer noopener"
href="https://grafana.com/docs/tempo/next/grafana-agent/service-graphs/"
>
Tempo documentation
</a>
.
</Alert>
) : null}
</div>
);
}
function queryToFilter(query: string): AdHocVariableFilter[] {
let match;
let filters: AdHocVariableFilter[] = [];
const re = /([\w_]+)(=|!=|<|>|=~|!~)"(.*?)"/g;
while ((match = re.exec(query)) !== null) {
filters.push({
key: match[1],
operator: match[2],
value: match[3],
condition: '',
});
}
return filters;
}
function filtersToQuery(filters: AdHocVariableFilter[]): string {
return `{${filters.map((f) => `${f.key}${f.operator}"${f.value}"`).join(',')}}`;
}
const getStyles = (theme: GrafanaTheme2) => ({
alert: css`
max-width: 75ch;
margin-top: ${theme.spacing(2)};
`,
});