diff --git a/web/src/components/CollectStatus/index.js b/web/src/components/CollectStatus/index.js new file mode 100644 index 00000000..0f43c040 --- /dev/null +++ b/web/src/components/CollectStatus/index.js @@ -0,0 +1,114 @@ +import request from "@/utils/request" +import { firstUpperCase, formatToUniversalTime } from "@/utils/utils"; +import { Descriptions, Icon, Tooltip } from "antd"; +import moment from "moment"; +import { useEffect, useMemo, useRef, useState } from "react"; +import styles from "./index.less"; +import { formatMessage } from "umi/locale"; + +const STATUS_ICONS = { + 'ok': ( +
+ ), + 'warning': ( + + ), + 'unknown': ( +
+ ) +} + +export default (props) => { + + const { fetchUrl } = props; + + const [loading, setLoading] = useState(false); + const [data, setData] = useState(); + const intervalRef = useRef() + + const fetchData = async (fetchUrl) => { + if (!fetchUrl) return + setLoading(true) + const res = await request(fetchUrl) + if (res && !res?.error) { + setData(res) + } + setLoading(false) + } + + useEffect(() => { + fetchData(fetchUrl) + if (intervalRef.current) { + clearInterval(intervalRef.current) + } + intervalRef.current = setInterval(() => { + fetchData(fetchUrl) + }, 300000) + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current) + } + } + }, [fetchUrl]) + + const stats = useMemo(() => { + return ['cluster_health', 'cluster_stats', 'node_stats', data?.metric_collection_mode === 'agent' ? 'shard_stats' : 'index_stats'] + }, [data?.metric_collection_mode]) + + const renderIcon = () => { + if (!data) { + return STATUS_ICONS['unknown'] + } else if (stats.every((key) => data?.[key]?.status === 'ok')) { + return STATUS_ICONS['ok'] + } else { + return STATUS_ICONS['warning'] + } + } + + return ( + + { + stats.map((key) => ( + +
+ {STATUS_ICONS[data?.[key]?.status || 'unknown']}{formatToUniversalTime(data?.[key]?.last_active_at)} +
+
+ )) + } + + )} + overlayStyle={{ maxWidth: 360 }} + > +
+ {renderIcon()} + {firstUpperCase(data?.metric_collection_mode) || "Unknown"} +
+
+ ); +} \ No newline at end of file diff --git a/web/src/components/CollectStatus/index.less b/web/src/components/CollectStatus/index.less new file mode 100644 index 00000000..9e9e2c3e --- /dev/null +++ b/web/src/components/CollectStatus/index.less @@ -0,0 +1,21 @@ +.content { + :global { + .ant-descriptions-item-label { + width: 110px; + } + .ant-descriptions-title, .ant-descriptions-item-label, .ant-descriptions-item-content { + color: #fff; + } + .ant-descriptions-title { + margin-bottom: 12px; + } + .ant-descriptions-item { + padding-bottom: 12px; + } + .ant-descriptions-row:last-child { + .ant-descriptions-item { + padding-bottom: 6px; + } + } + } +} \ No newline at end of file diff --git a/web/src/components/Overview/Monitor/index.jsx b/web/src/components/Overview/Monitor/index.jsx index 7cc43cdd..b49b8856 100644 --- a/web/src/components/Overview/Monitor/index.jsx +++ b/web/src/components/Overview/Monitor/index.jsx @@ -11,6 +11,8 @@ import DatePicker from "@/common/src/DatePicker"; import { getLocale } from "umi/locale"; import { getTimezone } from "@/utils/utils"; import { getContext } from "@/pages/DataManagement/context"; +import { ESPrefix } from "@/services/common"; +import CollectStatus from "@/components/CollectStatus"; const { TabPane } = Tabs; @@ -122,36 +124,35 @@ const Monitor = (props) => { selectedCluster ? ( <>
-
-
- { - handleTimeChange({ start, end }) - }} - {...refresh} - onRefreshChange={setRefresh} - onRefresh={handleTimeChange} - showTimeSetting={true} - showTimeInterval={true} - timeInterval={state.timeInterval} - showTimeout={true} - timeout={state.timeout} - onTimeSettingChange={(timeSetting) => { - localStorage.setItem(TIMEOUT_CACHE_KEY, timeSetting.timeout) - setState({ - ...state, - timeInterval: timeSetting.timeInterval, - timeout: timeSetting.timeout - }); - }} - timeZone={timeZone} - onTimeZoneChange={setTimeZone} - recentlyUsedRangesKey={'monitor'} - /> -
+
+ { + handleTimeChange({ start, end }) + }} + {...refresh} + onRefreshChange={setRefresh} + onRefresh={handleTimeChange} + showTimeSetting={true} + showTimeInterval={true} + timeInterval={state.timeInterval} + showTimeout={true} + timeout={state.timeout} + onTimeSettingChange={(timeSetting) => { + localStorage.setItem(TIMEOUT_CACHE_KEY, timeSetting.timeout) + setState({ + ...state, + timeInterval: timeSetting.timeInterval, + timeout: timeSetting.timeout + }); + }} + timeZone={timeZone} + onTimeZoneChange={setTimeZone} + recentlyUsedRangesKey={'monitor'} + /> +
diff --git a/web/src/locales/en-US/cluster.js b/web/src/locales/en-US/cluster.js index f62ac297..280f8ae7 100644 --- a/web/src/locales/en-US/cluster.js +++ b/web/src/locales/en-US/cluster.js @@ -39,6 +39,7 @@ export default { "cluster.manage.monitor_configs.cluster_stats": "Cluster stats", "cluster.manage.monitor_configs.node_stats": "Node stats", "cluster.manage.monitor_configs.index_stats": "Index stats", + "cluster.manage.monitor_configs.shard_stats": "Shard stats", "cluster.manage.metadata_configs.health_check": "Health check", "cluster.manage.metadata_configs.node_availability_check": "Node availability check", @@ -349,5 +350,8 @@ export default { "cluster.providers.ecloud": "Ecloud", "cluster.metrics.request.copy": "Copy request", - "cluster.metrics.request.copy.success": "Copy request successfully" + "cluster.metrics.request.copy.success": "Copy request successfully", + + "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 a3b564dd..37f67338 100644 --- a/web/src/locales/zh-CN/cluster.js +++ b/web/src/locales/zh-CN/cluster.js @@ -39,6 +39,7 @@ export default { "cluster.manage.monitor_configs.cluster_stats": "集群指标", "cluster.manage.monitor_configs.node_stats": "节点指标", "cluster.manage.monitor_configs.index_stats": "索引指标", + "cluster.manage.monitor_configs.shard_stats": "分片指标", "cluster.manage.metadata_configs.health_check": "健康检查", "cluster.manage.metadata_configs.node_availability_check": "节点可用性检查", "cluster.manage.metadata_configs.metadata_refresh": "元数据同步", @@ -334,5 +335,8 @@ export default { "cluster.providers.ecloud": "移动云", "cluster.metrics.request.copy": "复制请求", - "cluster.metrics.request.copy.success": "复制请求成功" + "cluster.metrics.request.copy.success": "复制请求成功", + + "cluster.collect.last_active_at": "最后活动时间", + }; diff --git a/web/src/pages/Platform/Overview/Indices/Monitor/shards.jsx b/web/src/pages/Platform/Overview/Indices/Monitor/shards.jsx index 55f65e00..d6801cd8 100644 --- a/web/src/pages/Platform/Overview/Indices/Monitor/shards.jsx +++ b/web/src/pages/Platform/Overview/Indices/Monitor/shards.jsx @@ -10,6 +10,7 @@ import { formatter } from "@/utils/format"; import { filterSearchValue, sorter, formatUtcTimeToLocal } from "@/utils/utils"; import { formatTimeRange } from "@/lib/elasticsearch/util"; import IconText from "@/components/infini/IconText"; +import AutoTextEllipsis from "@/components/AutoTextEllipsis"; const { Search } = Input; diff --git a/web/src/pages/Platform/Overview/Node/Monitor/shards.jsx b/web/src/pages/Platform/Overview/Node/Monitor/shards.jsx index 98e25ca1..f2a0f704 100644 --- a/web/src/pages/Platform/Overview/Node/Monitor/shards.jsx +++ b/web/src/pages/Platform/Overview/Node/Monitor/shards.jsx @@ -10,6 +10,7 @@ import { formatter } from "@/utils/format"; import { filterSearchValue, sorter, formatUtcTimeToLocal } from "@/utils/utils"; import { formatTimeRange } from "@/lib/elasticsearch/util"; import IconText from "@/components/infini/IconText"; +import AutoTextEllipsis from "@/components/AutoTextEllipsis"; const { Search } = Input; diff --git a/web/src/utils/utils.js b/web/src/utils/utils.js index 5770fa30..ed0ec208 100644 --- a/web/src/utils/utils.js +++ b/web/src/utils/utils.js @@ -422,3 +422,8 @@ export const firstUpperCase = (val) => { } return val; }; + +export const formatToUniversalTime = (time, format, timezone) => { + if (!time) return '-'; + return moment(time).tz(timezone || getTimezone()).format(format || "YYYY-MM-DD HH:mm:ss (G[M]TZ)") +}