From b8b24f8fabec556113def9869c8e2c5aaf0fae97 Mon Sep 17 00:00:00 2001 From: yaojp123 <15989103230@163.com> Date: Thu, 26 Dec 2024 12:52:38 +0800 Subject: [PATCH] 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 --- docs/content.en/docs/release-notes/_index.md | 1 + .../Detail/Metrics/MetricLineList.jsx | 3 +- .../Overview/Detail/Metrics/index.js | 50 +++++++++++--- .../Overview/Detail/Metrics/index.scss | 4 +- web/src/components/Overview/Monitor/index.jsx | 59 ++++++++++++++--- web/src/components/Overview/index.tsx | 2 +- web/src/locales/en-US/cluster.js | 7 ++ web/src/locales/zh-CN/cluster.js | 7 ++ .../Overview/Cluster/Monitor/advanced.jsx | 29 ++------- .../Overview/Cluster/Monitor/overview.jsx | 20 ++---- .../Overview/Indices/Monitor/advanced.jsx | 24 ++----- .../Overview/Indices/Monitor/overview.jsx | 28 +++----- .../Overview/Node/Monitor/advanced.jsx | 28 ++------ .../Overview/Node/Monitor/overview.jsx | 25 +++---- .../Overview/components/MetricChart.jsx | 65 +++++++++++++++---- .../Overview/components/cluster_metric.jsx | 3 +- .../Overview/components/index_metric.jsx | 2 + .../Overview/components/node_metric.jsx | 4 +- .../Overview/components/queue_metric.jsx | 4 +- 19 files changed, 214 insertions(+), 151 deletions(-) diff --git a/docs/content.en/docs/release-notes/_index.md b/docs/content.en/docs/release-notes/_index.md index 2d0f74bc..045433f1 100644 --- a/docs/content.en/docs/release-notes/_index.md +++ b/docs/content.en/docs/release-notes/_index.md @@ -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) diff --git a/web/src/components/Overview/Detail/Metrics/MetricLineList.jsx b/web/src/components/Overview/Detail/Metrics/MetricLineList.jsx index 756f55d9..77456903 100644 --- a/web/src/components/Overview/Detail/Metrics/MetricLineList.jsx +++ b/web/src/components/Overview/Detail/Metrics/MetricLineList.jsx @@ -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 (
@@ -60,6 +60,7 @@ export default (props) => { }; return }} + handleTimeIntervalChange={handleTimeIntervalChange} /> ) })} diff --git a/web/src/components/Overview/Detail/Metrics/index.js b/web/src/components/Overview/Detail/Metrics/index.js index 64877c0a..610c3603 100644 --- a/web/src/components/Overview/Detail/Metrics/index.js +++ b/web/src/components/Overview/Detail/Metrics/index.js @@ -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'} />
@@ -124,6 +147,15 @@ export default (props) => { renderExtraMetric={renderExtraMetric} metrics={metrics} {...state} + handleTimeIntervalChange={(timeInterval) => { + onTimeSettingsChange({ + timeInterval, + }) + setState({ + ...state, + timeInterval, + }); + }} bucketSize={state.timeInterval} /> diff --git a/web/src/components/Overview/Detail/Metrics/index.scss b/web/src/components/Overview/Detail/Metrics/index.scss index a9e28e4a..6cd527c3 100644 --- a/web/src/components/Overview/Detail/Metrics/index.scss +++ b/web/src/components/Overview/Detail/Metrics/index.scss @@ -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; } } diff --git a/web/src/components/Overview/Monitor/index.jsx b/web/src/components/Overview/Monitor/index.jsx index 9b294290..e319410a 100644 --- a/web/src/components/Overview/Monitor/index.jsx +++ b/web/src/components/Overview/Monitor/index.jsx @@ -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'} /> @@ -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} diff --git a/web/src/components/Overview/index.tsx b/web/src/components/Overview/index.tsx index 59f6d33a..7cb54a5f 100644 --- a/web/src/components/Overview/index.tsx +++ b/web/src/components/Overview/index.tsx @@ -274,7 +274,7 @@ export default forwardRef((props: IProps, ref: any) => { { setSelectedItem(item); drawRef.current?.open(); diff --git a/web/src/locales/en-US/cluster.js b/web/src/locales/en-US/cluster.js index 280f8ae7..88bffd9b 100644 --- a/web/src/locales/en-US/cluster.js +++ b/web/src/locales/en-US/cluster.js @@ -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", }; diff --git a/web/src/locales/zh-CN/cluster.js b/web/src/locales/zh-CN/cluster.js index 37f67338..c22e9a72 100644 --- a/web/src/locales/zh-CN/cluster.js +++ b/web/src/locales/zh-CN/cluster.js @@ -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": "最后活动时间", }; diff --git a/web/src/pages/Platform/Overview/Cluster/Monitor/advanced.jsx b/web/src/pages/Platform/Overview/Cluster/Monitor/advanced.jsx index f7a3746f..16d1a03b 100644 --- a/web/src/pages/Platform/Overview/Cluster/Monitor/advanced.jsx +++ b/web/src/pages/Platform/Overview/Cluster/Monitor/advanced.jsx @@ -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 ({ })} > { +export default (props) => { + + const { clusterID } = props + return ( ); diff --git a/web/src/pages/Platform/Overview/Indices/Monitor/advanced.jsx b/web/src/pages/Platform/Overview/Indices/Monitor/advanced.jsx index bc73ee74..365a2369 100644 --- a/web/src/pages/Platform/Overview/Indices/Monitor/advanced.jsx +++ b/web/src/pages/Platform/Overview/Indices/Monitor/advanced.jsx @@ -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 ( { +export default (props) => { + + const { + isAgent, + clusterID, + indexName, + shardID, + } = props + let url = `${ESPrefix}/${clusterID}/index/${indexName}/metrics`; if(shardID){ url += `?shard_id=${shardID}` } return ( { +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 ({ })} > { +export default (props) => { + + const { + isAgent, + clusterID, + nodeID, + } = props return ( { 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 = ( + <> +
+ {formatMessage({ id: "cluster.metrics.time_interval.empty" }, { min_bucket_size: metric.min_bucket_size})} +
+ + handleTimeIntervalChange(`${metric?.min_bucket_size}s`)}> + {formatMessage({ id: `cluster.metrics.time_interval.set.global`})} + + setTimeInterval(`${metric?.min_bucket_size}s`)}> + {formatMessage({ id: `cluster.metrics.time_interval.set.current`})} + + + )}> + e.preventDefault()}> + {formatMessage({ id: `cluster.metrics.time_interval.apply`})} + + + + ) + } return (
- +
) } @@ -297,12 +328,20 @@ export default (props) => { { + { + timeInterval && ( + + setTimeInterval()}/> + + ) + } { metric?.request && ( message.success(formatMessage({id: "cluster.metrics.request.copy.success"}))} /> @@ -311,7 +350,7 @@ export default (props) => { ) } - fetchData(...observerRef.current.deps, true)}/> + fetchData(...observerRef.current.deps, true)}/> } diff --git a/web/src/pages/Platform/Overview/components/cluster_metric.jsx b/web/src/pages/Platform/Overview/components/cluster_metric.jsx index 6b25e5df..951ceb6e 100644 --- a/web/src/pages/Platform/Overview/components/cluster_metric.jsx +++ b/web/src/pages/Platform/Overview/components/cluster_metric.jsx @@ -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} /> ))} { diff --git a/web/src/pages/Platform/Overview/components/index_metric.jsx b/web/src/pages/Platform/Overview/components/index_metric.jsx index 17d4ced5..30a26e67 100644 --- a/web/src/pages/Platform/Overview/components/index_metric.jsx +++ b/web/src/pages/Platform/Overview/components/index_metric.jsx @@ -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} /> )) } diff --git a/web/src/pages/Platform/Overview/components/node_metric.jsx b/web/src/pages/Platform/Overview/components/node_metric.jsx index ceaa8d40..92130fe0 100644 --- a/web/src/pages/Platform/Overview/components/node_metric.jsx +++ b/web/src/pages/Platform/Overview/components/node_metric.jsx @@ -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} /> )) } diff --git a/web/src/pages/Platform/Overview/components/queue_metric.jsx b/web/src/pages/Platform/Overview/components/queue_metric.jsx index 0f58e69c..b819455d 100644 --- a/web/src/pages/Platform/Overview/components/queue_metric.jsx +++ b/web/src/pages/Platform/Overview/components/queue_metric.jsx @@ -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} /> )) }