From 7f495181a7a9a7cc8fc3a72487b73f9353f0d06a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Chimanshu=E2=80=9D?= Date: Thu, 21 Jan 2021 00:27:54 +0530 Subject: [PATCH] View traces fix for past 15 min interval --- frontend/src/actions/types.ts | 20 ++-- frontend/src/components/DateTimeSelector.tsx | 105 +++++++++++++----- .../components/metrics/LatencyLineChart.tsx | 4 +- .../src/components/metrics/ServiceMetrics.tsx | 12 +- frontend/src/constants/query.ts | 5 +- frontend/src/reducers/global.ts | 2 +- 6 files changed, 106 insertions(+), 42 deletions(-) diff --git a/frontend/src/actions/types.ts b/frontend/src/actions/types.ts index 4ee504f2eb..d27e582ea4 100644 --- a/frontend/src/actions/types.ts +++ b/frontend/src/actions/types.ts @@ -10,16 +10,16 @@ import { getUsageDataAction } from "./usage"; import { updateTimeIntervalAction } from "./global"; export enum ActionTypes { - updateTraceFilters, - updateInput, - fetchTraces, - fetchTraceItem, - getServicesList, - getServiceMetrics, - getTopEndpoints, - getUsageData, - updateTimeInterval, - getFilteredTraceMetrics, + updateTraceFilters= "UPDATE_TRACES_FILTER", + updateInput = "UPDATE_INPUT", + fetchTraces = "FETCH_TRACES", + fetchTraceItem = "FETCH_TRACE_ITEM", + getServicesList = "GET_SERVICE_LIST", + getServiceMetrics = "GET_SERVICE_METRICS", + getTopEndpoints = "GET_TOP_ENDPOINTS", + getUsageData = "GET_USAGE_DATE", + updateTimeInterval = "UPDATE_TIME_INTERVAL", + getFilteredTraceMetrics = "GET_FILTERED_TRACE_METRICS", } export type Action = diff --git a/frontend/src/components/DateTimeSelector.tsx b/frontend/src/components/DateTimeSelector.tsx index 886df1621a..ca0f88a2df 100644 --- a/frontend/src/components/DateTimeSelector.tsx +++ b/frontend/src/components/DateTimeSelector.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { Select, Button, Space, Form } from "antd"; import styled from "styled-components"; import { withRouter } from "react-router"; -import { RouteComponentProps } from "react-router-dom"; +import { RouteComponentProps, useLocation } from "react-router-dom"; import { connect } from "react-redux"; import CustomDateTimeModal from "./CustomDateTimeModal"; @@ -13,6 +13,7 @@ import FormItem from "antd/lib/form/FormItem"; import { DateTimeRangeType } from "../actions"; import { METRICS_PAGE_QUERY_PARAM } from "../constants/query"; import { LOCAL_STORAGE } from "../constants/localStorage"; +import moment from "moment"; const { Option } = Select; const DateTimeWrapper = styled.div` @@ -25,45 +26,99 @@ interface DateTimeSelectorProps extends RouteComponentProps { globalTime: GlobalTime; } +/* +This components is mounted all the time. Use event listener to track changes. + */ const _DateTimeSelector = (props: DateTimeSelectorProps) => { - const defaultTime = "15min"; + const defaultTime = "30min"; const [customDTPickerVisible, setCustomDTPickerVisible] = useState(false); const [timeInterval, setTimeInterval] = useState(defaultTime); + const [startTime, setStartTime] = useState(null); + const [endTime, setEndTime] = useState(null); const [refreshButtonHidden, setRefreshButtonHidden] = useState(false); const [form_dtselector] = Form.useForm(); - - useEffect(() => { + const location = useLocation(); + const updateTimeOnQueryParamChange = ()=>{ const timeDurationInLocalStorage = localStorage.getItem( LOCAL_STORAGE.METRICS_TIME_IN_DURATION, ); + const urlParams = new URLSearchParams(window.location.search); - const timeInQueryParam = urlParams.get(METRICS_PAGE_QUERY_PARAM.time); - if (timeInQueryParam) { - setMetricsTime(timeInQueryParam); - } else if (timeDurationInLocalStorage) { - setMetricsTime(timeDurationInLocalStorage); + const intervalInQueryParam = urlParams.get(METRICS_PAGE_QUERY_PARAM.interval); + const startTimeString = urlParams.get(METRICS_PAGE_QUERY_PARAM.startTime); + const endTimeString = urlParams.get(METRICS_PAGE_QUERY_PARAM.endTime); + + // first pref: handle both startTime and endTime + if(startTimeString && startTimeString.length>0 && endTimeString && endTimeString.length>0){ + const startTime = moment(Number(startTimeString)); + const endTime = moment(Number(endTimeString)); + setCustomTime(startTime,endTime,true) } + // first pref: handle intervalInQueryParam + else if (intervalInQueryParam) { + window.localStorage.setItem( + LOCAL_STORAGE.METRICS_TIME_IN_DURATION, + intervalInQueryParam, + ); + setMetricsTimeInterval(intervalInQueryParam); + } else if (timeDurationInLocalStorage) { + setMetricsTimeInterval(timeDurationInLocalStorage); + } + + } + + // On URL Change + useEffect(() => { + updateTimeOnQueryParamChange(); + }, [location]); + + //On mount + useEffect(() => { + updateTimeOnQueryParamChange(); }, []); - const setMetricsTime = (value: string) => { - props.history.push({ - search: `?${METRICS_PAGE_QUERY_PARAM.time}=${value}`, - }); //pass time in URL query param for all choices except custom in datetime picker + const setMetricsTimeInterval= (value: string) => { props.updateTimeInterval(value); setTimeInterval(value); + setEndTime(null); + setStartTime(null); + + window.localStorage.setItem( + LOCAL_STORAGE.METRICS_TIME_IN_DURATION, + value, + ); }; + const setCustomTime= (startTime: moment.Moment, endTime: moment.Moment, triggeredByURLChange = false) => { + props.updateTimeInterval("custom", [ + startTime.valueOf(), + endTime.valueOf(), + ]); + setEndTime(endTime); + setStartTime(startTime); + }; + + const updateUrlForTimeInterval = (value: string) => { + props.history.push({ + search: `?${METRICS_PAGE_QUERY_PARAM.interval}=${value}`, + }); //pass time in URL query param for all choices except custom in datetime picker + }; + + const updateUrlForCustomTime= (startTime: moment.Moment, endTime: moment.Moment, triggeredByURLChange = false) => { + props.history.push(`?${METRICS_PAGE_QUERY_PARAM.startTime}=${startTime.valueOf()}&${METRICS_PAGE_QUERY_PARAM.endTime}=${endTime.valueOf()}`); + } + const handleOnSelect = (value: string) => { if (value === "custom") { setCustomDTPickerVisible(true); } else { - setTimeInterval(value); + updateUrlForTimeInterval(value); setRefreshButtonHidden(false); // for normal intervals, show refresh button } }; //function called on clicking apply in customDateTimeModal - const handleOk = (dateTimeRange: DateTimeRangeType) => { + const handleCustomDate = (dateTimeRange: DateTimeRangeType) => { // pass values in ms [minTime, maxTime] if ( dateTimeRange !== null && @@ -71,10 +126,10 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => { dateTimeRange[0] !== null && dateTimeRange[1] !== null ) { - props.updateTimeInterval("custom", [ - dateTimeRange[0].valueOf(), - dateTimeRange[1].valueOf(), - ]); + const startTime = dateTimeRange[0].valueOf(); + const endTime = dateTimeRange[1].valueOf(); + + updateUrlForCustomTime(moment(startTime),moment(endTime)) //setting globaltime setRefreshButtonHidden(true); form_dtselector.setFieldsValue({ @@ -104,11 +159,7 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => { }; const handleRefresh = () => { - window.localStorage.setItem( - LOCAL_STORAGE.METRICS_TIME_IN_DURATION, - timeInterval, - ); - setMetricsTime(timeInterval); + setMetricsTimeInterval(timeInterval); }; const options = [ @@ -123,6 +174,8 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => { if (props.location.pathname.startsWith("/usage-explorer")) { return null; } else { + + const inputLabeLToShow = startTime && endTime? (`${startTime.format("YYYY/MM/DD HH:mm")} - ${endTime.format("YYYY/MM/DD HH:mm")}`):timeInterval return ( @@ -133,7 +186,7 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => { style={{ marginTop: 10, marginBottom: 10 }} > - {options.map(({ value, label }) => ( ))} @@ -148,7 +201,7 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => { { setCustomDTPickerVisible(false); }} diff --git a/frontend/src/components/metrics/LatencyLineChart.tsx b/frontend/src/components/metrics/LatencyLineChart.tsx index 31cd23b010..d180b6838a 100644 --- a/frontend/src/components/metrics/LatencyLineChart.tsx +++ b/frontend/src/components/metrics/LatencyLineChart.tsx @@ -186,7 +186,9 @@ class LatencyLineChart extends React.Component { ycoordinate={this.state.ycoordinate} > this.props.popupClickHandler(this.state.firstpoint_ts)} + onClick={() => { + this.props.popupClickHandler(this.state.firstpoint_ts) + }} > View Traces diff --git a/frontend/src/components/metrics/ServiceMetrics.tsx b/frontend/src/components/metrics/ServiceMetrics.tsx index 00d3b62f63..b71862c4d9 100644 --- a/frontend/src/components/metrics/ServiceMetrics.tsx +++ b/frontend/src/components/metrics/ServiceMetrics.tsx @@ -17,6 +17,7 @@ import LatencyLineChart from "./LatencyLineChart"; import RequestRateChart from "./RequestRateChart"; import ErrorRateChart from "./ErrorRateChart"; import TopEndpointsTable from "./TopEndpointsTable"; +import { METRICS_PAGE_QUERY_PARAM } from "../../constants/query"; const { TabPane } = Tabs; @@ -38,11 +39,16 @@ const _ServiceMetrics = (props: ServicesMetricsProps) => { }, [props.globalTime, params.servicename]); const onTracePopupClick = (timestamp: number) => { + const tMinus5Min = timestamp / 1000000 - 5 * 60 * 1000; + const currentTime = timestamp / 1000000; + props.updateTimeInterval("custom", [ - timestamp / 1000000 - 5 * 60 * 1000, - timestamp / 1000000, + tMinus5Min, + currentTime, ]); // updateTimeInterval takes second range in ms -- give -5 min to selected time, - props.history.push("/traces"); + + + props.history.push(`/traces?${METRICS_PAGE_QUERY_PARAM.startTime}=${tMinus5Min}&${METRICS_PAGE_QUERY_PARAM.endTime}=${currentTime}`); }; return ( diff --git a/frontend/src/constants/query.ts b/frontend/src/constants/query.ts index 540aca58d2..efc9065b7d 100644 --- a/frontend/src/constants/query.ts +++ b/frontend/src/constants/query.ts @@ -1,3 +1,6 @@ export enum METRICS_PAGE_QUERY_PARAM { - time = "time", + interval = "interval", + startTime = "startTime", + endTime = "endTime" } + diff --git a/frontend/src/reducers/global.ts b/frontend/src/reducers/global.ts index c7ff91e7be..95d2b8429d 100644 --- a/frontend/src/reducers/global.ts +++ b/frontend/src/reducers/global.ts @@ -3,7 +3,7 @@ import { ActionTypes, Action, GlobalTime } from "../actions"; export const updateGlobalTimeReducer = ( state: GlobalTime = { maxTime: Date.now() * 1000000, - minTime: (Date.now() - 15 * 60 * 1000) * 1000000, + minTime: (Date.now() - 15 * 60 * 1000) * 1000000 }, action: Action, ) => {