Skip to content

Commit

Permalink
Merge pull request #97 from quickwit-oss/ddelemeny/log-end-selector
Browse files Browse the repository at this point in the history
Improve log limits UX : select log end & raised limit
  • Loading branch information
fmassot authored Mar 10, 2024
2 parents 9f7e8f0 + 591421b commit d4ddf3a
Show file tree
Hide file tree
Showing 10 changed files with 124 additions and 85 deletions.
4 changes: 2 additions & 2 deletions src/LogContext/LogContextProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
rangeUtil,
} from '@grafana/data';

import { ElasticsearchQuery, Logs} from '../types';
import { ElasticsearchQuery, Logs, LogsSortDirection} from '../types';

import { LogContextUI } from './components/LogContextUI';

Expand Down Expand Up @@ -64,7 +64,7 @@ export class LogContextProvider {
settings: {
limit: options?.limit ? options?.limit.toString() : '10',
// Sorting of results in the context query
sortDirection: direction === LogRowContextQueryDirection.Backward ? 'desc' : 'asc',
sortDirection: direction === LogRowContextQueryDirection.Backward ? LogsSortDirection.DESC : LogsSortDirection.ASC,
// Used to get the next log lines before/after the current log line using sort field of selected log line
searchAfter: searchAfter,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { css } from "@emotion/css";
import { RadioButtonGroup } from "@grafana/ui";
import React from "react";
import { Logs, LogsSortDirection, LogsEnd } from "types";
import { SettingField } from "./SettingField";
import { useDispatch } from "hooks/useStatelessReducer";
import { changeMetricSetting } from '../state/actions';
import { metricAggregationConfig } from "../utils";


// type LogsSortDirection = 'asc' | 'desc'

interface Props { metric: Logs }

export const LogsSettingsEditor = ({metric}: Props)=>{
const config = metricAggregationConfig['logs']
const dispatch = useDispatch();
return (
<div className={css({display:"inline-flex", justifyContent:"start", gap:"4px", height:"100%"})} >
<RadioButtonGroup
className={css({height:"100%"})}
options={Object.values(LogsSortDirection).map((v)=>({label:LogsEnd[v], value:v}))}
value={metric.settings?.sortDirection || config.defaults.settings?.sortDirection }
onChange={(v)=>{ dispatch(
changeMetricSetting({ metric, settingName: 'sortDirection', newValue: v })
)}}/>
<SettingField label="Limit" metric={metric} settingName="limit" placeholder={config.defaults.settings?.limit} />
</div>
)

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ import { MovingAverageSettingsEditor } from './MovingAverageSettingsEditor';
import { SettingField } from './SettingField';
import { TopMetricsSettingsEditor } from './TopMetricsSettingsEditor';
import { useDescription } from './useDescription';
import { LogsSettingsEditor } from './LogsSettingsEditor';

// TODO: Move this somewhere and share it with BucketsAggregation Editor
const inlineFieldProps: Partial<ComponentProps<typeof InlineField>> = {
export const inlineFieldProps: Partial<ComponentProps<typeof InlineField>> = {
labelWidth: 16,
};

Expand Down Expand Up @@ -84,7 +85,9 @@ export const SettingsEditor = ({ metric, previousMetrics }: Props) => {
</InlineField>
)}

{metric.type === 'logs' && <SettingField label="Limit" metric={metric} settingName="limit" placeholder="100" />}
{metric.type === 'logs' && (
<LogsSettingsEditor metric={metric}></LogsSettingsEditor>
)}

{metric.type === 'cardinality' && (
<SettingField label="Precision Threshold" metric={metric} settingName="precision_threshold" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { extendedStats } from 'queryDef';
import { MetricAggregation } from '@/types';
import { Logs, LogsEnd, MetricAggregation } from '@/types';
import { metricAggregationConfig } from '../utils';

const hasValue = (value: string) => (object: { value: string }) => object.value === value;

Expand Down Expand Up @@ -34,6 +35,14 @@ export const useDescription = (metric: MetricAggregation): string => {
return `Size: ${size}`;
}

case 'logs': {
const config = metricAggregationConfig['logs']
const logsmetric: Logs = metric
const sort = LogsEnd[logsmetric.settings?.sortDirection || config.defaults.settings!.sortDirection!]
const limit = logsmetric.settings?.limit || 100
return `${sort} ${limit}`
}

default:
return 'Options';
}
Expand Down
12 changes: 8 additions & 4 deletions src/components/QueryEditor/MetricAggregationsEditor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import React from 'react';
import { useDispatch } from '@/hooks/useStatelessReducer';
import { IconButton } from '../../IconButton';
import { useQuery } from '../ElasticsearchQueryContext';
import { QueryEditorRow } from '../QueryEditorRow';
import { QueryEditorRow, QueryEditorBaseRow } from '../QueryEditorRow';

import { MetricAggregation } from '@/types';
import { MetricEditor } from './MetricEditor';
import { addMetric, removeMetric, toggleMetricVisibility } from './state/actions';
import { metricAggregationConfig } from './utils';
import { QueryEditorSpecialMetricRow } from '../QueryEditorSpecialMetricRow';
import { SettingsEditor } from './SettingsEditor';

interface Props {
nextId: MetricAggregation['id'];
Expand All @@ -25,9 +25,13 @@ export const MetricAggregationsEditor = ({ nextId }: Props) => {
{metrics?.map((metric, index) => {
switch (metric.type) {
case 'logs':
return <QueryEditorSpecialMetricRow key={`${metric.type}-${metric.id}`} name="Logs" metric={metric} />;
return <QueryEditorBaseRow key={`${metric.type}-${metric.id}`} label="Logs">
<SettingsEditor metric={metric} previousMetrics={[]} />
</QueryEditorBaseRow>;
case 'raw_data':
return <QueryEditorSpecialMetricRow key={`${metric.type}-${metric.id}`} name="Raw Data" metric={metric} />;
return <QueryEditorBaseRow key={`${metric.type}-${metric.id}`} label="Raw Data">
<SettingsEditor metric={metric} previousMetrics={[]} />
</QueryEditorBaseRow>;
default:
return (
<QueryEditorRow
Expand Down
5 changes: 3 additions & 2 deletions src/components/QueryEditor/MetricAggregationsEditor/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { MetricsConfiguration, MetricAggregation, PipelineMetricAggregationType } from '@/types';
import { MetricsConfiguration, MetricAggregation, PipelineMetricAggregationType, LogsSortDirection } from '@/types';

import {
defaultPipelineVariable,
Expand Down Expand Up @@ -243,7 +243,8 @@ export const metricAggregationConfig: MetricsConfiguration = {
hasMeta: false,
defaults: {
settings: {
limit: '100',
limit: '1000',
sortDirection:'desc' as LogsSortDirection
},
},
},
Expand Down
91 changes: 51 additions & 40 deletions src/components/QueryEditor/QueryEditorRow.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,79 @@
import { css } from '@emotion/css';
import { noop } from 'lodash';
import React, { PropsWithChildren } from 'react';
import React, { PropsWithChildren, ReactNode } from 'react';

import { GrafanaTheme2 } from '@grafana/data';
import { IconButton, InlineFieldRow, InlineLabel, InlineSegmentGroup, useStyles2 } from '@grafana/ui';

interface Props {
label: string;
const getStyles = (theme: GrafanaTheme2) => {
return {
iconWrapper: css`
display: flex;
`,
icon: css`
color: ${theme.colors.text.secondary};
margin-left: ${theme.spacing(0.25)};
`,
};
};

interface BaseRowProps { label: ReactNode; };
export const QueryEditorBaseRow = ({ label, children }: PropsWithChildren<BaseRowProps>) => {
return (
<InlineFieldRow>
<InlineSegmentGroup>
<InlineLabel width={17} as="div">
{label}
</InlineLabel>
</InlineSegmentGroup>
{children}
</InlineFieldRow>
);
};

interface RowProps extends BaseRowProps {
onRemoveClick?: false | (() => void);
onHideClick?: false | (() => void);
hidden?: boolean;
}

export const QueryEditorRow = ({
children,
label,
onRemoveClick,
onHideClick,
hidden = false,
}: PropsWithChildren<Props>) => {
}: PropsWithChildren<RowProps>) => {
const styles = useStyles2(getStyles);

return (
<InlineFieldRow>
<InlineSegmentGroup>
<InlineLabel width={17} as="div">
<span>{label}</span>
<span className={styles.iconWrapper}>
{onHideClick && (
<IconButton
name={hidden ? 'eye-slash' : 'eye'}
onClick={onHideClick}
size="sm"
aria-pressed={hidden}
aria-label="hide metric"
className={styles.icon}
type="button"
/>
)}
<QueryEditorBaseRow label={(<>
<span>{label}</span>
<span className={styles.iconWrapper}>
{onHideClick && (
<IconButton
name="trash-alt"
name={hidden ? 'eye-slash' : 'eye'}
onClick={onHideClick}
size="sm"
aria-pressed={hidden}
aria-label="hide metric"
className={styles.icon}
onClick={onRemoveClick || noop}
disabled={!onRemoveClick}
aria-label="remove metric"
type="button"
/>
</span>
</InlineLabel>
</InlineSegmentGroup>
)}
<IconButton
name="trash-alt"
size="sm"
className={styles.icon}
onClick={onRemoveClick || noop}
disabled={!onRemoveClick}
aria-label="remove metric"
type="button"
/>
</span>
</>)}>
{children}
</InlineFieldRow>
</QueryEditorBaseRow>
);
};

const getStyles = (theme: GrafanaTheme2) => {
return {
iconWrapper: css`
display: flex;
`,
icon: css`
color: ${theme.colors.text.secondary};
margin-left: ${theme.spacing(0.25)};
`,
};
};

30 changes: 0 additions & 30 deletions src/components/QueryEditor/QueryEditorSpecialMetricRow.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/queryDef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export function defaultMetricAgg(id = '1'): MetricAggregation {
}

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

export function defaultBucketAgg(id = '1'): DateHistogram {
Expand Down
16 changes: 13 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,24 @@ import {
} from './dataquery.gen';

export * from './dataquery.gen';
export { Elasticsearch as ElasticsearchQuery } from './dataquery.gen';
export { type Elasticsearch as ElasticsearchQuery } from './dataquery.gen';

// We want to extend the settings of the Logs query with additional properties that
// are not part of the schema. This is a workaround, because exporting LogsSettings
// from dataquery.gen.ts and extending that produces error in SettingKeyOf.
export enum LogsSortDirection {
DESC = 'desc',
ASC = 'asc',
}

export const LogsEnd = {
[LogsSortDirection.ASC]: 'Head',
[LogsSortDirection.DESC]: 'Tail'
}

type ExtendedLogsSettings = SchemaLogs['settings'] & {
searchAfter?: unknown[];
sortDirection?: 'asc' | 'desc';
sortDirection?: LogsSortDirection;
};

export interface Logs extends SchemaLogs {
Expand Down Expand Up @@ -65,7 +75,7 @@ interface MetricConfiguration<T extends MetricAggregationType> {
impliedQueryType: QueryType;
hasSettings: boolean;
hasMeta: boolean;
defaults: Omit<Extract<MetricAggregation, { type: T }>, 'id' | 'type'>;
defaults: Omit<Extract<Exclude<MetricAggregation,SchemaLogs>|Logs, { type: T }>, 'id' | 'type'>;
}

type BucketConfiguration<T extends BucketAggregationType> = {
Expand Down

0 comments on commit d4ddf3a

Please sign in to comment.