diff --git a/docs/sources/datasources/tempo/configure-tempo-data-source.md b/docs/sources/datasources/tempo/configure-tempo-data-source.md index 39d3cdf751a..e737d1810a8 100644 --- a/docs/sources/datasources/tempo/configure-tempo-data-source.md +++ b/docs/sources/datasources/tempo/configure-tempo-data-source.md @@ -321,6 +321,22 @@ You can configure this setting as follows: | **Time shift start** | Time shift for start of search. Default: `30m`. | | **Time shift end** | Time shift for end of search. Default: `30m`. | +### Tags time range + +The **Tags time range** feature controls how tag and tag-value queries are executed by specifying the time window applied to these requests. You can select one of the following options to constrain your queries: + +| Name | Description | +| ------------------- | ---------------------------------- | +| **Last 30 minutes** | Last 30 minutes of selected range. | +| **Last 3 hours** | Last 3 hours of selected range. | +| **Last 24 hours** | Last 24 hours of selected range. | +| **Last 3 days** | Last 3 days of selected range. | +| **Last 7 days** | Last 7 days of selected range. | + +### Tag limit + +The **Tag limit** setting modifies the max number of tags and tag values to retrieve from Tempo. Default: 5000 + ### Span bar The **Span bar** setting helps you display additional information in the span bar row. @@ -333,10 +349,6 @@ 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' diff --git a/public/app/plugins/datasource/tempo/QueryField.tsx b/public/app/plugins/datasource/tempo/QueryField.tsx index d64fcba919e..c0a47c8f79c 100644 --- a/public/app/plugins/datasource/tempo/QueryField.tsx +++ b/public/app/plugins/datasource/tempo/QueryField.tsx @@ -161,6 +161,7 @@ class TempoQueryFieldComponent extends PureComponent { app={app} onClearResults={this.onClearResults} addVariablesToOptions={this.props.addVariablesToOptions} + range={this.props.range} /> )} {query.queryType === 'serviceMap' && ( @@ -174,6 +175,7 @@ class TempoQueryFieldComponent extends PureComponent { onChange={onChange} app={app} onClearResults={this.onClearResults} + range={this.props.range} /> )} diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/SearchField.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/SearchField.tsx index c7b7bb6832c..8478963bed0 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/SearchField.tsx +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/SearchField.tsx @@ -1,9 +1,9 @@ import { css } from '@emotion/css'; import { uniq } from 'lodash'; -import { useState, useMemo } from 'react'; +import { useMemo, useState } from 'react'; import useAsync from 'react-use/lib/useAsync'; -import { SelectableValue } from '@grafana/data'; +import { SelectableValue, TimeRange } from '@grafana/data'; import { TemporaryAlert } from '@grafana/o11y-ds-frontend'; import { FetchError, getTemplateSrv, isFetchError } from '@grafana/runtime'; import { Select, Stack, useStyles2, InputActionMeta } from '@grafana/ui'; @@ -30,6 +30,8 @@ interface Props { isMulti?: boolean; allowCustomValue?: boolean; addVariablesToOptions?: boolean; + range?: TimeRange; + timeRangeForTags?: number; } const SearchField = ({ filter, @@ -45,6 +47,8 @@ const SearchField = ({ addVariablesToOptions, isMulti = true, allowCustomValue = true, + range, + timeRangeForTags, }: Props) => { const styles = useStyles2(getStyles); const [alertText, setAlertText] = useState(); @@ -57,7 +61,14 @@ const SearchField = ({ const updateOptions = async () => { try { - const result = filter.tag ? await datasource.languageProvider.getOptionsV2(scopedTag, query) : []; + const result = filter.tag + ? await datasource.languageProvider.getOptionsV2({ + tag: scopedTag, + query, + timeRangeForTags, + range, + }) + : []; setAlertText(undefined); setError(null); return result; @@ -77,6 +88,8 @@ const SearchField = ({ datasource.languageProvider, setError, query, + range, + timeRangeForTags, ]); // Add selected option if it doesn't exist in the current list of options diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TagsInput.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TagsInput.tsx index d9ecd049504..a8266bfde8c 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TagsInput.tsx +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TagsInput.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import { useCallback, useEffect } from 'react'; import { v4 as uuidv4 } from 'uuid'; -import { GrafanaTheme2 } from '@grafana/data'; +import { GrafanaTheme2, TimeRange } from '@grafana/data'; import { AccessoryButton } from '@grafana/plugin-ui'; import { FetchError } from '@grafana/runtime'; import { useStyles2 } from '@grafana/ui'; @@ -41,6 +41,8 @@ interface Props { hideValues?: boolean; requireTagAndValue?: boolean; addVariablesToOptions?: boolean; + range?: TimeRange; + timeRangeForTags?: number; } const TagsInput = ({ updateFilter, @@ -54,6 +56,8 @@ const TagsInput = ({ requireTagAndValue, generateQueryWithoutFilter, addVariablesToOptions, + range, + timeRangeForTags, }: Props) => { const styles = useStyles2(getStyles); const handleOnAdd = useCallback( @@ -91,6 +95,8 @@ const TagsInput = ({ hideValue={hideValues} query={generateQueryWithoutFilter(f)} addVariablesToOptions={addVariablesToOptions} + range={range} + timeRangeForTags={timeRangeForTags} /> {(validInput(f) || filters.length > 1) && ( void; app?: CoreApp; addVariablesToOptions?: boolean; + range?: TimeRange; } const hardCodedFilterIds = ['min-duration', 'max-duration', 'status']; -const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVariablesToOptions = true }: Props) => { +const TraceQLSearch = ({ + datasource, + query, + onChange, + onClearResults, + app, + addVariablesToOptions = true, + range, +}: Props) => { const styles = useStyles2(getStyles); const [alertText, setAlertText] = useState(); const [error, setError] = useState(null); @@ -74,7 +83,7 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa useEffect(() => { const fetchTags = async () => { try { - await datasource.languageProvider.start(); + await datasource.languageProvider.start(range, datasource.timeRangeForTags); setIsTagsLoading(false); setAlertText(undefined); } catch (error) { @@ -84,7 +93,7 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa } }; fetchTags(); - }, [datasource, setAlertText]); + }, [datasource, setAlertText, range, datasource.timeRangeForTags]); useEffect(() => { // Initialize state with configured static filters that already have a value from the config @@ -155,6 +164,8 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa hideTag={true} query={generateQueryWithoutFilter(findFilter(f.id))} addVariablesToOptions={addVariablesToOptions} + range={range} + timeRangeForTags={datasource.timeRangeForTags} /> ) @@ -179,6 +190,8 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa isMulti={false} allowCustomValue={false} addVariablesToOptions={addVariablesToOptions} + range={range} + timeRangeForTags={datasource.timeRangeForTags} /> void; query: TempoVariableQuery; datasource: TempoDatasource; + range?: TimeRange; }; -export const TempoVariableQueryEditor = ({ onChange, query, datasource }: TempoVariableQueryEditorProps) => { +export const TempoVariableQueryEditor = ({ onChange, query, datasource, range }: TempoVariableQueryEditorProps) => { const [label, setLabel] = useState(query.label || ''); const [type, setType] = useState(query.type); const [labelOptions, setLabelOptions] = useState>>([]); const [labelQuery, setLabelQuery] = useState(''); + const [isLoading, setIsLoading] = useState(false); useEffect(() => { if (type === TempoVariableQueryType.LabelValues) { - datasource.labelNamesQuery().then((labelNames: Array<{ text: string }>) => { - setLabelOptions(labelNames.map(({ text }) => ({ label: text, value: text }))); - }); + setIsLoading(true); + datasource + .labelNamesQuery(range) + .then((labelNames: Array<{ text: string }>) => { + setLabelOptions(labelNames.map(({ text }) => ({ label: text, value: text }))); + setIsLoading(false); + }) + .catch(() => { + setIsLoading(false); + }); } - }, [datasource, query, type]); + }, [datasource, query, type, range]); const options = useMemo(() => { if (labelQuery.length === 0) { @@ -122,6 +131,7 @@ export const TempoVariableQueryEditor = ({ onChange, query, datasource }: TempoV width={32} allowCustomValue virtualized + isLoading={isLoading} /> diff --git a/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx b/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx index 941bcaadceb..47b79d82f74 100644 --- a/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx +++ b/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx @@ -25,6 +25,7 @@ import { QuerySettings } from './QuerySettings'; import { ServiceGraphSettings } from './ServiceGraphSettings'; import { StreamingSection } from './StreamingSection'; import { TagLimitSection } from './TagLimitSettings'; +import { TagsTimeRangeSettings } from './TagsTimeRangeSettings'; import { TraceQLSearchSettings } from './TraceQLSearchSettings'; export type ConfigEditorProps = DataSourcePluginOptionsEditorProps; @@ -118,9 +119,21 @@ const ConfigEditor = ({ options, onOptionsChange }: ConfigEditorProps) => { - + + } + > + + + diff --git a/public/app/plugins/datasource/tempo/configuration/TagsTimeRangeSettings.tsx b/public/app/plugins/datasource/tempo/configuration/TagsTimeRangeSettings.tsx new file mode 100644 index 00000000000..a3c8ee6c823 --- /dev/null +++ b/public/app/plugins/datasource/tempo/configuration/TagsTimeRangeSettings.tsx @@ -0,0 +1,45 @@ +import { DataSourcePluginOptionsEditorProps, updateDatasourcePluginJsonDataOption } from '@grafana/data'; +import { Combobox, InlineField, InlineFieldRow, useStyles2 } from '@grafana/ui'; + +import { TempoJsonData } from '../types'; + +import { getStyles } from './QuerySettings'; + +interface Props extends DataSourcePluginOptionsEditorProps {} + +export const DEFAULT_TIME_RANGE_FOR_TAGS = 1800; // 60 * 30 + +export function TagsTimeRangeSettings({ options, onOptionsChange }: Props) { + const styles = useStyles2(getStyles); + + const selectOptions = [ + { label: 'Last 30 minutes of selected range', value: DEFAULT_TIME_RANGE_FOR_TAGS }, + { label: 'Last 3 hours of selected range', value: 10800 }, // 60 * 60 * 3 + { label: 'Last 24 hours of selected range', value: 86400 }, // 60 * 60 * 24 + { label: 'Last 3 days of selected range', value: 259200 }, // 60 * 60 * 24 * 3 + { label: 'Last 7 days of selected range', value: 604800 }, // 60 * 60 * 24 * 7 + ]; + + return ( +
+ + + { + updateDatasourcePluginJsonDataOption( + { onOptionsChange, options }, + 'timeRangeForTags', + v?.value ?? DEFAULT_TIME_RANGE_FOR_TAGS + ); + }} + placeholder="Time range for tags" + width={40} + /> + + +
+ ); +} diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index fc5f71eb8bb..8d16b28f97d 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -21,6 +21,7 @@ import { ScopedVars, SelectableValue, TestDataSourceResponse, + TimeRange, urlUtil, } from '@grafana/data'; import { NodeGraphOptions, SpanBarOptions, TraceToLogsOptions } from '@grafana/o11y-ds-frontend'; @@ -134,6 +135,8 @@ export class TempoDatasource extends DataSourceWithBackend>(() => []); @@ -178,10 +181,10 @@ export class TempoDatasource extends DataSourceWithBackend> { - await this.languageProvider.fetchTags(); + async labelNamesQuery(range?: TimeRange): Promise> { + await this.languageProvider.start(range, this.timeRangeForTags); const tags = this.languageProvider.getAutocompleteTags(); return tags.filter((tag) => tag !== undefined).map((tag) => ({ text: tag })); } - async labelValuesQuery(labelName?: string): Promise> { + async labelValuesQuery(labelName?: string, range?: TimeRange): Promise> { if (!labelName) { return []; } + await this.languageProvider.start(range, this.timeRangeForTags); + let options; try { // Retrieve the scope of the tag @@ -217,7 +222,11 @@ export class TempoDatasource extends DataSourceWithBackend): Promise> { const query = this.languageProvider.generateQueryFromFilters({ adhocFilters: options.filters }); - return this.tagValuesQuery(options.key, query); + return this.tagValuesQuery(options.key, query, options.timeRange); } - async tagValuesQuery(tag: string, query: string): Promise> { + async tagValuesQuery(tag: string, query: string, range?: TimeRange): Promise> { let options; try { // For V2, we need to send scope and tag name, e.g. `span.http.status_code`, // unless the tag has intrinsic scope - options = await this.languageProvider.getOptionsV2(tag, query); + options = await this.languageProvider.getOptionsV2({ + tag, + query, + timeRangeForTags: this.timeRangeForTags, + range, + }); } catch { // For V1, the tag name (e.g. `http.status_code`) is enough options = await this.languageProvider.getOptionsV1(getTagWithoutScope(tag)); diff --git a/public/app/plugins/datasource/tempo/language_provider.ts b/public/app/plugins/datasource/tempo/language_provider.ts index 6aa67bb750e..c84406e5fa7 100644 --- a/public/app/plugins/datasource/tempo/language_provider.ts +++ b/public/app/plugins/datasource/tempo/language_provider.ts @@ -1,4 +1,4 @@ -import { AdHocVariableFilter, LanguageProvider, SelectableValue } from '@grafana/data'; +import { AdHocVariableFilter, LanguageProvider, SelectableValue, TimeRange } from '@grafana/data'; import { getTemplateSrv } from '@grafana/runtime'; import { VariableFormatID } from '@grafana/schema'; @@ -9,6 +9,7 @@ import { getTagsByScope, getUnscopedTags, } from './SearchTraceQLEditor/utils'; +import { DEFAULT_TIME_RANGE_FOR_TAGS } from './configuration/TagsTimeRangeSettings'; import { TraceqlFilter, TraceqlSearchScope } from './dataquery.gen'; import { TempoDatasource } from './datasource'; import { enumIntrinsics, intrinsicsV1 } from './traceql/traceql'; @@ -20,10 +21,19 @@ export const TAGS_LIMIT = 5000; // Limit maximum options in select dropdowns export const OPTIONS_LIMIT = 1000; +interface GetOptionsV2 { + tag: string; + query?: string; + timeRangeForTags?: number; + range?: TimeRange; +} + export default class TempoLanguageProvider extends LanguageProvider { datasource: TempoDatasource; tagsV1?: string[]; tagsV2?: Scope[]; + private previousRange?: TimeRange; + constructor(datasource: TempoDatasource, initialValues?: any) { super(); @@ -36,9 +46,15 @@ export default class TempoLanguageProvider extends LanguageProvider { return res?.data; }; - start = async () => { - if (!this.startTask) { - this.startTask = this.fetchTags().then(() => { + start = async (range?: TimeRange, timeRangeForTags?: number) => { + // Check if we need to refetch tags due to range changes (minute-level granularity) + const shouldRefetch = this.shouldRefreshLabels(range, this.previousRange); + + if (!this.startTask || shouldRefetch) { + // Store the current range for future comparison + this.previousRange = range; + + this.startTask = this.fetchTags(timeRangeForTags, range).then(() => { return []; }); } @@ -46,14 +62,42 @@ export default class TempoLanguageProvider extends LanguageProvider { return this.startTask; }; + roundMsToMin = (milliseconds: number) => { + return this.roundSecToMin(milliseconds / 1000); + }; + + roundSecToMin = (seconds: number) => { + return Math.floor(seconds / 60); + }; + + shouldRefreshLabels = (range?: TimeRange, prevRange?: TimeRange): boolean => { + if (range && prevRange) { + const sameMinuteFrom = this.roundMsToMin(range.from.valueOf()) === this.roundMsToMin(prevRange.from.valueOf()); + const sameMinuteTo = this.roundMsToMin(range.to.valueOf()) === this.roundMsToMin(prevRange.to.valueOf()); + // If both are same, don't need to refresh + return !(sameMinuteFrom && sameMinuteTo); + } + // If one is defined and the other is not, we should refresh + return prevRange !== range; + }; + getTagsLimit = () => { return this.datasource.instanceSettings.jsonData?.tagLimit || TAGS_LIMIT; }; - async fetchTags() { + async fetchTags(timeRangeForTags?: number, range?: TimeRange) { let v1Resp, v2Resp; + try { - v2Resp = await this.request('/api/v2/search/tags', { limit: this.getTagsLimit() }); + const params: { limit: number; start?: number; end?: number } = { + limit: this.getTagsLimit(), + }; + if (timeRangeForTags && range && timeRangeForTags !== DEFAULT_TIME_RANGE_FOR_TAGS) { + const { start, end } = this.getTimeRangeForTags(timeRangeForTags, range); + params.start = start; + params.end = end; + } + v2Resp = await this.request(`/api/v2/search/tags`, params); } catch (error) { v1Resp = await this.request('/api/search/tags', []); } @@ -144,14 +188,23 @@ export default class TempoLanguageProvider extends LanguageProvider { return options; } - async getOptionsV2(tag: string, query?: string): Promise>> { + async getOptionsV2({ tag, query, timeRangeForTags, range }: GetOptionsV2): Promise>> { const encodedTag = this.encodeTag(tag); - const response = await this.request( - `/api/v2/search/tag/${encodedTag}/values`, - query - ? { q: getTemplateSrv().replace(query, {}, VariableFormatID.Pipe), limit: this.getTagsLimit() } - : { limit: this.getTagsLimit() } - ); + const params: { q?: string; limit: number; start?: number; end?: number } = { + limit: this.getTagsLimit(), + }; + + if (query) { + params.q = getTemplateSrv().replace(query, {}, VariableFormatID.Pipe); + } + + if (timeRangeForTags && range && timeRangeForTags !== DEFAULT_TIME_RANGE_FOR_TAGS) { + const { start, end } = this.getTimeRangeForTags(timeRangeForTags, range); + params.start = start; + params.end = end; + } + + const response = await this.request(`/api/v2/search/tag/${encodedTag}/values`, params); let options: Array> = []; if (response && response.tagValues) { response.tagValues.forEach((v: { type: string; value?: string }) => { @@ -167,6 +220,15 @@ export default class TempoLanguageProvider extends LanguageProvider { return options; } + getTimeRangeForTags = (timeRangeForTags: number, range: TimeRange) => { + // Get tags from the last timeRangeForTags seconds, but don't go before the start of the range + // If timeRangeForTags is 1 hour and your query range is 24 hours, it will fetch tags from the last 1 hour of that 24-hour period + // If timeRangeForTags is larger than the total range duration, it will use the entire available range + const start = Math.max(range.from.unix(), range.to.unix() - timeRangeForTags); + const end = range.to.unix(); + return { start, end }; + }; + /** * Encode (serialize) a given tag for use in a URL. * diff --git a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx index 56d2ad225d5..9e9144776c9 100644 --- a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx +++ b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx @@ -88,6 +88,7 @@ export function QueryEditor(props: Props) { onChange={props.onChange} datasource={props.datasource} onRunQuery={props.onRunQuery} + range={props.range} />
void; datasource: TempoDatasource; readOnly?: boolean; + range?: TimeRange; } export function TraceQLEditor(props: Props) { const [alertText, setAlertText] = useState(); const { query, onChange, onRunQuery, placeholder } = props; - const setupAutocompleteFn = useAutocomplete(props.datasource, setAlertText); + const setupAutocompleteFn = useAutocomplete( + props.datasource, + setAlertText, + props.datasource.timeRangeForTags ?? DEFAULT_TIME_RANGE_FOR_TAGS, + props.range + ); const theme = useTheme2(); const styles = getStyles(theme, placeholder); @@ -192,20 +199,34 @@ function setupAutoSize(editor: monacoTypes.editor.IStandaloneCodeEditor) { * Hook that returns function that will set up monaco autocomplete for the label selector * @param datasource the Tempo datasource instance * @param setAlertText setter for alert's text + * @param timeRangeForTags time range for tags and tag values queries + * @param range time range */ -function useAutocomplete(datasource: TempoDatasource, setAlertText: (text?: string) => void) { +function useAutocomplete( + datasource: TempoDatasource, + setAlertText: (text?: string) => void, + timeRangeForTags: number, + range?: TimeRange +) { // We need the provider ref so we can pass it the label/values data later. This is because we run the call for the // values here but there is additional setup needed for the provider later on. We could run the getSeries() in the // returned function but that is run after the monaco is mounted so would delay the request a bit when it does not // need to. const providerRef = useRef( - new CompletionProvider({ languageProvider: datasource.languageProvider, setAlertText }) + new CompletionProvider({ + languageProvider: datasource.languageProvider, + setAlertText, + timeRangeForTags, + range, + }) ); + const previousRangeRef = useRef(range); + useEffect(() => { const fetchTags = async () => { try { - await datasource.languageProvider.start(); + await datasource.languageProvider.start(range, timeRangeForTags); setAlertText(undefined); } catch (error) { if (error instanceof Error) { @@ -214,7 +235,20 @@ function useAutocomplete(datasource: TempoDatasource, setAlertText: (text?: stri } }; fetchTags(); - }, [datasource, setAlertText]); + }, [datasource, setAlertText, range, timeRangeForTags]); + + useEffect(() => { + const rangeChanged = datasource.languageProvider.shouldRefreshLabels(range, previousRangeRef.current); + + if (rangeChanged) { + providerRef.current.range = range; + previousRangeRef.current = range; + } + }, [range, datasource.languageProvider]); + + useEffect(() => { + providerRef.current.timeRangeForTags = timeRangeForTags; + }, [timeRangeForTags]); const autocompleteDisposeFun = useRef<(() => void) | null>(null); useEffect(() => { diff --git a/public/app/plugins/datasource/tempo/traceql/autocomplete.ts b/public/app/plugins/datasource/tempo/traceql/autocomplete.ts index 906d96416d6..68af850bc7d 100644 --- a/public/app/plugins/datasource/tempo/traceql/autocomplete.ts +++ b/public/app/plugins/datasource/tempo/traceql/autocomplete.ts @@ -1,6 +1,6 @@ import { IMarkdownString, languages } from 'monaco-editor'; -import { SelectableValue } from '@grafana/data'; +import { SelectableValue, TimeRange } from '@grafana/data'; import { isFetchError } from '@grafana/runtime'; import type { Monaco, monacoTypes } from '@grafana/ui'; @@ -25,6 +25,8 @@ type CompletionItem = MinimalCompletionItem & { interface Props { languageProvider: TempoLanguageProvider; setAlertText: (text?: string) => void; + timeRangeForTags?: number; + range?: TimeRange; } /** @@ -38,11 +40,15 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP languageProvider: TempoLanguageProvider; registerInteractionCommandId: string | null; setAlertText: (text?: string) => void; + timeRangeForTags?: number; + range?: TimeRange; constructor(props: Props) { this.languageProvider = props.languageProvider; this.setAlertText = props.setAlertText; this.registerInteractionCommandId = null; + this.timeRangeForTags = props.timeRangeForTags; + this.range = props.range; } triggerCharacters = ['{', '.', '[', '(', '=', '~', ' ', '"']; @@ -391,14 +397,24 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP this.registerInteractionCommandId = id; } - private async getTagValues(tagName: string, query: string): Promise>> { + private async getTagValues( + tagName: string, + query: string, + timeRangeForTags?: number, + range?: TimeRange + ): Promise>> { let tagValues: Array>; const cacheKey = `${tagName}:${query}`; if (this.cachedValues.hasOwnProperty(cacheKey)) { tagValues = this.cachedValues[cacheKey]; } else { - tagValues = await this.languageProvider.getOptionsV2(tagName, query); + tagValues = await this.languageProvider.getOptionsV2({ + tag: tagName, + query, + timeRangeForTags, + range, + }); this.cachedValues[cacheKey] = tagValues; } return tagValues; @@ -461,7 +477,7 @@ export class CompletionProvider implements monacoTypes.languages.CompletionItemP case 'SPANSET_IN_VALUE': let tagValues; try { - tagValues = await this.getTagValues(situation.tagName, situation.query); + tagValues = await this.getTagValues(situation.tagName, situation.query, this.timeRangeForTags, this.range); setAlertText(undefined); } catch (error) { if (isFetchError(error)) { diff --git a/public/app/plugins/datasource/tempo/types.ts b/public/app/plugins/datasource/tempo/types.ts index 346195dcb4a..502f64ab8a7 100644 --- a/public/app/plugins/datasource/tempo/types.ts +++ b/public/app/plugins/datasource/tempo/types.ts @@ -26,6 +26,7 @@ export interface TempoJsonData extends DataSourceJsonData { streamingEnabled?: { search?: boolean; }; + timeRangeForTags?: number; } export interface TempoQuery extends TempoBase { diff --git a/public/app/plugins/datasource/tempo/variables.ts b/public/app/plugins/datasource/tempo/variables.ts index 29c76129ca7..b6f28583543 100644 --- a/public/app/plugins/datasource/tempo/variables.ts +++ b/public/app/plugins/datasource/tempo/variables.ts @@ -18,7 +18,7 @@ export class TempoVariableSupport extends CustomVariableSupport ({ data }))); } }