import { css } from '@emotion/css'; import { DataSourceApi, ExploreQueryFieldProps, SelectableValue } from '@grafana/data'; import { config, getDataSourceSrv } from '@grafana/runtime'; import { FileDropzone, InlineField, InlineFieldRow, InlineLabel, QueryField, RadioButtonGroup, Themeable2, withTheme2, } from '@grafana/ui'; import { TraceToLogsOptions } from 'app/core/components/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'; interface Props extends ExploreQueryFieldProps, Themeable2 {} 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, }; 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({ ...this.props.query, queryType: DEFAULT_QUERY_TYPE, }); } } onChangeLinkedQuery = (value: LokiQuery) => { const { query, onChange } = this.props; onChange({ ...query, linkedQuery: { ...value, refId: 'linked' }, }); }; onRunLinkedQuery = () => { this.props.onRunQuery(); }; render() { const { query, onChange, datasource } = this.props; // Find query field from linked datasource const tracesToLogsOptions: TraceToLogsOptions = datasource.tracesToLogs || {}; const logsDatasourceUid = tracesToLogsOptions.datasourceUid; const graphDatasourceUid = datasource.serviceMap?.datasourceUid; const queryTypeOptions: Array> = [ { value: 'traceId', label: 'TraceID' }, { value: 'upload', label: 'JSON file' }, ]; if (config.featureToggles.tempoServiceGraph) { queryTypeOptions.push({ value: 'serviceMap', label: 'Service Map' }); } if (config.featureToggles.tempoSearch) { queryTypeOptions.unshift({ value: 'nativeSearch', label: 'Search' }); } if (logsDatasourceUid) { if (!config.featureToggles.tempoSearch) { // Place at beginning as Search if no native search queryTypeOptions.unshift({ value: 'search', label: 'Search' }); } else { // Place at end as Loki Search if native search is enabled queryTypeOptions.push({ value: 'search', label: 'Loki Search' }); } } return ( <> options={queryTypeOptions} value={query.queryType} onChange={(v) => onChange({ ...query, queryType: v, }) } size="md" /> {query.queryType === 'search' && ( )} {query.queryType === 'nativeSearch' && ( )} {query.queryType === 'upload' && (
{ this.props.datasource.uploadedJson = result; this.props.onRunQuery(); }} />
)} {query.queryType === 'traceId' && ( { onChange({ ...query, query: val, queryType: 'traceId', linkedQuery: undefined, }); }} onBlur={this.props.onBlur} onRunQuery={this.props.onRunQuery} placeholder={'Enter a Trace ID (run with Shift+Enter)'} portalOrigin="tempo" /> )} {query.queryType === 'serviceMap' && } ); } } function ServiceMapSection({ graphDatasourceUid }: { graphDatasourceUid?: string }) { const dsState = useAsync(() => getDS(graphDatasourceUid), [graphDatasourceUid]); if (dsState.loading) { return null; } const ds = dsState.value as LokiDatasource; if (!graphDatasourceUid) { return
Please set up a service graph datasource in the datasource settings.
; } if (graphDatasourceUid && !ds) { return (
Service graph datasource is configured but the data source no longer exists. Please configure existing data source to use the service graph functionality.
); } return null; } interface SearchSectionProps { linkedDatasourceUid?: string; onChange: (value: LokiQuery) => void; onRunQuery: () => void; query: TempoQuery; } function SearchSection({ linkedDatasourceUid, onChange, onRunQuery, query }: SearchSectionProps) { const dsState = useAsync(() => getDS(linkedDatasourceUid), [linkedDatasourceUid]); if (dsState.loading) { return null; } const ds = dsState.value as LokiDatasource; if (ds) { return ( <> Tempo uses {ds.name} to find traces. ); } if (!linkedDatasourceUid) { return
Please set up a Traces-to-logs datasource in the datasource settings.
; } if (linkedDatasourceUid && !ds) { return (
Traces-to-logs datasource is configured but the data source no longer exists. Please configure existing data source to use the search.
); } return null; } async function getDS(uid?: string): Promise { if (!uid) { return undefined; } const dsSrv = getDataSourceSrv(); try { return await dsSrv.get(uid); } catch (error) { console.error('Failed to load data source', error); return undefined; } } export const TempoQueryField = withTheme2(TempoQueryFieldComponent);