feat: add `Collection mode` to `Monitor` (#26)
Co-authored-by: yaojiping <yaojiping@infini.ltd>
This commit is contained in:
parent
85879c5ea7
commit
020c8aae47
|
@ -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': (
|
||||
<div
|
||||
style={{
|
||||
background: "#00bb1b",
|
||||
height: 12,
|
||||
width: 12,
|
||||
borderRadius: 12,
|
||||
display: "inline-block",
|
||||
verticalAlign: "text-bottom",
|
||||
}}
|
||||
/>
|
||||
),
|
||||
'warning': (
|
||||
<Icon
|
||||
type="warning"
|
||||
theme="filled"
|
||||
style={{ color: "#ff3030", fontSize: 14 }}
|
||||
/>
|
||||
),
|
||||
'unknown': (
|
||||
<div
|
||||
style={{
|
||||
background: "#bbbbbb",
|
||||
height: 12,
|
||||
width: 12,
|
||||
borderRadius: 12,
|
||||
display: "inline-block",
|
||||
verticalAlign: "text-bottom",
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
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 (
|
||||
<Tooltip
|
||||
placement="bottomRight"
|
||||
title={(
|
||||
<Descriptions className={styles.content} title={formatMessage({ id: 'cluster.collect.last_active_at'})} column={1}>
|
||||
{
|
||||
stats.map((key) => (
|
||||
<Descriptions.Item key={key} label={formatMessage({ id: `cluster.manage.monitor_configs.${key}`})}>
|
||||
<div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
|
||||
{STATUS_ICONS[data?.[key]?.status || 'unknown']}{formatToUniversalTime(data?.[key]?.last_active_at)}
|
||||
</div>
|
||||
</Descriptions.Item>
|
||||
))
|
||||
}
|
||||
</Descriptions>
|
||||
)}
|
||||
overlayStyle={{ maxWidth: 360 }}
|
||||
>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: 6 }}>
|
||||
{renderIcon()}
|
||||
{firstUpperCase(data?.metric_collection_mode) || "Unknown"}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 ? (
|
||||
<>
|
||||
<div style={{ marginBottom: 5 }}>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
<div style={{ flexGrow: 0 }}>
|
||||
<DatePicker
|
||||
locale={getLocale()}
|
||||
start={state.timeRange.min}
|
||||
end={state.timeRange.max}
|
||||
onRangeChange={({ start, end }) => {
|
||||
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'}
|
||||
/>
|
||||
</div>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<DatePicker
|
||||
locale={getLocale()}
|
||||
start={state.timeRange.min}
|
||||
end={state.timeRange.max}
|
||||
onRangeChange={({ start, end }) => {
|
||||
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'}
|
||||
/>
|
||||
<CollectStatus fetchUrl={`${ESPrefix}/${selectedCluster?.id}/_collection_stats`}/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
||||
};
|
||||
|
|
|
@ -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": "最后活动时间",
|
||||
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue