LokiOptionFields.tsx 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // Libraries
  2. import { css, cx } from '@emotion/css';
  3. import { map } from 'lodash';
  4. import React, { memo } from 'react';
  5. // Types
  6. import { SelectableValue } from '@grafana/data';
  7. import { config } from '@grafana/runtime';
  8. import { InlineFormLabel, RadioButtonGroup, InlineField, Input, Select } from '@grafana/ui';
  9. import { LokiQuery, LokiQueryType } from '../types';
  10. export interface LokiOptionFieldsProps {
  11. lineLimitValue: string;
  12. resolution: number;
  13. query: LokiQuery;
  14. onChange: (value: LokiQuery) => void;
  15. onRunQuery: () => void;
  16. runOnBlur?: boolean;
  17. }
  18. export const queryTypeOptions: Array<SelectableValue<LokiQueryType>> = [
  19. { value: LokiQueryType.Range, label: 'Range', description: 'Run query over a range of time.' },
  20. {
  21. value: LokiQueryType.Instant,
  22. label: 'Instant',
  23. description: 'Run query against a single point in time. For this query, the "To" time is used.',
  24. },
  25. ];
  26. if (config.featureToggles.lokiLive) {
  27. queryTypeOptions.push({
  28. value: LokiQueryType.Stream,
  29. label: 'Stream',
  30. description: 'Run a query and keep sending results on an interval',
  31. });
  32. }
  33. export const DEFAULT_RESOLUTION: SelectableValue<number> = {
  34. value: 1,
  35. label: '1/1',
  36. };
  37. export const RESOLUTION_OPTIONS: Array<SelectableValue<number>> = [DEFAULT_RESOLUTION].concat(
  38. map([2, 3, 4, 5, 10], (value: number) => ({
  39. value,
  40. label: '1/' + value,
  41. }))
  42. );
  43. export function LokiOptionFields(props: LokiOptionFieldsProps) {
  44. const { lineLimitValue, resolution, onRunQuery, runOnBlur, onChange } = props;
  45. const query = props.query ?? {};
  46. let queryType = query.queryType ?? (query.instant ? LokiQueryType.Instant : LokiQueryType.Range);
  47. function onChangeQueryLimit(value: string) {
  48. const nextQuery = { ...query, maxLines: preprocessMaxLines(value) };
  49. onChange(nextQuery);
  50. }
  51. function onQueryTypeChange(queryType: LokiQueryType) {
  52. const { instant, range, ...rest } = query;
  53. onChange({ ...rest, queryType });
  54. }
  55. function onMaxLinesChange(e: React.SyntheticEvent<HTMLInputElement>) {
  56. if (query.maxLines !== preprocessMaxLines(e.currentTarget.value)) {
  57. onChangeQueryLimit(e.currentTarget.value);
  58. }
  59. }
  60. function onReturnKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
  61. if (e.key === 'Enter') {
  62. onRunQuery();
  63. }
  64. }
  65. function onResolutionChange(option: SelectableValue<number>) {
  66. const nextQuery = { ...query, resolution: option.value };
  67. onChange(nextQuery);
  68. }
  69. return (
  70. <div aria-label="Loki extra field" className="gf-form-inline">
  71. {/*Query type field*/}
  72. <div
  73. data-testid="queryTypeField"
  74. className={cx(
  75. 'gf-form explore-input-margin',
  76. css`
  77. flex-wrap: nowrap;
  78. `
  79. )}
  80. aria-label="Query type field"
  81. >
  82. <InlineFormLabel width="auto">Query type</InlineFormLabel>
  83. <RadioButtonGroup
  84. options={queryTypeOptions}
  85. value={queryType}
  86. onChange={(type: LokiQueryType) => {
  87. onQueryTypeChange(type);
  88. if (runOnBlur) {
  89. onRunQuery();
  90. }
  91. }}
  92. />
  93. </div>
  94. {/*Line limit field*/}
  95. <div
  96. data-testid="lineLimitField"
  97. className={cx(
  98. 'gf-form',
  99. css`
  100. flex-wrap: nowrap;
  101. `
  102. )}
  103. aria-label="Line limit field"
  104. >
  105. <InlineField label="Line limit" tooltip={'Upper limit for number of log lines returned by query.'}>
  106. <Input
  107. className="width-4"
  108. placeholder="auto"
  109. type="number"
  110. min={0}
  111. onChange={onMaxLinesChange}
  112. onKeyDown={onReturnKeyDown}
  113. value={lineLimitValue}
  114. onBlur={() => {
  115. if (runOnBlur) {
  116. onRunQuery();
  117. }
  118. }}
  119. />
  120. </InlineField>
  121. <InlineField
  122. label="Resolution"
  123. tooltip={
  124. 'Resolution 1/1 sets step parameter of Loki metrics range queries such that each pixel corresponds to one data point. For better performance, lower resolutions can be picked. 1/2 only retrieves a data point for every other pixel, and 1/10 retrieves one data point per 10 pixels.'
  125. }
  126. >
  127. <Select
  128. isSearchable={false}
  129. onChange={onResolutionChange}
  130. options={RESOLUTION_OPTIONS}
  131. value={resolution}
  132. aria-label="Select resolution"
  133. />
  134. </InlineField>
  135. </div>
  136. </div>
  137. );
  138. }
  139. export default memo(LokiOptionFields);
  140. export function preprocessMaxLines(value: string): number {
  141. if (value.length === 0) {
  142. // empty input - falls back to dataSource.maxLines limit
  143. return NaN;
  144. } else if (value.length > 0 && (isNaN(+value) || +value < 0)) {
  145. // input with at least 1 character and that is either incorrect (value in the input field is not a number) or negative
  146. // falls back to the limit of 0 lines
  147. return 0;
  148. } else {
  149. // default case - correct input
  150. return +value;
  151. }
  152. }