mirror of https://github.com/grafana/grafana.git
239 lines
8.5 KiB
TypeScript
239 lines
8.5 KiB
TypeScript
import { css } from '@emotion/css';
|
|
import * as React from 'react';
|
|
import { useToggle } from 'react-use';
|
|
|
|
import { CoreApp, GrafanaTheme2 } from '@grafana/data';
|
|
import { EditorField, EditorRow } from '@grafana/plugin-ui';
|
|
import { AutoSizeInput, RadioButtonGroup, TextLink, useStyles2 } from '@grafana/ui';
|
|
|
|
import { QueryOptionGroup } from '../_importedDependencies/datasources/prometheus/QueryOptionGroup';
|
|
import { SearchTableType, MetricsQueryType } from '../dataquery.gen';
|
|
import { DEFAULT_LIMIT, DEFAULT_SPSS } from '../datasource';
|
|
import { TempoQuery } from '../types';
|
|
|
|
interface Props {
|
|
onChange: (value: TempoQuery) => void;
|
|
query: Partial<TempoQuery> & TempoQuery;
|
|
searchStreaming: boolean;
|
|
metricsStreaming: boolean;
|
|
app?: CoreApp;
|
|
}
|
|
|
|
/**
|
|
* Parse a string value to integer. If the conversion fails, for example because we are prosessing an empty value for
|
|
* a field, return a fallback (default) value.
|
|
*
|
|
* @param val the value to be parsed to an integer
|
|
* @param fallback the fallback value
|
|
* @returns the converted value or the fallback value if the conversion fails
|
|
*/
|
|
const parseIntWithFallback = (val: string, fallback: number) => {
|
|
const parsed = parseInt(val, 10);
|
|
return isNaN(parsed) ? fallback : parsed;
|
|
};
|
|
|
|
export const TempoQueryBuilderOptions = React.memo<Props>(
|
|
({ onChange, query, searchStreaming, metricsStreaming, app }) => {
|
|
const styles = useStyles2(getStyles);
|
|
const [isOpen, toggleOpen] = useToggle(false);
|
|
const isAlerting = app === CoreApp.UnifiedAlerting;
|
|
const isMetricsStreamingEnabled = metricsStreaming && !isAlerting;
|
|
|
|
if (!query.hasOwnProperty('limit')) {
|
|
query.limit = DEFAULT_LIMIT;
|
|
}
|
|
|
|
if (!query.hasOwnProperty('tableType')) {
|
|
query.tableType = SearchTableType.Traces;
|
|
}
|
|
|
|
if (!query.hasOwnProperty('metricsQueryType')) {
|
|
query.metricsQueryType = MetricsQueryType.Range;
|
|
}
|
|
|
|
if (isAlerting && query.metricsQueryType === MetricsQueryType.Range) {
|
|
onChange({ ...query, metricsQueryType: MetricsQueryType.Instant });
|
|
}
|
|
|
|
const onLimitChange = (e: React.FormEvent<HTMLInputElement>) => {
|
|
onChange({ ...query, limit: parseIntWithFallback(e.currentTarget.value, DEFAULT_LIMIT) });
|
|
};
|
|
const onSpssChange = (e: React.FormEvent<HTMLInputElement>) => {
|
|
onChange({ ...query, spss: parseIntWithFallback(e.currentTarget.value, DEFAULT_SPSS) });
|
|
};
|
|
const onTableTypeChange = (val: SearchTableType) => {
|
|
onChange({ ...query, tableType: val });
|
|
};
|
|
const onMetricsQueryTypeChange = (val: MetricsQueryType) => {
|
|
onChange({ ...query, metricsQueryType: val });
|
|
};
|
|
const onStepChange = (e: React.FormEvent<HTMLInputElement>) => {
|
|
onChange({ ...query, step: e.currentTarget.value });
|
|
};
|
|
|
|
// There's a bug in Tempo which causes the exemplars param to be ignored. It's commented out for now.
|
|
|
|
// const onExemplarsChange = (e: React.FormEvent<HTMLInputElement>) => {
|
|
// const exemplars = parseInt(e.currentTarget.value, 10);
|
|
// if (!isNaN(exemplars) && exemplars >= 0) {
|
|
// onChange({ ...query, exemplars });
|
|
// } else {
|
|
// onChange({ ...query, exemplars: undefined });
|
|
// }
|
|
// };
|
|
|
|
const collapsedSearchOptions = [
|
|
`Limit: ${query.limit || DEFAULT_LIMIT}`,
|
|
`Spans Limit: ${query.spss || DEFAULT_SPSS}`,
|
|
`Table Format: ${query.tableType === SearchTableType.Traces ? 'Traces' : 'Spans'}`,
|
|
'|',
|
|
`Streaming: ${searchStreaming ? 'Enabled' : 'Disabled'}`,
|
|
];
|
|
|
|
const collapsedMetricsOptions = [
|
|
`Step: ${query.step || 'auto'}`,
|
|
`Type: ${query.metricsQueryType === MetricsQueryType.Range ? 'Range' : 'Instant'}`,
|
|
'|',
|
|
`Streaming: ${isMetricsStreamingEnabled ? 'Enabled' : 'Disabled'}`,
|
|
// `Exemplars: ${query.exemplars !== undefined ? query.exemplars : 'auto'}`,
|
|
];
|
|
|
|
return (
|
|
<EditorRow>
|
|
<div className={styles.options}>
|
|
{!isAlerting && (
|
|
<QueryOptionGroup
|
|
title="Search Options"
|
|
collapsedInfo={collapsedSearchOptions}
|
|
isOpen={isOpen}
|
|
onToggle={toggleOpen}
|
|
>
|
|
<EditorField label="Limit" tooltip="Maximum number of traces to return.">
|
|
<AutoSizeInput
|
|
className="width-4"
|
|
placeholder="auto"
|
|
type="number"
|
|
min={1}
|
|
defaultValue={query.limit || DEFAULT_LIMIT}
|
|
onCommitChange={onLimitChange}
|
|
value={query.limit}
|
|
/>
|
|
</EditorField>
|
|
<EditorField label="Span Limit" tooltip="Maximum number of spans to return for each span set.">
|
|
<AutoSizeInput
|
|
className="width-4"
|
|
placeholder="auto"
|
|
type="number"
|
|
min={1}
|
|
defaultValue={query.spss || DEFAULT_SPSS}
|
|
onCommitChange={onSpssChange}
|
|
value={query.spss}
|
|
/>
|
|
</EditorField>
|
|
<EditorField label="Table Format" tooltip="How the query data should be displayed in the results table">
|
|
<RadioButtonGroup
|
|
options={[
|
|
{ label: 'Traces', value: SearchTableType.Traces },
|
|
{ label: 'Spans', value: SearchTableType.Spans },
|
|
]}
|
|
value={query.tableType}
|
|
onChange={onTableTypeChange}
|
|
/>
|
|
</EditorField>
|
|
<EditorField label="Streaming" tooltip={<StreamingTooltip />} tooltipInteractive>
|
|
<div>{searchStreaming ? 'Enabled' : 'Disabled'}</div>
|
|
</EditorField>
|
|
</QueryOptionGroup>
|
|
)}
|
|
|
|
<QueryOptionGroup
|
|
title="Metrics Options"
|
|
collapsedInfo={collapsedMetricsOptions}
|
|
isOpen={isOpen}
|
|
onToggle={toggleOpen}
|
|
>
|
|
{!isAlerting && (
|
|
<EditorField
|
|
label="Step"
|
|
tooltip="Defines the step for metric queries. Use duration notation, for example 30s or 1m"
|
|
>
|
|
<AutoSizeInput
|
|
className="width-4"
|
|
placeholder="auto"
|
|
type="string"
|
|
defaultValue={query.step}
|
|
onCommitChange={onStepChange}
|
|
value={query.step}
|
|
/>
|
|
</EditorField>
|
|
)}
|
|
<EditorField label="Type" tooltip="Type of metrics query to run">
|
|
<RadioButtonGroup
|
|
options={[
|
|
{ label: 'Range', value: MetricsQueryType.Range },
|
|
{ label: 'Instant', value: MetricsQueryType.Instant },
|
|
]}
|
|
value={query.metricsQueryType}
|
|
onChange={onMetricsQueryTypeChange}
|
|
disabled={isAlerting}
|
|
/>
|
|
</EditorField>
|
|
|
|
<EditorField label="Streaming" tooltip={<StreamingTooltip />} tooltipInteractive>
|
|
<div>{isMetricsStreamingEnabled ? 'Enabled' : 'Disabled'}</div>
|
|
</EditorField>
|
|
{/*<EditorField*/}
|
|
{/* label="Exemplars"*/}
|
|
{/* tooltip="Defines the amount of exemplars to request for metric queries. A value of 0 means no exemplars."*/}
|
|
{/*>*/}
|
|
{/* <AutoSizeInput*/}
|
|
{/* className="width-4"*/}
|
|
{/* placeholder="auto"*/}
|
|
{/* type="string"*/}
|
|
{/* defaultValue={query.exemplars}*/}
|
|
{/* onCommitChange={onExemplarsChange}*/}
|
|
{/* value={query.exemplars}*/}
|
|
{/* />*/}
|
|
{/*</EditorField>*/}
|
|
</QueryOptionGroup>
|
|
</div>
|
|
</EditorRow>
|
|
);
|
|
}
|
|
);
|
|
|
|
const StreamingTooltip = () => {
|
|
return (
|
|
<div style={{ display: 'flex', gap: '4px' }}>
|
|
<span>
|
|
Indicates if streaming is currently enabled. Streaming allows you to view partial query results before the
|
|
entire query completes.
|
|
</span>
|
|
<TextLink
|
|
external
|
|
href={'https://grafana.com/docs/tempo/latest/traceql/#stream-query-results'}
|
|
aria-label={'Learn more about streaming query results'}
|
|
style={{ textDecoration: 'underline' }}
|
|
>
|
|
Learn more
|
|
</TextLink>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
TempoQueryBuilderOptions.displayName = 'TempoQueryBuilderOptions';
|
|
|
|
const getStyles = (theme: GrafanaTheme2) => {
|
|
return {
|
|
options: css({
|
|
display: 'flex',
|
|
width: '-webkit-fill-available',
|
|
gap: theme.spacing(1),
|
|
|
|
'> div': {
|
|
width: 'auto',
|
|
},
|
|
}),
|
|
};
|
|
};
|