fix[logs][FE]: live tail is fixed (#1759)

* fix: live tail is fixed

* fix: graph state is updated

* chore: step size is updated

* chore: xaxis config is updated

* chore: isDisabled state is updated for top navigation

* chore: selected interval is updated in the reducer

* fix: build is fixed

* chore: xAxis config is updated

Co-authored-by: Pranay Prateek <pranay@signoz.io>
Co-authored-by: Ankit Nayan <ankit@signoz.io>
This commit is contained in:
Palash Gupta 2022-11-28 15:44:33 +05:30 committed by GitHub
parent d06d41af87
commit 6c9036fbf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 112 additions and 67 deletions

View File

@ -4,9 +4,6 @@ import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
interface ITimeUnit {
[key: string]: TimeUnit;
}
interface IAxisTimeUintConfig { interface IAxisTimeUintConfig {
unitName: TimeUnit; unitName: TimeUnit;
multiplier: number; multiplier: number;
@ -22,7 +19,7 @@ export interface ITimeRange {
maxTime: number | null; maxTime: number | null;
} }
export const TIME_UNITS: ITimeUnit = { export const TIME_UNITS: Record<TimeUnit, TimeUnit> = {
millisecond: 'millisecond', millisecond: 'millisecond',
second: 'second', second: 'second',
minute: 'minute', minute: 'minute',
@ -31,6 +28,7 @@ export const TIME_UNITS: ITimeUnit = {
week: 'week', week: 'week',
month: 'month', month: 'month',
year: 'year', year: 'year',
quarter: 'quarter',
}; };
const TIME_UNITS_CONFIG: IAxisTimeUintConfig[] = [ const TIME_UNITS_CONFIG: IAxisTimeUintConfig[] = [
@ -93,6 +91,7 @@ export const convertTimeRange = (
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
return { return {
unitName: relevantTimeUnit.unitName, unitName: relevantTimeUnit.unitName,
stepSize: Math.floor(stepSize) || 1, stepSize: Math.floor(stepSize) || 1,

View File

@ -11,6 +11,7 @@ import { throttle } from 'lodash-es';
import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { UPDATE_AUTO_REFRESH_DISABLED } from 'types/actions/globalTime';
import { import {
FLUSH_LOGS, FLUSH_LOGS,
PUSH_LIVE_TAIL_EVENT, PUSH_LIVE_TAIL_EVENT,
@ -19,6 +20,7 @@ import {
} from 'types/actions/logs'; } from 'types/actions/logs';
import { TLogsLiveTailState } from 'types/api/logs/liveTail'; import { TLogsLiveTailState } from 'types/api/logs/liveTail';
import AppReducer from 'types/reducer/app'; import AppReducer from 'types/reducer/app';
import { GlobalReducer } from 'types/reducer/globalTime';
import { ILogsReducer } from 'types/reducer/logs'; import { ILogsReducer } from 'types/reducer/logs';
import { TIME_PICKER_OPTIONS } from './config'; import { TIME_PICKER_OPTIONS } from './config';
@ -34,12 +36,20 @@ function LogLiveTail(): JSX.Element {
logs, logs,
} = useSelector<AppState, ILogsReducer>((state) => state.logs); } = useSelector<AppState, ILogsReducer>((state) => state.logs);
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app); const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
const { selectedAutoRefreshInterval } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const dispatch = useDispatch(); const dispatch = useDispatch();
const handleLiveTail = (toggleState: TLogsLiveTailState): void => { const handleLiveTail = (toggleState: TLogsLiveTailState): void => {
dispatch({ dispatch({
type: TOGGLE_LIVE_TAIL, type: TOGGLE_LIVE_TAIL,
payload: toggleState, payload: toggleState,
}); });
dispatch({
type: UPDATE_AUTO_REFRESH_DISABLED,
payload: toggleState === 'PLAYING',
});
}; };
const batchedEventsRef = useRef<Record<string, unknown>[]>([]); const batchedEventsRef = useRef<Record<string, unknown>[]>([]);
@ -131,6 +141,10 @@ function LogLiveTail(): JSX.Element {
[dispatch, liveTail, liveTailStartRange], [dispatch, liveTail, liveTailStartRange],
); );
const isDisabled = useMemo(() => selectedAutoRefreshInterval?.length > 0, [
selectedAutoRefreshInterval,
]);
return ( return (
<TimePickerCard> <TimePickerCard>
<Space size={0} align="center"> <Space size={0} align="center">
@ -149,6 +163,7 @@ function LogLiveTail(): JSX.Element {
type="primary" type="primary"
onClick={handleLiveTailStart} onClick={handleLiveTailStart}
title="Start live tail" title="Start live tail"
disabled={isDisabled}
> >
Go Live <PlayCircleOutlined /> Go Live <PlayCircleOutlined />
</Button> </Button>

View File

@ -31,6 +31,7 @@ function LogsAggregate({ getLogsAggregate }: LogsAggregateProps): JSX.Element {
); );
const reFetchIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null); const reFetchIntervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
useEffect(() => { useEffect(() => {
switch (liveTail) { switch (liveTail) {
case 'STOPPED': { case 'STOPPED': {
@ -38,18 +39,6 @@ function LogsAggregate({ getLogsAggregate }: LogsAggregateProps): JSX.Element {
clearInterval(reFetchIntervalRef.current); clearInterval(reFetchIntervalRef.current);
} }
reFetchIntervalRef.current = null; reFetchIntervalRef.current = null;
// getLogsAggregate({
// timestampStart: minTime,
// timestampEnd: maxTime,
// step: getStep({
// start: minTime,
// end: maxTime,
// inputFormat: 'ns',
// }),
// q: queryString,
// ...(idStart ? { idGt: idStart } : {}),
// ...(idEnd ? { idLt: idEnd } : {}),
// });
break; break;
} }
@ -106,7 +95,7 @@ function LogsAggregate({ getLogsAggregate }: LogsAggregateProps): JSX.Element {
}} }}
type="bar" type="bar"
containerHeight="100%" containerHeight="100%"
animate={false} animate
/> />
)} )}
</Container> </Container>

View File

@ -21,7 +21,7 @@ import { v4 } from 'uuid';
import { SearchFieldsProps } from '..'; import { SearchFieldsProps } from '..';
import FieldKey from '../FieldKey'; import FieldKey from '../FieldKey';
import { QueryConditionContainer, QueryFieldContainer } from '../styles'; import { QueryFieldContainer } from '../styles';
import { createParsedQueryStructure } from '../utils'; import { createParsedQueryStructure } from '../utils';
import { Container, QueryWrapper } from './styles'; import { Container, QueryWrapper } from './styles';
import { hashCode, parseQuery } from './utils'; import { hashCode, parseQuery } from './utils';
@ -32,33 +32,27 @@ function QueryConditionField({
query, query,
queryIndex, queryIndex,
onUpdate, onUpdate,
style,
}: QueryConditionFieldProps): JSX.Element { }: QueryConditionFieldProps): JSX.Element {
const allOptions = Object.values(ConditionalOperators);
return ( return (
<QueryConditionContainer style={{ ...style }}> <Select
<Select defaultValue={
defaultValue={ (query as any).value &&
(query as any).value && (((query as any)?.value as any) as string).toUpperCase()
(((query as any)?.value as any) as string).toUpperCase() }
} onChange={(e): void => {
onChange={(e): void => { onUpdate({ ...query, value: e }, queryIndex);
onUpdate({ ...query, value: e }, queryIndex); }}
}} >
> {allOptions.map((cond) => (
{Object.values(ConditionalOperators).map((cond) => ( <Option key={cond} value={cond} label={cond}>
<Option key={cond} value={cond} label={cond}> {cond}
{cond} </Option>
</Option> ))}
))} </Select>
</Select>
</QueryConditionContainer>
); );
} }
QueryConditionField.defaultProps = {
style: undefined,
};
interface QueryFieldProps { interface QueryFieldProps {
query: Query; query: Query;
queryIndex: number; queryIndex: number;
@ -174,7 +168,6 @@ interface QueryConditionFieldProps {
query: { value: string | string[]; type: string }[]; query: { value: string | string[]; type: string }[];
queryIndex: number; queryIndex: number;
onUpdate: (arg0: unknown, arg1: number) => void; onUpdate: (arg0: unknown, arg1: number) => void;
style?: React.CSSProperties;
} }
export type Query = { value: string | string[]; type: string }[]; export type Query = { value: string | string[]; type: string }[];
@ -233,12 +226,13 @@ function QueryBuilder({
); );
return ( return (
<QueryConditionField <div key={keyPrefix + idx}>
key={keyPrefix + idx} <QueryConditionField
query={query} query={query}
queryIndex={idx} queryIndex={idx}
onUpdate={handleUpdate as never} onUpdate={handleUpdate as never}
/> />
</div>
); );
}); });

View File

@ -14,8 +14,3 @@ export const QueryFieldContainer = styled.div`
background: ${blue[6]}; background: ${blue[6]};
} }
`; `;
export const QueryConditionContainer = styled.div`
display: flex;
flex-direction: row;
`;

View File

@ -104,7 +104,8 @@ function SearchFilter({
useEffect(() => { useEffect(() => {
handleSearch(urlQueryString || ''); handleSearch(urlQueryString || '');
}, [handleSearch, urlQueryString]); // eslint-disable-next-line react-hooks/exhaustive-deps
}, [urlQueryString, maxTime, minTime]);
return ( return (
<Container> <Container>

View File

@ -22,19 +22,28 @@ import { useInterval } from 'react-use';
import { Dispatch } from 'redux'; import { Dispatch } from 'redux';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import AppActions from 'types/actions'; import AppActions from 'types/actions';
import { UPDATE_TIME_INTERVAL } from 'types/actions/globalTime'; import {
UPDATE_AUTO_REFRESH_INTERVAL,
UPDATE_TIME_INTERVAL,
} from 'types/actions/globalTime';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import { options } from './config'; import { options } from './config';
import { ButtonContainer, Container } from './styles'; import { ButtonContainer, Container } from './styles';
function AutoRefresh({ disabled = false }: AutoRefreshProps): JSX.Element { function AutoRefresh({ disabled = false }: AutoRefreshProps): JSX.Element {
const { minTime: initialMinTime, selectedTime } = useSelector< const {
AppState, minTime: initialMinTime,
GlobalReducer selectedTime,
>((state) => state.globalTime); isAutoRefreshDisabled,
} = useSelector<AppState, GlobalReducer>((state) => state.globalTime);
const { pathname } = useLocation(); const { pathname } = useLocation();
const isDisabled = useMemo(() => disabled || isAutoRefreshDisabled, [
isAutoRefreshDisabled,
disabled,
]);
const localStorageData = JSON.parse(get(DASHBOARD_TIME_IN_DURATION) || '{}'); const localStorageData = JSON.parse(get(DASHBOARD_TIME_IN_DURATION) || '{}');
const localStorageValue = useMemo(() => localStorageData[pathname], [ const localStorageValue = useMemo(() => localStorageData[pathname], [
@ -46,13 +55,19 @@ function AutoRefresh({ disabled = false }: AutoRefreshProps): JSX.Element {
Boolean(localStorageValue), Boolean(localStorageValue),
); );
const dispatch = useDispatch<Dispatch<AppActions>>();
useEffect(() => { useEffect(() => {
setIsAutoRefreshfreshEnabled(Boolean(localStorageValue)); const isAutoRefreshEnabled = Boolean(localStorageValue);
}, [localStorageValue]); dispatch({
type: UPDATE_AUTO_REFRESH_INTERVAL,
payload: localStorageValue,
});
setIsAutoRefreshfreshEnabled(isAutoRefreshEnabled);
}, [localStorageValue, dispatch]);
const params = useUrlQuery(); const params = useUrlQuery();
const dispatch = useDispatch<Dispatch<AppActions>>();
const [selectedOption, setSelectedOption] = useState<string>( const [selectedOption, setSelectedOption] = useState<string>(
localStorageValue || options[0].key, localStorageValue || options[0].key,
); );
@ -69,7 +84,7 @@ function AutoRefresh({ disabled = false }: AutoRefreshProps): JSX.Element {
useInterval(() => { useInterval(() => {
const selectedValue = getOption?.value; const selectedValue = getOption?.value;
if (disabled || !isAutoRefreshEnabled) { if (isDisabled || !isAutoRefreshEnabled) {
return; return;
} }
@ -125,21 +140,23 @@ function AutoRefresh({ disabled = false }: AutoRefreshProps): JSX.Element {
<Checkbox <Checkbox
onChange={onChangeAutoRefreshHandler} onChange={onChangeAutoRefreshHandler}
checked={isAutoRefreshEnabled} checked={isAutoRefreshEnabled}
disabled={disabled} disabled={isDisabled}
> >
Auto Refresh Auto Refresh
</Checkbox> </Checkbox>
<Divider /> <Divider />
<Typography.Paragraph>Refresh Interval</Typography.Paragraph> <Typography.Paragraph disabled={isDisabled}>
Refresh Interval
</Typography.Paragraph>
<Radio.Group onChange={onChangeHandler} value={selectedOption}> <Radio.Group onChange={onChangeHandler} value={selectedOption}>
<Space direction="vertical"> <Space direction="vertical">
{options {options
.filter((e) => e.label !== 'off') .filter((e) => e.label !== 'off')
.map((option) => ( .map((option) => (
<Radio key={option.key} value={option.key}> <Radio disabled={isDisabled} key={option.key} value={option.key}>
{option.label} {option.label}
</Radio> </Radio>
))} ))}

View File

@ -2,6 +2,8 @@ import { getDefaultOption } from 'container/TopNav/DateTimeSelection/config';
import { import {
GLOBAL_TIME_LOADING_START, GLOBAL_TIME_LOADING_START,
GlobalTimeAction, GlobalTimeAction,
UPDATE_AUTO_REFRESH_DISABLED,
UPDATE_AUTO_REFRESH_INTERVAL,
UPDATE_TIME_INTERVAL, UPDATE_TIME_INTERVAL,
} from 'types/actions/globalTime'; } from 'types/actions/globalTime';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
@ -13,6 +15,8 @@ const intitalState: GlobalReducer = {
selectedTime: getDefaultOption( selectedTime: getDefaultOption(
typeof window !== 'undefined' ? window?.location?.pathname : '', typeof window !== 'undefined' ? window?.location?.pathname : '',
), ),
isAutoRefreshDisabled: false,
selectedAutoRefreshInterval: '',
}; };
const globalTimeReducer = ( const globalTimeReducer = (
@ -35,6 +39,20 @@ const globalTimeReducer = (
}; };
} }
case UPDATE_AUTO_REFRESH_DISABLED: {
return {
...state,
isAutoRefreshDisabled: action.payload,
};
}
case UPDATE_AUTO_REFRESH_INTERVAL: {
return {
...state,
selectedAutoRefreshInterval: action.payload,
};
}
default: default:
return state; return state;
} }

View File

@ -106,7 +106,6 @@ export const LogsReducer = (
}${action.payload}`; }${action.payload}`;
const updatedParsedQuery = parseQuery(updatedQueryString); const updatedParsedQuery = parseQuery(updatedQueryString);
console.log({ updatedParsedQuery, updatedQueryString, action });
return { return {
...state, ...state,
searchFilter: { searchFilter: {

View File

@ -2,6 +2,8 @@ import { Time } from 'container/TopNav/DateTimeSelection/config';
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_INTERVAL = 'UPDATE_AUTO_REFRESH_INTERVAL';
export type GlobalTime = { export type GlobalTime = {
maxTime: number; maxTime: number;
@ -17,8 +19,22 @@ interface UpdateTimeInterval {
payload: UpdateTime; payload: UpdateTime;
} }
interface UpdateAutoRefreshDisabled {
type: typeof UPDATE_AUTO_REFRESH_DISABLED;
payload: boolean;
}
interface GlobalTimeLoading { interface GlobalTimeLoading {
type: typeof GLOBAL_TIME_LOADING_START; type: typeof GLOBAL_TIME_LOADING_START;
} }
export type GlobalTimeAction = UpdateTimeInterval | GlobalTimeLoading; interface UpdateAutoRefreshInterval {
type: typeof UPDATE_AUTO_REFRESH_INTERVAL;
payload: string;
}
export type GlobalTimeAction =
| UpdateTimeInterval
| GlobalTimeLoading
| UpdateAutoRefreshDisabled
| UpdateAutoRefreshInterval;

View File

@ -6,4 +6,6 @@ export interface GlobalReducer {
minTime: GlobalTime['minTime']; minTime: GlobalTime['minTime'];
loading: boolean; loading: boolean;
selectedTime: Time; selectedTime: Time;
isAutoRefreshDisabled: boolean;
selectedAutoRefreshInterval: string;
} }