Skip to content

Tempo: Add time range to tags and tag value requests #107872

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 21, 2025
20 changes: 16 additions & 4 deletions docs/sources/datasources/tempo/configure-tempo-data-source.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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'
Expand Down
2 changes: 2 additions & 0 deletions public/app/plugins/datasource/tempo/QueryField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class TempoQueryFieldComponent extends PureComponent<Props, State> {
app={app}
onClearResults={this.onClearResults}
addVariablesToOptions={this.props.addVariablesToOptions}
range={this.props.range}
/>
)}
{query.queryType === 'serviceMap' && (
Expand All @@ -174,6 +175,7 @@ class TempoQueryFieldComponent extends PureComponent<Props, State> {
onChange={onChange}
app={app}
onClearResults={this.onClearResults}
range={this.props.range}
/>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -30,6 +30,8 @@ interface Props {
isMulti?: boolean;
allowCustomValue?: boolean;
addVariablesToOptions?: boolean;
range?: TimeRange;
timeRangeForTags?: number;
}
const SearchField = ({
filter,
Expand All @@ -45,6 +47,8 @@ const SearchField = ({
addVariablesToOptions,
isMulti = true,
allowCustomValue = true,
range,
timeRangeForTags,
}: Props) => {
const styles = useStyles2(getStyles);
const [alertText, setAlertText] = useState<string>();
Expand All @@ -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;
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -41,6 +41,8 @@ interface Props {
hideValues?: boolean;
requireTagAndValue?: boolean;
addVariablesToOptions?: boolean;
range?: TimeRange;
timeRangeForTags?: number;
}
const TagsInput = ({
updateFilter,
Expand All @@ -54,6 +56,8 @@ const TagsInput = ({
requireTagAndValue,
generateQueryWithoutFilter,
addVariablesToOptions,
range,
timeRangeForTags,
}: Props) => {
const styles = useStyles2(getStyles);
const handleOnAdd = useCallback(
Expand Down Expand Up @@ -91,6 +95,8 @@ const TagsInput = ({
hideValue={hideValues}
query={generateQueryWithoutFilter(f)}
addVariablesToOptions={addVariablesToOptions}
range={range}
timeRangeForTags={timeRangeForTags}
/>
{(validInput(f) || filters.length > 1) && (
<AccessoryButton
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { css } from '@emotion/css';
import { useCallback, useEffect, useState } from 'react';

import { CoreApp, GrafanaTheme2 } from '@grafana/data';
import { CoreApp, GrafanaTheme2, TimeRange } from '@grafana/data';
import { TemporaryAlert } from '@grafana/o11y-ds-frontend';
import { config, FetchError, getTemplateSrv, reportInteraction } from '@grafana/runtime';
import { Alert, Button, Stack, Select, useStyles2 } from '@grafana/ui';
Expand All @@ -28,11 +28,20 @@ interface Props {
onClearResults: () => 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<string>();
const [error, setError] = useState<Error | FetchError | null>(null);
Expand Down Expand Up @@ -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) {
Expand All @@ -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
Expand Down Expand Up @@ -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}
/>
</InlineSearchField>
)
Expand All @@ -179,6 +190,8 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa
isMulti={false}
allowCustomValue={false}
addVariablesToOptions={addVariablesToOptions}
range={range}
timeRangeForTags={datasource.timeRangeForTags}
/>
</InlineSearchField>
<InlineSearchField
Expand Down Expand Up @@ -240,6 +253,8 @@ const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app, addVa
generateQueryWithoutFilter={generateQueryWithoutFilter}
requireTagAndValue={true}
addVariablesToOptions={addVariablesToOptions}
range={range}
timeRangeForTags={datasource.timeRangeForTags}
/>
</InlineSearchField>
<AggregateByAlert
Expand Down
22 changes: 16 additions & 6 deletions public/app/plugins/datasource/tempo/VariableQueryEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from 'react';

import { DataQuery, SelectableValue } from '@grafana/data';
import { DataQuery, SelectableValue, TimeRange } from '@grafana/data';
import { InlineField, InlineFieldRow, InputActionMeta, Select } from '@grafana/ui';

import { TempoDatasource } from './datasource';
Expand Down Expand Up @@ -28,21 +28,30 @@ export type TempoVariableQueryEditorProps = {
onChange: (value: TempoVariableQuery) => 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<number | undefined>(query.type);
const [labelOptions, setLabelOptions] = useState<Array<SelectableValue<string>>>([]);
const [labelQuery, setLabelQuery] = useState<string>('');
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) {
Expand Down Expand Up @@ -122,6 +131,7 @@ export const TempoVariableQueryEditor = ({ onChange, query, datasource }: TempoV
width={32}
allowCustomValue
virtualized
isLoading={isLoading}
/>
</InlineField>
</InlineFieldRow>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -118,9 +119,21 @@ const ConfigEditor = ({ options, onOptionsChange }: ConfigEditorProps) => {
<QuerySettings options={options} onOptionsChange={onOptionsChange} />
</ConfigSubSection>

<SpanBarSection options={options} onOptionsChange={onOptionsChange} />
<ConfigSubSection
title="Tags time range"
description={
<ConfigDescriptionLink
description="Modify how tags and tag values queries are run."
suffix="tempo/configure-tempo-data-source/#tags-time-range"
feature="the tags time range"
/>
}
>
<TagsTimeRangeSettings options={options} onOptionsChange={onOptionsChange} />
</ConfigSubSection>

<TagLimitSection options={options} onOptionsChange={onOptionsChange} />
<SpanBarSection options={options} onOptionsChange={onOptionsChange} />
</Stack>
</ConfigSection>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -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<TempoJsonData> {}

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 (
<div className={styles.container}>
<InlineFieldRow className={styles.row}>
<InlineField tooltip="Time range in tags and tag value queries" label="Time range in query" labelWidth={26}>
<Combobox
id="time-range-for-tags-select"
options={selectOptions}
value={options.jsonData?.timeRangeForTags || DEFAULT_TIME_RANGE_FOR_TAGS}
onChange={(v) => {
updateDatasourcePluginJsonDataOption(
{ onOptionsChange, options },
'timeRangeForTags',
v?.value ?? DEFAULT_TIME_RANGE_FOR_TAGS
);
}}
placeholder="Time range for tags"
width={40}
/>
</InlineField>
</InlineFieldRow>
</div>
);
}
Loading
Loading