mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Explore: Save log results sort order (#45423)
* Explore: Save log results sort order * Change to radio group, add test and make language consistent with logs plugin
This commit is contained in:
parent
434697e0fd
commit
31090a75eb
87
public/app/features/explore/Logs.test.tsx
Normal file
87
public/app/features/explore/Logs.test.tsx
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render, screen, fireEvent } from '@testing-library/react';
|
||||||
|
import { LoadingState, LogLevel, LogRowModel, MutableDataFrame, toUtc } from '@grafana/data';
|
||||||
|
|
||||||
|
import { Logs } from './Logs';
|
||||||
|
|
||||||
|
describe('Logs', () => {
|
||||||
|
const setup = (propOverrides?: object) => {
|
||||||
|
const rows = [
|
||||||
|
makeLog({ uid: '1', timeEpochMs: 1 }),
|
||||||
|
makeLog({ uid: '2', timeEpochMs: 2 }),
|
||||||
|
makeLog({ uid: '3', timeEpochMs: 3 }),
|
||||||
|
];
|
||||||
|
|
||||||
|
return render(
|
||||||
|
<Logs
|
||||||
|
logRows={rows}
|
||||||
|
timeZone={'utc'}
|
||||||
|
width={50}
|
||||||
|
loading={false}
|
||||||
|
loadingState={LoadingState.Done}
|
||||||
|
absoluteRange={{
|
||||||
|
from: toUtc('2019-01-01 10:00:00').valueOf(),
|
||||||
|
to: toUtc('2019-01-01 16:00:00').valueOf(),
|
||||||
|
}}
|
||||||
|
addResultsToCache={() => {}}
|
||||||
|
onChangeTime={() => {}}
|
||||||
|
clearCache={() => {}}
|
||||||
|
getFieldLinks={() => {
|
||||||
|
return [];
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers('modern');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render logs', () => {
|
||||||
|
setup();
|
||||||
|
const logsSection = screen.getByTestId('logRows');
|
||||||
|
let logRows = logsSection.querySelectorAll('tr');
|
||||||
|
expect(logRows.length).toBe(3);
|
||||||
|
expect(logRows[0].textContent).toContain('log message 3');
|
||||||
|
expect(logRows[2].textContent).toContain('log message 1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should flip the order', () => {
|
||||||
|
setup();
|
||||||
|
const oldestFirstSelection = screen.getByLabelText('Oldest first');
|
||||||
|
fireEvent.click(oldestFirstSelection);
|
||||||
|
jest.advanceTimersByTime(1000);
|
||||||
|
const logsSection = screen.getByTestId('logRows');
|
||||||
|
let logRows = logsSection.querySelectorAll('tr');
|
||||||
|
expect(logRows.length).toBe(3);
|
||||||
|
expect(logRows[0].textContent).toContain('log message 1');
|
||||||
|
expect(logRows[2].textContent).toContain('log message 3');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const makeLog = (overrides: Partial<LogRowModel>): LogRowModel => {
|
||||||
|
const uid = overrides.uid || '1';
|
||||||
|
const entry = `log message ${uid}`;
|
||||||
|
return {
|
||||||
|
uid,
|
||||||
|
entryFieldIndex: 0,
|
||||||
|
rowIndex: 0,
|
||||||
|
dataFrame: new MutableDataFrame(),
|
||||||
|
logLevel: LogLevel.debug,
|
||||||
|
entry,
|
||||||
|
hasAnsi: false,
|
||||||
|
hasUnescapedContent: false,
|
||||||
|
labels: {},
|
||||||
|
raw: entry,
|
||||||
|
timeFromNow: '',
|
||||||
|
timeEpochMs: 1,
|
||||||
|
timeEpochNs: '1000000',
|
||||||
|
timeLocal: '',
|
||||||
|
timeUtc: '',
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
};
|
@ -43,6 +43,7 @@ const SETTINGS_KEYS = {
|
|||||||
showTime: 'grafana.explore.logs.showTime',
|
showTime: 'grafana.explore.logs.showTime',
|
||||||
wrapLogMessage: 'grafana.explore.logs.wrapLogMessage',
|
wrapLogMessage: 'grafana.explore.logs.wrapLogMessage',
|
||||||
prettifyLogMessage: 'grafana.explore.logs.prettifyLogMessage',
|
prettifyLogMessage: 'grafana.explore.logs.prettifyLogMessage',
|
||||||
|
logsSortOrder: 'grafana.explore.logs.sortOrder',
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Props extends Themeable2 {
|
interface Props extends Themeable2 {
|
||||||
@ -96,7 +97,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
|||||||
prettifyLogMessage: store.getBool(SETTINGS_KEYS.prettifyLogMessage, false),
|
prettifyLogMessage: store.getBool(SETTINGS_KEYS.prettifyLogMessage, false),
|
||||||
dedupStrategy: LogsDedupStrategy.none,
|
dedupStrategy: LogsDedupStrategy.none,
|
||||||
hiddenLogLevels: [],
|
hiddenLogLevels: [],
|
||||||
logsSortOrder: null,
|
logsSortOrder: store.get(SETTINGS_KEYS.logsSortOrder) || LogsSortOrder.Descending,
|
||||||
isFlipping: false,
|
isFlipping: false,
|
||||||
showDetectedFields: [],
|
showDetectedFields: [],
|
||||||
forceEscape: false,
|
forceEscape: false,
|
||||||
@ -117,10 +118,10 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
|||||||
// we are using setTimeout here to make sure that disabled button is rendered before the rendering of reordered logs
|
// we are using setTimeout here to make sure that disabled button is rendered before the rendering of reordered logs
|
||||||
this.flipOrderTimer = window.setTimeout(() => {
|
this.flipOrderTimer = window.setTimeout(() => {
|
||||||
this.setState((prevState) => {
|
this.setState((prevState) => {
|
||||||
if (prevState.logsSortOrder === null || prevState.logsSortOrder === LogsSortOrder.Descending) {
|
const newSortOrder =
|
||||||
return { logsSortOrder: LogsSortOrder.Ascending };
|
prevState.logsSortOrder === LogsSortOrder.Descending ? LogsSortOrder.Ascending : LogsSortOrder.Descending;
|
||||||
}
|
store.set(SETTINGS_KEYS.logsSortOrder, newSortOrder);
|
||||||
return { logsSortOrder: LogsSortOrder.Descending };
|
return { logsSortOrder: newSortOrder };
|
||||||
});
|
});
|
||||||
}, 0);
|
}, 0);
|
||||||
this.cancelFlippingTimer = window.setTimeout(() => this.setState({ isFlipping: false }), 1000);
|
this.cancelFlippingTimer = window.setTimeout(() => this.setState({ isFlipping: false }), 1000);
|
||||||
@ -291,7 +292,6 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
|||||||
const { dedupedRows, dedupCount } = this.dedupRows(filteredLogs, dedupStrategy);
|
const { dedupedRows, dedupCount } = this.dedupRows(filteredLogs, dedupStrategy);
|
||||||
|
|
||||||
const scanText = scanRange ? `Scanning ${rangeUtil.describeTimeRange(scanRange)}` : 'Scanning...';
|
const scanText = scanRange ? `Scanning ${rangeUtil.describeTimeRange(scanRange)}` : 'Scanning...';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{logsSeries && logsSeries.length ? (
|
{logsSeries && logsSeries.length ? (
|
||||||
@ -366,16 +366,26 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
|||||||
</InlineField>
|
</InlineField>
|
||||||
</InlineFieldRow>
|
</InlineFieldRow>
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<InlineField label="Display results" className={styles.horizontalInlineLabel} transparent>
|
||||||
variant="secondary"
|
<RadioButtonGroup
|
||||||
disabled={isFlipping}
|
disabled={isFlipping}
|
||||||
title={logsSortOrder === LogsSortOrder.Ascending ? 'Change to newest first' : 'Change to oldest first'}
|
options={[
|
||||||
aria-label="Flip results order"
|
{
|
||||||
className={styles.headerButton}
|
label: 'Newest first',
|
||||||
onClick={this.onChangeLogsSortOrder}
|
value: LogsSortOrder.Descending,
|
||||||
>
|
description: 'Show results newest to oldest',
|
||||||
{isFlipping ? 'Flipping...' : 'Flip results order'}
|
},
|
||||||
</Button>
|
{
|
||||||
|
label: 'Oldest first',
|
||||||
|
value: LogsSortOrder.Ascending,
|
||||||
|
description: 'Show results oldest to newest',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
value={logsSortOrder}
|
||||||
|
onChange={this.onChangeLogsSortOrder}
|
||||||
|
className={styles.radioButtons}
|
||||||
|
/>
|
||||||
|
</InlineField>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<LogsMetaRow
|
<LogsMetaRow
|
||||||
@ -390,7 +400,7 @@ class UnthemedLogs extends PureComponent<Props, State> {
|
|||||||
clearDetectedFields={this.clearDetectedFields}
|
clearDetectedFields={this.clearDetectedFields}
|
||||||
/>
|
/>
|
||||||
<div className={styles.logsSection}>
|
<div className={styles.logsSection}>
|
||||||
<div className={styles.logRows}>
|
<div className={styles.logRows} data-testid="logRows">
|
||||||
<LogRows
|
<LogRows
|
||||||
logRows={logRows}
|
logRows={logRows}
|
||||||
deduplicatedRows={dedupedRows}
|
deduplicatedRows={dedupedRows}
|
||||||
|
@ -74,8 +74,8 @@ export const plugin = new PanelPlugin<Options>(LogsPanel)
|
|||||||
description: '',
|
description: '',
|
||||||
settings: {
|
settings: {
|
||||||
options: [
|
options: [
|
||||||
{ value: LogsSortOrder.Descending, label: 'Descending' },
|
{ value: LogsSortOrder.Descending, label: 'Newest first' },
|
||||||
{ value: LogsSortOrder.Ascending, label: 'Ascending' },
|
{ value: LogsSortOrder.Ascending, label: 'Oldest first' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
defaultValue: LogsSortOrder.Descending,
|
defaultValue: LogsSortOrder.Descending,
|
||||||
|
Loading…
Reference in New Issue
Block a user