Skip to content

Make log limits configurable #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 9, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions src/components/QueryEditor/ElasticsearchQueryContext.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { createContext, PropsWithChildren, useCallback, useEffect, useState } from 'react';
import React, { createContext, PropsWithChildren, useCallback, useEffect, useState, FunctionComponent } from 'react';

import { CoreApp, TimeRange } from '@grafana/data';

@@ -10,6 +10,9 @@ import { createReducer as createBucketAggsReducer } from './BucketAggregationsEd
import { reducer as metricsReducer } from './MetricAggregationsEditor/state/reducer';
import { aliasPatternReducer, queryReducer, initQuery, initExploreQuery } from './state';
import { getHook } from '@/utils/context';
import { Provider, useDispatch } from "react-redux";
import { initDefaults } from '@/store/defaults';
import { store } from "@/store"

export const RangeContext = createContext<TimeRange | undefined>(undefined);
export const useRange = getHook(RangeContext);
@@ -29,15 +32,31 @@ interface Props {
range: TimeRange;
}

export const ElasticsearchProvider = ({
function withStore<P extends PropsWithChildren<Props>>(Component: FunctionComponent<P>): FunctionComponent<P>{
const newComp = (props: P) => (
<Provider store={store}>
<Component {...props}/>
</Provider>
)
newComp.displayName = Component.displayName
return newComp
}

export const ElasticsearchProvider = withStore(({
children,
onChange,
onRunQuery,
query,
app,
datasource,
range,
}: PropsWithChildren<Props>) => {
}: PropsWithChildren<Props>): JSX.Element => {

const storeDispatch = useDispatch();
useEffect(()=>{
storeDispatch(initDefaults(datasource.queryEditorConfig?.defaults))
}, [storeDispatch, datasource])

const onStateChange = useCallback(
(query: ElasticsearchQuery) => {
onChange(query);
@@ -77,7 +96,7 @@ export const ElasticsearchProvider = ({
}, [shouldRunInit, dispatch, isUninitialized, app]);

if (isUninitialized) {
return null;
return (<></>);
}

return (
@@ -89,4 +108,4 @@ export const ElasticsearchProvider = ({
</QueryContext.Provider>
</DatasourceContext.Provider>
);
};
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Action } from '@reduxjs/toolkit';
import { defaultLogsAgg, defaultMetricAgg } from '@/queryDef';
import { defaultMetricAgg } from '@/queryDef';
import { ElasticsearchQuery, MetricAggregation } from '@/types';
import { removeEmpty } from '@/utils';
import { initExploreQuery, initQuery } from '../../state';
@@ -17,7 +17,11 @@ import {
toggleMetricVisibility,
} from './actions';

import { store } from "@/store"

export const reducer = (state: ElasticsearchQuery['metrics'], action: Action): ElasticsearchQuery['metrics'] => {
const defaultsMetricAggregation = store.getState().defaults.metricAggregation;

if (addMetric.match(action)) {
return [...state!, defaultMetricAgg(action.payload)];
}
@@ -55,7 +59,7 @@ export const reducer = (state: ElasticsearchQuery['metrics'], action: Action): E
return {
id: metric.id,
type: action.payload.type,
...metricAggregationConfig[action.payload.type].defaults,
...defaultsMetricAggregation[action.payload.type as keyof typeof defaultsMetricAggregation],
} as MetricAggregation;
});
}
@@ -164,7 +168,7 @@ export const reducer = (state: ElasticsearchQuery['metrics'], action: Action): E
if (state && state.length > 0) {
return state;
}
return [defaultLogsAgg('3')];
return [{ type: 'logs', id: '3', ...defaultsMetricAggregation.logs }];
}

return state;
1 change: 1 addition & 0 deletions src/components/QueryEditor/index.tsx
Original file line number Diff line number Diff line change
@@ -84,6 +84,7 @@ export const ElasticSearchQueryField = ({ value, onChange, onSubmit }: ElasticSe
};

const QueryEditorForm = ({ value, onRunQuery }: Props) => {

const dispatch = useDispatch();
const nextId = useNextId();
const styles = useStyles2(getStyles);
12 changes: 12 additions & 0 deletions src/configuration/ConfigEditor.tsx
Original file line number Diff line number Diff line change
@@ -5,6 +5,7 @@ import { QuickwitOptions } from '../quickwit';
import { coerceOptions } from './utils';
import { Divider } from '../components/Divider';
import { DataLinks } from './DataLinks';
import _ from 'lodash';

interface Props extends DataSourcePluginOptionsEditorProps<QuickwitOptions> {}

@@ -92,6 +93,17 @@ export const QuickwitDetails = ({ value, onChange }: DetailsProps) => {
/>
</InlineField>
</FieldSet>
<FieldSet label="Editor settings">
<InlineField label="Default logs limit" labelWidth={26} tooltip="The log level field must be a fast field">
<Input
id="quickwit_defaults_metricaggregation_logs_limit"
value={value.jsonData.queryEditorConfig?.defaults?.['metricAggregation.logs.settings.limit']}
onChange={(event) => onChange(_.merge(value, {jsonData:{queryEditorConfig:{defaults:{'metricAggregation.logs.settings.limit':event.currentTarget.value}}}}))}
placeholder="100"
width={40}
/>
</InlineField>
</FieldSet>
</div>
</>
);
5 changes: 5 additions & 0 deletions src/datasource/base.ts
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ import { SECOND } from 'utils/time';
import { GConstructor } from 'utils/mixins';
import { LuceneQuery } from '@/utils/lucene';
import { uidMaker } from "@/utils/uid"
import { DefaultsConfigOverrides } from 'store/defaults/conf';

export type BaseQuickwitDataSourceConstructor = GConstructor<BaseQuickwitDataSource>

@@ -59,6 +60,9 @@ export class BaseQuickwitDataSource
logMessageField?: string;
logLevelField?: string;
dataLinks: DataLinkConfig[];
queryEditorConfig?: {
defaults?: DefaultsConfigOverrides
};
languageProvider: ElasticsearchLanguageProvider;


@@ -73,6 +77,7 @@ export class BaseQuickwitDataSource
this.logMessageField = settingsData.logMessageField || '';
this.logLevelField = settingsData.logLevelField || '';
this.dataLinks = settingsData.dataLinks || [];
this.queryEditorConfig = settingsData.queryEditorConfig || {};
this.languageProvider = new ElasticsearchLanguageProvider(this);
this.annotations = {};
}
4 changes: 0 additions & 4 deletions src/queryDef.ts
Original file line number Diff line number Diff line change
@@ -31,10 +31,6 @@ export function defaultMetricAgg(id = '1'): MetricAggregation {
return { type: 'count', id };
}

export function defaultLogsAgg(id = '1'): MetricAggregation {
return { type: 'logs', id, ...metricAggregationConfig['logs'].defaults };
}

export function defaultBucketAgg(id = '1'): DateHistogram {
return { type: 'date_histogram', id, settings: { interval: 'auto' } };
}
4 changes: 4 additions & 0 deletions src/quickwit.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { DataSourceJsonData } from "@grafana/data";
import { DataLinkConfig } from "./types";
import { DefaultsConfigOverrides } from "store/defaults/conf";

export interface QuickwitOptions extends DataSourceJsonData {
timeField: string;
@@ -8,4 +9,7 @@ export interface QuickwitOptions extends DataSourceJsonData {
logLevelField?: string;
dataLinks?: DataLinkConfig[];
index: string;
queryEditorConfig?: {
defaults?: DefaultsConfigOverrides
}
}
40 changes: 40 additions & 0 deletions src/store/defaults/conf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
MetricAggregation,
MetricAggregationType,
Logs as SchemaLogs,
} from '@/dataquery.gen';
import { Logs, LogsSortDirection } from "@/types";

export type QuickwitMetricAggregationType = Extract<'count' | 'avg' | 'sum' | 'min' | 'max' | 'percentiles' | 'raw_data' | 'logs', MetricAggregationType >

export type MetricsDefaultSettings = Partial<{
[T in QuickwitMetricAggregationType]: Omit<Extract<Exclude<MetricAggregation,SchemaLogs>|Logs, { type: T }>, 'id' | 'type'>;
}>;


export const defaultMetricAggregationConfig: MetricsDefaultSettings = {
percentiles: {
settings: {
percents: ['25', '50', '75', '95', '99'],
},
},
raw_data: {
settings: {
size: '100',
},
},
logs: {
settings: {
limit: '100',
sortDirection:'desc' as LogsSortDirection
},
},
};

export const defaultConfig = {
metricAggregation: defaultMetricAggregationConfig
};

export type DefaultsConfig = typeof defaultConfig

export type DefaultsConfigOverrides = {[key: string]: any};
33 changes: 33 additions & 0 deletions src/store/defaults/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { defaultConfig , DefaultsConfigOverrides } from "./conf";
import _ from "lodash";

export const initialState = defaultConfig

const defaultsSlice = createSlice({
name: "defaults",
initialState: defaultConfig,
reducers: {
initDefaults(_s, action: PayloadAction<DefaultsConfigOverrides | undefined>) {
// Initialize from default state, dont keep the old one
let newState = _.cloneDeep(defaultConfig);
// override values with payload
if (action.payload) {
const overrides = action.payload;
for (const key in overrides) {
// XXX : this is very not type-safe. Can do better ?
const value = overrides[key];
newState = _.set(newState, key, value);
}
}
return newState
}
}
})

const {actions, reducer} = defaultsSlice
export const {
initDefaults,
} = actions

export default reducer;
13 changes: 13 additions & 0 deletions src/store/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { configureStore } from "@reduxjs/toolkit";
import defaultsReducer from "./defaults"

export const store = configureStore({
reducer: {
defaults: defaultsReducer,
}
})

// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch