mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Adds Loki explore query editor (#21497)
* Explore: updates grafana-data explore query field props with explore mode * Explore: updates query row to pass down explore mode to query fields * Explore: adds LokiExploreQueryEditor * Explore: updates loki query field form to render children * Explore: adds loki explore extra field component * Explore: adds extra field element to loki query field form * Explore: updates loki explore query editor to use extra field element * Explore: moves ExploreMode to grafana-data * Explore: updates query row limit string * Explore: adds maxLines to DataQuery * Explore: adds maxLines to loki datasource runRangeQueryWithFallback * Explore: adds onChangeQueryLimit to LokiExploreQueryEditor * Explore: updates loki explore query editor to render extra field only in logs mode * Explore: fixes query limits for live and legacy queries * Explore: fixes result processor max lines limit in get logs result * Explore: fixes Loki datasource limit test * Explore: removes unnecessary ExploreMode from Loki language provider * Explore: fixes formatting * Explore: updates grafana-data datasource types - replaces strings with explore mode enum * Explore: updates loki explore query field props to take ReactNode * Explore: updates the way we calculate loki query lines limit to fall back to 0 lines on negative or invalid input instead of datasource maxLines * Explore: updates result processor get logs result method to avoid counting invalid/negative line limits * Explore: updates loki result transformer to process only an appropriate slice of a result instead of an entire one * Explore: adds a method for query limit preprocessing/mapping * Explore: updates loki datasource run range query with fallback method to use options.maxDataPoints in dashboards * Explore: removes unnecessary maxlineslimt from getLogsResult in resultProcessor * Explore: moves line limit to metadata * Explore: adds an ability to specify input type of extra field * Explore: updates LokiExploreQueryEditor - adds an input type * Explore: updates LokiExploreQueryEditor to run queries when maxLines is positive * Explore: fixes failing import of ExploreMode * Explore: fixes reducers test imports formatting * Explore: updates Loki extra field with min value set to 0 * Explore: exports LokiExploreExtraFieldProps * Explore: adds render test of LokiExploreQueryEditor * Explore: adds LokiExploreQueryEditor snapshot * Explore: updates LokiExploreQueryEditor onChangeQueryLimit method to prevent it from running when query input is empty - fixes cheatsheet display issue * Explore: updates Loki editor snapshots * Explore: fixes typo in test set name in LokiExploreQueryEditor * Explore: adds a render test of LokiExploreExtraField * Explore: fixes typo in LokiExploreQueryEditor * Explore: updates LokiExploreQueryEditor snapshot due to timezone issues * Explore: updates LokiExploreExtraField to export both functional component and a version using memo * Explore: updates LokiExploreQueryEditor to export both functional component and memoized function * Explore: updates LokiExploreQueryEditor - removes unnecessary react fragment * Explore: updates LokiExploreQueryEditor snapshot * Explore: adds LokiExploreQueryEditor tests for different explore mode cases * Explore: fixes Loki datasource and result transformer * Explore: updates LokiExploreQueryEditor snapshot * Explore: updates LokiExploreQueryEditor tests and test setup * Explore: updates LokiExploreQueryEditor - refactors component * Explore: updates LokiExploreQueryEditor to use default import from LokiExploreExtraField * Explore: updates LokiExploreQueryEditor snapshot * Explore: fixes formatting * Explore: updates LokiExploreQueryEditor max lines change * Explore: updates LokiExploreQueryEditor tests checking ExtraFieldElement * Explore: adds mock loki datasource to LokiExploreQueryEditor * Explore: updates LokiExploreQueryEditor test mock - adds language provider * Explore: updates LokiExploreQueryEditor snapshot * Explore: updates Loki ResultTransformer to filter out rows on limit - logic to be moved into a component with new form styles * Explore: updates LokiExploreQueryEditor tests
This commit is contained in:
parent
9b9f1ad1b9
commit
df48d1c19f
@ -312,6 +312,11 @@ export enum DataSourceStatus {
|
|||||||
Disconnected,
|
Disconnected,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ExploreMode {
|
||||||
|
Logs = 'Logs',
|
||||||
|
Metrics = 'Metrics',
|
||||||
|
}
|
||||||
|
|
||||||
export interface ExploreQueryFieldProps<
|
export interface ExploreQueryFieldProps<
|
||||||
DSType extends DataSourceApi<TQuery, TOptions>,
|
DSType extends DataSourceApi<TQuery, TOptions>,
|
||||||
TQuery extends DataQuery = DataQuery,
|
TQuery extends DataQuery = DataQuery,
|
||||||
@ -320,11 +325,12 @@ export interface ExploreQueryFieldProps<
|
|||||||
history: any[];
|
history: any[];
|
||||||
onBlur?: () => void;
|
onBlur?: () => void;
|
||||||
absoluteRange?: AbsoluteTimeRange;
|
absoluteRange?: AbsoluteTimeRange;
|
||||||
|
exploreMode?: ExploreMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExploreStartPageProps {
|
export interface ExploreStartPageProps {
|
||||||
datasource?: DataSourceApi;
|
datasource?: DataSourceApi;
|
||||||
exploreMode: 'Logs' | 'Metrics';
|
exploreMode: ExploreMode;
|
||||||
onClickExample: (query: DataQuery) => void;
|
onClickExample: (query: DataQuery) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -384,6 +390,11 @@ export interface DataQuery {
|
|||||||
datasource?: string | null;
|
datasource?: string | null;
|
||||||
|
|
||||||
metric?: any;
|
metric?: any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For limiting result lines.
|
||||||
|
*/
|
||||||
|
maxLines?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataQueryError {
|
export interface DataQueryError {
|
||||||
@ -414,7 +425,7 @@ export interface DataQueryRequest<TQuery extends DataQuery = DataQuery> {
|
|||||||
app: CoreApp | string;
|
app: CoreApp | string;
|
||||||
|
|
||||||
cacheTimeout?: string;
|
cacheTimeout?: string;
|
||||||
exploreMode?: 'Logs' | 'Metrics';
|
exploreMode?: ExploreMode;
|
||||||
rangeRaw?: RawTimeRange;
|
rangeRaw?: RawTimeRange;
|
||||||
timeInfo?: string; // The query time description (blue text in the upper right)
|
timeInfo?: string; // The query time description (blue text in the upper right)
|
||||||
|
|
||||||
|
@ -353,11 +353,17 @@ export function logSeriesToLogsModel(logSeries: DataFrame[]): LogsModel | undefi
|
|||||||
}
|
}
|
||||||
|
|
||||||
const limits = logSeries.filter(series => series.meta && series.meta.limit);
|
const limits = logSeries.filter(series => series.meta && series.meta.limit);
|
||||||
|
const limitValue = Object.values(
|
||||||
|
limits.reduce((acc: any, elem: any) => {
|
||||||
|
acc[elem.refId] = elem.meta.limit;
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
|
).reduce((acc: number, elem: any) => (acc += elem), 0);
|
||||||
|
|
||||||
if (limits.length > 0) {
|
if (limits.length > 0) {
|
||||||
meta.push({
|
meta.push({
|
||||||
label: 'Limit',
|
label: 'Limit',
|
||||||
value: `${limits[0].meta.limit} (${deduplicatedLogRows.length} returned)`,
|
value: `${limitValue} (${deduplicatedLogRows.length} returned)`,
|
||||||
kind: LogsMetaKind.String,
|
kind: LogsMetaKind.String,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
sortLogsResult,
|
sortLogsResult,
|
||||||
buildQueryTransaction,
|
buildQueryTransaction,
|
||||||
} from './explore';
|
} from './explore';
|
||||||
import { ExploreUrlState, ExploreMode } from 'app/types/explore';
|
import { ExploreUrlState } from 'app/types/explore';
|
||||||
import store from 'app/core/store';
|
import store from 'app/core/store';
|
||||||
import {
|
import {
|
||||||
DataQueryError,
|
DataQueryError,
|
||||||
@ -22,6 +22,7 @@ import {
|
|||||||
LogLevel,
|
LogLevel,
|
||||||
dateTime,
|
dateTime,
|
||||||
MutableDataFrame,
|
MutableDataFrame,
|
||||||
|
ExploreMode,
|
||||||
LogRowModel,
|
LogRowModel,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { RefreshPicker } from '@grafana/ui';
|
import { RefreshPicker } from '@grafana/ui';
|
||||||
|
@ -21,6 +21,7 @@ import {
|
|||||||
TimeRange,
|
TimeRange,
|
||||||
TimeZone,
|
TimeZone,
|
||||||
toUtc,
|
toUtc,
|
||||||
|
ExploreMode,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { renderUrl } from 'app/core/utils/url';
|
import { renderUrl } from 'app/core/utils/url';
|
||||||
import store from 'app/core/store';
|
import store from 'app/core/store';
|
||||||
@ -28,7 +29,7 @@ import kbn from 'app/core/utils/kbn';
|
|||||||
import { getNextRefIdChar } from './query';
|
import { getNextRefIdChar } from './query';
|
||||||
// Types
|
// Types
|
||||||
import { RefreshPicker } from '@grafana/ui';
|
import { RefreshPicker } from '@grafana/ui';
|
||||||
import { ExploreMode, ExploreUrlState, QueryOptions, QueryTransaction } from 'app/types/explore';
|
import { ExploreUrlState, QueryOptions, QueryTransaction } from 'app/types/explore';
|
||||||
import { config } from '../config';
|
import { config } from '../config';
|
||||||
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||||
import { DataSourceSrv } from '@grafana/runtime';
|
import { DataSourceSrv } from '@grafana/runtime';
|
||||||
|
@ -36,16 +36,10 @@ import {
|
|||||||
TimeZone,
|
TimeZone,
|
||||||
AbsoluteTimeRange,
|
AbsoluteTimeRange,
|
||||||
LoadingState,
|
LoadingState,
|
||||||
|
ExploreMode,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
import {
|
import { ExploreItemState, ExploreUrlState, ExploreId, ExploreUpdateState, ExploreUIState } from 'app/types/explore';
|
||||||
ExploreItemState,
|
|
||||||
ExploreUrlState,
|
|
||||||
ExploreId,
|
|
||||||
ExploreUpdateState,
|
|
||||||
ExploreUIState,
|
|
||||||
ExploreMode,
|
|
||||||
} from 'app/types/explore';
|
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
import {
|
import {
|
||||||
ensureQueries,
|
ensureQueries,
|
||||||
|
@ -6,9 +6,9 @@ import memoizeOne from 'memoize-one';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { css } from 'emotion';
|
import { css } from 'emotion';
|
||||||
|
|
||||||
import { ExploreId, ExploreItemState, ExploreMode } from 'app/types/explore';
|
import { ExploreId, ExploreItemState } from 'app/types/explore';
|
||||||
import { ToggleButtonGroup, ToggleButton, Tooltip, ButtonSelect, SetInterval } from '@grafana/ui';
|
import { ToggleButtonGroup, ToggleButton, Tooltip, ButtonSelect, SetInterval } from '@grafana/ui';
|
||||||
import { RawTimeRange, TimeZone, TimeRange, DataQuery } from '@grafana/data';
|
import { RawTimeRange, TimeZone, TimeRange, DataQuery, ExploreMode } from '@grafana/data';
|
||||||
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
|
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
|
||||||
import { StoreState } from 'app/types/store';
|
import { StoreState } from 'app/types/store';
|
||||||
import {
|
import {
|
||||||
|
@ -20,9 +20,10 @@ import {
|
|||||||
TimeRange,
|
TimeRange,
|
||||||
AbsoluteTimeRange,
|
AbsoluteTimeRange,
|
||||||
LoadingState,
|
LoadingState,
|
||||||
|
ExploreMode,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
import { ExploreItemState, ExploreId, ExploreMode } from 'app/types/explore';
|
import { ExploreItemState, ExploreId } from 'app/types/explore';
|
||||||
import { Emitter } from 'app/core/utils/emitter';
|
import { Emitter } from 'app/core/utils/emitter';
|
||||||
import { highlightLogsExpressionAction, removeQueryRowAction } from './state/actionTypes';
|
import { highlightLogsExpressionAction, removeQueryRowAction } from './state/actionTypes';
|
||||||
|
|
||||||
@ -148,6 +149,7 @@ export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
|
|||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
data={queryResponse}
|
data={queryResponse}
|
||||||
absoluteRange={absoluteRange}
|
absoluteRange={absoluteRange}
|
||||||
|
exploreMode={mode}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<QueryEditor
|
<QueryEditor
|
||||||
|
@ -13,8 +13,9 @@ import {
|
|||||||
PanelData,
|
PanelData,
|
||||||
QueryFixAction,
|
QueryFixAction,
|
||||||
TimeRange,
|
TimeRange,
|
||||||
|
ExploreMode,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { ExploreId, ExploreItemState, ExploreMode, ExploreUIState } from 'app/types/explore';
|
import { ExploreId, ExploreItemState, ExploreUIState } from 'app/types/explore';
|
||||||
|
|
||||||
export interface AddQueryRowPayload {
|
export interface AddQueryRowPayload {
|
||||||
exploreId: ExploreId;
|
exploreId: ExploreId;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { PayloadAction } from '@reduxjs/toolkit';
|
import { PayloadAction } from '@reduxjs/toolkit';
|
||||||
import { DataQuery, DefaultTimeZone, LogsDedupStrategy, RawTimeRange, toUtc } from '@grafana/data';
|
import { DataQuery, DefaultTimeZone, LogsDedupStrategy, RawTimeRange, toUtc, ExploreMode } from '@grafana/data';
|
||||||
|
|
||||||
import * as Actions from './actions';
|
import * as Actions from './actions';
|
||||||
import { changeDatasource, loadDatasource, navigateToExplore, refreshExplore } from './actions';
|
import { changeDatasource, loadDatasource, navigateToExplore, refreshExplore } from './actions';
|
||||||
import { ExploreId, ExploreMode, ExploreUpdateState, ExploreUrlState } from 'app/types';
|
import { ExploreId, ExploreUpdateState, ExploreUrlState } from 'app/types';
|
||||||
import { thunkTester } from 'test/core/thunk/thunkTester';
|
import { thunkTester } from 'test/core/thunk/thunkTester';
|
||||||
import {
|
import {
|
||||||
initializeExploreAction,
|
initializeExploreAction,
|
||||||
|
@ -16,6 +16,7 @@ import {
|
|||||||
QueryFixAction,
|
QueryFixAction,
|
||||||
RawTimeRange,
|
RawTimeRange,
|
||||||
TimeRange,
|
TimeRange,
|
||||||
|
ExploreMode,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
// Services & Utils
|
// Services & Utils
|
||||||
import store from 'app/core/store';
|
import store from 'app/core/store';
|
||||||
@ -40,7 +41,7 @@ import {
|
|||||||
// Types
|
// Types
|
||||||
import { ExploreItemState, ExploreUrlState, ThunkResult } from 'app/types';
|
import { ExploreItemState, ExploreUrlState, ThunkResult } from 'app/types';
|
||||||
|
|
||||||
import { ExploreId, ExploreMode, ExploreUIState, QueryOptions } from 'app/types/explore';
|
import { ExploreId, ExploreUIState, QueryOptions } from 'app/types/explore';
|
||||||
import {
|
import {
|
||||||
addQueryRowAction,
|
addQueryRowAction,
|
||||||
changeModeAction,
|
changeModeAction,
|
||||||
|
@ -4,8 +4,9 @@ import {
|
|||||||
dateTime,
|
dateTime,
|
||||||
LoadingState,
|
LoadingState,
|
||||||
LogsDedupStrategy,
|
LogsDedupStrategy,
|
||||||
RawTimeRange,
|
|
||||||
toDataFrame,
|
toDataFrame,
|
||||||
|
ExploreMode,
|
||||||
|
RawTimeRange,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -16,7 +17,7 @@ import {
|
|||||||
makeExploreItemState,
|
makeExploreItemState,
|
||||||
makeInitialUpdateState,
|
makeInitialUpdateState,
|
||||||
} from './reducers';
|
} from './reducers';
|
||||||
import { ExploreId, ExploreItemState, ExploreMode, ExploreState, ExploreUrlState } from 'app/types/explore';
|
import { ExploreId, ExploreItemState, ExploreState, ExploreUrlState } from 'app/types/explore';
|
||||||
import { reducerTester } from 'test/core/redux/reducerTester';
|
import { reducerTester } from 'test/core/redux/reducerTester';
|
||||||
import {
|
import {
|
||||||
changeModeAction,
|
changeModeAction,
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
PanelEvents,
|
PanelEvents,
|
||||||
TimeZone,
|
TimeZone,
|
||||||
toLegacyResponseData,
|
toLegacyResponseData,
|
||||||
|
ExploreMode,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { RefreshPicker } from '@grafana/ui';
|
import { RefreshPicker } from '@grafana/ui';
|
||||||
import { LocationUpdate } from '@grafana/runtime';
|
import { LocationUpdate } from '@grafana/runtime';
|
||||||
@ -25,7 +26,7 @@ import {
|
|||||||
sortLogsResult,
|
sortLogsResult,
|
||||||
stopQueryState,
|
stopQueryState,
|
||||||
} from 'app/core/utils/explore';
|
} from 'app/core/utils/explore';
|
||||||
import { ExploreId, ExploreItemState, ExploreMode, ExploreState, ExploreUpdateState } from 'app/types/explore';
|
import { ExploreId, ExploreItemState, ExploreState, ExploreUpdateState } from 'app/types/explore';
|
||||||
import {
|
import {
|
||||||
addQueryRowAction,
|
addQueryRowAction,
|
||||||
changeLoadingStateAction,
|
changeLoadingStateAction,
|
||||||
|
@ -14,9 +14,9 @@ jest.mock('@grafana/data/src/datetime/moment_wrapper', () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
import { ResultProcessor } from './ResultProcessor';
|
import { ResultProcessor } from './ResultProcessor';
|
||||||
import { ExploreItemState, ExploreMode } from 'app/types/explore';
|
import { ExploreItemState } from 'app/types/explore';
|
||||||
import TableModel from 'app/core/table_model';
|
import TableModel from 'app/core/table_model';
|
||||||
import { TimeSeries, LogRowModel, toDataFrame, FieldType } from '@grafana/data';
|
import { TimeSeries, LogRowModel, toDataFrame, FieldType, ExploreMode } from '@grafana/data';
|
||||||
|
|
||||||
const testContext = (options: any = {}) => {
|
const testContext = (options: any = {}) => {
|
||||||
const timeSeries = toDataFrame({
|
const timeSeries = toDataFrame({
|
||||||
|
@ -6,8 +6,9 @@ import {
|
|||||||
TimeZone,
|
TimeZone,
|
||||||
toDataFrame,
|
toDataFrame,
|
||||||
getDisplayProcessor,
|
getDisplayProcessor,
|
||||||
|
ExploreMode,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { ExploreItemState, ExploreMode } from 'app/types/explore';
|
import { ExploreItemState } from 'app/types/explore';
|
||||||
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
|
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
|
||||||
import { sortLogsResult, refreshIntervalToSortOrder } from 'app/core/utils/explore';
|
import { sortLogsResult, refreshIntervalToSortOrder } from 'app/core/utils/explore';
|
||||||
import { dataFrameToLogsModel } from 'app/core/logs_model';
|
import { dataFrameToLogsModel } from 'app/core/logs_model';
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { shuffle } from 'lodash';
|
import { shuffle } from 'lodash';
|
||||||
import { ExploreStartPageProps, DataQuery } from '@grafana/data';
|
import { ExploreStartPageProps, DataQuery, ExploreMode } from '@grafana/data';
|
||||||
import LokiLanguageProvider from '../language_provider';
|
import LokiLanguageProvider from '../language_provider';
|
||||||
import { ExploreMode } from 'app/types';
|
|
||||||
|
|
||||||
const DEFAULT_EXAMPLES = ['{job="default/prometheus"}'];
|
const DEFAULT_EXAMPLES = ['{job="default/prometheus"}'];
|
||||||
const PREFERRED_LABELS = ['job', 'app', 'k8s_app'];
|
const PREFERRED_LABELS = ['job', 'app', 'k8s_app'];
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import { LokiExploreExtraField, LokiExploreExtraFieldProps } from './LokiExploreExtraField';
|
||||||
|
|
||||||
|
const setup = (propOverrides?: LokiExploreExtraFieldProps) => {
|
||||||
|
const label = 'Loki Explore Extra Field';
|
||||||
|
const value = '123';
|
||||||
|
const type = 'number';
|
||||||
|
const min = 0;
|
||||||
|
const onChangeFunc = jest.fn();
|
||||||
|
const onKeyDownFunc = jest.fn();
|
||||||
|
|
||||||
|
const props: any = {
|
||||||
|
label,
|
||||||
|
value,
|
||||||
|
type,
|
||||||
|
min,
|
||||||
|
onChangeFunc,
|
||||||
|
onKeyDownFunc,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(props, propOverrides);
|
||||||
|
|
||||||
|
return shallow(<LokiExploreExtraField {...props} />);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('LokiExploreExtraField', () => {
|
||||||
|
it('should render component', () => {
|
||||||
|
const wrapper = setup();
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,37 @@
|
|||||||
|
// Libraries
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { FormLabel } from '@grafana/ui';
|
||||||
|
|
||||||
|
export interface LokiExploreExtraFieldProps {
|
||||||
|
label: string;
|
||||||
|
onChangeFunc: (e: React.SyntheticEvent<HTMLInputElement>) => void;
|
||||||
|
onKeyDownFunc: (e: React.KeyboardEvent<HTMLInputElement>) => void;
|
||||||
|
value: string;
|
||||||
|
type?: string;
|
||||||
|
min?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LokiExploreExtraField(props: LokiExploreExtraFieldProps) {
|
||||||
|
const { label, onChangeFunc, onKeyDownFunc, value, type, min } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="gf-form-inline explore-input--ml">
|
||||||
|
<div className="gf-form">
|
||||||
|
<FormLabel width={6}>{label}</FormLabel>
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
className="gf-form-input width-6"
|
||||||
|
placeholder={'auto'}
|
||||||
|
onChange={onChangeFunc}
|
||||||
|
onKeyDown={onKeyDownFunc}
|
||||||
|
min={min}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(LokiExploreExtraField);
|
@ -0,0 +1,95 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { shallow, mount } from 'enzyme';
|
||||||
|
import { act } from 'react-dom/test-utils';
|
||||||
|
import LokiExploreQueryEditor from './LokiExploreQueryEditor';
|
||||||
|
import { LokiExploreExtraField } from './LokiExploreExtraField';
|
||||||
|
import { LokiDatasource } from '../datasource';
|
||||||
|
import { LokiQuery } from '../types';
|
||||||
|
import { ExploreMode, PanelData, LoadingState, dateTime } from '@grafana/data';
|
||||||
|
import { makeMockLokiDatasource } from '../mocks';
|
||||||
|
import LokiLanguageProvider from '../language_provider';
|
||||||
|
|
||||||
|
const setup = (renderMethod: any, propOverrides?: object) => {
|
||||||
|
const datasource: LokiDatasource = makeMockLokiDatasource({});
|
||||||
|
datasource.languageProvider = new LokiLanguageProvider(datasource);
|
||||||
|
const onRunQuery = jest.fn();
|
||||||
|
const onChange = jest.fn();
|
||||||
|
const query: LokiQuery = { expr: '', refId: 'A', maxLines: 0 };
|
||||||
|
const data: PanelData = {
|
||||||
|
state: LoadingState.NotStarted,
|
||||||
|
series: [],
|
||||||
|
request: {
|
||||||
|
requestId: '1',
|
||||||
|
dashboardId: 1,
|
||||||
|
interval: '1s',
|
||||||
|
panelId: 1,
|
||||||
|
range: {
|
||||||
|
from: dateTime('2020-01-01', 'YYYY-MM-DD'),
|
||||||
|
to: dateTime('2020-01-02', 'YYYY-MM-DD'),
|
||||||
|
raw: {
|
||||||
|
from: dateTime('2020-01-01', 'YYYY-MM-DD'),
|
||||||
|
to: dateTime('2020-01-02', 'YYYY-MM-DD'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scopedVars: {},
|
||||||
|
targets: [],
|
||||||
|
timezone: 'GMT',
|
||||||
|
app: 'Grafana',
|
||||||
|
startTime: 0,
|
||||||
|
},
|
||||||
|
timeRange: {
|
||||||
|
from: dateTime('2020-01-01', 'YYYY-MM-DD'),
|
||||||
|
to: dateTime('2020-01-02', 'YYYY-MM-DD'),
|
||||||
|
raw: {
|
||||||
|
from: dateTime('2020-01-01', 'YYYY-MM-DD'),
|
||||||
|
to: dateTime('2020-01-02', 'YYYY-MM-DD'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const history: any[] = [];
|
||||||
|
const exploreMode: ExploreMode = ExploreMode.Logs;
|
||||||
|
|
||||||
|
const props: any = {
|
||||||
|
query,
|
||||||
|
data,
|
||||||
|
datasource,
|
||||||
|
exploreMode,
|
||||||
|
history,
|
||||||
|
onChange,
|
||||||
|
onRunQuery,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.assign(props, { ...props, ...propOverrides });
|
||||||
|
return renderMethod(<LokiExploreQueryEditor {...props} />);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('LokiExploreQueryEditor', () => {
|
||||||
|
let originalGetSelection: typeof window.getSelection;
|
||||||
|
beforeAll(() => {
|
||||||
|
originalGetSelection = window.getSelection;
|
||||||
|
window.getSelection = () => null;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
window.getSelection = originalGetSelection;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render component', () => {
|
||||||
|
const wrapper = setup(shallow);
|
||||||
|
expect(wrapper).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render LokiQueryField with ExtraFieldElement when ExploreMode is set to Logs', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
const wrapper = setup(mount);
|
||||||
|
expect(wrapper.find(LokiExploreExtraField).length).toBe(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render LokiQueryField with no ExtraFieldElement when ExploreMode is not Logs', async () => {
|
||||||
|
await act(async () => {
|
||||||
|
const wrapper = setup(mount, { exploreMode: ExploreMode.Metrics });
|
||||||
|
expect(wrapper.find(LokiExploreExtraField).length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,89 @@
|
|||||||
|
// Libraries
|
||||||
|
import React, { memo } from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { AbsoluteTimeRange, ExploreQueryFieldProps, ExploreMode } from '@grafana/data';
|
||||||
|
import { LokiDatasource } from '../datasource';
|
||||||
|
import { LokiQuery, LokiOptions } from '../types';
|
||||||
|
import { LokiQueryField } from './LokiQueryField';
|
||||||
|
import LokiExploreExtraField from './LokiExploreExtraField';
|
||||||
|
|
||||||
|
type Props = ExploreQueryFieldProps<LokiDatasource, LokiQuery, LokiOptions>;
|
||||||
|
|
||||||
|
export function LokiExploreQueryEditor(props: Props) {
|
||||||
|
const { query, data, datasource, exploreMode, history, onChange, onRunQuery } = props;
|
||||||
|
|
||||||
|
let absolute: AbsoluteTimeRange;
|
||||||
|
if (data && !_.isEmpty(data.request)) {
|
||||||
|
const { range } = data.request;
|
||||||
|
|
||||||
|
absolute = {
|
||||||
|
from: range.from.valueOf(),
|
||||||
|
to: range.to.valueOf(),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
absolute = {
|
||||||
|
from: Date.now() - 10000,
|
||||||
|
to: Date.now(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeQueryLimit(value: string) {
|
||||||
|
const { query, onChange } = props;
|
||||||
|
const nextQuery = { ...query, maxLines: preprocessMaxLines(value) };
|
||||||
|
onChange(nextQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
function preprocessMaxLines(value: string): number {
|
||||||
|
if (value.length === 0) {
|
||||||
|
// empty input - falls back to dataSource.maxLines limit
|
||||||
|
return NaN;
|
||||||
|
} else if (value.length > 0 && (isNaN(+value) || +value < 0)) {
|
||||||
|
// input with at least 1 character and that is either incorrect (value in the input field is not a number) or negative
|
||||||
|
// falls back to the limit of 0 lines
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
// default case - correct input
|
||||||
|
return +value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMaxLinesChange(e: React.SyntheticEvent<HTMLInputElement>) {
|
||||||
|
if (query.maxLines !== preprocessMaxLines(e.currentTarget.value)) {
|
||||||
|
onChangeQueryLimit(e.currentTarget.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onReturnKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
onRunQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<LokiQueryField
|
||||||
|
datasource={datasource}
|
||||||
|
query={query}
|
||||||
|
onChange={onChange}
|
||||||
|
onRunQuery={onRunQuery}
|
||||||
|
history={history}
|
||||||
|
data={data}
|
||||||
|
absoluteRange={absolute}
|
||||||
|
ExtraFieldElement={
|
||||||
|
exploreMode === ExploreMode.Logs ? (
|
||||||
|
<LokiExploreExtraField
|
||||||
|
label={'Line limit'}
|
||||||
|
onChangeFunc={onMaxLinesChange}
|
||||||
|
onKeyDownFunc={onReturnKeyDown}
|
||||||
|
value={query?.maxLines?.toString() || ''}
|
||||||
|
type={'number'}
|
||||||
|
min={0}
|
||||||
|
/>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(LokiExploreQueryEditor);
|
@ -1,5 +1,5 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import React from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ButtonCascader,
|
ButtonCascader,
|
||||||
@ -69,6 +69,7 @@ export interface LokiQueryFieldFormProps extends ExploreQueryFieldProps<LokiData
|
|||||||
absoluteRange: AbsoluteTimeRange;
|
absoluteRange: AbsoluteTimeRange;
|
||||||
onLoadOptions: (selectedOptions: CascaderOption[]) => void;
|
onLoadOptions: (selectedOptions: CascaderOption[]) => void;
|
||||||
onLabelsRefresh?: () => void;
|
onLabelsRefresh?: () => void;
|
||||||
|
ExtraFieldElement?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormProps> {
|
export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormProps> {
|
||||||
@ -134,7 +135,16 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { data, query, syntaxLoaded, logLabelOptions, onLoadOptions, onLabelsRefresh, datasource } = this.props;
|
const {
|
||||||
|
ExtraFieldElement,
|
||||||
|
data,
|
||||||
|
query,
|
||||||
|
syntaxLoaded,
|
||||||
|
logLabelOptions,
|
||||||
|
onLoadOptions,
|
||||||
|
onLabelsRefresh,
|
||||||
|
datasource,
|
||||||
|
} = this.props;
|
||||||
const lokiLanguageProvider = datasource.languageProvider as LokiLanguageProvider;
|
const lokiLanguageProvider = datasource.languageProvider as LokiLanguageProvider;
|
||||||
const cleanText = datasource.languageProvider ? lokiLanguageProvider.cleanText : undefined;
|
const cleanText = datasource.languageProvider ? lokiLanguageProvider.cleanText : undefined;
|
||||||
const hasLogLabels = logLabelOptions && logLabelOptions.length > 0;
|
const hasLogLabels = logLabelOptions && logLabelOptions.length > 0;
|
||||||
@ -144,8 +154,8 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="gf-form-inline">
|
<div className="gf-form-inline gf-form-inline--nowrap flex-grow-1">
|
||||||
<div className="gf-form">
|
<div className="gf-form flex-shrink-0">
|
||||||
<ButtonCascader
|
<ButtonCascader
|
||||||
options={logLabelOptions || []}
|
options={logLabelOptions || []}
|
||||||
disabled={buttonDisabled}
|
disabled={buttonDisabled}
|
||||||
@ -156,7 +166,7 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
|
|||||||
{chooserText}
|
{chooserText}
|
||||||
</ButtonCascader>
|
</ButtonCascader>
|
||||||
</div>
|
</div>
|
||||||
<div className="gf-form gf-form--grow">
|
<div className="gf-form gf-form--grow flex-shrink-1">
|
||||||
<QueryField
|
<QueryField
|
||||||
additionalPlugins={this.plugins}
|
additionalPlugins={this.plugins}
|
||||||
cleanText={cleanText}
|
cleanText={cleanText}
|
||||||
@ -171,8 +181,13 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
|
|||||||
syntaxLoaded={syntaxLoaded}
|
syntaxLoaded={syntaxLoaded}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
{ExtraFieldElement}
|
||||||
</div>
|
</div>
|
||||||
<div>{showError ? <div className="prom-query-field-info text-error">{data.error.message}</div> : null}</div>
|
{showError ? (
|
||||||
|
<div className="query-row-break">
|
||||||
|
<div className="prom-query-field-info text-error">{data.error.message}</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,26 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`LokiExploreExtraField should render component 1`] = `
|
||||||
|
<div
|
||||||
|
className="gf-form-inline explore-input--ml"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="gf-form"
|
||||||
|
>
|
||||||
|
<Component
|
||||||
|
width={6}
|
||||||
|
>
|
||||||
|
Loki Explore Extra Field
|
||||||
|
</Component>
|
||||||
|
<input
|
||||||
|
className="gf-form-input width-6"
|
||||||
|
min={0}
|
||||||
|
onChange={[MockFunction]}
|
||||||
|
onKeyDown={[MockFunction]}
|
||||||
|
placeholder="auto"
|
||||||
|
type="number"
|
||||||
|
value="123"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
@ -0,0 +1,80 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`LokiExploreQueryEditor should render component 1`] = `
|
||||||
|
<Component
|
||||||
|
ExtraFieldElement={
|
||||||
|
<Memo(LokiExploreExtraField)
|
||||||
|
label="Line limit"
|
||||||
|
min={0}
|
||||||
|
onChangeFunc={[Function]}
|
||||||
|
onKeyDownFunc={[Function]}
|
||||||
|
type="number"
|
||||||
|
value="0"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
absoluteRange={
|
||||||
|
Object {
|
||||||
|
"from": 1577836800000,
|
||||||
|
"to": 1577923200000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data={
|
||||||
|
Object {
|
||||||
|
"request": Object {
|
||||||
|
"app": "Grafana",
|
||||||
|
"dashboardId": 1,
|
||||||
|
"interval": "1s",
|
||||||
|
"panelId": 1,
|
||||||
|
"range": Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"raw": Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
"requestId": "1",
|
||||||
|
"scopedVars": Object {},
|
||||||
|
"startTime": 0,
|
||||||
|
"targets": Array [],
|
||||||
|
"timezone": "GMT",
|
||||||
|
},
|
||||||
|
"series": Array [],
|
||||||
|
"state": "NotStarted",
|
||||||
|
"timeRange": Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"raw": Object {
|
||||||
|
"from": "2020-01-01T00:00:00.000Z",
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
"to": "2020-01-02T00:00:00.000Z",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
datasource={
|
||||||
|
Object {
|
||||||
|
"languageProvider": LokiLanguageProvider {
|
||||||
|
"cleanText": [Function],
|
||||||
|
"datasource": [Circular],
|
||||||
|
"getBeginningCompletionItems": [Function],
|
||||||
|
"getTermCompletionItems": [Function],
|
||||||
|
"labelKeys": Object {},
|
||||||
|
"labelValues": Object {},
|
||||||
|
"request": [Function],
|
||||||
|
"start": [Function],
|
||||||
|
},
|
||||||
|
"metadataRequest": [Function],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
history={Array []}
|
||||||
|
onChange={[MockFunction]}
|
||||||
|
onRunQuery={[MockFunction]}
|
||||||
|
query={
|
||||||
|
Object {
|
||||||
|
"expr": "",
|
||||||
|
"maxLines": 0,
|
||||||
|
"refId": "A",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
`;
|
@ -1,11 +1,18 @@
|
|||||||
import LokiDatasource, { RangeQueryOptions } from './datasource';
|
import LokiDatasource, { RangeQueryOptions } from './datasource';
|
||||||
import { LokiQuery, LokiResultType, LokiResponse, LokiLegacyStreamResponse } from './types';
|
import { LokiQuery, LokiResultType, LokiResponse, LokiLegacyStreamResponse } from './types';
|
||||||
import { getQueryOptions } from 'test/helpers/getQueryOptions';
|
import { getQueryOptions } from 'test/helpers/getQueryOptions';
|
||||||
import { AnnotationQueryRequest, DataSourceApi, DataFrame, dateTime, TimeRange, FieldCache } from '@grafana/data';
|
import {
|
||||||
|
AnnotationQueryRequest,
|
||||||
|
DataSourceApi,
|
||||||
|
DataFrame,
|
||||||
|
dateTime,
|
||||||
|
TimeRange,
|
||||||
|
ExploreMode,
|
||||||
|
FieldCache,
|
||||||
|
} from '@grafana/data';
|
||||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||||
import { CustomVariable } from 'app/features/templating/custom_variable';
|
import { CustomVariable } from 'app/features/templating/custom_variable';
|
||||||
import { makeMockLokiDatasource } from './mocks';
|
import { makeMockLokiDatasource } from './mocks';
|
||||||
import { ExploreMode } from 'app/types';
|
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
|
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
|
||||||
@ -478,7 +485,7 @@ function makeLimitTest(instanceSettings: any, datasourceRequestMock: any, templa
|
|||||||
const ds = new LokiDatasource(settings, templateSrvMock);
|
const ds = new LokiDatasource(settings, templateSrvMock);
|
||||||
datasourceRequestMock.mockImplementation(() => Promise.resolve(testResp));
|
datasourceRequestMock.mockImplementation(() => Promise.resolve(testResp));
|
||||||
|
|
||||||
const options = getQueryOptions<LokiQuery>({ targets: [{ expr: 'foo', refId: 'B' }] });
|
const options = getQueryOptions<LokiQuery>({ targets: [{ expr: 'foo', refId: 'B', maxLines: maxDataPoints }] });
|
||||||
if (Number.isFinite(maxDataPoints)) {
|
if (Number.isFinite(maxDataPoints)) {
|
||||||
options.maxDataPoints = maxDataPoints;
|
options.maxDataPoints = maxDataPoints;
|
||||||
} else {
|
} else {
|
||||||
|
@ -35,6 +35,7 @@ import {
|
|||||||
DataQueryRequest,
|
DataQueryRequest,
|
||||||
DataQueryResponse,
|
DataQueryResponse,
|
||||||
AnnotationQueryRequest,
|
AnnotationQueryRequest,
|
||||||
|
ExploreMode,
|
||||||
ScopedVars,
|
ScopedVars,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
@ -48,7 +49,6 @@ import {
|
|||||||
LokiRangeQueryRequest,
|
LokiRangeQueryRequest,
|
||||||
LokiStreamResponse,
|
LokiStreamResponse,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { ExploreMode } from 'app/types';
|
|
||||||
import { LegacyTarget, LiveStreams } from './live_streams';
|
import { LegacyTarget, LiveStreams } from './live_streams';
|
||||||
import LanguageProvider from './language_provider';
|
import LanguageProvider from './language_provider';
|
||||||
|
|
||||||
@ -267,25 +267,45 @@ export class LokiDatasource extends DataSourceApi<LokiQuery, LokiOptions> {
|
|||||||
options: RangeQueryOptions,
|
options: RangeQueryOptions,
|
||||||
responseListLength = 1
|
responseListLength = 1
|
||||||
): Observable<DataQueryResponse> => {
|
): Observable<DataQueryResponse> => {
|
||||||
if (target.liveStreaming) {
|
// target.maxLines value already preprocessed
|
||||||
return this.runLiveQuery(target, options);
|
// available cases:
|
||||||
|
// 1) empty input -> mapped to NaN, falls back to dataSource.maxLines limit
|
||||||
|
// 2) input with at least 1 character and that is either incorrect (value in the input field is not a number) or negative
|
||||||
|
// - mapped to 0, falls back to the limit of 0 lines
|
||||||
|
// 3) default case - correct input, mapped to the value from the input field
|
||||||
|
|
||||||
|
let linesLimit = 0;
|
||||||
|
if (target.maxLines === undefined) {
|
||||||
|
// no target.maxLines, using options.maxDataPoints
|
||||||
|
linesLimit = Math.min(options.maxDataPoints || Infinity, this.maxLines);
|
||||||
|
} else {
|
||||||
|
// using target.maxLines
|
||||||
|
if (isNaN(target.maxLines)) {
|
||||||
|
linesLimit = this.maxLines;
|
||||||
|
} else {
|
||||||
|
linesLimit = target.maxLines;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const query = this.createRangeQuery(target, options);
|
const queryOptions = { ...options, maxDataPoints: linesLimit };
|
||||||
|
if (target.liveStreaming) {
|
||||||
|
return this.runLiveQuery(target, queryOptions);
|
||||||
|
}
|
||||||
|
const query = this.createRangeQuery(target, queryOptions);
|
||||||
return this._request(RANGE_QUERY_ENDPOINT, query).pipe(
|
return this._request(RANGE_QUERY_ENDPOINT, query).pipe(
|
||||||
catchError((err: any) => this.throwUnless(err, err.cancelled || err.status === 404, target)),
|
catchError((err: any) => this.throwUnless(err, err.cancelled || err.status === 404, target)),
|
||||||
filter((response: any) => (response.cancelled ? false : true)),
|
filter((response: any) => (response.cancelled ? false : true)),
|
||||||
switchMap((response: { data: LokiResponse; status: number }) =>
|
switchMap((response: { data: LokiResponse; status: number }) =>
|
||||||
iif<DataQueryResponse, DataQueryResponse>(
|
iif<DataQueryResponse, DataQueryResponse>(
|
||||||
() => response.status === 404,
|
() => response.status === 404,
|
||||||
defer(() => this.runLegacyQuery(target, options)),
|
defer(() => this.runLegacyQuery(target, queryOptions)),
|
||||||
defer(() =>
|
defer(() =>
|
||||||
processRangeQueryResponse(
|
processRangeQueryResponse(
|
||||||
response.data,
|
response.data,
|
||||||
target,
|
target,
|
||||||
query,
|
query,
|
||||||
responseListLength,
|
responseListLength,
|
||||||
this.maxLines,
|
linesLimit,
|
||||||
this.instanceSettings.jsonData,
|
this.instanceSettings.jsonData,
|
||||||
options.reverse
|
options.reverse
|
||||||
)
|
)
|
||||||
|
@ -2,7 +2,7 @@ import { DataSourcePlugin } from '@grafana/data';
|
|||||||
import Datasource from './datasource';
|
import Datasource from './datasource';
|
||||||
|
|
||||||
import LokiCheatSheet from './components/LokiCheatSheet';
|
import LokiCheatSheet from './components/LokiCheatSheet';
|
||||||
import LokiQueryField from './components/LokiQueryField';
|
import LokiExploreQueryEditor from './components/LokiExploreQueryEditor';
|
||||||
import LokiQueryEditor from './components/LokiQueryEditor';
|
import LokiQueryEditor from './components/LokiQueryEditor';
|
||||||
import { LokiAnnotationsQueryCtrl } from './LokiAnnotationsQueryCtrl';
|
import { LokiAnnotationsQueryCtrl } from './LokiAnnotationsQueryCtrl';
|
||||||
import { ConfigEditor } from './configuration/ConfigEditor';
|
import { ConfigEditor } from './configuration/ConfigEditor';
|
||||||
@ -10,6 +10,6 @@ import { ConfigEditor } from './configuration/ConfigEditor';
|
|||||||
export const plugin = new DataSourcePlugin(Datasource)
|
export const plugin = new DataSourcePlugin(Datasource)
|
||||||
.setQueryEditor(LokiQueryEditor)
|
.setQueryEditor(LokiQueryEditor)
|
||||||
.setConfigEditor(ConfigEditor)
|
.setConfigEditor(ConfigEditor)
|
||||||
.setExploreQueryField(LokiQueryField)
|
.setExploreQueryField(LokiExploreQueryEditor)
|
||||||
.setExploreStartPage(LokiCheatSheet)
|
.setExploreStartPage(LokiCheatSheet)
|
||||||
.setAnnotationQueryCtrl(LokiAnnotationsQueryCtrl);
|
.setAnnotationQueryCtrl(LokiAnnotationsQueryCtrl);
|
||||||
|
@ -467,7 +467,7 @@ export function processRangeQueryResponse(
|
|||||||
switch (response.data.resultType) {
|
switch (response.data.resultType) {
|
||||||
case LokiResultType.Stream:
|
case LokiResultType.Stream:
|
||||||
return of({
|
return of({
|
||||||
data: lokiStreamsToDataframes(response.data.result, target, limit, config, reverse),
|
data: lokiStreamsToDataframes(limit > 0 ? response.data.result : [], target, limit, config, reverse),
|
||||||
key: `${target.refId}_log`,
|
key: `${target.refId}_log`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -14,15 +14,11 @@ import {
|
|||||||
AbsoluteTimeRange,
|
AbsoluteTimeRange,
|
||||||
GraphSeriesXY,
|
GraphSeriesXY,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
|
ExploreMode,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
import { Emitter } from 'app/core/core';
|
import { Emitter } from 'app/core/core';
|
||||||
|
|
||||||
export enum ExploreMode {
|
|
||||||
Metrics = 'Metrics',
|
|
||||||
Logs = 'Logs',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum ExploreId {
|
export enum ExploreId {
|
||||||
left = 'left',
|
left = 'left',
|
||||||
right = 'right',
|
right = 'right',
|
||||||
|
Loading…
Reference in New Issue
Block a user