Tempo: Limit tags and tag values (#98306)

* Limit tags and tag values API response

* Add tag limit setting in datasource

* Add docs

* Update limit options

* Update docs
This commit is contained in:
Joey 2025-01-27 16:20:51 +00:00 committed by GitHub
parent 9bdeca8d10
commit 1f19fc8e0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 115 additions and 23 deletions

View File

@ -333,6 +333,10 @@ You can choose one of three options:
| **Duration** | _(Default)_ Displays the span duration on the span bar row. |
| **Tag** | Displays the span tag on the span bar row. You must also specify which tag key to use to get the tag value, such as `component`. |
### Tag limit
The **Tag limit** setting modifies the max number of tags and tag values to retrieve from Tempo. Default: 5000
### Private data source connect
[//]: # 'Shared content for authentication section procedure in data sources'

View File

@ -8,10 +8,11 @@ import { Alert, HorizontalGroup, InputActionMeta, Select, useStyles2 } from '@gr
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 { maxOptions, withTemplateVariableOptions } from './SearchField';
import { withTemplateVariableOptions } from './SearchField';
import { replaceAt } from './utils';
interface Props {
@ -46,11 +47,11 @@ export const GroupByField = (props: Props) => {
() => (f: TraceqlFilter) => {
const tags = datasource!.languageProvider.getMetricsSummaryTags(f.scope);
if (tagQuery.length === 0) {
return tags.slice(0, maxOptions);
return tags.slice(0, OPTIONS_LIMIT);
}
const queryLowerCase = tagQuery.toLowerCase();
return tags.filter((tag) => tag.toLowerCase().includes(queryLowerCase)).slice(0, maxOptions);
return tags.filter((tag) => tag.toLowerCase().includes(queryLowerCase)).slice(0, OPTIONS_LIMIT);
},
[datasource, tagQuery]
);

View File

@ -10,16 +10,11 @@ import { Select, HorizontalGroup, useStyles2, InputActionMeta } from '@grafana/u
import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen';
import { TempoDatasource } from '../datasource';
import { OPTIONS_LIMIT } from '../language_provider';
import { operators as allOperators, stringOperators, numberOperators, keywordOperators } from '../traceql/traceql';
import { filterScopedTag, operatorSelectableValue } from './utils';
const getStyles = () => ({
dropdown: css({
boxShadow: 'none',
}),
});
interface Props {
filter: TraceqlFilter;
datasource: TempoDatasource;
@ -119,11 +114,11 @@ const SearchField = ({
const tagOptions = useMemo(() => {
if (tagQuery.length === 0) {
return formatTagOptions(tags.slice(0, maxOptions), filter.tag);
return formatTagOptions(tags.slice(0, OPTIONS_LIMIT), filter.tag);
}
const queryLowerCase = tagQuery.toLowerCase();
const filterdOptions = tags.filter((tag) => tag.toLowerCase().includes(queryLowerCase)).slice(0, maxOptions);
const filterdOptions = tags.filter((tag) => tag.toLowerCase().includes(queryLowerCase)).slice(0, OPTIONS_LIMIT);
return formatTagOptions(filterdOptions, filter.tag);
}, [filter.tag, tagQuery, tags]);
@ -133,7 +128,7 @@ const SearchField = ({
}
if (tagValuesQuery.length === 0) {
return options.slice(0, maxOptions);
return options.slice(0, OPTIONS_LIMIT);
}
const queryLowerCase = tagValuesQuery.toLowerCase();
@ -144,7 +139,7 @@ const SearchField = ({
}
return false;
})
.slice(0, maxOptions);
.slice(0, OPTIONS_LIMIT);
}, [tagValuesQuery, options]);
return (
@ -239,6 +234,8 @@ const SearchField = ({
);
};
export default SearchField;
/**
* Add to a list of options the current template variables.
*
@ -250,7 +247,8 @@ export const withTemplateVariableOptions = (options: SelectableValue[] | undefin
return [...(options || []), ...templateVariables.map((v) => ({ label: `$${v.name}`, value: `$${v.name}` }))];
};
// Limit maximum options in select dropdowns for performance reasons
export const maxOptions = 1000;
export default SearchField;
const getStyles = () => ({
dropdown: css({
boxShadow: 'none',
}),
});

View File

@ -3,8 +3,8 @@ import { useEffect, useMemo, useState } from 'react';
import { DataQuery, SelectableValue } from '@grafana/data';
import { InlineField, InlineFieldRow, InputActionMeta, Select } from '@grafana/ui';
import { maxOptions } from './SearchTraceQLEditor/SearchField';
import { TempoDatasource } from './datasource';
import { OPTIONS_LIMIT } from './language_provider';
export enum TempoVariableQueryType {
LabelNames,
@ -46,7 +46,7 @@ export const TempoVariableQueryEditor = ({ onChange, query, datasource }: TempoV
const options = useMemo(() => {
if (labelQuery.length === 0) {
return labelOptions.slice(0, maxOptions);
return labelOptions.slice(0, OPTIONS_LIMIT);
}
const queryLowerCase = labelQuery.toLowerCase();
@ -57,7 +57,7 @@ export const TempoVariableQueryEditor = ({ onChange, query, datasource }: TempoV
}
return false;
})
.slice(0, maxOptions);
.slice(0, OPTIONS_LIMIT);
}, [labelQuery, labelOptions]);
const onQueryTypeChange = (newType: SelectableValue<TempoVariableQueryType>) => {

View File

@ -24,6 +24,7 @@ import { SecureSocksProxySettings, useStyles2, Divider, Stack } from '@grafana/u
import { QuerySettings } from './QuerySettings';
import { ServiceGraphSettings } from './ServiceGraphSettings';
import { StreamingSection } from './StreamingSection';
import { TagLimitSection } from './TagLimitSettings';
import { TraceQLSearchSettings } from './TraceQLSearchSettings';
export type ConfigEditorProps = DataSourcePluginOptionsEditorProps;
@ -118,6 +119,8 @@ const ConfigEditor = ({ options, onOptionsChange }: ConfigEditorProps) => {
</ConfigSubSection>
<SpanBarSection options={options} onOptionsChange={onOptionsChange} />
<TagLimitSection options={options} onOptionsChange={onOptionsChange} />
</Stack>
</ConfigSection>
</div>

View File

@ -0,0 +1,71 @@
import { css } from '@emotion/css';
import {
DataSourceJsonData,
DataSourcePluginOptionsEditorProps,
GrafanaTheme2,
updateDatasourcePluginJsonDataOption,
} from '@grafana/data';
import { ConfigDescriptionLink, ConfigSubSection } from '@grafana/experimental';
import { InlineField, InlineFieldRow, Input, useStyles2 } from '@grafana/ui';
export interface TagLimitOptions extends DataSourceJsonData {
tagLimit?: number;
}
interface Props extends DataSourcePluginOptionsEditorProps<TagLimitOptions> {}
export default function TagLimitSettings({ options, onOptionsChange }: Props) {
const styles = useStyles2(getStyles);
return (
<div className={css({ width: '100%' })}>
<InlineFieldRow className={styles.row}>
<InlineField
label="Max tags and tag values"
labelWidth={26}
tooltip="Specify the max number of tags and tag values to display in the Tempo editor. Default: 5000"
>
<Input
type="number"
placeholder="5000"
onChange={(v) =>
updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tagLimit', v.currentTarget.value)
}
value={options.jsonData.tagLimit || ''}
width={40}
/>
</InlineField>
</InlineFieldRow>
</div>
);
}
export const TagLimitSection = ({ options, onOptionsChange }: DataSourcePluginOptionsEditorProps) => {
return (
<ConfigSubSection
title="Tag limit"
description={
<ConfigDescriptionLink
description="Specify the limit for tags and tag values."
suffix={'/#tag-limit'}
feature="the tag limit"
/>
}
>
<TagLimitSettings options={options} onOptionsChange={onOptionsChange} />
</ConfigSubSection>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
infoText: css({
label: 'infoText',
paddingBottom: theme.spacing(2),
color: theme.colors.text.secondary,
}),
row: css({
label: 'row',
alignItems: 'baseline',
}),
});

View File

@ -37,6 +37,7 @@ import { BarGaugeDisplayMode, TableCellDisplayMode, VariableFormatID } from '@gr
import { generateQueryFromAdHocFilters, getTagWithoutScope, interpolateFilters } from './SearchTraceQLEditor/utils';
import { TempoVariableQuery, TempoVariableQueryType } from './VariableQueryEditor';
import { PrometheusDatasource, PromQuery } from './_importedDependencies/datasources/prometheus/types';
import { TagLimitOptions } from './configuration/TagLimitSettings';
import { SearchTableType, TraceqlFilter, TraceqlSearchScope } from './dataquery.gen';
import {
defaultTableFilter,
@ -109,6 +110,7 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
};
uploadedJson?: string | null = null;
spanBar?: SpanBarOptions;
tagLimit?: TagLimitOptions;
languageProvider: TempoLanguageProvider;
streamingEnabled?: {
@ -119,7 +121,7 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
tempoVersion?: string | null;
constructor(
private instanceSettings: DataSourceInstanceSettings<TempoJsonData>,
public instanceSettings: DataSourceInstanceSettings<TempoJsonData>,
private readonly templateSrv: TemplateSrv = getTemplateSrv()
) {
super(instanceSettings);

View File

@ -14,6 +14,12 @@ import { TempoDatasource } from './datasource';
import { intrinsicsV1 } from './traceql/traceql';
import { Scope } from './types';
// Limit maximum tags retrieved from the backend
export const TAGS_LIMIT = 5000;
// Limit maximum options in select dropdowns
export const OPTIONS_LIMIT = 1000;
export default class TempoLanguageProvider extends LanguageProvider {
datasource: TempoDatasource;
tagsV1?: string[];
@ -40,10 +46,14 @@ export default class TempoLanguageProvider extends LanguageProvider {
return this.startTask;
};
getTagsLimit = () => {
return this.datasource.instanceSettings.jsonData?.tagLimit || TAGS_LIMIT;
};
async fetchTags() {
let v1Resp, v2Resp;
try {
v2Resp = await this.request('/api/v2/search/tags', []);
v2Resp = await this.request('/api/v2/search/tags', { limit: this.getTagsLimit() });
} catch (error) {
v1Resp = await this.request('/api/search/tags', []);
}
@ -150,7 +160,9 @@ export default class TempoLanguageProvider extends LanguageProvider {
const encodedTag = this.encodeTag(tag);
const response = await this.request(
`/api/v2/search/tag/${encodedTag}/values`,
query ? { q: getTemplateSrv().replace(query, {}, VariableFormatID.Pipe) } : {}
query
? { q: getTemplateSrv().replace(query, {}, VariableFormatID.Pipe), limit: this.getTagsLimit() }
: { limit: this.getTagsLimit() }
);
let options: Array<SelectableValue<string>> = [];
if (response && response.tagValues) {

View File

@ -16,6 +16,7 @@ export interface TempoJsonData extends DataSourceJsonData {
spanBar?: {
tag: string;
};
tagLimit?: number;
traceQuery?: {
timeShiftEnabled?: boolean;
spanStartTimeShift?: string;