fix: add suggestion to chart in monitor if is no data because the time interval is less than the collection interval (#58)
* fix: add suggestion to chart in monitor if is no data because the time interval is less than the collection interval * chore: update release notes --------- Co-authored-by: yaojiping <yaojiping@infini.ltd>
This commit is contained in:
parent
53178fa869
commit
b8b24f8fab
|
@ -21,6 +21,7 @@ Information about release notes of INFINI Console is provided here.
|
|||
- Optimize UI of agent list when its columns are overflow.
|
||||
- Add loading to each row in overview table.
|
||||
- Adapter metrics query with cluster id and cluster uuid
|
||||
- Add suggestion to chart in monitor if is no data because the time interval is less than the collection interval.
|
||||
|
||||
|
||||
## 1.27.0 (2024-12-09)
|
||||
|
|
|
@ -8,7 +8,7 @@ import request from "@/utils/request";
|
|||
import MetricChart from "@/pages/Platform/Overview/components/MetricChart";
|
||||
|
||||
export default (props) => {
|
||||
const { action, timeRange, timezone, timeout, overview, renderExtraMetric, metrics = [], queryParams } = props
|
||||
const { action, timeRange, timezone, timeout, overview, renderExtraMetric, metrics = [], queryParams, handleTimeIntervalChange } = props
|
||||
|
||||
return (
|
||||
<div className={styles.metricChart}>
|
||||
|
@ -60,6 +60,7 @@ export default (props) => {
|
|||
};
|
||||
return <MetricLine {...config} key={metric.key} />
|
||||
}}
|
||||
handleTimeIntervalChange={handleTimeIntervalChange}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useMemo } from "react";
|
||||
import React, { useState, useMemo, useEffect } from "react";
|
||||
import { Tabs, Button } from "antd";
|
||||
import { ESPrefix } from "@/services/common";
|
||||
import useFetch from "@/lib/hooks/use_fetch";
|
||||
|
@ -15,7 +15,7 @@ import { formatMessage } from "umi/locale";
|
|||
import DatePicker from "@/common/src/DatePicker";
|
||||
import { getLocale } from "umi/locale";
|
||||
import { getTimezone } from "@/utils/utils";
|
||||
import { TIMEOUT_CACHE_KEY } from "../../Monitor";
|
||||
import { getAllTimeSettingsCache, TIME_SETTINGS_KEY } from "../../Monitor";
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
|
@ -30,6 +30,8 @@ export default (props) => {
|
|||
metrics = [],
|
||||
} = props;
|
||||
|
||||
const allTimeSettingsCache = getAllTimeSettingsCache() || {}
|
||||
|
||||
const [spinning, setSpinning] = useState(false);
|
||||
const [state, setState] = useState({
|
||||
timeRange: {
|
||||
|
@ -37,12 +39,12 @@ export default (props) => {
|
|||
max: "now",
|
||||
timeFormatter: formatter.dates(1),
|
||||
},
|
||||
timeInterval: '',
|
||||
timeout: localStorage.getItem(TIMEOUT_CACHE_KEY) || '120s',
|
||||
timeInterval: allTimeSettingsCache.timeInterval,
|
||||
timeout: allTimeSettingsCache.timeout || '120s',
|
||||
});
|
||||
|
||||
const [refresh, setRefresh] = useState({ isRefreshPaused: false });
|
||||
const [timeZone, setTimeZone] = useState(() => getTimezone());
|
||||
const [refresh, setRefresh] = useState({ isRefreshPaused: allTimeSettingsCache.isRefreshPaused || false, refreshInterval: allTimeSettingsCache.refreshInterval || 30000 });
|
||||
const [timeZone, setTimeZone] = useState(() => allTimeSettingsCache.timeZone || getTimezone());
|
||||
|
||||
const handleTimeChange = ({ start, end, timeInterval, timeout }) => {
|
||||
const bounds = calculateBounds({
|
||||
|
@ -65,6 +67,15 @@ export default (props) => {
|
|||
setSpinning(true);
|
||||
};
|
||||
|
||||
const onTimeSettingsChange = (timeSettings) => {
|
||||
let allTimeSettings = getAllTimeSettingsCache();
|
||||
allTimeSettings = {
|
||||
...(allTimeSettings || {}),
|
||||
...(timeSettings || {})
|
||||
}
|
||||
localStorage.setItem(TIME_SETTINGS_KEY, JSON.stringify(allTimeSettings))
|
||||
}
|
||||
|
||||
const [linkMoreNew] = useMemo(() => {
|
||||
let urlObj = parseUrl(linkMore);
|
||||
let query = urlObj.query;
|
||||
|
@ -94,14 +105,21 @@ export default (props) => {
|
|||
end={state.timeRange.max}
|
||||
onRangeChange={handleTimeChange}
|
||||
{...refresh}
|
||||
onRefreshChange={setRefresh}
|
||||
onRefreshChange={(newRefresh) => {
|
||||
onTimeSettingsChange(newRefresh)
|
||||
setRefresh(newRefresh)
|
||||
}}
|
||||
onRefresh={handleTimeChange}
|
||||
showTimeSetting={true}
|
||||
showTimeInterval={true}
|
||||
showTimeout={true}
|
||||
timeout={state.timeout}
|
||||
timeInterval={state.timeInterval}
|
||||
onTimeSettingChange={(timeSetting) => {
|
||||
localStorage.setItem(TIMEOUT_CACHE_KEY, timeSetting.timeout)
|
||||
onTimeSettingsChange({
|
||||
timeInterval: timeSetting.timeInterval,
|
||||
timeout: timeSetting.timeout
|
||||
})
|
||||
setState({
|
||||
...state,
|
||||
timeInterval: timeSetting.timeInterval,
|
||||
|
@ -109,7 +127,12 @@ export default (props) => {
|
|||
});
|
||||
}}
|
||||
timeZone={timeZone}
|
||||
onTimeZoneChange={setTimeZone}
|
||||
onTimeZoneChange={(timeZone) => {
|
||||
onTimeSettingsChange({
|
||||
timeZone,
|
||||
})
|
||||
setTimeZone(timeZone)
|
||||
}}
|
||||
recentlyUsedRangesKey={'overview-detail'}
|
||||
/>
|
||||
</div>
|
||||
|
@ -124,6 +147,15 @@ export default (props) => {
|
|||
renderExtraMetric={renderExtraMetric}
|
||||
metrics={metrics}
|
||||
{...state}
|
||||
handleTimeIntervalChange={(timeInterval) => {
|
||||
onTimeSettingsChange({
|
||||
timeInterval,
|
||||
})
|
||||
setState({
|
||||
...state,
|
||||
timeInterval,
|
||||
});
|
||||
}}
|
||||
bucketSize={state.timeInterval}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
.lineWrapper {
|
||||
height: 150px;
|
||||
height: 180px;
|
||||
width: 48.8%;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 2px;
|
||||
|
@ -45,7 +45,7 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
.chartBody {
|
||||
height: 120px;
|
||||
height: 150px;
|
||||
padding: 0 2px 2px 2px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,7 +41,17 @@ const formatTimeout = (timeout) => {
|
|||
return timeout
|
||||
}
|
||||
|
||||
export const TIMEOUT_CACHE_KEY = "monitor-timeout"
|
||||
export const TIME_SETTINGS_KEY = "monitor-time-settings"
|
||||
|
||||
export const getAllTimeSettingsCache = () => {
|
||||
const allTimeSettings = localStorage.getItem(TIME_SETTINGS_KEY) || `{}`
|
||||
try {
|
||||
const object = JSON.parse(allTimeSettings);
|
||||
return object || {}
|
||||
} catch (error) {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
const Monitor = (props) => {
|
||||
const {
|
||||
|
@ -54,6 +64,8 @@ const Monitor = (props) => {
|
|||
checkPaneParams,
|
||||
} = props;
|
||||
|
||||
const allTimeSettingsCache = getAllTimeSettingsCache()
|
||||
|
||||
const [param, setParam] = useQueryParam("_g", JsonParam);
|
||||
|
||||
const [spinning, setSpinning] = useState(false);
|
||||
|
@ -65,15 +77,15 @@ const Monitor = (props) => {
|
|||
max: param?.timeRange?.max || "now",
|
||||
timeFormatter: formatter.dates(1),
|
||||
},
|
||||
timeInterval: formatTimeInterval(param?.timeInterval),
|
||||
timeout: formatTimeout(param?.timeout) || localStorage.getItem(TIMEOUT_CACHE_KEY) || '120s',
|
||||
timeInterval: formatTimeInterval(param?.timeInterval) || allTimeSettingsCache.timeInterval,
|
||||
timeout: formatTimeout(param?.timeout) || allTimeSettingsCache.timeout || '120s',
|
||||
param: param,
|
||||
refresh: true
|
||||
refresh: true,
|
||||
})
|
||||
);
|
||||
|
||||
const [refresh, setRefresh] = useState({ isRefreshPaused: false, refreshInterval: 30000 });
|
||||
const [timeZone, setTimeZone] = useState(() => getTimezone());
|
||||
const [refresh, setRefresh] = useState({ isRefreshPaused: allTimeSettingsCache.isRefreshPaused || false, refreshInterval: allTimeSettingsCache.refreshInterval || 30000 });
|
||||
const [timeZone, setTimeZone] = useState(() => allTimeSettingsCache.timeZone || getTimezone());
|
||||
|
||||
useEffect(() => {
|
||||
setParam({ ...param, timeRange: state.timeRange, timeInterval: state.timeInterval, timeout: state.timeout });
|
||||
|
@ -108,6 +120,15 @@ const Monitor = (props) => {
|
|||
});
|
||||
};
|
||||
|
||||
const onTimeSettingsChange = (timeSettings) => {
|
||||
let allTimeSettings = getAllTimeSettingsCache();
|
||||
allTimeSettings = {
|
||||
...(allTimeSettings || {}),
|
||||
...(timeSettings || {})
|
||||
}
|
||||
localStorage.setItem(TIME_SETTINGS_KEY, JSON.stringify(allTimeSettings))
|
||||
}
|
||||
|
||||
const breadcrumbList = getBreadcrumbList(state);
|
||||
|
||||
const isAgent = useMemo(() => {
|
||||
|
@ -133,7 +154,10 @@ const Monitor = (props) => {
|
|||
handleTimeChange({ start, end })
|
||||
}}
|
||||
{...refresh}
|
||||
onRefreshChange={setRefresh}
|
||||
onRefreshChange={(newRefresh) => {
|
||||
onTimeSettingsChange(newRefresh)
|
||||
setRefresh(newRefresh)
|
||||
}}
|
||||
onRefresh={handleTimeChange}
|
||||
showTimeSetting={true}
|
||||
showTimeInterval={true}
|
||||
|
@ -141,7 +165,10 @@ const Monitor = (props) => {
|
|||
showTimeout={true}
|
||||
timeout={state.timeout}
|
||||
onTimeSettingChange={(timeSetting) => {
|
||||
localStorage.setItem(TIMEOUT_CACHE_KEY, timeSetting.timeout)
|
||||
onTimeSettingsChange({
|
||||
timeInterval: timeSetting.timeInterval,
|
||||
timeout: timeSetting.timeout
|
||||
})
|
||||
setState({
|
||||
...state,
|
||||
timeInterval: timeSetting.timeInterval,
|
||||
|
@ -149,7 +176,12 @@ const Monitor = (props) => {
|
|||
});
|
||||
}}
|
||||
timeZone={timeZone}
|
||||
onTimeZoneChange={setTimeZone}
|
||||
onTimeZoneChange={(timeZone) => {
|
||||
onTimeSettingsChange({
|
||||
timeZone,
|
||||
})
|
||||
setTimeZone(timeZone)
|
||||
}}
|
||||
recentlyUsedRangesKey={'monitor'}
|
||||
/>
|
||||
<CollectStatus fetchUrl={`${ESPrefix}/${selectedCluster?.id}/_collection_stats`}/>
|
||||
|
@ -188,6 +220,15 @@ const Monitor = (props) => {
|
|||
isAgent={isAgent}
|
||||
{...state}
|
||||
handleTimeChange={handleTimeChange}
|
||||
handleTimeIntervalChange={(timeInterval) => {
|
||||
onTimeSettingsChange({
|
||||
timeInterval,
|
||||
})
|
||||
setState({
|
||||
...state,
|
||||
timeInterval,
|
||||
});
|
||||
}}
|
||||
setSpinning={setSpinning}
|
||||
{...extraParams}
|
||||
bucketSize={state.timeInterval}
|
||||
|
|
|
@ -274,7 +274,7 @@ export default forwardRef((props: IProps, ref: any) => {
|
|||
<listItemConfig.component
|
||||
data={item}
|
||||
id={infoField}
|
||||
isActive={selectedItem?._id == item?._id}
|
||||
isActive={listItemConfig.getId(selectedItem?._id) == infoField}
|
||||
onSelect={() => {
|
||||
setSelectedItem(item);
|
||||
drawRef.current?.open();
|
||||
|
|
|
@ -89,6 +89,7 @@ export default {
|
|||
"cluster.monitor.node.title": "Node",
|
||||
"cluster.monitor.index.title": "Index",
|
||||
"cluster.monitor.queue.title": "Thread Pool",
|
||||
"cluster.monitor.shard.title": "Shard",
|
||||
"cluster.monitor.summary.name": "Cluster Name",
|
||||
"cluster.monitor.summary.online_time": "Uptime",
|
||||
"cluster.monitor.summary.version": "Version",
|
||||
|
@ -352,6 +353,12 @@ export default {
|
|||
"cluster.metrics.request.copy": "Copy request",
|
||||
"cluster.metrics.request.copy.success": "Copy request successfully",
|
||||
|
||||
"cluster.metrics.time_interval.reload": "Apply global time interval({time_interval})",
|
||||
"cluster.metrics.time_interval.set.global": "Global",
|
||||
"cluster.metrics.time_interval.set.current": "Current",
|
||||
"cluster.metrics.time_interval.empty": "No data, the time interval is less than the collection interval, suggest to set the time interval to {min_bucket_size} seconds.",
|
||||
"cluster.metrics.time_interval.apply": "Apply suggestion",
|
||||
|
||||
"cluster.collect.last_active_at": "Last Active At",
|
||||
|
||||
};
|
||||
|
|
|
@ -80,6 +80,7 @@ export default {
|
|||
"cluster.monitor.node.title": "节点",
|
||||
"cluster.monitor.index.title": "索引",
|
||||
"cluster.monitor.queue.title": "线程池",
|
||||
"cluster.monitor.shard.title": "分片",
|
||||
"cluster.monitor.summary.name": "集群名称",
|
||||
"cluster.monitor.summary.online_time": "在线时长",
|
||||
"cluster.monitor.summary.version": "集群版本",
|
||||
|
@ -337,6 +338,12 @@ export default {
|
|||
"cluster.metrics.request.copy": "复制请求",
|
||||
"cluster.metrics.request.copy.success": "复制请求成功",
|
||||
|
||||
"cluster.metrics.time_interval.reload": "设置为全局时间间隔({time_interval})",
|
||||
"cluster.metrics.time_interval.set.global": "全局",
|
||||
"cluster.metrics.time_interval.set.current": "当前",
|
||||
"cluster.metrics.time_interval.empty": "暂无数据,当前时间间隔小于采集间隔,建议设置时间间隔为{min_bucket_size}秒",
|
||||
"cluster.metrics.time_interval.apply": "应用建议",
|
||||
|
||||
"cluster.collect.last_active_at": "最后活动时间",
|
||||
|
||||
};
|
||||
|
|
|
@ -26,26 +26,9 @@ export const isVersionGTE6 = (cluster) => {
|
|||
return false
|
||||
}
|
||||
|
||||
export default ({
|
||||
selectedCluster,
|
||||
clusterID,
|
||||
timeRange,
|
||||
handleTimeChange,
|
||||
timezone,
|
||||
bucketSize,
|
||||
timeout,
|
||||
refresh,
|
||||
}) => {
|
||||
export default (props) => {
|
||||
|
||||
const tabProps = {
|
||||
clusterID,
|
||||
timeRange,
|
||||
handleTimeChange,
|
||||
timezone,
|
||||
bucketSize,
|
||||
timeout,
|
||||
refresh
|
||||
}
|
||||
const { selectedCluster, clusterID } = props
|
||||
|
||||
const isVersionGTE8_6 = useMemo(() => {
|
||||
return shouldHaveModelInferenceBreaker(selectedCluster)
|
||||
|
@ -83,7 +66,7 @@ export default ({
|
|||
})}
|
||||
>
|
||||
<ClusterMetric
|
||||
{...tabProps}
|
||||
{...props}
|
||||
fetchUrl={`${ESPrefix}/${clusterID}/cluster_metrics`}
|
||||
metrics={[
|
||||
'cluster_health',
|
||||
|
@ -107,7 +90,7 @@ export default ({
|
|||
})}
|
||||
>
|
||||
<NodeMetric
|
||||
{...tabProps}
|
||||
{...props}
|
||||
param={param}
|
||||
setParam={setParam}
|
||||
metrics={[
|
||||
|
@ -249,7 +232,7 @@ export default ({
|
|||
})}
|
||||
>
|
||||
<IndexMetric
|
||||
{...tabProps}
|
||||
{...props}
|
||||
param={param}
|
||||
setParam={setParam}
|
||||
metrics={[
|
||||
|
@ -327,7 +310,7 @@ export default ({
|
|||
})}
|
||||
>
|
||||
<QueueMetric
|
||||
{...tabProps}
|
||||
{...props}
|
||||
param={param}
|
||||
setParam={setParam}
|
||||
metrics={[
|
||||
|
|
|
@ -1,25 +1,15 @@
|
|||
import ClusterMetric from "../../components/cluster_metric";
|
||||
import { ESPrefix } from "@/services/common";
|
||||
|
||||
export default ({
|
||||
clusterID,
|
||||
timeRange,
|
||||
handleTimeChange,
|
||||
bucketSize,
|
||||
timeout,
|
||||
timezone,
|
||||
refresh
|
||||
}) => {
|
||||
export default (props) => {
|
||||
|
||||
const { clusterID } = props
|
||||
|
||||
return (
|
||||
<ClusterMetric
|
||||
timezone={timezone}
|
||||
timeRange={timeRange}
|
||||
timeout={timeout}
|
||||
refresh={refresh}
|
||||
handleTimeChange={handleTimeChange}
|
||||
{...props}
|
||||
overview={1}
|
||||
fetchUrl={`${ESPrefix}/${clusterID}/cluster_metrics`}
|
||||
bucketSize={bucketSize}
|
||||
metrics={['index_throughput', 'search_throughput', 'index_latency', 'search_latency']}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -2,33 +2,19 @@ import { useState } from "react";
|
|||
import StatisticBar from "./statistic_bar";
|
||||
import IndexMetric from "../../components/index_metric";
|
||||
|
||||
export default ({
|
||||
clusterID,
|
||||
indexName,
|
||||
timeRange,
|
||||
handleTimeChange,
|
||||
shardID,
|
||||
bucketSize,
|
||||
timezone,
|
||||
timeout,
|
||||
refresh,
|
||||
}) => {
|
||||
export default (props) => {
|
||||
|
||||
const { indexName } = props
|
||||
|
||||
const [param, setParam] = useState({
|
||||
show_top: false,
|
||||
index_name: indexName,
|
||||
});
|
||||
return (
|
||||
<IndexMetric
|
||||
clusterID={clusterID}
|
||||
timezone={timezone}
|
||||
timeRange={timeRange}
|
||||
handleTimeChange={handleTimeChange}
|
||||
{...props}
|
||||
param={param}
|
||||
setParam={setParam}
|
||||
shardID={shardID}
|
||||
bucketSize={bucketSize}
|
||||
timeout={timeout}
|
||||
refresh={refresh}
|
||||
metrics={[
|
||||
[
|
||||
"operations",
|
||||
|
|
|
@ -2,32 +2,24 @@ import { ESPrefix } from "@/services/common";
|
|||
import StatisticBar from "./statistic_bar";
|
||||
import ClusterMetric from "../../components/cluster_metric";
|
||||
|
||||
export default ({
|
||||
isAgent,
|
||||
clusterID,
|
||||
indexName,
|
||||
timeRange,
|
||||
handleTimeChange,
|
||||
shardID,
|
||||
bucketSize,
|
||||
timezone,
|
||||
timeout,
|
||||
refresh
|
||||
}) => {
|
||||
export default (props) => {
|
||||
|
||||
const {
|
||||
isAgent,
|
||||
clusterID,
|
||||
indexName,
|
||||
shardID,
|
||||
} = props
|
||||
|
||||
let url = `${ESPrefix}/${clusterID}/index/${indexName}/metrics`;
|
||||
if(shardID){
|
||||
url += `?shard_id=${shardID}`
|
||||
}
|
||||
return (
|
||||
<ClusterMetric
|
||||
timezone={timezone}
|
||||
timeRange={timeRange}
|
||||
handleTimeChange={handleTimeChange}
|
||||
{...props}
|
||||
overview={1}
|
||||
fetchUrl={url}
|
||||
bucketSize={bucketSize}
|
||||
timeout={timeout}
|
||||
refresh={refresh}
|
||||
metrics={[
|
||||
isAgent && shardID ? 'shard_state' : "index_health",
|
||||
"index_throughput",
|
||||
|
|
|
@ -6,27 +6,13 @@ import { formatMessage } from "umi/locale";
|
|||
import { SearchEngines } from "@/lib/search_engines";
|
||||
import { isVersionGTE6, shouldHaveModelInferenceBreaker } from "../../Cluster/Monitor/advanced";
|
||||
|
||||
export default ({
|
||||
selectedCluster,
|
||||
clusterID,
|
||||
nodeID,
|
||||
timeRange,
|
||||
handleTimeChange,
|
||||
timezone,
|
||||
bucketSize,
|
||||
timeout,
|
||||
refresh,
|
||||
}) => {
|
||||
export default (props) => {
|
||||
|
||||
const tabProps = {
|
||||
const {
|
||||
selectedCluster,
|
||||
clusterID,
|
||||
timeRange,
|
||||
handleTimeChange,
|
||||
timezone,
|
||||
bucketSize,
|
||||
timeout,
|
||||
refresh
|
||||
}
|
||||
nodeID,
|
||||
} = props
|
||||
|
||||
const isVersionGTE8_6 = useMemo(() => {
|
||||
return shouldHaveModelInferenceBreaker(selectedCluster)
|
||||
|
@ -69,7 +55,7 @@ export default ({
|
|||
})}
|
||||
>
|
||||
<NodeMetric
|
||||
{...tabProps}
|
||||
{...props}
|
||||
param={param}
|
||||
setParam={setParam}
|
||||
metrics={[
|
||||
|
@ -211,7 +197,7 @@ export default ({
|
|||
})}
|
||||
>
|
||||
<QueueMetric
|
||||
{...tabProps}
|
||||
{...props}
|
||||
param={param}
|
||||
setParam={setParam}
|
||||
metrics={[
|
||||
|
|
|
@ -3,28 +3,19 @@ import StatisticBar from "./statistic_bar";
|
|||
import ClusterMetric from "../../components/cluster_metric";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export default ({
|
||||
isAgent,
|
||||
clusterID,
|
||||
nodeID,
|
||||
timeRange,
|
||||
handleTimeChange,
|
||||
bucketSize,
|
||||
timezone,
|
||||
timeout,
|
||||
refresh
|
||||
}) => {
|
||||
export default (props) => {
|
||||
|
||||
const {
|
||||
isAgent,
|
||||
clusterID,
|
||||
nodeID,
|
||||
} = props
|
||||
|
||||
return (
|
||||
<ClusterMetric
|
||||
timezone={timezone}
|
||||
timeRange={timeRange}
|
||||
timeout={timeout}
|
||||
refresh={refresh}
|
||||
handleTimeChange={handleTimeChange}
|
||||
{...props}
|
||||
overview={1}
|
||||
fetchUrl={`${ESPrefix}/${clusterID}/node/${nodeID}/metrics`}
|
||||
bucketSize={bucketSize}
|
||||
metrics={[
|
||||
"node_health",
|
||||
"cpu",
|
||||
|
|
|
@ -3,7 +3,7 @@ import { cloneDeep } from "lodash";
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { formatMessage } from "umi/locale";
|
||||
import styles from "./Metrics.scss";
|
||||
import { Alert, Empty, Icon, message, Spin, Tooltip } from "antd";
|
||||
import { Alert, Dropdown, Empty, Icon, Menu, message, Spin, Tooltip } from "antd";
|
||||
import {
|
||||
Axis,
|
||||
Chart,
|
||||
|
@ -39,7 +39,8 @@ export default (props) => {
|
|||
height = 200,
|
||||
customRenderChart,
|
||||
instance,
|
||||
pointerUpdate
|
||||
pointerUpdate,
|
||||
handleTimeIntervalChange
|
||||
} = props;
|
||||
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
@ -55,20 +56,26 @@ export default (props) => {
|
|||
const containerRef = useRef(null)
|
||||
|
||||
const firstFetchRef = useRef(true)
|
||||
|
||||
const [timeInterval, setTimeInterval] = useState()
|
||||
|
||||
const fetchData = async (queryParams, fetchUrl, metricKey, showLoading) => {
|
||||
const fetchData = async (queryParams, fetchUrl, metricKey, timeInterval, showLoading) => {
|
||||
if (!observerRef.current.isInView || !fetchUrl) return;
|
||||
setError()
|
||||
if (firstFetchRef.current || showLoading) {
|
||||
setLoading(true)
|
||||
}
|
||||
const newQueryParams = {
|
||||
...queryParams,
|
||||
key: metricKey,
|
||||
timeout
|
||||
}
|
||||
if (timeInterval) {
|
||||
newQueryParams.bucket_size = timeInterval
|
||||
}
|
||||
const res = await request(fetchUrl, {
|
||||
method: 'GET',
|
||||
queryParams: {
|
||||
...queryParams,
|
||||
key: metricKey,
|
||||
timeout
|
||||
},
|
||||
queryParams: newQueryParams,
|
||||
ignoreTimeout: true
|
||||
}, false, false)
|
||||
if (res?.error) {
|
||||
|
@ -86,9 +93,9 @@ export default (props) => {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
observerRef.current.deps = cloneDeep([queryParams, fetchUrl, metricKey, refresh])
|
||||
fetchData(queryParams, fetchUrl, metricKey, refresh)
|
||||
}, [JSON.stringify(queryParams), fetchUrl, metricKey, refresh])
|
||||
observerRef.current.deps = cloneDeep([queryParams, fetchUrl, metricKey, timeInterval, refresh])
|
||||
fetchData(queryParams, fetchUrl, metricKey, timeInterval, refresh)
|
||||
}, [JSON.stringify(queryParams), fetchUrl, metricKey, timeInterval, refresh])
|
||||
|
||||
useEffect(() => {
|
||||
const observer = new IntersectionObserver(
|
||||
|
@ -150,9 +157,33 @@ export default (props) => {
|
|||
const axis = metric?.axis || [];
|
||||
const lines = metric?.lines || [];
|
||||
if (lines.every((item) => !item.data || item.data.length === 0)) {
|
||||
const emptyProps = {}
|
||||
if (metric?.min_bucket_size > 0 && metric?.hits_total > 0) {
|
||||
emptyProps.description = (
|
||||
<>
|
||||
<div style={{ wordBreak: 'break-all', textAlign: 'left', marginBotton: 2 }} >
|
||||
{formatMessage({ id: "cluster.metrics.time_interval.empty" }, { min_bucket_size: metric.min_bucket_size})}
|
||||
</div>
|
||||
<Dropdown overlay={(
|
||||
<Menu>
|
||||
<Menu.Item onClick={() => handleTimeIntervalChange(`${metric?.min_bucket_size}s`)}>
|
||||
{formatMessage({ id: `cluster.metrics.time_interval.set.global`})}
|
||||
</Menu.Item>
|
||||
<Menu.Item onClick={() => setTimeInterval(`${metric?.min_bucket_size}s`)}>
|
||||
{formatMessage({ id: `cluster.metrics.time_interval.set.current`})}
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
)}>
|
||||
<a onClick={e => e.preventDefault()}>
|
||||
{formatMessage({ id: `cluster.metrics.time_interval.apply`})} <Icon type="down" />
|
||||
</a>
|
||||
</Dropdown>
|
||||
</>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div style={{ height, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||
<Empty style={{ margin: 0}} image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||
<Empty style={{ margin: 0}} image={Empty.PRESENTED_IMAGE_SIMPLE} {...emptyProps} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -297,12 +328,20 @@ export default (props) => {
|
|||
</span>
|
||||
{
|
||||
<span>
|
||||
{
|
||||
timeInterval && (
|
||||
<Tooltip title={formatMessage({id: "cluster.metrics.time_interval.reload"}, { time_interval: queryParams.bucket_size })}>
|
||||
<Icon className={styles.copy} style={{ marginRight: 12 }} type="history" onClick={() => setTimeInterval()}/>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
{
|
||||
metric?.request && (
|
||||
<CopyToClipboard text={`GET .infini_metrics/_search\n${metric.request}`}>
|
||||
<Tooltip title={formatMessage({id: "cluster.metrics.request.copy"})}>
|
||||
<Icon
|
||||
className={styles.copy}
|
||||
style={{ marginRight: 12 }}
|
||||
type="copy"
|
||||
onClick={() => message.success(formatMessage({id: "cluster.metrics.request.copy.success"}))}
|
||||
/>
|
||||
|
@ -311,7 +350,7 @@ export default (props) => {
|
|||
)
|
||||
}
|
||||
<Tooltip title={formatMessage({id: "form.button.refresh"})}>
|
||||
<Icon className={styles.copy} style={{ marginLeft: 12 }} type="reload" onClick={() => fetchData(...observerRef.current.deps, true)}/>
|
||||
<Icon className={styles.copy} type="sync" onClick={() => fetchData(...observerRef.current.deps, true)}/>
|
||||
</Tooltip>
|
||||
</span>
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import { formatTimeRange } from "@/lib/elasticsearch/util";
|
|||
|
||||
export default (props) => {
|
||||
|
||||
const { fetchUrl, overview, metrics = [], renderExtra, timeRange, timeout, timezone, refresh, bucketSize, handleTimeChange } = props
|
||||
const { fetchUrl, overview, metrics = [], renderExtra, timeRange, timeout, timezone, refresh, bucketSize, handleTimeChange, handleTimeIntervalChange } = props
|
||||
|
||||
if (!fetchUrl || metrics.length === 0) {
|
||||
return null;
|
||||
|
@ -81,6 +81,7 @@ export default (props) => {
|
|||
}
|
||||
return metric
|
||||
}}
|
||||
handleTimeIntervalChange={handleTimeIntervalChange}
|
||||
/>
|
||||
))}
|
||||
{
|
||||
|
|
|
@ -25,6 +25,7 @@ export default (props) => {
|
|||
metrics = [],
|
||||
timeout,
|
||||
refresh,
|
||||
handleTimeIntervalChange
|
||||
} = props
|
||||
|
||||
if (!clusterID || metrics.length == 0) {
|
||||
|
@ -158,6 +159,7 @@ export default (props) => {
|
|||
className={"metric-item"}
|
||||
timeout={timeout}
|
||||
refresh={refresh}
|
||||
handleTimeIntervalChange={handleTimeIntervalChange}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ export default (props) => {
|
|||
bucketSize,
|
||||
timeout,
|
||||
refresh,
|
||||
metrics = []
|
||||
metrics = [],
|
||||
handleTimeIntervalChange
|
||||
} = props
|
||||
|
||||
if (!clusterID || metrics.length == 0) {
|
||||
|
@ -181,6 +182,7 @@ export default (props) => {
|
|||
className={"metric-item"}
|
||||
timeout={timeout}
|
||||
refresh={refresh}
|
||||
handleTimeIntervalChange={handleTimeIntervalChange}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ export default (props) => {
|
|||
bucketSize,
|
||||
metrics = [],
|
||||
timeout,
|
||||
refresh
|
||||
refresh,
|
||||
handleTimeIntervalChange
|
||||
} = props
|
||||
|
||||
if (!clusterID || metrics.length == 0) {
|
||||
|
@ -181,6 +182,7 @@ export default (props) => {
|
|||
className={"metric-item"}
|
||||
timeout={timeout}
|
||||
refresh={refresh}
|
||||
handleTimeIntervalChange={handleTimeIntervalChange}
|
||||
/>
|
||||
))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue