feat: sort logs in ascending order (#2895)

* feat: sort logs in ascending order

Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: RubensRafael <rubensrafael2@live.com>

* refactor: requested changes

Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: niteshsingh1357 <niteshsingh1357@gmail.com>
Co-authored-by: RubensRafael <rubensrafael2@live.com>

* fix: lint

Co-authored-by: niteshsingh1357 <niteshsingh1357@gmail.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: RubensRafael <rubensrafael2@live.com>

* chore: removed the magic string

---------

Co-authored-by: gitstart <gitstart@users.noreply.github.com>
Co-authored-by: gitstart <gitstart@gitstart.com>
Co-authored-by: Nitesh Singh <nitesh.singh@gitstart.dev>
Co-authored-by: Rubens Rafael <70234898+RubensRafael@users.noreply.github.com>
Co-authored-by: Palash Gupta <palashgdev@gmail.com>
Co-authored-by: RubensRafael <rubensrafael2@live.com>
Co-authored-by: Vishal Sharma <makeavish786@gmail.com>
Co-authored-by: niteshsingh1357 <niteshsingh1357@gmail.com>
This commit is contained in:
GitStart 2023-06-29 09:41:49 +01:00 committed by GitHub
parent 1eabacbaf4
commit 64d4532a6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 125 additions and 13 deletions

View File

@ -14,4 +14,6 @@ export enum QueryParams {
resourceAttributes = 'resourceAttribute', resourceAttributes = 'resourceAttribute',
graphType = 'graphType', graphType = 'graphType',
widgetId = 'widgetId', widgetId = 'widgetId',
order = 'order',
q = 'q',
} }

View File

@ -7,6 +7,7 @@ import { getMinMax } from 'container/TopNav/AutoRefresh/config';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { Pagination } from 'hooks/queryPagination'; import { Pagination } from 'hooks/queryPagination';
import { FlatLogData } from 'lib/logs/flatLogData'; import { FlatLogData } from 'lib/logs/flatLogData';
import { OrderPreferenceItems } from 'pages/Logs/config';
import * as Papa from 'papaparse'; import * as Papa from 'papaparse';
import { memo, useCallback, useMemo } from 'react'; import { memo, useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
@ -31,6 +32,7 @@ function LogControls(): JSX.Element | null {
isLoading: isLogsLoading, isLoading: isLogsLoading,
isLoadingAggregate, isLoadingAggregate,
logs, logs,
order,
} = useSelector<AppState, ILogsReducer>((state) => state.logs); } = useSelector<AppState, ILogsReducer>((state) => state.logs);
const globalTime = useSelector<AppState, GlobalReducer>( const globalTime = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime, (state) => state.globalTime,
@ -160,6 +162,7 @@ function LogControls(): JSX.Element | null {
loading={isLoading} loading={isLoading}
size="small" size="small"
type="link" type="link"
disabled={order === OrderPreferenceItems.ASC}
onClick={handleGoToLatest} onClick={handleGoToLatest}
> >
<FastBackwardOutlined /> Go to latest <FastBackwardOutlined /> Go to latest

View File

@ -2,6 +2,7 @@ import { MinusCircleOutlined, PlusCircleOutlined } from '@ant-design/icons';
import { Button, Col, Popover } from 'antd'; import { Button, Col, Popover } from 'antd';
import getStep from 'lib/getStep'; import getStep from 'lib/getStep';
import { generateFilterQuery } from 'lib/logs/generateFilterQuery'; import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
import { getIdConditions } from 'pages/Logs/utils';
import { memo, useMemo } from 'react'; import { memo, useMemo } from 'react';
import { connect, useDispatch, useSelector } from 'react-redux'; import { connect, useDispatch, useSelector } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux'; import { bindActionCreators, Dispatch } from 'redux';
@ -45,6 +46,7 @@ function ActionItem({
idStart, idStart,
liveTail, liveTail,
idEnd, idEnd,
order,
} = useSelector<AppState, ILogsReducer>((store) => store.logs); } = useSelector<AppState, ILogsReducer>((store) => store.logs);
const dispatch = useDispatch<Dispatch<AppActions>>(); const dispatch = useDispatch<Dispatch<AppActions>>();
@ -72,11 +74,10 @@ function ActionItem({
q: updatedQueryString, q: updatedQueryString,
limit: logLinesPerPage, limit: logLinesPerPage,
orderBy: 'timestamp', orderBy: 'timestamp',
order: 'desc', order,
timestampStart: minTime, timestampStart: minTime,
timestampEnd: maxTime, timestampEnd: maxTime,
...(idStart ? { idGt: idStart } : {}), ...getIdConditions(idStart, idEnd, order),
...(idEnd ? { idLt: idEnd } : {}),
}); });
getLogsAggregate({ getLogsAggregate({
timestampStart: minTime, timestampStart: minTime,

View File

@ -2,6 +2,7 @@ import { Input, InputRef, Popover } from 'antd';
import useUrlQuery from 'hooks/useUrlQuery'; import useUrlQuery from 'hooks/useUrlQuery';
import getStep from 'lib/getStep'; import getStep from 'lib/getStep';
import debounce from 'lodash-es/debounce'; import debounce from 'lodash-es/debounce';
import { getIdConditions } from 'pages/Logs/utils';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { connect, useDispatch, useSelector } from 'react-redux'; import { connect, useDispatch, useSelector } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux'; import { bindActionCreators, Dispatch } from 'redux';
@ -33,7 +34,7 @@ function SearchFilter({
const [searchText, setSearchText] = useState(queryString); const [searchText, setSearchText] = useState(queryString);
const [showDropDown, setShowDropDown] = useState(false); const [showDropDown, setShowDropDown] = useState(false);
const searchRef = useRef<InputRef>(null); const searchRef = useRef<InputRef>(null);
const { logLinesPerPage, idEnd, idStart, liveTail } = useSelector< const { logLinesPerPage, idEnd, idStart, liveTail, order } = useSelector<
AppState, AppState,
ILogsReducer ILogsReducer
>((state) => state.logs); >((state) => state.logs);
@ -99,11 +100,10 @@ function SearchFilter({
q: customQuery, q: customQuery,
limit: logLinesPerPage, limit: logLinesPerPage,
orderBy: 'timestamp', orderBy: 'timestamp',
order: 'desc', order,
timestampStart: minTime, timestampStart: minTime,
timestampEnd: maxTime, timestampEnd: maxTime,
...(idStart ? { idGt: idStart } : {}), ...getIdConditions(idStart, idEnd, order),
...(idEnd ? { idLt: idEnd } : {}),
}); });
getLogsAggregate({ getLogsAggregate({
@ -128,6 +128,7 @@ function SearchFilter({
logLinesPerPage, logLinesPerPage,
globalTime, globalTime,
getLogsFields, getLogsFields,
order,
], ],
); );
@ -160,6 +161,7 @@ function SearchFilter({
dispatch, dispatch,
globalTime.maxTime, globalTime.maxTime,
globalTime.minTime, globalTime.minTime,
order,
]); ]);
const onPopOverChange = useCallback( const onPopOverChange = useCallback(

View File

@ -1,3 +1,4 @@
import { QueryParams } from 'constants/query';
import { getMinMax } from 'container/TopNav/AutoRefresh/config'; import { getMinMax } from 'container/TopNav/AutoRefresh/config';
import useUrlQuery from 'hooks/useUrlQuery'; import useUrlQuery from 'hooks/useUrlQuery';
import history from 'lib/history'; import history from 'lib/history';
@ -25,6 +26,7 @@ export function useSearchParser(): {
const dispatch = useDispatch<Dispatch<AppActions>>(); const dispatch = useDispatch<Dispatch<AppActions>>();
const { const {
searchFilter: { parsedQuery, queryString }, searchFilter: { parsedQuery, queryString },
order,
} = useSelector<AppState, ILogsReducer>((store) => store.logs); } = useSelector<AppState, ILogsReducer>((store) => store.logs);
const urlQuery = useUrlQuery(); const urlQuery = useUrlQuery();
@ -39,7 +41,7 @@ export function useSearchParser(): {
(updatedQueryString: string) => { (updatedQueryString: string) => {
history.replace({ history.replace({
pathname: history.location.pathname, pathname: history.location.pathname,
search: `?q=${updatedQueryString}`, search: `?${QueryParams.q}=${updatedQueryString}&${QueryParams.order}=${order}`,
}); });
const globalTime = getMinMax(selectedTime, minTime, maxTime); const globalTime = getMinMax(selectedTime, minTime, maxTime);

View File

@ -25,3 +25,24 @@ export const logsOptions = ['raw', 'table'];
export const defaultSelectStyle: CSSProperties = { export const defaultSelectStyle: CSSProperties = {
minWidth: '6rem', minWidth: '6rem',
}; };
export enum OrderPreferenceItems {
DESC = 'desc',
ASC = 'asc',
}
export const orderItems: OrderPreference[] = [
{
name: 'Descending',
enum: OrderPreferenceItems.DESC,
},
{
name: 'Ascending',
enum: OrderPreferenceItems.ASC,
},
];
export interface OrderPreference {
name: string;
enum: OrderPreferenceItems;
}

View File

@ -1,4 +1,5 @@
import { Button, Col, Divider, Popover, Row, Select, Space } from 'antd'; import { Button, Col, Divider, Popover, Row, Select, Space } from 'antd';
import { QueryParams } from 'constants/query';
import LogControls from 'container/LogControls'; import LogControls from 'container/LogControls';
import LogDetailedView from 'container/LogDetailedView'; import LogDetailedView from 'container/LogDetailedView';
import LogLiveTail from 'container/LogLiveTail'; import LogLiveTail from 'container/LogLiveTail';
@ -6,20 +7,31 @@ import LogsAggregate from 'container/LogsAggregate';
import LogsFilters from 'container/LogsFilters'; import LogsFilters from 'container/LogsFilters';
import LogsSearchFilter from 'container/LogsSearchFilter'; import LogsSearchFilter from 'container/LogsSearchFilter';
import LogsTable from 'container/LogsTable'; import LogsTable from 'container/LogsTable';
import history from 'lib/history';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import { useDispatch } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions'; import AppActions from 'types/actions';
import { SET_DETAILED_LOG_DATA } from 'types/actions/logs'; import { SET_DETAILED_LOG_DATA, SET_LOGS_ORDER } from 'types/actions/logs';
import { ILog } from 'types/api/logs/log'; import { ILog } from 'types/api/logs/log';
import { ILogsReducer } from 'types/reducer/logs';
import { defaultSelectStyle, logsOptions } from './config'; import {
defaultSelectStyle,
logsOptions,
orderItems,
OrderPreferenceItems,
} from './config';
import { useSelectedLogView } from './hooks'; import { useSelectedLogView } from './hooks';
import PopoverContent from './PopoverContent'; import PopoverContent from './PopoverContent';
import SpaceContainer from './styles'; import SpaceContainer from './styles';
function Logs(): JSX.Element { function Logs(): JSX.Element {
const dispatch = useDispatch<Dispatch<AppActions>>(); const dispatch = useDispatch<Dispatch<AppActions>>();
const { order } = useSelector<AppState, ILogsReducer>((store) => store.logs);
const location = useLocation();
const showExpandedLog = useCallback( const showExpandedLog = useCallback(
(logData: ILog) => { (logData: ILog) => {
@ -67,6 +79,16 @@ function Logs(): JSX.Element {
[handleViewModeOptionChange], [handleViewModeOptionChange],
); );
const handleChangeOrder = (value: OrderPreferenceItems): void => {
dispatch({
type: SET_LOGS_ORDER,
payload: value,
});
const params = new URLSearchParams(location.search);
params.set(QueryParams.order, value);
history.push({ search: params.toString() });
};
return ( return (
<> <>
<SpaceContainer <SpaceContainer
@ -101,6 +123,16 @@ function Logs(): JSX.Element {
<Button>Format</Button> <Button>Format</Button>
</Popover> </Popover>
)} )}
<Select
style={defaultSelectStyle}
defaultValue={order}
onChange={handleChangeOrder}
>
{orderItems.map((item) => (
<Select.Option key={item.enum}>{item.name}</Select.Option>
))}
</Select>
</Space> </Space>
</Col> </Col>

View File

@ -1,7 +1,29 @@
import { LogViewMode } from 'container/LogsTable'; import { LogViewMode } from 'container/LogsTable';
import { viewModeOptionList } from './config'; import { OrderPreferenceItems, viewModeOptionList } from './config';
export const isLogViewMode = (value: unknown): value is LogViewMode => export const isLogViewMode = (value: unknown): value is LogViewMode =>
typeof value === 'string' && typeof value === 'string' &&
viewModeOptionList.some((option) => option.key === value); viewModeOptionList.some((option) => option.key === value);
export const getIdConditions = (
idStart: string,
idEnd: string,
order: OrderPreferenceItems,
): Record<string, string> => {
const idConditions: Record<string, string> = {};
if (idStart && order === OrderPreferenceItems.ASC) {
idConditions.idLt = idStart;
} else if (idStart) {
idConditions.idGt = idStart;
}
if (idEnd && order === OrderPreferenceItems.ASC) {
idConditions.idGt = idEnd;
} else if (idEnd) {
idConditions.idLt = idEnd;
}
return idConditions;
};

View File

@ -1,4 +1,5 @@
import { parseQuery } from 'lib/logql'; import { parseQuery } from 'lib/logql';
import { OrderPreferenceItems } from 'pages/Logs/config';
import { import {
ADD_SEARCH_FIELD_QUERY_STRING, ADD_SEARCH_FIELD_QUERY_STRING,
FLUSH_LOGS, FLUSH_LOGS,
@ -17,6 +18,7 @@ import {
SET_LOG_LINES_PER_PAGE, SET_LOG_LINES_PER_PAGE,
SET_LOGS, SET_LOGS,
SET_LOGS_AGGREGATE_SERIES, SET_LOGS_AGGREGATE_SERIES,
SET_LOGS_ORDER,
SET_SEARCH_QUERY_PARSED_PAYLOAD, SET_SEARCH_QUERY_PARSED_PAYLOAD,
SET_SEARCH_QUERY_STRING, SET_SEARCH_QUERY_STRING,
SET_VIEW_MODE, SET_VIEW_MODE,
@ -49,6 +51,10 @@ const initialState: ILogsReducer = {
liveTailStartRange: 15, liveTailStartRange: 15,
selectedLogId: null, selectedLogId: null,
detailedLog: null, detailedLog: null,
order:
(new URLSearchParams(window.location.search).get(
'order',
) as ILogsReducer['order']) ?? OrderPreferenceItems.DESC,
}; };
export const LogsReducer = ( export const LogsReducer = (
@ -129,6 +135,17 @@ export const LogsReducer = (
logs: logsData, logs: logsData,
}; };
} }
case SET_LOGS_ORDER: {
const order = action.payload;
return {
...state,
order,
idStart: '',
idEnd: '',
};
}
case SET_LOG_LINES_PER_PAGE: { case SET_LOG_LINES_PER_PAGE: {
return { return {
...state, ...state,

View File

@ -1,6 +1,7 @@
import { LogViewMode } from 'container/LogsTable'; import { LogViewMode } from 'container/LogsTable';
import { Pagination } from 'hooks/queryPagination'; import { Pagination } from 'hooks/queryPagination';
import { ILogQLParsedQueryItem } from 'lib/logql/types'; import { ILogQLParsedQueryItem } from 'lib/logql/types';
import { OrderPreferenceItems } from 'pages/Logs/config';
import { IField, IFieldMoveToSelected, IFields } from 'types/api/logs/fields'; import { IField, IFieldMoveToSelected, IFields } from 'types/api/logs/fields';
import { TLogsLiveTailState } from 'types/api/logs/liveTail'; import { TLogsLiveTailState } from 'types/api/logs/liveTail';
import { ILog } from 'types/api/logs/log'; import { ILog } from 'types/api/logs/log';
@ -34,6 +35,7 @@ export const SET_LINES_PER_ROW = 'SET_LINES_PER_ROW';
export const SET_VIEW_MODE = 'SET_VIEW_MODE'; export const SET_VIEW_MODE = 'SET_VIEW_MODE';
export const UPDATE_SELECTED_FIELDS = 'LOGS_UPDATE_SELECTED_FIELDS'; export const UPDATE_SELECTED_FIELDS = 'LOGS_UPDATE_SELECTED_FIELDS';
export const UPDATE_INTERESTING_FIELDS = 'LOGS_UPDATE_INTERESTING_FIELDS'; export const UPDATE_INTERESTING_FIELDS = 'LOGS_UPDATE_INTERESTING_FIELDS';
export const SET_LOGS_ORDER = 'SET_LOGS_ORDER';
export interface GetFields { export interface GetFields {
type: typeof GET_FIELDS; type: typeof GET_FIELDS;
@ -141,6 +143,11 @@ export interface UpdateSelectedInterestFields {
}; };
} }
export interface SetLogsOrder {
type: typeof SET_LOGS_ORDER;
payload: OrderPreferenceItems;
}
export type LogsActions = export type LogsActions =
| GetFields | GetFields
| SetFields | SetFields
@ -164,4 +171,5 @@ export type LogsActions =
| SetLiveTailStartTime | SetLiveTailStartTime
| SetLinesPerRow | SetLinesPerRow
| SetViewMode | SetViewMode
| UpdateSelectedInterestFields; | UpdateSelectedInterestFields
| SetLogsOrder;

View File

@ -1,6 +1,7 @@
import { LogViewMode } from 'container/LogsTable'; import { LogViewMode } from 'container/LogsTable';
import { Pagination } from 'hooks/queryPagination'; import { Pagination } from 'hooks/queryPagination';
import { ILogQLParsedQueryItem } from 'lib/logql/types'; import { ILogQLParsedQueryItem } from 'lib/logql/types';
import { OrderPreferenceItems } from 'pages/Logs/config';
import { IFields } from 'types/api/logs/fields'; import { IFields } from 'types/api/logs/fields';
import { TLogsLiveTailState } from 'types/api/logs/liveTail'; import { TLogsLiveTailState } from 'types/api/logs/liveTail';
import { ILog } from 'types/api/logs/log'; import { ILog } from 'types/api/logs/log';
@ -25,6 +26,7 @@ export interface ILogsReducer {
detailedLog: null | ILog; detailedLog: null | ILog;
liveTail: TLogsLiveTailState; liveTail: TLogsLiveTailState;
liveTailStartRange: number; // time in minutes liveTailStartRange: number; // time in minutes
order: OrderPreferenceItems;
} }
export default ILogsReducer; export default ILogsReducer;