diff --git a/frontend/src/constants/localStorage.ts b/frontend/src/constants/localStorage.ts
index 26512b627d..5dd89c624c 100644
--- a/frontend/src/constants/localStorage.ts
+++ b/frontend/src/constants/localStorage.ts
@@ -1,3 +1,3 @@
export enum LOCAL_STORAGE {
- METRICS_TIME_IN_DURATION = "metricsTimeDuration",
+ METRICS_TIME_IN_DURATION = "metricsTimeDurations",
}
diff --git a/frontend/src/modules/AppWrapper.tsx b/frontend/src/modules/AppWrapper.tsx
index 716345594b..b261ad3e32 100644
--- a/frontend/src/modules/AppWrapper.tsx
+++ b/frontend/src/modules/AppWrapper.tsx
@@ -1,17 +1,11 @@
import React, { Suspense } from "react";
-import { Layout, Spin } from "antd";
+import { Spin } from "antd";
import { useThemeSwitcher } from "react-css-theme-switcher";
import ROUTES from "Src/constants/routes";
import { IS_LOGGED_IN } from "Src/constants/auth";
-import {
- BrowserRouter as Router,
- Route,
- Switch,
- Redirect,
-} from "react-router-dom";
+import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
-import SideNav from "./Nav/SideNav";
-import TopNav from "./Nav/TopNav";
+import BaseLayout from "./BaseLayout";
import {
ServiceMetrics,
ServiceMap,
@@ -24,8 +18,6 @@ import {
IntstrumentationPage,
} from "Src/pages";
-const { Content, Footer } = Layout;
-
const App = () => {
const { status } = useThemeSwitcher();
@@ -34,47 +26,44 @@ const App = () => {
}
return (
-
-
-
-
-
-
- }>
-
-
-
-
-
-
-
-
-
-
- {
- return localStorage.getItem(IS_LOGGED_IN) === "yes" ? (
-
- ) : (
-
- );
- }}
- />
-
-
-
-
-
-
-
+
+ }>
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ return localStorage.getItem(IS_LOGGED_IN) === "yes" ? (
+
+ ) : (
+
+ );
+ }}
+ />
+
+
+
+
+
);
};
diff --git a/frontend/src/modules/BaseLayout.tsx b/frontend/src/modules/BaseLayout.tsx
new file mode 100644
index 0000000000..a36ac96e66
--- /dev/null
+++ b/frontend/src/modules/BaseLayout.tsx
@@ -0,0 +1,29 @@
+import React, { ReactNode } from "react";
+
+import { Layout } from "antd";
+import SideNav from "./Nav/SideNav";
+import TopNav from "./Nav/TopNav";
+const { Content, Footer } = Layout;
+
+interface BaseLayoutProps {
+ children: ReactNode;
+}
+
+const BaseLayout: React.FC = ({ children }) => {
+ return (
+
+
+
+
+
+ {children}
+
+
+
+
+ );
+};
+
+export default BaseLayout;
diff --git a/frontend/src/modules/Nav/TopNav/DateTimeSelector.tsx b/frontend/src/modules/Nav/TopNav/DateTimeSelector.tsx
index c3b890673b..03758461d4 100644
--- a/frontend/src/modules/Nav/TopNav/DateTimeSelector.tsx
+++ b/frontend/src/modules/Nav/TopNav/DateTimeSelector.tsx
@@ -1,27 +1,32 @@
import React, { useEffect, useState } from "react";
-import { Select, Button, Space, Form } from "antd";
+import { cloneDeep } from "lodash";
+import { Select as DefaultSelect, Button, Space, Form } from "antd";
import styled from "styled-components";
import { withRouter } from "react-router";
+import { getLocalStorageRouteKey } from "./utils";
import { RouteComponentProps, useLocation } from "react-router-dom";
import { connect } from "react-redux";
import ROUTES from "Src/constants/routes";
-
import CustomDateTimeModal from "./CustomDateTimeModal";
import { GlobalTime, updateTimeInterval } from "../../../store/actions";
import { StoreState } from "../../../store/reducers";
import FormItem from "antd/lib/form/FormItem";
-
+import {
+ Options,
+ ServiceMapOptions,
+ DefaultOptionsBasedOnRoute,
+} from "./config";
import { DateTimeRangeType } from "../../../store/actions";
import { METRICS_PAGE_QUERY_PARAM } from "Src/constants/query";
import { LOCAL_STORAGE } from "Src/constants/localStorage";
import moment from "moment";
-const { Option } = Select;
+const { Option } = DefaultSelect;
const DateTimeWrapper = styled.div`
margin-top: 20px;
justify-content: flex-end !important;
`;
-
+const Select = styled(DefaultSelect)``;
interface DateTimeSelectorProps extends RouteComponentProps {
currentpath?: string;
updateTimeInterval: Function;
@@ -32,21 +37,34 @@ interface DateTimeSelectorProps extends RouteComponentProps {
This components is mounted all the time. Use event listener to track changes.
*/
const _DateTimeSelector = (props: DateTimeSelectorProps) => {
- const defaultTime = "30min";
+ const location = useLocation();
+ const LocalStorageRouteKey: string = getLocalStorageRouteKey(
+ location.pathname,
+ );
+ const timeDurationInLocalStorage =
+ JSON.parse(localStorage.getItem(LOCAL_STORAGE.METRICS_TIME_IN_DURATION)) ||
+ {};
+ const options =
+ location.pathname === ROUTES.SERVICE_MAP ? ServiceMapOptions : Options;
+ let defaultTime = DefaultOptionsBasedOnRoute[LocalStorageRouteKey]
+ ? DefaultOptionsBasedOnRoute[LocalStorageRouteKey]
+ : DefaultOptionsBasedOnRoute.default;
+ if (timeDurationInLocalStorage[LocalStorageRouteKey]) {
+ defaultTime = timeDurationInLocalStorage[LocalStorageRouteKey];
+ }
+ const [currentLocalStorageRouteKey, setCurrentLocalStorageRouteKey] = useState(
+ LocalStorageRouteKey,
+ );
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 [refreshText, setRefreshText] = useState("");
- const [refreshButtonClick, setRefreshButtoClick] = useState(0);
+ const [refreshButtonClick, setRefreshButtonClick] = useState(0);
const [form_dtselector] = Form.useForm();
- const location = useLocation();
const updateTimeOnQueryParamChange = () => {
- const timeDurationInLocalStorage = localStorage.getItem(
- LOCAL_STORAGE.METRICS_TIME_IN_DURATION,
- );
const urlParams = new URLSearchParams(location.search);
const intervalInQueryParam = urlParams.get(METRICS_PAGE_QUERY_PARAM.interval);
const startTimeString = urlParams.get(METRICS_PAGE_QUERY_PARAM.startTime);
@@ -62,36 +80,46 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => {
const startTime = moment(Number(startTimeString));
const endTime = moment(Number(endTimeString));
setCustomTime(startTime, endTime, true);
+ } else if (currentLocalStorageRouteKey !== LocalStorageRouteKey) {
+ setMetricsTimeInterval(defaultTime);
+ setCurrentLocalStorageRouteKey(LocalStorageRouteKey);
}
// first pref: handle intervalInQueryParam
else if (intervalInQueryParam) {
- window.localStorage.setItem(
- LOCAL_STORAGE.METRICS_TIME_IN_DURATION,
- intervalInQueryParam,
- );
setMetricsTimeInterval(intervalInQueryParam);
- } else if (timeDurationInLocalStorage) {
- setMetricsTimeInterval(timeDurationInLocalStorage);
}
};
+ const setToLocalStorage = (val: string) => {
+ let timeDurationInLocalStorageObj = cloneDeep(timeDurationInLocalStorage);
+ if (timeDurationInLocalStorageObj) {
+ timeDurationInLocalStorageObj[LocalStorageRouteKey] = val;
+ } else {
+ timeDurationInLocalStorageObj = {
+ [LocalStorageRouteKey]: val,
+ };
+ }
+ window.localStorage.setItem(
+ LOCAL_STORAGE.METRICS_TIME_IN_DURATION,
+ JSON.stringify(timeDurationInLocalStorageObj),
+ );
+ };
+
+ useEffect(() => {
+ setMetricsTimeInterval(defaultTime);
+ }, []);
+
// On URL Change
useEffect(() => {
updateTimeOnQueryParamChange();
}, [location]);
- //On mount
- useEffect(() => {
- updateTimeOnQueryParamChange();
- }, []);
-
const setMetricsTimeInterval = (value: string) => {
props.updateTimeInterval(value);
setTimeInterval(value);
setEndTime(null);
setStartTime(null);
-
- window.localStorage.setItem(LOCAL_STORAGE.METRICS_TIME_IN_DURATION, value);
+ setToLocalStorage(value);
};
const setCustomTime = (
startTime: moment.Moment,
@@ -173,7 +201,7 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => {
};
const handleRefresh = () => {
- setRefreshButtoClick(refreshButtonClick + 1);
+ setRefreshButtonClick(refreshButtonClick + 1);
setMetricsTimeInterval(timeInterval);
};
@@ -187,15 +215,6 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => {
};
}, [props.location, refreshButtonClick]);
- const options = [
- { value: "custom", label: "Custom" },
- { value: "15min", label: "Last 15 min" },
- { value: "30min", label: "Last 30 min" },
- { value: "1hr", label: "Last 1 hour" },
- { value: "6hr", label: "Last 6 hour" },
- { value: "1day", label: "Last 1 day" },
- { value: "1week", label: "Last 1 week" },
- ];
if (props.location.pathname.startsWith(ROUTES.USAGE_EXPLORER)) {
return null;
} else {
@@ -205,6 +224,7 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => {
"YYYY/MM/DD HH:mm",
)}`
: timeInterval;
+
return (
@@ -256,8 +276,10 @@ const mapStateToProps = (state: StoreState): { globalTime: GlobalTime } => {
return { globalTime: state.globalTime };
};
-export const DateTimeSelector = connect(mapStateToProps, {
- updateTimeInterval: updateTimeInterval,
-})(_DateTimeSelector);
+export const DateTimeSelector = withRouter(
+ connect(mapStateToProps, {
+ updateTimeInterval: updateTimeInterval,
+ })(_DateTimeSelector),
+);
-export default withRouter(DateTimeSelector);
+export default DateTimeSelector;
diff --git a/frontend/src/modules/Nav/TopNav/config.ts b/frontend/src/modules/Nav/TopNav/config.ts
new file mode 100644
index 0000000000..1cad032461
--- /dev/null
+++ b/frontend/src/modules/Nav/TopNav/config.ts
@@ -0,0 +1,24 @@
+import ROUTES from "Src/constants/routes";
+
+export const Options = [
+ { value: "5min", label: "Last 5 min" },
+ { value: "15min", label: "Last 15 min" },
+ { value: "30min", label: "Last 30 min" },
+ { value: "1hr", label: "Last 1 hour" },
+ { value: "6hr", label: "Last 6 hour" },
+ { value: "1day", label: "Last 1 day" },
+ { value: "1week", label: "Last 1 week" },
+ { value: "custom", label: "Custom" },
+];
+
+export const ServiceMapOptions = [
+ { value: "1min", label: "Last 1 min" },
+ { value: "5min", label: "Last 5 min" },
+];
+
+export const DefaultOptionsBasedOnRoute = {
+ [ROUTES.SERVICE_MAP]: ServiceMapOptions[0].value,
+ [ROUTES.APPLICATION]: Options[0].value,
+ [ROUTES.SERVICE_METRICS]: Options[2].value,
+ default: Options[2].value,
+};
diff --git a/frontend/src/modules/Nav/TopNav/utils.ts b/frontend/src/modules/Nav/TopNav/utils.ts
new file mode 100644
index 0000000000..e46503312e
--- /dev/null
+++ b/frontend/src/modules/Nav/TopNav/utils.ts
@@ -0,0 +1,18 @@
+import ROUTES from "Src/constants/routes";
+
+export const getLocalStorageRouteKey = (pathName: string) => {
+ let localStorageKey = "";
+ const pathNameSplit = pathName.split("/");
+ if (!pathNameSplit[2]) {
+ localStorageKey = pathName;
+ } else {
+ Object.keys(ROUTES).forEach((key) => {
+ if (ROUTES[key].indexOf(":") > -1) {
+ if (ROUTES[key].indexOf(pathNameSplit[1]) > -1) {
+ localStorageKey = ROUTES[key];
+ }
+ }
+ });
+ }
+ return localStorageKey;
+};
diff --git a/frontend/src/modules/Servicemap/ServiceMap.tsx b/frontend/src/modules/Servicemap/ServiceMap.tsx
index e2db06ad70..e9cf6c2425 100644
--- a/frontend/src/modules/Servicemap/ServiceMap.tsx
+++ b/frontend/src/modules/Servicemap/ServiceMap.tsx
@@ -10,7 +10,7 @@ import {
import { Spin } from "antd";
import styled from "styled-components";
import { StoreState } from "../../store/reducers";
-import { getGraphData } from "./utils";
+import { getZoomPx, getGraphData, getTooltip } from "./utils";
import SelectService from "./SelectService";
import { ForceGraph2D } from "react-force-graph";
@@ -62,7 +62,7 @@ const ServiceMap = (props: ServiceMapProps) => {
useEffect(() => {
getServiceMapItems(globalTime);
getDetailedServiceMapItems(globalTime);
- }, []);
+ }, [globalTime]);
useEffect(() => {
fgRef.current && fgRef.current.d3Force("charge").strength(-400);
@@ -72,7 +72,7 @@ const ServiceMap = (props: ServiceMapProps) => {
}
const zoomToService = (value: string) => {
- fgRef && fgRef.current.zoomToFit(700, 380, (e) => e.id === value);
+ fgRef && fgRef.current.zoomToFit(700, getZoomPx(), (e) => e.id === value);
};
const { nodes, links } = getGraphData(serviceMap);
@@ -90,7 +90,7 @@ const ServiceMap = (props: ServiceMapProps) => {
fgRef.current.zoomToFit(100, 120);
}}
graphData={graphData}
- nodeLabel="id"
+ nodeLabel={getTooltip}
linkAutoColorBy={(d) => d.target}
linkDirectionalParticles="value"
linkDirectionalParticleSpeed={(d) => d.value}
@@ -112,21 +112,7 @@ const ServiceMap = (props: ServiceMapProps) => {
onNodeClick={(node) => {
const tooltip = document.querySelector(".graph-tooltip");
if (tooltip && node) {
- tooltip.innerHTML = `
-
${node.id}
-
-
P99 latency:
-
${node.p99 / 1000000}ms
-
-
-
Request:
-
${node.callRate}/sec
-
-
-
Error Rate:
-
${node.errorRate}%
-
-
`;
+ tooltip.innerHTML = getTooltip(node);
}
}}
nodePointerAreaPaint={(node, color, ctx) => {
diff --git a/frontend/src/modules/Servicemap/utils.ts b/frontend/src/modules/Servicemap/utils.ts
index 296cd05060..bec9c16a68 100644
--- a/frontend/src/modules/Servicemap/utils.ts
+++ b/frontend/src/modules/Servicemap/utils.ts
@@ -73,3 +73,37 @@ export const getGraphData = (serviceMap: serviceMapStore): graphDataType => {
links,
};
};
+
+export const getZoomPx = (): number => {
+ const width = window.screen.width;
+ if (width < 1400) {
+ return 190;
+ } else if (width > 1400 && width < 2500) {
+ return 380;
+ } else if (width > 2500) {
+ return 360;
+ }
+};
+
+export const getTooltip = (node: {
+ p99: number;
+ errorRate: number;
+ callRate: number;
+ id: string;
+}) => {
+ return `
+
${node.id}
+
+
P99 latency:
+
${node.p99 / 1000000}ms
+
+
+
Request:
+
${node.callRate}/sec
+
+
+
Error Rate:
+
${node.errorRate}%
+
+
`;
+};
diff --git a/frontend/src/store/actions/global.ts b/frontend/src/store/actions/global.ts
index 4af5e7adce..25c25852dc 100644
--- a/frontend/src/store/actions/global.ts
+++ b/frontend/src/store/actions/global.ts
@@ -23,6 +23,15 @@ export const updateTimeInterval = (
// set directly based on that. Assuming datetimeRange values are in ms, and minTime is 0th element
switch (interval) {
+ case "1min":
+ maxTime = Date.now() * 1000000; // in nano sec
+ minTime = (Date.now() - 1 * 60 * 1000) * 1000000;
+ break;
+ case "5min":
+ maxTime = Date.now() * 1000000; // in nano sec
+ minTime = (Date.now() - 5 * 60 * 1000) * 1000000;
+ break;
+
case "15min":
maxTime = Date.now() * 1000000; // in nano sec
minTime = (Date.now() - 15 * 60 * 1000) * 1000000;
diff --git a/frontend/src/store/actions/serviceMap.ts b/frontend/src/store/actions/serviceMap.ts
index a8a660d423..fbad4148c3 100644
--- a/frontend/src/store/actions/serviceMap.ts
+++ b/frontend/src/store/actions/serviceMap.ts
@@ -38,6 +38,11 @@ export interface servicesAction {
export const getServiceMapItems = (globalTime: GlobalTime) => {
return async (dispatch: Dispatch) => {
+ dispatch({
+ type: ActionTypes.getServiceMapItems,
+ payload: [],
+ });
+
let request_string =
"/serviceMapDependencies?start=" +
globalTime.minTime +
@@ -45,7 +50,7 @@ export const getServiceMapItems = (globalTime: GlobalTime) => {
globalTime.maxTime;
const response = await api.get(apiV1 + request_string);
-
+
dispatch({
type: ActionTypes.getServiceMapItems,
payload: response.data,
@@ -55,11 +60,16 @@ export const getServiceMapItems = (globalTime: GlobalTime) => {
export const getDetailedServiceMapItems = (globalTime: GlobalTime) => {
return async (dispatch: Dispatch) => {
+ dispatch({
+ type: ActionTypes.getServices,
+ payload: [],
+ });
+
let request_string =
"/services?start=" + globalTime.minTime + "&end=" + globalTime.maxTime;
const response = await api.get(apiV1 + request_string);
-
+
dispatch({
type: ActionTypes.getServices,
payload: response.data,