feat: global time is updated (#2013)

This commit is contained in:
Palash Gupta 2023-02-02 11:12:12 +05:30 committed by GitHub
parent 48659a2957
commit 17f32e9765
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 429 additions and 224 deletions

View File

@ -2,7 +2,9 @@ import { Button, Popover } from 'antd';
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
import React, { memo, useCallback, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { SET_SEARCH_QUERY_STRING } from 'types/actions/logs';
import { ILogsReducer } from 'types/reducer/logs';
@ -14,7 +16,7 @@ function AddToQueryHOC({
const {
searchFilter: { queryString },
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
const dispatch = useDispatch();
const dispatch = useDispatch<Dispatch<AppActions>>();
const generatedQuery = useMemo(
() => generateFilterQuery({ fieldKey, fieldValue, type: 'IN' }),
@ -31,7 +33,9 @@ function AddToQueryHOC({
}
dispatch({
type: SET_SEARCH_QUERY_STRING,
payload: updatedQueryString,
payload: {
searchQueryString: updatedQueryString,
},
});
}, [dispatch, generatedQuery, queryString]);

View File

@ -101,15 +101,9 @@ function LogItem({ logData }: LogItemProps): JSX.Element {
{'{'}
<LogContainer>
<>
<LogGeneralField
fieldKey="log"
fieldValue={flattenLogData.body as never}
/>
<LogGeneralField fieldKey="log" fieldValue={flattenLogData.body} />
{flattenLogData.stream && (
<LogGeneralField
fieldKey="stream"
fieldValue={flattenLogData.stream as never}
/>
<LogGeneralField fieldKey="stream" fieldValue={flattenLogData.stream} />
)}
<LogGeneralField
fieldKey="timestamp"

View File

@ -37,6 +37,7 @@ const themeColors = {
matterhornGrey: '#555555',
whiteCream: '#ffffffd5',
black: '#000000',
lightgrey: '#ddd',
};
export { themeColors };

View File

@ -4,6 +4,8 @@ import {
RightOutlined,
} from '@ant-design/icons';
import { Button, Divider, Select } from 'antd';
import { getGlobalTime } from 'container/LogsSearchFilter/utils';
import { getMinMax } from 'container/TopNav/AutoRefresh/config';
import React, { memo, useMemo } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
@ -13,6 +15,7 @@ import {
RESET_ID_START_AND_END,
SET_LOG_LINES_PER_PAGE,
} from 'types/actions/logs';
import { GlobalReducer } from 'types/reducer/globalTime';
import { ILogsReducer } from 'types/reducer/logs';
import { ITEMS_PER_PAGE_OPTIONS } from './config';
@ -28,18 +31,33 @@ function LogControls(): JSX.Element | null {
isLoadingAggregate,
logs,
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
const globalTime = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const dispatch = useDispatch();
const handleLogLinesPerPageChange = (e: number): void => {
dispatch({
type: SET_LOG_LINES_PER_PAGE,
payload: e,
payload: {
logLinesPerPage: e,
},
});
};
const handleGoToLatest = (): void => {
const { maxTime, minTime } = getMinMax(
globalTime.selectedTime,
globalTime.minTime,
globalTime.maxTime,
);
dispatch({
type: RESET_ID_START_AND_END,
payload: getGlobalTime(globalTime.selectedTime, {
maxTime,
minTime,
}),
});
};

View File

@ -4,7 +4,7 @@ import getStep from 'lib/getStep';
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
import React, { memo, useMemo } from 'react';
import { connect, useDispatch, useSelector } from 'react-redux';
import { bindActionCreators } from 'redux';
import { bindActionCreators, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { getLogs } from 'store/actions/logs/getLogs';
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
@ -46,7 +46,7 @@ function ActionItem({
liveTail,
idEnd,
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
const dispatch = useDispatch();
const dispatch = useDispatch<Dispatch<AppActions>>();
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
@ -62,7 +62,9 @@ function ActionItem({
}
dispatch({
type: SET_SEARCH_QUERY_STRING,
payload: updatedQueryString,
payload: {
searchQueryString: updatedQueryString,
},
});
if (liveTail === 'STOPPED') {

View File

@ -64,7 +64,6 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
return (
<AddToQueryHOC fieldKey={fieldKey[0]} fieldValue={flattenLogData[field]}>
{' '}
{renderedField}
</AddToQueryHOC>
);

View File

@ -1,20 +1,17 @@
import { LoadingOutlined } from '@ant-design/icons';
import { Button, Popover, Spin } from 'antd';
import { Button, Popover, Spin, Typography } from 'antd';
import { useIsDarkMode } from 'hooks/useDarkMode';
import React, { useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import {
IField,
IInterestingFields,
ISelectedFields,
} from 'types/api/logs/fields';
import { ICON_STYLE } from './config';
import { Field } from './styles';
interface FieldItemProps {
name: string;
buttonIcon: React.ReactNode;
buttonOnClick: (arg0: Record<string, unknown>) => void;
fieldData: Record<string, never>;
fieldIndex: number;
isLoading: boolean;
iconHoverText: string;
}
export function FieldItem({
function FieldItem({
name,
buttonIcon,
buttonOnClick,
@ -23,33 +20,65 @@ export function FieldItem({
isLoading,
iconHoverText,
}: FieldItemProps): JSX.Element {
const [isHovered, setIsHovered] = useState(false);
const [isHovered, setIsHovered] = useState<boolean>(false);
const isDarkMode = useIsDarkMode();
const onClickHandler = useCallback(() => {
if (!isLoading && buttonOnClick) buttonOnClick({ fieldData, fieldIndex });
}, [buttonOnClick, fieldData, fieldIndex, isLoading]);
const renderContent = useMemo(() => {
if (isLoading) {
return <Spin spinning size="small" indicator={<LoadingOutlined spin />} />;
}
if (isHovered) {
return (
<Field
onMouseEnter={(): void => {
setIsHovered(true);
}}
onMouseLeave={(): void => setIsHovered(false)}
isDarkMode={isDarkMode}
>
<span>{name}</span>
{isLoading ? (
<Spin spinning size="small" indicator={<LoadingOutlined spin />} />
) : (
isHovered &&
buttonOnClick && (
<Popover content={<span>{iconHoverText}</span>}>
<Popover content={<Typography>{iconHoverText}</Typography>}>
<Button
type="text"
size="small"
type="text"
icon={buttonIcon}
onClick={(): void => buttonOnClick({ fieldData, fieldIndex })}
style={{ color: 'inherit', padding: 0, height: '1rem', width: '1rem' }}
onClick={onClickHandler}
/>
</Popover>
)
)}
);
}
return null;
}, [buttonIcon, iconHoverText, isHovered, isLoading, onClickHandler]);
const onMouseHoverHandler = useCallback(
(value: boolean) => (): void => {
setIsHovered(value);
},
[],
);
return (
<Field
onMouseEnter={onMouseHoverHandler(true)}
onMouseLeave={onMouseHoverHandler(false)}
isDarkMode={isDarkMode}
>
<Typography style={ICON_STYLE.PLUS}>{name}</Typography>
{renderContent}
</Field>
);
}
interface FieldItemProps {
name: string;
buttonIcon: React.ReactNode;
buttonOnClick: (props: {
fieldData: IInterestingFields | ISelectedFields;
fieldIndex: number;
}) => void;
fieldData: IField;
fieldIndex: number;
isLoading: boolean;
iconHoverText: string;
}
export default FieldItem;

View File

@ -0,0 +1,8 @@
import { blue, red } from '@ant-design/colors';
export const RESTRICTED_SELECTED_FIELDS = ['timestamp', 'id'];
export const ICON_STYLE = {
PLUS: { color: blue[5] },
CLOSE: { color: red[5] },
};

View File

@ -1,27 +1,19 @@
/* eslint-disable react/no-array-index-key */
import { red } from '@ant-design/colors';
import { CloseOutlined, PlusCircleFilled } from '@ant-design/icons';
import { Input } from 'antd';
import AddToSelectedFields from 'api/logs/AddToSelectedField';
import RemoveSelectedField from 'api/logs/RemoveFromSelectedField';
import CategoryHeading from 'components/Logs/CategoryHeading';
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
import React, { memo, useState } from 'react';
import { connect, useSelector } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { GetLogsFields } from 'store/actions/logs/getFields';
import React, { useCallback, useState } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import { IInterestingFields, ISelectedFields } from 'types/api/logs/fields';
import { ILogsReducer } from 'types/reducer/logs';
import { FieldItem } from './FieldItem';
import { ICON_STYLE } from './config';
import FieldItem from './FieldItem';
import { CategoryContainer, Container, FieldContainer } from './styles';
import { IHandleInterestProps, IHandleRemoveInterestProps } from './types';
import { onHandleAddInterest, onHandleRemoveInterest } from './utils';
const RESTRICTED_SELECTED_FIELDS = ['timestamp', 'id'];
function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
function LogsFilters(): JSX.Element {
const {
fields: { interesting, selected },
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
@ -36,57 +28,40 @@ function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
setFilterValuesInput((e.target as HTMLInputElement).value);
};
const handleAddInterestingToSelected = async ({
const onHandleAddSelectedToInteresting = useCallback(
({ fieldData, fieldIndex }: IHandleInterestProps) => (): Promise<void> =>
onHandleAddInterest({
fieldData,
fieldIndex,
}: {
fieldData: IInterestingFields;
fieldIndex: number;
}): Promise<void> => {
setInterestingFieldLoading((prevState: number[]) => {
prevState.push(fieldIndex);
return [...prevState];
});
await AddToSelectedFields({
...fieldData,
selected: true,
});
getLogsFields();
setInterestingFieldLoading(
interestingFieldLoading.filter((e) => e !== fieldIndex),
interesting,
interestingFieldLoading,
setInterestingFieldLoading,
selected,
}),
[interesting, interestingFieldLoading, selected],
);
};
const handleRemoveSelectedField = async ({
const onHandleRemoveSelected = useCallback(
({
fieldData,
fieldIndex,
}: {
fieldData: ISelectedFields;
fieldIndex: number;
}): Promise<void> => {
setSelectedFieldLoading((prevState) => {
prevState.push(fieldIndex);
return [...prevState];
});
await RemoveSelectedField({
...fieldData,
selected: false,
});
getLogsFields();
setSelectedFieldLoading(
interestingFieldLoading.filter((e) => e !== fieldIndex),
}: IHandleRemoveInterestProps) => (): Promise<void> =>
onHandleRemoveInterest({
fieldData,
fieldIndex,
interesting,
interestingFieldLoading,
selected,
setSelectedFieldLoading,
}),
[interesting, interestingFieldLoading, selected, setSelectedFieldLoading],
);
};
return (
<Container flex="450px">
<Input
placeholder="Filter Values"
onInput={handleSearch}
style={{ width: '100%' }}
value={filterValuesInput}
onChange={handleSearch}
/>
@ -98,15 +73,15 @@ function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
.map((field, idx) => (
<FieldItem
key={`${JSON.stringify(field)}-${idx}`}
key={`${JSON.stringify(field)}`}
name={field.name}
fieldData={field as never}
fieldData={field}
fieldIndex={idx}
buttonIcon={<CloseOutlined style={{ color: red[5] }} />}
buttonOnClick={
(!RESTRICTED_SELECTED_FIELDS.includes(field.name) &&
handleRemoveSelectedField) as never
}
buttonIcon={<CloseOutlined style={ICON_STYLE.CLOSE} />}
buttonOnClick={onHandleRemoveSelected({
fieldData: field,
fieldIndex: idx,
})}
isLoading={selectedFieldLoading.includes(idx)}
iconHoverText="Remove from Selected Fields"
/>
@ -120,33 +95,23 @@ function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
.map((field, idx) => (
<FieldItem
key={`${JSON.stringify(field)}-${idx}`}
key={`${JSON.stringify(field)}`}
name={field.name}
fieldData={field as never}
fieldData={field}
fieldIndex={idx}
buttonIcon={<PlusCircleFilled />}
buttonOnClick={handleAddInterestingToSelected as never}
buttonIcon={<PlusCircleFilled style={ICON_STYLE.PLUS} />}
buttonOnClick={onHandleAddSelectedToInteresting({
fieldData: field,
fieldIndex: idx,
})}
isLoading={interestingFieldLoading.includes(idx)}
iconHoverText="Add to Selected Fields"
/>
))}
</FieldContainer>
</CategoryContainer>
{/* <ExtractField>Extract Fields</ExtractField> */}
</Container>
);
}
interface DispatchProps {
getLogsFields: () => (dispatch: Dispatch<AppActions>) => void;
}
const mapDispatchToProps = (
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
): DispatchProps => ({
getLogsFields: bindActionCreators(GetLogsFields, dispatch),
});
type LogsFiltersProps = DispatchProps;
export default connect(null, mapDispatchToProps)(memo(LogsFilters));
export default LogsFilters;

View File

@ -4,8 +4,8 @@ import styled from 'styled-components';
export const Container = styled(Col)`
padding-top: 0.3rem;
min-width: 250px;
max-width: 350px;
min-width: 15.625rem;
max-width: 21.875rem;
`;
export const CategoryContainer = styled.div`
@ -21,6 +21,7 @@ export const FieldContainer = styled(Typography.Text)`
export const Field = styled.div<{ isDarkMode: boolean }>`
border-radius: 0.5rem;
padding: 0.3rem 0.5rem;
height: 2rem;
display: flex;
justify-content: space-between;
align-items: center;

View File

@ -0,0 +1,35 @@
import {
IField,
IInterestingFields,
ISelectedFields,
} from 'types/api/logs/fields';
type SetLoading = (value: React.SetStateAction<number[]>) => void;
export type IHandleInterestProps = {
fieldData: IInterestingFields;
fieldIndex: number;
};
export type IHandleRemoveInterestProps = {
fieldData: ISelectedFields;
fieldIndex: number;
};
export interface OnHandleAddInterestProps {
setInterestingFieldLoading: SetLoading;
fieldIndex: number;
fieldData: ISelectedFields;
interesting: IField[];
interestingFieldLoading: number[];
selected: IField[];
}
export interface OnHandleRemoveInterestProps {
setSelectedFieldLoading: SetLoading;
selected: IField[];
interesting: IField[];
interestingFieldLoading: number[];
fieldData: IInterestingFields;
fieldIndex: number;
}

View File

@ -0,0 +1,94 @@
import AddToSelectedFields from 'api/logs/AddToSelectedField';
import RemoveSelectedField from 'api/logs/RemoveFromSelectedField';
import store from 'store';
import {
UPDATE_INTERESTING_FIELDS,
UPDATE_SELECTED_FIELDS,
} from 'types/actions/logs';
import { RESTRICTED_SELECTED_FIELDS } from './config';
import { OnHandleAddInterestProps, OnHandleRemoveInterestProps } from './types';
export const onHandleAddInterest = async ({
setInterestingFieldLoading,
fieldIndex,
fieldData,
interesting,
interestingFieldLoading,
selected,
}: OnHandleAddInterestProps): Promise<void> => {
const { dispatch } = store;
setInterestingFieldLoading((prevState: number[]) => {
prevState.push(fieldIndex);
return [...prevState];
});
await AddToSelectedFields({
...fieldData,
selected: true,
});
dispatch({
type: UPDATE_INTERESTING_FIELDS,
payload: {
field: interesting.filter((e) => e.name !== fieldData.name),
type: 'selected',
},
});
dispatch({
type: UPDATE_SELECTED_FIELDS,
payload: {
field: [...selected, fieldData],
type: 'selected',
},
});
setInterestingFieldLoading(
interestingFieldLoading.filter((e) => e !== fieldIndex),
);
};
export const onHandleRemoveInterest = async ({
setSelectedFieldLoading,
selected,
interesting,
interestingFieldLoading,
fieldData,
fieldIndex,
}: OnHandleRemoveInterestProps): Promise<void> => {
if (RESTRICTED_SELECTED_FIELDS.includes(fieldData.name)) return;
const { dispatch } = store;
setSelectedFieldLoading((prevState) => {
prevState.push(fieldIndex);
return [...prevState];
});
await RemoveSelectedField({
...fieldData,
selected: false,
});
dispatch({
type: UPDATE_SELECTED_FIELDS,
payload: {
field: selected.filter((e) => e.name !== fieldData.name),
type: 'selected',
},
});
dispatch({
type: UPDATE_INTERESTING_FIELDS,
payload: {
field: [...interesting, fieldData],
type: 'interesting',
},
});
setSelectedFieldLoading(
interestingFieldLoading.filter((e) => e !== fieldIndex),
);
};

View File

@ -1,8 +1,9 @@
import { Input, InputRef, Popover } from 'antd';
import useUrlQuery from 'hooks/useUrlQuery';
import getStep from 'lib/getStep';
import { debounce } from 'lodash-es';
import debounce from 'lodash-es/debounce';
import React, {
memo,
useCallback,
useEffect,
useMemo,
@ -12,6 +13,7 @@ import React, {
import { connect, useDispatch, useSelector } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { GetLogsFields } from 'store/actions/logs/getFields';
import { getLogs } from 'store/actions/logs/getLogs';
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
import { AppState } from 'store/reducers';
@ -32,6 +34,7 @@ import { useSearchParser } from './useSearchParser';
function SearchFilter({
getLogs,
getLogsAggregate,
getLogsFields,
}: SearchFilterProps): JSX.Element {
const {
updateParsedQuery,
@ -45,7 +48,7 @@ function SearchFilter({
AppState,
ILogsReducer
>((state) => state.logs);
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
const globalTime = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const dispatch = useDispatch<Dispatch<AppActions>>();
@ -69,6 +72,8 @@ function SearchFilter({
const handleSearch = useCallback(
(customQuery: string) => {
getLogsFields();
if (liveTail === 'PLAYING') {
dispatch({
type: TOGGLE_LIVE_TAIL,
@ -77,15 +82,13 @@ function SearchFilter({
dispatch({
type: FLUSH_LOGS,
});
setTimeout(
() =>
dispatch({
type: TOGGLE_LIVE_TAIL,
payload: liveTail,
}),
0,
);
});
} else {
const { maxTime, minTime } = globalTime;
getLogs({
q: customQuery,
limit: logLinesPerPage,
@ -117,8 +120,8 @@ function SearchFilter({
idStart,
liveTail,
logLinesPerPage,
maxTime,
minTime,
globalTime,
getLogsFields,
],
);
@ -145,12 +148,12 @@ function SearchFilter({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
urlQueryString,
maxTime,
minTime,
idEnd,
idStart,
logLinesPerPage,
dispatch,
globalTime.maxTime,
globalTime.minTime,
]);
return (
@ -194,6 +197,7 @@ function SearchFilter({
interface DispatchProps {
getLogs: typeof getLogs;
getLogsAggregate: typeof getLogsAggregate;
getLogsFields: typeof GetLogsFields;
}
type SearchFilterProps = DispatchProps;
@ -203,6 +207,7 @@ const mapDispatchToProps = (
): DispatchProps => ({
getLogs: bindActionCreators(getLogs, dispatch),
getLogsAggregate: bindActionCreators(getLogsAggregate, dispatch),
getLogsFields: bindActionCreators(GetLogsFields, dispatch),
});
export default connect(null, mapDispatchToProps)(SearchFilter);
export default connect(null, mapDispatchToProps)(memo(SearchFilter));

View File

@ -1,38 +1,54 @@
import { getMinMax } from 'container/TopNav/AutoRefresh/config';
import history from 'lib/history';
import { parseQuery, reverseParser } from 'lib/logql';
import { ILogQLParsedQueryItem } from 'lib/logql/types';
import isEqual from 'lodash-es/isEqual';
import { useCallback, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Dispatch } from 'redux';
import { AppState } from 'store/reducers';
import AppActions from 'types/actions';
import {
SET_SEARCH_QUERY_PARSED_PAYLOAD,
SET_SEARCH_QUERY_STRING,
} from 'types/actions/logs';
import { GlobalReducer } from 'types/reducer/globalTime';
import { ILogsReducer } from 'types/reducer/logs';
import { getGlobalTime } from './utils';
export function useSearchParser(): {
queryString: string;
parsedQuery: unknown;
updateParsedQuery: (arg0: ILogQLParsedQueryItem[]) => void;
updateQueryString: (arg0: string) => void;
} {
const dispatch = useDispatch();
const dispatch = useDispatch<Dispatch<AppActions>>();
const {
searchFilter: { parsedQuery, queryString },
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
const { minTime, maxTime, selectedTime } = useSelector<
AppState,
GlobalReducer
>((store) => store.globalTime);
const updateQueryString = useCallback(
(updatedQueryString: string) => {
history.replace({
pathname: history.location.pathname,
search: updatedQueryString ? `?q=${updatedQueryString}` : '',
search: `?q=${updatedQueryString}`,
});
const globalTime = getMinMax(selectedTime, minTime, maxTime);
dispatch({
type: SET_SEARCH_QUERY_STRING,
payload: updatedQueryString,
payload: {
searchQueryString: updatedQueryString,
globalTime: getGlobalTime(selectedTime, globalTime),
},
});
const parsedQueryFromString = parseQuery(updatedQueryString);
if (!isEqual(parsedQuery, parsedQueryFromString)) {
dispatch({
@ -41,11 +57,13 @@ export function useSearchParser(): {
});
}
},
// need to hide this warning as we don't want to update the query string on every change
// eslint-disable-next-line react-hooks/exhaustive-deps
[dispatch, parsedQuery],
);
useEffect(() => {
if (queryString !== null) updateQueryString(queryString);
updateQueryString(queryString);
}, [queryString, updateQueryString]);
const updateParsedQuery = useCallback(
@ -61,7 +79,9 @@ export function useSearchParser(): {
) {
dispatch({
type: SET_SEARCH_QUERY_STRING,
payload: reversedParsedQuery,
payload: {
searchQueryString: reversedParsedQuery,
},
});
}
},

View File

@ -0,0 +1,12 @@
import { Time } from 'container/TopNav/DateTimeSelection/config';
import { GetMinMaxPayload } from 'lib/getMinMax';
export const getGlobalTime = (
selectedTime: Time,
globalTime: GetMinMaxPayload,
): GetMinMaxPayload | undefined => {
if (selectedTime === 'custom') {
return undefined;
}
return globalTime;
};

View File

@ -1,7 +1,7 @@
import { ILog } from 'types/api/logs/log';
export function FlatLogData(log: ILog): Record<string, unknown> {
const flattenLogObject: Record<string, unknown> = {};
export function FlatLogData(log: ILog): Record<string, string> {
const flattenLogObject: Record<string, string> = {};
Object.keys(log).forEach((key: string): void => {
if (typeof log[key as never] !== 'object') {

View File

@ -6,39 +6,11 @@ import LogsAggregate from 'container/LogsAggregate';
import LogsFilters from 'container/LogsFilters';
import LogsSearchFilter from 'container/LogsSearchFilter';
import LogsTable from 'container/LogsTable';
import useMountedState from 'hooks/useMountedState';
import useUrlQuery from 'hooks/useUrlQuery';
import React, { memo, useEffect } from 'react';
import { connect, useDispatch } from 'react-redux';
import { bindActionCreators, Dispatch } from 'redux';
import { ThunkDispatch } from 'redux-thunk';
import { GetLogsFields } from 'store/actions/logs/getFields';
import AppActions from 'types/actions';
import { SET_SEARCH_QUERY_STRING } from 'types/actions/logs';
import React from 'react';
import SpaceContainer from './styles';
function Logs({ getLogsFields }: LogsProps): JSX.Element {
const getMountedState = useMountedState();
const urlQuery = useUrlQuery();
const dispatch = useDispatch();
useEffect(() => {
const hasMounted = getMountedState();
if (!hasMounted) {
dispatch({
type: SET_SEARCH_QUERY_STRING,
payload: urlQuery.get('q'),
});
}
}, [dispatch, getMountedState, urlQuery]);
useEffect(() => {
getLogsFields();
}, [getLogsFields]);
function Logs(): JSX.Element {
return (
<>
<SpaceContainer
@ -63,16 +35,4 @@ function Logs({ getLogsFields }: LogsProps): JSX.Element {
);
}
type LogsProps = DispatchProps;
interface DispatchProps {
getLogsFields: () => (dispatch: Dispatch<AppActions>) => void;
}
const mapDispatchToProps = (
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
): DispatchProps => ({
getLogsFields: bindActionCreators(GetLogsFields, dispatch),
});
export default connect(null, mapDispatchToProps)(memo(Logs));
export default Logs;

View File

@ -6,6 +6,10 @@ import {
UPDATE_AUTO_REFRESH_INTERVAL,
UPDATE_TIME_INTERVAL,
} from 'types/actions/globalTime';
import {
RESET_ID_START_AND_END,
SET_SEARCH_QUERY_STRING,
} from 'types/actions/logs';
import { GlobalReducer } from 'types/reducer/globalTime';
const intitalState: GlobalReducer = {
@ -53,6 +57,29 @@ const globalTimeReducer = (
};
}
case RESET_ID_START_AND_END: {
return {
...state,
maxTime: action.payload.maxTime,
minTime: action.payload.minTime,
};
}
case SET_SEARCH_QUERY_STRING: {
const { globalTime } = action.payload;
if (globalTime) {
return {
...state,
maxTime: globalTime.maxTime,
minTime: globalTime.minTime,
};
}
return {
...state,
};
}
default:
return state;
}

View File

@ -20,6 +20,8 @@ import {
SET_SEARCH_QUERY_STRING,
STOP_LIVE_TAIL,
TOGGLE_LIVE_TAIL,
UPDATE_INTERESTING_FIELDS,
UPDATE_SELECTED_FIELDS,
} from 'types/actions/logs';
import { ILogsReducer } from 'types/reducer/logs';
@ -83,7 +85,7 @@ export const LogsReducer = (
...state,
searchFilter: {
...state.searchFilter,
queryString: action.payload,
queryString: action.payload.searchQueryString,
},
};
}
@ -126,7 +128,7 @@ export const LogsReducer = (
case SET_LOG_LINES_PER_PAGE: {
return {
...state,
logLinesPerPage: action.payload,
logLinesPerPage: action.payload.logsLinesPerPage,
};
}
@ -203,6 +205,26 @@ export const LogsReducer = (
};
}
case UPDATE_INTERESTING_FIELDS: {
return {
...state,
fields: {
...state.fields,
interesting: action.payload.field,
},
};
}
case UPDATE_SELECTED_FIELDS: {
return {
...state,
fields: {
...state.fields,
selected: action.payload.field,
},
};
}
default:
return state;
}

View File

@ -1,5 +1,7 @@
import { Time } from 'container/TopNav/DateTimeSelection/config';
import { ResetIdStartAndEnd, SetSearchQueryString } from './logs';
export const UPDATE_TIME_INTERVAL = 'UPDATE_TIME_INTERVAL';
export const GLOBAL_TIME_LOADING_START = 'GLOBAL_TIME_LOADING_START';
export const UPDATE_AUTO_REFRESH_DISABLED = 'UPDATE_AUTO_REFRESH_DISABLED';
@ -37,4 +39,6 @@ export type GlobalTimeAction =
| UpdateTimeInterval
| GlobalTimeLoading
| UpdateAutoRefreshDisabled
| UpdateAutoRefreshInterval;
| UpdateAutoRefreshInterval
| ResetIdStartAndEnd
| SetSearchQueryString;

View File

@ -1,26 +1,11 @@
// import { DBOverView } from 'types/api/metrics/getDBOverview';
// import { ExternalAverageDuration } from 'types/api/metrics/getExternalAverageDuration';
// import { ExternalError } from 'types/api/metrics/getExternalError';
// import { ExternalService } from 'types/api/metrics/getExternalService';
// import { IResourceAttributeQuery } from 'container/MetricsApplication/ResourceAttributesFilter/types';
// import { ServicesList } from 'types/api/metrics/getService';
// import { ServiceOverview } from 'types/api/metrics/getServiceOverview';
// import { TopEndPoints } from 'types/api/metrics/getTopEndPoints';
import { ILogQLParsedQueryItem } from 'lib/logql/types';
import { IFieldMoveToSelected, IFields } from 'types/api/logs/fields';
import { IField, IFieldMoveToSelected, IFields } from 'types/api/logs/fields';
import { TLogsLiveTailState } from 'types/api/logs/liveTail';
import { ILog } from 'types/api/logs/log';
import { ILogsAggregate } from 'types/api/logs/logAggregate';
// export const GET_SERVICE_LIST_SUCCESS = 'GET_SERVICE_LIST_SUCCESS';
// export const GET_SERVICE_LIST_LOADING_START = 'GET_SERVICE_LIST_LOADING_START';
// export const GET_SERVICE_LIST_ERROR = 'GET_SERVICE_LIST_ERROR';
// export const GET_INITIAL_APPLICATION_LOADING =
// 'GET_INITIAL_APPLICATION_LOADING';
// export const GET_INITIAL_APPLICATION_ERROR = 'GET_INITIAL_APPLICATION_ERROR';
// export const GET_INTIAL_APPLICATION_DATA = 'GET_INTIAL_APPLICATION_DATA';
// export const RESET_INITIAL_APPLICATION_DATA = 'RESET_INITIAL_APPLICATION_DATA';
import { GlobalTime } from './globalTime';
export const GET_FIELDS = 'LOGS_GET_FIELDS';
export const SET_FIELDS = 'LOGS_SET_FIELDS';
export const SET_SEARCH_QUERY_STRING = 'LOGS_SET_SEARCH_QUERY_STRING';
@ -43,6 +28,9 @@ export const PUSH_LIVE_TAIL_EVENT = 'LOGS_PUSH_LIVE_TAIL_EVENT';
export const STOP_LIVE_TAIL = 'LOGS_STOP_LIVE_TAIL';
export const FLUSH_LOGS = 'LOGS_FLUSH_LOGS';
export const SET_LIVE_TAIL_START_TIME = 'LOGS_SET_LIVE_TAIL_START_TIME';
export const UPDATE_SELECTED_FIELDS = 'LOGS_UPDATE_SELECTED_FIELDS';
export const UPDATE_INTERESTING_FIELDS = 'LOGS_UPDATE_INTERESTING_FIELDS';
export interface GetFields {
type: typeof GET_FIELDS;
}
@ -53,7 +41,10 @@ export interface SetFields {
}
export interface SetSearchQueryString {
type: typeof SET_SEARCH_QUERY_STRING;
payload: string;
payload: {
searchQueryString: string;
globalTime?: GlobalTime;
};
}
export interface SetSearchQueryParsedPayload {
@ -75,7 +66,9 @@ export interface UpdateLogs {
}
export interface SetLogsLinesPerPage {
type: typeof SET_LOG_LINES_PER_PAGE;
payload: number;
payload: {
logsLinesPerPage: number;
};
}
export interface PreviousLogsLines {
@ -86,6 +79,7 @@ export interface NextLogsLines {
}
export interface ResetIdStartAndEnd {
type: typeof RESET_ID_START_AND_END;
payload: GlobalTime;
}
export interface SetLoading {
type: typeof SET_LOADING;
@ -124,6 +118,16 @@ export interface SetLiveTailStartTime {
payload: number;
}
type IFieldType = 'interesting' | 'selected';
export interface UpdateSelectedInterestFields {
type: typeof UPDATE_INTERESTING_FIELDS | typeof UPDATE_SELECTED_FIELDS;
payload: {
field: IField[];
type: IFieldType;
};
}
export type LogsActions =
| GetFields
| SetFields
@ -144,4 +148,5 @@ export type LogsActions =
| PushLiveTailEvent
| StopLiveTail
| FlushLogs
| SetLiveTailStartTime;
| SetLiveTailStartTime
| UpdateSelectedInterestFields;