mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 15:39:06 +08:00
feat: global time is updated (#2013)
This commit is contained in:
parent
48659a2957
commit
17f32e9765
@ -2,7 +2,9 @@ import { Button, Popover } from 'antd';
|
|||||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||||
import React, { memo, useCallback, useMemo } from 'react';
|
import React, { memo, useCallback, useMemo } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
import AppActions from 'types/actions';
|
||||||
import { SET_SEARCH_QUERY_STRING } from 'types/actions/logs';
|
import { SET_SEARCH_QUERY_STRING } from 'types/actions/logs';
|
||||||
import { ILogsReducer } from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
@ -14,7 +16,7 @@ function AddToQueryHOC({
|
|||||||
const {
|
const {
|
||||||
searchFilter: { queryString },
|
searchFilter: { queryString },
|
||||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
|
|
||||||
const generatedQuery = useMemo(
|
const generatedQuery = useMemo(
|
||||||
() => generateFilterQuery({ fieldKey, fieldValue, type: 'IN' }),
|
() => generateFilterQuery({ fieldKey, fieldValue, type: 'IN' }),
|
||||||
@ -31,7 +33,9 @@ function AddToQueryHOC({
|
|||||||
}
|
}
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_SEARCH_QUERY_STRING,
|
type: SET_SEARCH_QUERY_STRING,
|
||||||
payload: updatedQueryString,
|
payload: {
|
||||||
|
searchQueryString: updatedQueryString,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}, [dispatch, generatedQuery, queryString]);
|
}, [dispatch, generatedQuery, queryString]);
|
||||||
|
|
||||||
|
@ -101,15 +101,9 @@ function LogItem({ logData }: LogItemProps): JSX.Element {
|
|||||||
{'{'}
|
{'{'}
|
||||||
<LogContainer>
|
<LogContainer>
|
||||||
<>
|
<>
|
||||||
<LogGeneralField
|
<LogGeneralField fieldKey="log" fieldValue={flattenLogData.body} />
|
||||||
fieldKey="log"
|
|
||||||
fieldValue={flattenLogData.body as never}
|
|
||||||
/>
|
|
||||||
{flattenLogData.stream && (
|
{flattenLogData.stream && (
|
||||||
<LogGeneralField
|
<LogGeneralField fieldKey="stream" fieldValue={flattenLogData.stream} />
|
||||||
fieldKey="stream"
|
|
||||||
fieldValue={flattenLogData.stream as never}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
<LogGeneralField
|
<LogGeneralField
|
||||||
fieldKey="timestamp"
|
fieldKey="timestamp"
|
||||||
|
@ -37,6 +37,7 @@ const themeColors = {
|
|||||||
matterhornGrey: '#555555',
|
matterhornGrey: '#555555',
|
||||||
whiteCream: '#ffffffd5',
|
whiteCream: '#ffffffd5',
|
||||||
black: '#000000',
|
black: '#000000',
|
||||||
|
lightgrey: '#ddd',
|
||||||
};
|
};
|
||||||
|
|
||||||
export { themeColors };
|
export { themeColors };
|
||||||
|
@ -4,6 +4,8 @@ import {
|
|||||||
RightOutlined,
|
RightOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { Button, Divider, Select } from 'antd';
|
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 React, { memo, useMemo } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
@ -13,6 +15,7 @@ import {
|
|||||||
RESET_ID_START_AND_END,
|
RESET_ID_START_AND_END,
|
||||||
SET_LOG_LINES_PER_PAGE,
|
SET_LOG_LINES_PER_PAGE,
|
||||||
} from 'types/actions/logs';
|
} from 'types/actions/logs';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { ILogsReducer } from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
import { ITEMS_PER_PAGE_OPTIONS } from './config';
|
import { ITEMS_PER_PAGE_OPTIONS } from './config';
|
||||||
@ -28,18 +31,33 @@ function LogControls(): JSX.Element | null {
|
|||||||
isLoadingAggregate,
|
isLoadingAggregate,
|
||||||
logs,
|
logs,
|
||||||
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
|
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||||
|
(state) => state.globalTime,
|
||||||
|
);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const handleLogLinesPerPageChange = (e: number): void => {
|
const handleLogLinesPerPageChange = (e: number): void => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_LOG_LINES_PER_PAGE,
|
type: SET_LOG_LINES_PER_PAGE,
|
||||||
payload: e,
|
payload: {
|
||||||
|
logLinesPerPage: e,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGoToLatest = (): void => {
|
const handleGoToLatest = (): void => {
|
||||||
|
const { maxTime, minTime } = getMinMax(
|
||||||
|
globalTime.selectedTime,
|
||||||
|
globalTime.minTime,
|
||||||
|
globalTime.maxTime,
|
||||||
|
);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: RESET_ID_START_AND_END,
|
type: RESET_ID_START_AND_END,
|
||||||
|
payload: getGlobalTime(globalTime.selectedTime, {
|
||||||
|
maxTime,
|
||||||
|
minTime,
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import getStep from 'lib/getStep';
|
|||||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||||
import React, { memo, useMemo } from 'react';
|
import React, { memo, useMemo } from 'react';
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators, Dispatch } from 'redux';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
import { getLogs } from 'store/actions/logs/getLogs';
|
import { getLogs } from 'store/actions/logs/getLogs';
|
||||||
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
|
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
|
||||||
@ -46,7 +46,7 @@ function ActionItem({
|
|||||||
liveTail,
|
liveTail,
|
||||||
idEnd,
|
idEnd,
|
||||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
|
|
||||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
@ -62,7 +62,9 @@ function ActionItem({
|
|||||||
}
|
}
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_SEARCH_QUERY_STRING,
|
type: SET_SEARCH_QUERY_STRING,
|
||||||
payload: updatedQueryString,
|
payload: {
|
||||||
|
searchQueryString: updatedQueryString,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (liveTail === 'STOPPED') {
|
if (liveTail === 'STOPPED') {
|
||||||
|
@ -64,7 +64,6 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
|
|||||||
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
||||||
return (
|
return (
|
||||||
<AddToQueryHOC fieldKey={fieldKey[0]} fieldValue={flattenLogData[field]}>
|
<AddToQueryHOC fieldKey={fieldKey[0]} fieldValue={flattenLogData[field]}>
|
||||||
{' '}
|
|
||||||
{renderedField}
|
{renderedField}
|
||||||
</AddToQueryHOC>
|
</AddToQueryHOC>
|
||||||
);
|
);
|
||||||
|
@ -1,20 +1,17 @@
|
|||||||
import { LoadingOutlined } from '@ant-design/icons';
|
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 { 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';
|
import { Field } from './styles';
|
||||||
|
|
||||||
interface FieldItemProps {
|
function FieldItem({
|
||||||
name: string;
|
|
||||||
buttonIcon: React.ReactNode;
|
|
||||||
buttonOnClick: (arg0: Record<string, unknown>) => void;
|
|
||||||
fieldData: Record<string, never>;
|
|
||||||
fieldIndex: number;
|
|
||||||
isLoading: boolean;
|
|
||||||
iconHoverText: string;
|
|
||||||
}
|
|
||||||
export function FieldItem({
|
|
||||||
name,
|
name,
|
||||||
buttonIcon,
|
buttonIcon,
|
||||||
buttonOnClick,
|
buttonOnClick,
|
||||||
@ -23,33 +20,65 @@ export function FieldItem({
|
|||||||
isLoading,
|
isLoading,
|
||||||
iconHoverText,
|
iconHoverText,
|
||||||
}: FieldItemProps): JSX.Element {
|
}: FieldItemProps): JSX.Element {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState<boolean>(false);
|
||||||
const isDarkMode = useIsDarkMode();
|
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 (
|
||||||
|
<Popover content={<Typography>{iconHoverText}</Typography>}>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
type="text"
|
||||||
|
icon={buttonIcon}
|
||||||
|
onClick={onClickHandler}
|
||||||
|
/>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}, [buttonIcon, iconHoverText, isHovered, isLoading, onClickHandler]);
|
||||||
|
|
||||||
|
const onMouseHoverHandler = useCallback(
|
||||||
|
(value: boolean) => (): void => {
|
||||||
|
setIsHovered(value);
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Field
|
<Field
|
||||||
onMouseEnter={(): void => {
|
onMouseEnter={onMouseHoverHandler(true)}
|
||||||
setIsHovered(true);
|
onMouseLeave={onMouseHoverHandler(false)}
|
||||||
}}
|
|
||||||
onMouseLeave={(): void => setIsHovered(false)}
|
|
||||||
isDarkMode={isDarkMode}
|
isDarkMode={isDarkMode}
|
||||||
>
|
>
|
||||||
<span>{name}</span>
|
<Typography style={ICON_STYLE.PLUS}>{name}</Typography>
|
||||||
{isLoading ? (
|
|
||||||
<Spin spinning size="small" indicator={<LoadingOutlined spin />} />
|
{renderContent}
|
||||||
) : (
|
|
||||||
isHovered &&
|
|
||||||
buttonOnClick && (
|
|
||||||
<Popover content={<span>{iconHoverText}</span>}>
|
|
||||||
<Button
|
|
||||||
type="text"
|
|
||||||
size="small"
|
|
||||||
icon={buttonIcon}
|
|
||||||
onClick={(): void => buttonOnClick({ fieldData, fieldIndex })}
|
|
||||||
style={{ color: 'inherit', padding: 0, height: '1rem', width: '1rem' }}
|
|
||||||
/>
|
|
||||||
</Popover>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Field>
|
</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;
|
||||||
|
8
frontend/src/container/LogsFilters/config.ts
Normal file
8
frontend/src/container/LogsFilters/config.ts
Normal 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] },
|
||||||
|
};
|
@ -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 { CloseOutlined, PlusCircleFilled } from '@ant-design/icons';
|
||||||
import { Input } from 'antd';
|
import { Input } from 'antd';
|
||||||
import AddToSelectedFields from 'api/logs/AddToSelectedField';
|
|
||||||
import RemoveSelectedField from 'api/logs/RemoveFromSelectedField';
|
|
||||||
import CategoryHeading from 'components/Logs/CategoryHeading';
|
import CategoryHeading from 'components/Logs/CategoryHeading';
|
||||||
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
|
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
|
||||||
import React, { memo, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { connect, useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { bindActionCreators, Dispatch } from 'redux';
|
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
|
||||||
import { GetLogsFields } from 'store/actions/logs/getFields';
|
|
||||||
import { AppState } from 'store/reducers';
|
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 { 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 { CategoryContainer, Container, FieldContainer } from './styles';
|
||||||
|
import { IHandleInterestProps, IHandleRemoveInterestProps } from './types';
|
||||||
|
import { onHandleAddInterest, onHandleRemoveInterest } from './utils';
|
||||||
|
|
||||||
const RESTRICTED_SELECTED_FIELDS = ['timestamp', 'id'];
|
function LogsFilters(): JSX.Element {
|
||||||
|
|
||||||
function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
|
|
||||||
const {
|
const {
|
||||||
fields: { interesting, selected },
|
fields: { interesting, selected },
|
||||||
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||||
@ -36,57 +28,40 @@ function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
|
|||||||
setFilterValuesInput((e.target as HTMLInputElement).value);
|
setFilterValuesInput((e.target as HTMLInputElement).value);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleAddInterestingToSelected = async ({
|
const onHandleAddSelectedToInteresting = useCallback(
|
||||||
fieldData,
|
({ fieldData, fieldIndex }: IHandleInterestProps) => (): Promise<void> =>
|
||||||
fieldIndex,
|
onHandleAddInterest({
|
||||||
}: {
|
fieldData,
|
||||||
fieldData: IInterestingFields;
|
fieldIndex,
|
||||||
fieldIndex: number;
|
interesting,
|
||||||
}): Promise<void> => {
|
interestingFieldLoading,
|
||||||
setInterestingFieldLoading((prevState: number[]) => {
|
setInterestingFieldLoading,
|
||||||
prevState.push(fieldIndex);
|
selected,
|
||||||
return [...prevState];
|
}),
|
||||||
});
|
[interesting, interestingFieldLoading, selected],
|
||||||
|
);
|
||||||
|
|
||||||
await AddToSelectedFields({
|
const onHandleRemoveSelected = useCallback(
|
||||||
...fieldData,
|
({
|
||||||
selected: true,
|
fieldData,
|
||||||
});
|
fieldIndex,
|
||||||
getLogsFields();
|
}: IHandleRemoveInterestProps) => (): Promise<void> =>
|
||||||
|
onHandleRemoveInterest({
|
||||||
|
fieldData,
|
||||||
|
fieldIndex,
|
||||||
|
interesting,
|
||||||
|
interestingFieldLoading,
|
||||||
|
selected,
|
||||||
|
setSelectedFieldLoading,
|
||||||
|
}),
|
||||||
|
[interesting, interestingFieldLoading, selected, setSelectedFieldLoading],
|
||||||
|
);
|
||||||
|
|
||||||
setInterestingFieldLoading(
|
|
||||||
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const handleRemoveSelectedField = async ({
|
|
||||||
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),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<Container flex="450px">
|
<Container flex="450px">
|
||||||
<Input
|
<Input
|
||||||
placeholder="Filter Values"
|
placeholder="Filter Values"
|
||||||
onInput={handleSearch}
|
onInput={handleSearch}
|
||||||
style={{ width: '100%' }}
|
|
||||||
value={filterValuesInput}
|
value={filterValuesInput}
|
||||||
onChange={handleSearch}
|
onChange={handleSearch}
|
||||||
/>
|
/>
|
||||||
@ -98,15 +73,15 @@ function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
|
|||||||
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
|
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
|
||||||
.map((field, idx) => (
|
.map((field, idx) => (
|
||||||
<FieldItem
|
<FieldItem
|
||||||
key={`${JSON.stringify(field)}-${idx}`}
|
key={`${JSON.stringify(field)}`}
|
||||||
name={field.name}
|
name={field.name}
|
||||||
fieldData={field as never}
|
fieldData={field}
|
||||||
fieldIndex={idx}
|
fieldIndex={idx}
|
||||||
buttonIcon={<CloseOutlined style={{ color: red[5] }} />}
|
buttonIcon={<CloseOutlined style={ICON_STYLE.CLOSE} />}
|
||||||
buttonOnClick={
|
buttonOnClick={onHandleRemoveSelected({
|
||||||
(!RESTRICTED_SELECTED_FIELDS.includes(field.name) &&
|
fieldData: field,
|
||||||
handleRemoveSelectedField) as never
|
fieldIndex: idx,
|
||||||
}
|
})}
|
||||||
isLoading={selectedFieldLoading.includes(idx)}
|
isLoading={selectedFieldLoading.includes(idx)}
|
||||||
iconHoverText="Remove from Selected Fields"
|
iconHoverText="Remove from Selected Fields"
|
||||||
/>
|
/>
|
||||||
@ -120,33 +95,23 @@ function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
|
|||||||
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
|
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
|
||||||
.map((field, idx) => (
|
.map((field, idx) => (
|
||||||
<FieldItem
|
<FieldItem
|
||||||
key={`${JSON.stringify(field)}-${idx}`}
|
key={`${JSON.stringify(field)}`}
|
||||||
name={field.name}
|
name={field.name}
|
||||||
fieldData={field as never}
|
fieldData={field}
|
||||||
fieldIndex={idx}
|
fieldIndex={idx}
|
||||||
buttonIcon={<PlusCircleFilled />}
|
buttonIcon={<PlusCircleFilled style={ICON_STYLE.PLUS} />}
|
||||||
buttonOnClick={handleAddInterestingToSelected as never}
|
buttonOnClick={onHandleAddSelectedToInteresting({
|
||||||
|
fieldData: field,
|
||||||
|
fieldIndex: idx,
|
||||||
|
})}
|
||||||
isLoading={interestingFieldLoading.includes(idx)}
|
isLoading={interestingFieldLoading.includes(idx)}
|
||||||
iconHoverText="Add to Selected Fields"
|
iconHoverText="Add to Selected Fields"
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</FieldContainer>
|
</FieldContainer>
|
||||||
</CategoryContainer>
|
</CategoryContainer>
|
||||||
{/* <ExtractField>Extract Fields</ExtractField> */}
|
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DispatchProps {
|
export default LogsFilters;
|
||||||
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));
|
|
||||||
|
@ -4,8 +4,8 @@ import styled from 'styled-components';
|
|||||||
|
|
||||||
export const Container = styled(Col)`
|
export const Container = styled(Col)`
|
||||||
padding-top: 0.3rem;
|
padding-top: 0.3rem;
|
||||||
min-width: 250px;
|
min-width: 15.625rem;
|
||||||
max-width: 350px;
|
max-width: 21.875rem;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const CategoryContainer = styled.div`
|
export const CategoryContainer = styled.div`
|
||||||
@ -21,6 +21,7 @@ export const FieldContainer = styled(Typography.Text)`
|
|||||||
export const Field = styled.div<{ isDarkMode: boolean }>`
|
export const Field = styled.div<{ isDarkMode: boolean }>`
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
padding: 0.3rem 0.5rem;
|
padding: 0.3rem 0.5rem;
|
||||||
|
height: 2rem;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
35
frontend/src/container/LogsFilters/types.ts
Normal file
35
frontend/src/container/LogsFilters/types.ts
Normal 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;
|
||||||
|
}
|
94
frontend/src/container/LogsFilters/utils.ts
Normal file
94
frontend/src/container/LogsFilters/utils.ts
Normal 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),
|
||||||
|
);
|
||||||
|
};
|
@ -1,8 +1,9 @@
|
|||||||
import { Input, InputRef, Popover } from 'antd';
|
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';
|
import debounce from 'lodash-es/debounce';
|
||||||
import React, {
|
import React, {
|
||||||
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
@ -12,6 +13,7 @@ import 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';
|
||||||
import { ThunkDispatch } from 'redux-thunk';
|
import { ThunkDispatch } from 'redux-thunk';
|
||||||
|
import { GetLogsFields } from 'store/actions/logs/getFields';
|
||||||
import { getLogs } from 'store/actions/logs/getLogs';
|
import { getLogs } from 'store/actions/logs/getLogs';
|
||||||
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
|
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
@ -32,6 +34,7 @@ import { useSearchParser } from './useSearchParser';
|
|||||||
function SearchFilter({
|
function SearchFilter({
|
||||||
getLogs,
|
getLogs,
|
||||||
getLogsAggregate,
|
getLogsAggregate,
|
||||||
|
getLogsFields,
|
||||||
}: SearchFilterProps): JSX.Element {
|
}: SearchFilterProps): JSX.Element {
|
||||||
const {
|
const {
|
||||||
updateParsedQuery,
|
updateParsedQuery,
|
||||||
@ -45,7 +48,7 @@ function SearchFilter({
|
|||||||
AppState,
|
AppState,
|
||||||
ILogsReducer
|
ILogsReducer
|
||||||
>((state) => state.logs);
|
>((state) => state.logs);
|
||||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
);
|
);
|
||||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
@ -69,6 +72,8 @@ function SearchFilter({
|
|||||||
|
|
||||||
const handleSearch = useCallback(
|
const handleSearch = useCallback(
|
||||||
(customQuery: string) => {
|
(customQuery: string) => {
|
||||||
|
getLogsFields();
|
||||||
|
|
||||||
if (liveTail === 'PLAYING') {
|
if (liveTail === 'PLAYING') {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TOGGLE_LIVE_TAIL,
|
type: TOGGLE_LIVE_TAIL,
|
||||||
@ -77,15 +82,13 @@ function SearchFilter({
|
|||||||
dispatch({
|
dispatch({
|
||||||
type: FLUSH_LOGS,
|
type: FLUSH_LOGS,
|
||||||
});
|
});
|
||||||
setTimeout(
|
dispatch({
|
||||||
() =>
|
type: TOGGLE_LIVE_TAIL,
|
||||||
dispatch({
|
payload: liveTail,
|
||||||
type: TOGGLE_LIVE_TAIL,
|
});
|
||||||
payload: liveTail,
|
|
||||||
}),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
|
const { maxTime, minTime } = globalTime;
|
||||||
|
|
||||||
getLogs({
|
getLogs({
|
||||||
q: customQuery,
|
q: customQuery,
|
||||||
limit: logLinesPerPage,
|
limit: logLinesPerPage,
|
||||||
@ -117,8 +120,8 @@ function SearchFilter({
|
|||||||
idStart,
|
idStart,
|
||||||
liveTail,
|
liveTail,
|
||||||
logLinesPerPage,
|
logLinesPerPage,
|
||||||
maxTime,
|
globalTime,
|
||||||
minTime,
|
getLogsFields,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -145,12 +148,12 @@ function SearchFilter({
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
urlQueryString,
|
urlQueryString,
|
||||||
maxTime,
|
|
||||||
minTime,
|
|
||||||
idEnd,
|
idEnd,
|
||||||
idStart,
|
idStart,
|
||||||
logLinesPerPage,
|
logLinesPerPage,
|
||||||
dispatch,
|
dispatch,
|
||||||
|
globalTime.maxTime,
|
||||||
|
globalTime.minTime,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -194,6 +197,7 @@ function SearchFilter({
|
|||||||
interface DispatchProps {
|
interface DispatchProps {
|
||||||
getLogs: typeof getLogs;
|
getLogs: typeof getLogs;
|
||||||
getLogsAggregate: typeof getLogsAggregate;
|
getLogsAggregate: typeof getLogsAggregate;
|
||||||
|
getLogsFields: typeof GetLogsFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
type SearchFilterProps = DispatchProps;
|
type SearchFilterProps = DispatchProps;
|
||||||
@ -203,6 +207,7 @@ const mapDispatchToProps = (
|
|||||||
): DispatchProps => ({
|
): DispatchProps => ({
|
||||||
getLogs: bindActionCreators(getLogs, dispatch),
|
getLogs: bindActionCreators(getLogs, dispatch),
|
||||||
getLogsAggregate: bindActionCreators(getLogsAggregate, dispatch),
|
getLogsAggregate: bindActionCreators(getLogsAggregate, dispatch),
|
||||||
|
getLogsFields: bindActionCreators(GetLogsFields, dispatch),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(null, mapDispatchToProps)(SearchFilter);
|
export default connect(null, mapDispatchToProps)(memo(SearchFilter));
|
||||||
|
@ -1,38 +1,54 @@
|
|||||||
|
import { getMinMax } from 'container/TopNav/AutoRefresh/config';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import { parseQuery, reverseParser } from 'lib/logql';
|
import { parseQuery, reverseParser } from 'lib/logql';
|
||||||
import { ILogQLParsedQueryItem } from 'lib/logql/types';
|
import { ILogQLParsedQueryItem } from 'lib/logql/types';
|
||||||
import isEqual from 'lodash-es/isEqual';
|
import isEqual from 'lodash-es/isEqual';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
import AppActions from 'types/actions';
|
||||||
import {
|
import {
|
||||||
SET_SEARCH_QUERY_PARSED_PAYLOAD,
|
SET_SEARCH_QUERY_PARSED_PAYLOAD,
|
||||||
SET_SEARCH_QUERY_STRING,
|
SET_SEARCH_QUERY_STRING,
|
||||||
} from 'types/actions/logs';
|
} from 'types/actions/logs';
|
||||||
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
import { ILogsReducer } from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
|
import { getGlobalTime } from './utils';
|
||||||
|
|
||||||
export function useSearchParser(): {
|
export function useSearchParser(): {
|
||||||
queryString: string;
|
queryString: string;
|
||||||
parsedQuery: unknown;
|
parsedQuery: unknown;
|
||||||
updateParsedQuery: (arg0: ILogQLParsedQueryItem[]) => void;
|
updateParsedQuery: (arg0: ILogQLParsedQueryItem[]) => void;
|
||||||
updateQueryString: (arg0: string) => void;
|
updateQueryString: (arg0: string) => void;
|
||||||
} {
|
} {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
const {
|
const {
|
||||||
searchFilter: { parsedQuery, queryString },
|
searchFilter: { parsedQuery, queryString },
|
||||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||||
|
const { minTime, maxTime, selectedTime } = useSelector<
|
||||||
|
AppState,
|
||||||
|
GlobalReducer
|
||||||
|
>((store) => store.globalTime);
|
||||||
|
|
||||||
const updateQueryString = useCallback(
|
const updateQueryString = useCallback(
|
||||||
(updatedQueryString: string) => {
|
(updatedQueryString: string) => {
|
||||||
history.replace({
|
history.replace({
|
||||||
pathname: history.location.pathname,
|
pathname: history.location.pathname,
|
||||||
search: updatedQueryString ? `?q=${updatedQueryString}` : '',
|
search: `?q=${updatedQueryString}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const globalTime = getMinMax(selectedTime, minTime, maxTime);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_SEARCH_QUERY_STRING,
|
type: SET_SEARCH_QUERY_STRING,
|
||||||
payload: updatedQueryString,
|
payload: {
|
||||||
|
searchQueryString: updatedQueryString,
|
||||||
|
globalTime: getGlobalTime(selectedTime, globalTime),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const parsedQueryFromString = parseQuery(updatedQueryString);
|
const parsedQueryFromString = parseQuery(updatedQueryString);
|
||||||
if (!isEqual(parsedQuery, parsedQueryFromString)) {
|
if (!isEqual(parsedQuery, parsedQueryFromString)) {
|
||||||
dispatch({
|
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],
|
[dispatch, parsedQuery],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (queryString !== null) updateQueryString(queryString);
|
updateQueryString(queryString);
|
||||||
}, [queryString, updateQueryString]);
|
}, [queryString, updateQueryString]);
|
||||||
|
|
||||||
const updateParsedQuery = useCallback(
|
const updateParsedQuery = useCallback(
|
||||||
@ -61,7 +79,9 @@ export function useSearchParser(): {
|
|||||||
) {
|
) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: SET_SEARCH_QUERY_STRING,
|
type: SET_SEARCH_QUERY_STRING,
|
||||||
payload: reversedParsedQuery,
|
payload: {
|
||||||
|
searchQueryString: reversedParsedQuery,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
12
frontend/src/container/LogsSearchFilter/utils.ts
Normal file
12
frontend/src/container/LogsSearchFilter/utils.ts
Normal 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;
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
|
||||||
export function FlatLogData(log: ILog): Record<string, unknown> {
|
export function FlatLogData(log: ILog): Record<string, string> {
|
||||||
const flattenLogObject: Record<string, unknown> = {};
|
const flattenLogObject: Record<string, string> = {};
|
||||||
|
|
||||||
Object.keys(log).forEach((key: string): void => {
|
Object.keys(log).forEach((key: string): void => {
|
||||||
if (typeof log[key as never] !== 'object') {
|
if (typeof log[key as never] !== 'object') {
|
||||||
|
@ -6,39 +6,11 @@ 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 useMountedState from 'hooks/useMountedState';
|
import React from 'react';
|
||||||
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 SpaceContainer from './styles';
|
import SpaceContainer from './styles';
|
||||||
|
|
||||||
function Logs({ getLogsFields }: LogsProps): JSX.Element {
|
function Logs(): 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]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SpaceContainer
|
<SpaceContainer
|
||||||
@ -63,16 +35,4 @@ function Logs({ getLogsFields }: LogsProps): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogsProps = DispatchProps;
|
export default Logs;
|
||||||
|
|
||||||
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));
|
|
||||||
|
@ -6,6 +6,10 @@ import {
|
|||||||
UPDATE_AUTO_REFRESH_INTERVAL,
|
UPDATE_AUTO_REFRESH_INTERVAL,
|
||||||
UPDATE_TIME_INTERVAL,
|
UPDATE_TIME_INTERVAL,
|
||||||
} from 'types/actions/globalTime';
|
} from 'types/actions/globalTime';
|
||||||
|
import {
|
||||||
|
RESET_ID_START_AND_END,
|
||||||
|
SET_SEARCH_QUERY_STRING,
|
||||||
|
} from 'types/actions/logs';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
const intitalState: GlobalReducer = {
|
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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ import {
|
|||||||
SET_SEARCH_QUERY_STRING,
|
SET_SEARCH_QUERY_STRING,
|
||||||
STOP_LIVE_TAIL,
|
STOP_LIVE_TAIL,
|
||||||
TOGGLE_LIVE_TAIL,
|
TOGGLE_LIVE_TAIL,
|
||||||
|
UPDATE_INTERESTING_FIELDS,
|
||||||
|
UPDATE_SELECTED_FIELDS,
|
||||||
} from 'types/actions/logs';
|
} from 'types/actions/logs';
|
||||||
import { ILogsReducer } from 'types/reducer/logs';
|
import { ILogsReducer } from 'types/reducer/logs';
|
||||||
|
|
||||||
@ -83,7 +85,7 @@ export const LogsReducer = (
|
|||||||
...state,
|
...state,
|
||||||
searchFilter: {
|
searchFilter: {
|
||||||
...state.searchFilter,
|
...state.searchFilter,
|
||||||
queryString: action.payload,
|
queryString: action.payload.searchQueryString,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -126,7 +128,7 @@ export const LogsReducer = (
|
|||||||
case SET_LOG_LINES_PER_PAGE: {
|
case SET_LOG_LINES_PER_PAGE: {
|
||||||
return {
|
return {
|
||||||
...state,
|
...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:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||||
|
|
||||||
|
import { ResetIdStartAndEnd, SetSearchQueryString } from './logs';
|
||||||
|
|
||||||
export const UPDATE_TIME_INTERVAL = 'UPDATE_TIME_INTERVAL';
|
export const UPDATE_TIME_INTERVAL = 'UPDATE_TIME_INTERVAL';
|
||||||
export const GLOBAL_TIME_LOADING_START = 'GLOBAL_TIME_LOADING_START';
|
export const GLOBAL_TIME_LOADING_START = 'GLOBAL_TIME_LOADING_START';
|
||||||
export const UPDATE_AUTO_REFRESH_DISABLED = 'UPDATE_AUTO_REFRESH_DISABLED';
|
export const UPDATE_AUTO_REFRESH_DISABLED = 'UPDATE_AUTO_REFRESH_DISABLED';
|
||||||
@ -37,4 +39,6 @@ export type GlobalTimeAction =
|
|||||||
| UpdateTimeInterval
|
| UpdateTimeInterval
|
||||||
| GlobalTimeLoading
|
| GlobalTimeLoading
|
||||||
| UpdateAutoRefreshDisabled
|
| UpdateAutoRefreshDisabled
|
||||||
| UpdateAutoRefreshInterval;
|
| UpdateAutoRefreshInterval
|
||||||
|
| ResetIdStartAndEnd
|
||||||
|
| SetSearchQueryString;
|
||||||
|
@ -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 { 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 { TLogsLiveTailState } from 'types/api/logs/liveTail';
|
||||||
import { ILog } from 'types/api/logs/log';
|
import { ILog } from 'types/api/logs/log';
|
||||||
import { ILogsAggregate } from 'types/api/logs/logAggregate';
|
import { ILogsAggregate } from 'types/api/logs/logAggregate';
|
||||||
|
|
||||||
// export const GET_SERVICE_LIST_SUCCESS = 'GET_SERVICE_LIST_SUCCESS';
|
import { GlobalTime } from './globalTime';
|
||||||
// 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';
|
|
||||||
export const GET_FIELDS = 'LOGS_GET_FIELDS';
|
export const GET_FIELDS = 'LOGS_GET_FIELDS';
|
||||||
export const SET_FIELDS = 'LOGS_SET_FIELDS';
|
export const SET_FIELDS = 'LOGS_SET_FIELDS';
|
||||||
export const SET_SEARCH_QUERY_STRING = 'LOGS_SET_SEARCH_QUERY_STRING';
|
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 STOP_LIVE_TAIL = 'LOGS_STOP_LIVE_TAIL';
|
||||||
export const FLUSH_LOGS = 'LOGS_FLUSH_LOGS';
|
export const FLUSH_LOGS = 'LOGS_FLUSH_LOGS';
|
||||||
export const SET_LIVE_TAIL_START_TIME = 'LOGS_SET_LIVE_TAIL_START_TIME';
|
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 {
|
export interface GetFields {
|
||||||
type: typeof GET_FIELDS;
|
type: typeof GET_FIELDS;
|
||||||
}
|
}
|
||||||
@ -53,7 +41,10 @@ export interface SetFields {
|
|||||||
}
|
}
|
||||||
export interface SetSearchQueryString {
|
export interface SetSearchQueryString {
|
||||||
type: typeof SET_SEARCH_QUERY_STRING;
|
type: typeof SET_SEARCH_QUERY_STRING;
|
||||||
payload: string;
|
payload: {
|
||||||
|
searchQueryString: string;
|
||||||
|
globalTime?: GlobalTime;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SetSearchQueryParsedPayload {
|
export interface SetSearchQueryParsedPayload {
|
||||||
@ -75,7 +66,9 @@ export interface UpdateLogs {
|
|||||||
}
|
}
|
||||||
export interface SetLogsLinesPerPage {
|
export interface SetLogsLinesPerPage {
|
||||||
type: typeof SET_LOG_LINES_PER_PAGE;
|
type: typeof SET_LOG_LINES_PER_PAGE;
|
||||||
payload: number;
|
payload: {
|
||||||
|
logsLinesPerPage: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PreviousLogsLines {
|
export interface PreviousLogsLines {
|
||||||
@ -86,6 +79,7 @@ export interface NextLogsLines {
|
|||||||
}
|
}
|
||||||
export interface ResetIdStartAndEnd {
|
export interface ResetIdStartAndEnd {
|
||||||
type: typeof RESET_ID_START_AND_END;
|
type: typeof RESET_ID_START_AND_END;
|
||||||
|
payload: GlobalTime;
|
||||||
}
|
}
|
||||||
export interface SetLoading {
|
export interface SetLoading {
|
||||||
type: typeof SET_LOADING;
|
type: typeof SET_LOADING;
|
||||||
@ -124,6 +118,16 @@ export interface SetLiveTailStartTime {
|
|||||||
payload: number;
|
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 =
|
export type LogsActions =
|
||||||
| GetFields
|
| GetFields
|
||||||
| SetFields
|
| SetFields
|
||||||
@ -144,4 +148,5 @@ export type LogsActions =
|
|||||||
| PushLiveTailEvent
|
| PushLiveTailEvent
|
||||||
| StopLiveTail
|
| StopLiveTail
|
||||||
| FlushLogs
|
| FlushLogs
|
||||||
| SetLiveTailStartTime;
|
| SetLiveTailStartTime
|
||||||
|
| UpdateSelectedInterestFields;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user