Tempo: Filter available service graph filter values to most relevant options (#43728)

* Restrict service graph filter tag values to labels from service graph metrics
This commit is contained in:
Connor Lindsey 2022-01-06 07:09:31 -07:00 committed by GitHub
parent cb34c47123
commit 1a7567d8c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 58 additions and 13 deletions

View File

@ -12,6 +12,9 @@ interface Props {
addFilter: (filter: AdHocVariableFilter) => void;
removeFilter: (index: number) => void;
changeFilter: (index: number, newFilter: AdHocVariableFilter) => void;
// Passes options to the datasources getTagKeys(options?: any) method
// which is called to fetch the available filter key options in AdHocFilterKey.tsx
getTagKeysOptions?: any;
}
/**
@ -51,6 +54,7 @@ export class AdHocFilter extends PureComponent<Props> {
datasource={this.props.datasource!}
appendBefore={filters.length > 0 ? <ConditionSegment label="AND" /> : null}
onCompleted={this.appendFilterToVariable}
getTagKeysOptions={this.props.getTagKeysOptions}
/>
</div>
);
@ -75,6 +79,7 @@ export class AdHocFilter extends PureComponent<Props> {
onKeyChange={this.onChange(index, 'key')}
onOperatorChange={this.onChange(index, 'operator')}
onValueChange={this.onChange(index, 'value')}
getTagKeysOptions={this.props.getTagKeysOptions}
/>
</React.Fragment>
);

View File

@ -8,9 +8,10 @@ interface Props {
datasource: DataSourceRef;
onCompleted: (filter: AdHocVariableFilter) => void;
appendBefore?: React.ReactNode;
getTagKeysOptions?: any;
}
export const AdHocFilterBuilder: FC<Props> = ({ datasource, appendBefore, onCompleted }) => {
export const AdHocFilterBuilder: FC<Props> = ({ datasource, appendBefore, onCompleted, getTagKeysOptions }) => {
const [key, setKey] = useState<string | null>(null);
const [operator, setOperator] = useState<string>('=');
@ -44,7 +45,14 @@ export const AdHocFilterBuilder: FC<Props> = ({ datasource, appendBefore, onComp
);
if (key === null) {
return <AdHocFilterKey datasource={datasource} filterKey={key} onChange={onKeyChanged} />;
return (
<AdHocFilterKey
datasource={datasource}
filterKey={key}
onChange={onKeyChanged}
getTagKeysOptions={getTagKeysOptions}
/>
);
}
return (
@ -57,6 +65,7 @@ export const AdHocFilterBuilder: FC<Props> = ({ datasource, appendBefore, onComp
onKeyChange={onKeyChanged}
onOperatorChange={onOperatorChanged}
onValueChange={onValueChanged}
getTagKeysOptions={getTagKeysOptions}
/>
</React.Fragment>
);

View File

@ -7,12 +7,13 @@ interface Props {
datasource: DataSourceRef;
filterKey: string | null;
onChange: (item: SelectableValue<string | null>) => void;
getTagKeysOptions?: any;
}
const MIN_WIDTH = 90;
export const AdHocFilterKey: FC<Props> = ({ datasource, onChange, filterKey }) => {
const loadKeys = () => fetchFilterKeys(datasource);
const loadKeysWithRemove = () => fetchFilterKeysWithRemove(datasource);
export const AdHocFilterKey: FC<Props> = ({ datasource, onChange, filterKey, getTagKeysOptions }) => {
const loadKeys = () => fetchFilterKeys(datasource, getTagKeysOptions);
const loadKeysWithRemove = () => fetchFilterKeysWithRemove(datasource, getTagKeysOptions);
if (filterKey === null) {
return (
@ -51,18 +52,24 @@ const plusSegment: ReactElement = (
</a>
);
const fetchFilterKeys = async (datasource: DataSourceRef): Promise<Array<SelectableValue<string>>> => {
const fetchFilterKeys = async (
datasource: DataSourceRef,
getTagKeysOptions?: any
): Promise<Array<SelectableValue<string>>> => {
const ds = await getDatasourceSrv().get(datasource);
if (!ds || !ds.getTagKeys) {
return [];
}
const metrics = await ds.getTagKeys();
const metrics = await ds.getTagKeys(getTagKeysOptions);
return metrics.map((m) => ({ label: m.text, value: m.text }));
};
const fetchFilterKeysWithRemove = async (datasource: DataSourceRef): Promise<Array<SelectableValue<string>>> => {
const keys = await fetchFilterKeys(datasource);
const fetchFilterKeysWithRemove = async (
datasource: DataSourceRef,
getTagKeysOptions?: any
): Promise<Array<SelectableValue<string>>> => {
const keys = await fetchFilterKeys(datasource, getTagKeysOptions);
return [REMOVE_VALUE, ...keys];
};

View File

@ -12,6 +12,7 @@ interface Props {
onOperatorChange: (item: SelectableValue<string>) => void;
onValueChange: (item: SelectableValue<string>) => void;
placeHolder?: string;
getTagKeysOptions?: any;
}
export const AdHocFilterRenderer: FC<Props> = ({
@ -21,10 +22,16 @@ export const AdHocFilterRenderer: FC<Props> = ({
onOperatorChange,
onValueChange,
placeHolder,
getTagKeysOptions,
}) => {
return (
<>
<AdHocFilterKey datasource={datasource} filterKey={key} onChange={onKeyChange} />
<AdHocFilterKey
datasource={datasource}
filterKey={key}
onChange={onKeyChange}
getTagKeysOptions={getTagKeysOptions}
/>
<div className="gf-form">
<OperatorSegment value={operator} onChange={onOperatorChange} />
</div>

View File

@ -790,9 +790,19 @@ export class PrometheusDatasource
);
}
async getTagKeys() {
const result = await this.metadataRequest('/api/v1/labels');
return result?.data?.data?.map((value: any) => ({ text: value })) ?? [];
async getTagKeys(options?: any) {
if (options?.series) {
// Get tags for the provided series only
const seriesLabels: Array<Record<string, string[]>> = await Promise.all(
options.series.map((series: string) => this.languageProvider.fetchSeriesLabels(series))
);
const uniqueLabels = [...new Set(...seriesLabels.map((value) => Object.keys(value)))];
return uniqueLabels.map((value: any) => ({ text: value }));
} else {
// Get all tags
const result = await this.metadataRequest('/api/v1/labels');
return result?.data?.data?.map((value: any) => ({ text: value })) ?? [];
}
}
async getTagValues(options: { key?: string } = {}) {

View File

@ -44,6 +44,13 @@ export function ServiceGraphSection({
<AdHocFilter
datasource={{ uid: graphDatasourceUid }}
filters={filters}
getTagKeysOptions={{
series: [
'traces_service_graph_request_server_seconds_sum',
'traces_service_graph_request_total',
'traces_service_graph_request_failed_total',
],
}}
addFilter={(filter: AdHocVariableFilter) => {
onChange({
...query,