diff --git a/docs/sources/datasources/jaeger.md b/docs/sources/datasources/jaeger.md index f0b686cd6d8..3f4b690ec7a 100644 --- a/docs/sources/datasources/jaeger.md +++ b/docs/sources/datasources/jaeger.md @@ -156,7 +156,6 @@ datasources: spanEndTimeShift: '1h' filterByTraceID: false filterBySpanID: false - lokiSearch: true secureJsonData: basicAuthPassword: my_password ``` diff --git a/docs/sources/datasources/tempo.md b/docs/sources/datasources/tempo.md index 06d80bd19fa..01b64bce0cc 100644 --- a/docs/sources/datasources/tempo.md +++ b/docs/sources/datasources/tempo.md @@ -57,6 +57,12 @@ This is a configuration for the beta Node Graph visualization. The Node Graph is -- **Enable Node Graph -** Enables the Node Graph visualization. +### Loki Search + +This is a configuration for the Loki search query type. + +-- **Data source -** The Loki instance in which you want to search traces. You must configure derived fields in the Loki instance. + ## Query traces You can query and display traces from Tempo via [Explore]({{< relref "../explore/_index.md" >}}). @@ -81,7 +87,7 @@ You must also configure your Tempo data source to use this feature.Refer to the ### Loki search -You can search for traces if you set up the trace to logs setting in the data source configuration page. To find traces to visualize, use the [Loki query editor]({{< relref "loki.md#loki-query-editor" >}}). To get search results, you must have [derived fields]({{< relref "loki.md#derived-fields" >}}) configured, which point to this data source. +To find traces to visualize, use the [Loki query editor]({{< relref "loki.md#loki-query-editor" >}}). To get search results, you must have [derived fields]({{< relref "loki.md#derived-fields" >}}) configured, which point to this data source. {{< figure src="/static/img/docs/tempo/query-editor-search.png" class="docs-image--no-shadow" max-width="750px" caption="Screenshot of the Tempo query editor showing the search tab" >}} @@ -197,11 +203,12 @@ datasources: spanEndTimeShift: '1h' filterByTraceID: false filterBySpanID: false - lokiSearch: true serviceMap: datasourceUid: 'prometheus' search: hide: false nodeGraph: enabled: true + lokiSearch: + datasourceUid: 'loki' ``` diff --git a/public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx b/public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx index e8a47e9bcff..f101ffa0cee 100644 --- a/public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx +++ b/public/app/core/components/TraceToLogs/TraceToLogsSettings.tsx @@ -20,7 +20,7 @@ export interface TraceToLogsOptions { spanEndTimeShift?: string; filterByTraceID?: boolean; filterBySpanID?: boolean; - lokiSearch?: boolean; + lokiSearch?: boolean; // legacy } export interface TraceToLogsData extends DataSourceJsonData { @@ -205,22 +205,6 @@ export function TraceToLogsSettings({ options, onOptionsChange }: Props) { /> - - - - ) => - updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'tracesToLogs', { - ...options.jsonData.tracesToLogs, - lokiSearch: event.currentTarget.checked, - }) - } - /> - - ); } diff --git a/public/app/plugins/datasource/tempo/QueryEditor/QueryField.tsx b/public/app/plugins/datasource/tempo/QueryEditor/QueryField.tsx index b5bc57e135d..eeeede156a6 100644 --- a/public/app/plugins/datasource/tempo/QueryEditor/QueryField.tsx +++ b/public/app/plugins/datasource/tempo/QueryEditor/QueryField.tsx @@ -12,13 +12,11 @@ import { Themeable2, withTheme2, } from '@grafana/ui'; -import { TraceToLogsOptions } from 'app/core/components/TraceToLogs/TraceToLogsSettings'; import React from 'react'; import { LokiQueryField } from '../../loki/components/LokiQueryField'; import { LokiQuery } from '../../loki/types'; import { TempoDatasource, TempoQuery, TempoQueryType } from '../datasource'; import LokiDatasource from '../../loki/datasource'; -import { PrometheusDatasource } from '../../prometheus/datasource'; import useAsync from 'react-use/lib/useAsync'; import NativeSearch from './NativeSearch'; import { getDS } from './utils'; @@ -28,43 +26,12 @@ interface Props extends QueryEditorProps, Themeable const DEFAULT_QUERY_TYPE: TempoQueryType = 'traceId'; -interface State { - linkedDatasourceUid?: string; - linkedDatasource?: LokiDatasource; - serviceMapDatasourceUid?: string; - serviceMapDatasource?: PrometheusDatasource; -} - -class TempoQueryFieldComponent extends React.PureComponent { - state = { - linkedDatasourceUid: undefined, - linkedDatasource: undefined, - serviceMapDatasourceUid: undefined, - serviceMapDatasource: undefined, - }; - +class TempoQueryFieldComponent extends React.PureComponent { constructor(props: Props) { super(props); } async componentDidMount() { - const { datasource } = this.props; - // Find query field from linked datasource - const tracesToLogsOptions: TraceToLogsOptions = datasource.tracesToLogs || {}; - const linkedDatasourceUid = tracesToLogsOptions.datasourceUid; - - const serviceMapDsUid = datasource.serviceMap?.datasourceUid; - - // Check status of linked data sources so we can show warnings if needed. - const [logsDs, serviceMapDs] = await Promise.all([getDS(linkedDatasourceUid), getDS(serviceMapDsUid)]); - - this.setState({ - linkedDatasourceUid: linkedDatasourceUid, - linkedDatasource: logsDs as LokiDatasource, - serviceMapDatasourceUid: serviceMapDsUid, - serviceMapDatasource: serviceMapDs as PrometheusDatasource, - }); - // Set initial query type to ensure traceID field appears if (!this.props.query.queryType) { this.props.onChange({ @@ -98,9 +65,9 @@ class TempoQueryFieldComponent extends React.PureComponent { render() { const { query, onChange, datasource } = this.props; - // Find query field from linked datasource - const tracesToLogsOptions: TraceToLogsOptions = datasource.tracesToLogs || {}; - const logsDatasourceUid = tracesToLogsOptions.datasourceUid; + + const logsDatasourceUid = datasource.getLokiSearchDS(); + const graphDatasourceUid = datasource.serviceMap?.datasourceUid; const queryTypeOptions: Array> = [ @@ -116,7 +83,7 @@ class TempoQueryFieldComponent extends React.PureComponent { queryTypeOptions.unshift({ value: 'nativeSearch', label: 'Search - Beta' }); } - if (logsDatasourceUid && tracesToLogsOptions?.lokiSearch !== false) { + if (logsDatasourceUid) { if (!config.featureToggles.tempoSearch) { // Place at beginning as Search if no native search queryTypeOptions.unshift({ value: 'search', label: 'Search' }); @@ -161,7 +128,7 @@ class TempoQueryFieldComponent extends React.PureComponent { )} {query.queryType === 'search' && ( { } interface SearchSectionProps { - linkedDatasourceUid?: string; + logsDatasourceUid?: string; onChange: (value: LokiQuery) => void; onRunQuery: () => void; query: TempoQuery; } -function SearchSection({ linkedDatasourceUid, onChange, onRunQuery, query }: SearchSectionProps) { - const dsState = useAsync(() => getDS(linkedDatasourceUid), [linkedDatasourceUid]); +function SearchSection({ logsDatasourceUid, onChange, onRunQuery, query }: SearchSectionProps) { + const dsState = useAsync(() => getDS(logsDatasourceUid), [logsDatasourceUid]); if (dsState.loading) { return null; } @@ -246,15 +213,15 @@ function SearchSection({ linkedDatasourceUid, onChange, onRunQuery, query }: Sea ); } - if (!linkedDatasourceUid) { - return
Please set up a Traces-to-logs datasource in the datasource settings.
; + if (!logsDatasourceUid) { + return
Please set up a Loki search datasource in the datasource settings.
; } - if (linkedDatasourceUid && !ds) { + if (logsDatasourceUid && !ds) { return (
- Traces-to-logs datasource is configured but the data source no longer exists. Please configure existing data - source to use the search. + Loki search datasource is configured but the data source no longer exists. Please configure existing data source + to use the search.
); } diff --git a/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx b/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx index d00d8955ae0..ac9276770b7 100644 --- a/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx +++ b/public/app/plugins/datasource/tempo/configuration/ConfigEditor.tsx @@ -6,6 +6,7 @@ import { ServiceGraphSettings } from './ServiceGraphSettings'; import { config } from '@grafana/runtime'; import { SearchSettings } from './SearchSettings'; import { NodeGraphSettings } from 'app/core/components/NodeGraphSettings'; +import { LokiSearchSettings } from './LokiSearchSettings'; export type Props = DataSourcePluginOptionsEditorProps; @@ -35,6 +36,9 @@ export const ConfigEditor: React.FC = ({ options, onOptionsChange }) => {
+
+ +
); }; diff --git a/public/app/plugins/datasource/tempo/configuration/LokiSearchSettings.tsx b/public/app/plugins/datasource/tempo/configuration/LokiSearchSettings.tsx new file mode 100644 index 00000000000..c0374df4d17 --- /dev/null +++ b/public/app/plugins/datasource/tempo/configuration/LokiSearchSettings.tsx @@ -0,0 +1,77 @@ +import { css } from '@emotion/css'; +import { DataSourcePluginOptionsEditorProps, GrafanaTheme, updateDatasourcePluginJsonDataOption } from '@grafana/data'; +import { DataSourcePicker } from '@grafana/runtime'; +import { Button, InlineField, InlineFieldRow, useStyles } from '@grafana/ui'; +import React from 'react'; +import { TempoJsonData } from '../datasource'; + +interface Props extends DataSourcePluginOptionsEditorProps {} + +export function LokiSearchSettings({ options, onOptionsChange }: Props) { + const styles = useStyles(getStyles); + + // Default to the trace to logs datasource if configured and loki search was enabled + // but only if jsonData.lokiSearch hasn't been set + const legacyDatasource = + options.jsonData.tracesToLogs?.lokiSearch !== false ? options.jsonData.tracesToLogs?.datasourceUid : undefined; + if (legacyDatasource && options.jsonData.lokiSearch === undefined) { + updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'lokiSearch', { + datasourceUid: legacyDatasource, + }); + } + + return ( +
+

Loki Search

+ +
+ Select a Loki datasource to search for traces. Derived fields must be configured in the Loki data source. +
+ + + + + updateDatasourcePluginJsonDataOption({ onOptionsChange, options }, 'lokiSearch', { + datasourceUid: ds.uid, + }) + } + /> + + {options.jsonData.lokiSearch?.datasourceUid ? ( + + ) : null} + +
+ ); +} + +const getStyles = (theme: GrafanaTheme) => ({ + infoText: css` + label: infoText; + padding-bottom: ${theme.spacing.md}; + color: ${theme.colors.textSemiWeak}; + `, + + row: css` + label: row; + align-items: baseline; + `, +}); diff --git a/public/app/plugins/datasource/tempo/datasource.test.ts b/public/app/plugins/datasource/tempo/datasource.test.ts index 2fe42ea2da0..e5884138fe9 100644 --- a/public/app/plugins/datasource/tempo/datasource.test.ts +++ b/public/app/plugins/datasource/tempo/datasource.test.ts @@ -223,6 +223,55 @@ describe('Tempo data source', () => { 'Service Name: frontend, Span Name: /config, Search: root.http.status_code=500, Min Duration: 1ms, Max Duration: 100s, Limit: 10' ); }); + + it('should get loki search datasource', () => { + // 1. Get lokiSearch.datasource if present + const ds1 = new TempoDatasource({ + ...defaultSettings, + jsonData: { + lokiSearch: { + datasourceUid: 'loki-1', + }, + }, + }); + const lokiDS1 = ds1.getLokiSearchDS(); + expect(lokiDS1).toBe('loki-1'); + + // 2. Get traceToLogs.datasource + const ds2 = new TempoDatasource({ + ...defaultSettings, + jsonData: { + tracesToLogs: { + lokiSearch: true, + datasourceUid: 'loki-2', + }, + }, + }); + const lokiDS2 = ds2.getLokiSearchDS(); + expect(lokiDS2).toBe('loki-2'); + + // 3. Return undefined if neither is available + const ds3 = new TempoDatasource(defaultSettings); + const lokiDS3 = ds3.getLokiSearchDS(); + expect(lokiDS3).toBe(undefined); + + // 4. Return undefined if lokiSearch is undefined, even if traceToLogs is present + // since this indicates the user cleared the fallback setting + const ds4 = new TempoDatasource({ + ...defaultSettings, + jsonData: { + tracesToLogs: { + lokiSearch: true, + datasourceUid: 'loki-2', + }, + lokiSearch: { + datasourceUid: undefined, + }, + }, + }); + const lokiDS4 = ds4.getLokiSearchDS(); + expect(lokiDS4).toBe(undefined); + }); }); const backendSrvWithPrometheus = { diff --git a/public/app/plugins/datasource/tempo/datasource.ts b/public/app/plugins/datasource/tempo/datasource.ts index 4b77522a3eb..b12bba78b59 100644 --- a/public/app/plugins/datasource/tempo/datasource.ts +++ b/public/app/plugins/datasource/tempo/datasource.ts @@ -45,6 +45,9 @@ export interface TempoJsonData extends DataSourceJsonData { hide?: boolean; }; nodeGraph?: NodeGraphOptions; + lokiSearch?: { + datasourceUid?: string; + }; } export interface TempoQuery extends DataQuery { @@ -81,6 +84,9 @@ export class TempoDatasource extends DataSourceWithBackend) { @@ -89,6 +95,7 @@ export class TempoDatasource extends DataSourceWithBackend): Observable { @@ -100,11 +107,13 @@ export class TempoDatasource extends DataSourceWithBackend 0) { + if (logsDatasourceUid && targets.search?.length > 0) { const dsSrv = getDatasourceSrv(); subQueries.push( - from(dsSrv.get(this.tracesToLogs.datasourceUid)).pipe( + from(dsSrv.get(logsDatasourceUid)).pipe( mergeMap((linkedDatasource: DataSourceApi) => { // Wrap linked query into a data request based on original request const linkedRequest: DataQueryRequest = { ...options, targets: targets.search.map((t) => t.linkedQuery!) }; @@ -300,6 +309,15 @@ export class TempoDatasource extends DataSourceWithBackend { + const legacyLogsDatasourceUid = + this.tracesToLogs?.lokiSearch !== false && this.lokiSearch === undefined + ? this.tracesToLogs?.datasourceUid + : undefined; + return this.lokiSearch?.datasourceUid ?? legacyLogsDatasourceUid; + }; } function queryServiceMapPrometheus(request: DataQueryRequest, datasourceUid: string) {