Skip to content

Improve log limits UX : select log end & raised limit #97

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 3 commits into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
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
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