QueryField.tsx 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import { css } from '@emotion/css';
  2. import React from 'react';
  3. import useAsync from 'react-use/lib/useAsync';
  4. import { QueryEditorProps, SelectableValue } from '@grafana/data';
  5. import { config, reportInteraction } from '@grafana/runtime';
  6. import {
  7. Badge,
  8. FileDropzone,
  9. InlineField,
  10. InlineFieldRow,
  11. InlineLabel,
  12. QueryField,
  13. RadioButtonGroup,
  14. Themeable2,
  15. withTheme2,
  16. } from '@grafana/ui';
  17. import { LokiQueryField } from '../../loki/components/LokiQueryField';
  18. import { LokiDatasource } from '../../loki/datasource';
  19. import { LokiQuery } from '../../loki/types';
  20. import { TempoDatasource, TempoQuery, TempoQueryType } from '../datasource';
  21. import NativeSearch from './NativeSearch';
  22. import { ServiceGraphSection } from './ServiceGraphSection';
  23. import { getDS } from './utils';
  24. interface Props extends QueryEditorProps<TempoDatasource, TempoQuery>, Themeable2 {}
  25. const DEFAULT_QUERY_TYPE: TempoQueryType = 'traceId';
  26. class TempoQueryFieldComponent extends React.PureComponent<Props> {
  27. constructor(props: Props) {
  28. super(props);
  29. }
  30. async componentDidMount() {
  31. // Set initial query type to ensure traceID field appears
  32. if (!this.props.query.queryType) {
  33. this.props.onChange({
  34. ...this.props.query,
  35. queryType: DEFAULT_QUERY_TYPE,
  36. });
  37. }
  38. }
  39. onChangeLinkedQuery = (value: LokiQuery) => {
  40. const { query, onChange } = this.props;
  41. onChange({
  42. ...query,
  43. linkedQuery: { ...value, refId: 'linked' },
  44. });
  45. };
  46. onRunLinkedQuery = () => {
  47. this.props.onRunQuery();
  48. };
  49. onClearResults = () => {
  50. // Run clear query to clear results
  51. const { onChange, query, onRunQuery } = this.props;
  52. onChange({
  53. ...query,
  54. queryType: 'clear',
  55. });
  56. onRunQuery();
  57. };
  58. render() {
  59. const { query, onChange, datasource, app } = this.props;
  60. const logsDatasourceUid = datasource.getLokiSearchDS();
  61. const graphDatasourceUid = datasource.serviceMap?.datasourceUid;
  62. const queryTypeOptions: Array<SelectableValue<TempoQueryType>> = [
  63. { value: 'traceId', label: 'TraceID' },
  64. { value: 'upload', label: 'JSON file' },
  65. { value: 'serviceMap', label: 'Service Graph' },
  66. ];
  67. if (config.featureToggles.tempoSearch && !datasource?.search?.hide) {
  68. queryTypeOptions.unshift({ value: 'nativeSearch', label: 'Search - Beta' });
  69. }
  70. if (logsDatasourceUid) {
  71. if (!config.featureToggles.tempoSearch) {
  72. // Place at beginning as Search if no native search
  73. queryTypeOptions.unshift({ value: 'search', label: 'Search' });
  74. } else {
  75. // Place at end as Loki Search if native search is enabled
  76. queryTypeOptions.push({ value: 'search', label: 'Loki Search' });
  77. }
  78. }
  79. return (
  80. <>
  81. <InlineFieldRow>
  82. <InlineField label="Query type">
  83. <RadioButtonGroup<TempoQueryType>
  84. options={queryTypeOptions}
  85. value={query.queryType}
  86. onChange={(v) => {
  87. reportInteraction('grafana_traces_query_type_changed', {
  88. datasourceType: 'tempo',
  89. app: app ?? '',
  90. newQueryType: v,
  91. previousQueryType: query.queryType ?? '',
  92. });
  93. this.onClearResults();
  94. onChange({
  95. ...query,
  96. queryType: v,
  97. });
  98. }}
  99. size="md"
  100. />
  101. </InlineField>
  102. </InlineFieldRow>
  103. {query.queryType === 'nativeSearch' && (
  104. <div style={{ maxWidth: '65ch' }}>
  105. <Badge icon="rocket" text="Beta" color="blue" />
  106. {config.featureToggles.tempoBackendSearch ? (
  107. <>&nbsp;Tempo search is currently in beta.</>
  108. ) : (
  109. <>
  110. &nbsp;Tempo search is currently in beta and is designed to return recent traces only. It ignores the
  111. time range picker. We are actively working on full backend search. Look for improvements in the near
  112. future!
  113. </>
  114. )}
  115. </div>
  116. )}
  117. {query.queryType === 'search' && (
  118. <SearchSection
  119. logsDatasourceUid={logsDatasourceUid}
  120. query={query}
  121. onRunQuery={this.onRunLinkedQuery}
  122. onChange={this.onChangeLinkedQuery}
  123. />
  124. )}
  125. {query.queryType === 'nativeSearch' && (
  126. <NativeSearch
  127. datasource={this.props.datasource}
  128. query={query}
  129. onChange={onChange}
  130. onBlur={this.props.onBlur}
  131. onRunQuery={this.props.onRunQuery}
  132. />
  133. )}
  134. {query.queryType === 'upload' && (
  135. <div className={css({ padding: this.props.theme.spacing(2) })}>
  136. <FileDropzone
  137. options={{ multiple: false }}
  138. onLoad={(result) => {
  139. this.props.datasource.uploadedJson = result;
  140. this.props.onRunQuery();
  141. }}
  142. />
  143. </div>
  144. )}
  145. {query.queryType === 'traceId' && (
  146. <InlineFieldRow>
  147. <InlineField label="Trace ID" labelWidth={14} grow>
  148. <QueryField
  149. query={query.query}
  150. onChange={(val) => {
  151. onChange({
  152. ...query,
  153. query: val,
  154. queryType: 'traceId',
  155. linkedQuery: undefined,
  156. });
  157. }}
  158. onBlur={this.props.onBlur}
  159. onRunQuery={this.props.onRunQuery}
  160. placeholder={'Enter a Trace ID (run with Shift+Enter)'}
  161. portalOrigin="tempo"
  162. />
  163. </InlineField>
  164. </InlineFieldRow>
  165. )}
  166. {query.queryType === 'serviceMap' && (
  167. <ServiceGraphSection graphDatasourceUid={graphDatasourceUid} query={query} onChange={onChange} />
  168. )}
  169. </>
  170. );
  171. }
  172. }
  173. interface SearchSectionProps {
  174. logsDatasourceUid?: string;
  175. onChange: (value: LokiQuery) => void;
  176. onRunQuery: () => void;
  177. query: TempoQuery;
  178. }
  179. function SearchSection({ logsDatasourceUid, onChange, onRunQuery, query }: SearchSectionProps) {
  180. const dsState = useAsync(() => getDS(logsDatasourceUid), [logsDatasourceUid]);
  181. if (dsState.loading) {
  182. return null;
  183. }
  184. const ds = dsState.value as LokiDatasource;
  185. if (ds) {
  186. return (
  187. <>
  188. <InlineLabel>Tempo uses {ds.name} to find traces.</InlineLabel>
  189. <LokiQueryField
  190. datasource={ds}
  191. onChange={onChange}
  192. onRunQuery={onRunQuery}
  193. query={query.linkedQuery ?? ({ refId: 'linked' } as any)}
  194. history={[]}
  195. />
  196. </>
  197. );
  198. }
  199. if (!logsDatasourceUid) {
  200. return <div className="text-warning">Please set up a Loki search datasource in the datasource settings.</div>;
  201. }
  202. if (logsDatasourceUid && !ds) {
  203. return (
  204. <div className="text-warning">
  205. Loki search datasource is configured but the data source no longer exists. Please configure existing data source
  206. to use the search.
  207. </div>
  208. );
  209. }
  210. return null;
  211. }
  212. export const TempoQueryField = withTheme2(TempoQueryFieldComponent);