From d6a8e2ff6b74b7c514687ddde7b150a2117a32a8 Mon Sep 17 00:00:00 2001 From: yaojp123 <15989103230@163.com> Date: Fri, 6 Dec 2024 12:35:13 +0800 Subject: [PATCH] fix: optimize monitor data fetching for large time ranges (#5) * fix: adjust cluster monitor (fetch data independently, lazy load, copy request) Co-authored-by: yaojiping --- web/src/components/Overview/Monitor/index.jsx | 25 +- web/src/locales/en-US/cluster.js | 3 + web/src/locales/zh-CN/cluster.js | 3 + web/src/models/global.js | 6 +- .../Overview/Cluster/Monitor/advanced.jsx | 299 ++++++++++++++- .../Overview/Cluster/Monitor/index.jsx | 1 + .../Overview/Cluster/Monitor/overview.jsx | 3 + .../Cluster/Monitor/statistic_bar.jsx | 7 + .../Overview/Indices/Monitor/advanced.jsx | 70 +++- .../Overview/Indices/Monitor/index.jsx | 10 +- .../Overview/Indices/Monitor/overview.jsx | 11 + .../Overview/Node/Monitor/advanced.jsx | 218 ++++++++++- .../Platform/Overview/Node/Monitor/index.jsx | 11 +- .../Overview/Node/Monitor/overview.jsx | 16 + .../Overview/components/MetricChart.jsx | 319 ++++++++++++++++ .../Platform/Overview/components/Metrics.scss | 7 + .../Overview/components/cluster_metric.jsx | 329 +++------------- .../Overview/components/index_metric.jsx | 316 +++------------- .../Overview/components/node_metric.jsx | 293 +++------------ .../Overview/components/queue_metric.jsx | 355 +++++------------- 20 files changed, 1227 insertions(+), 1075 deletions(-) create mode 100644 web/src/pages/Platform/Overview/components/MetricChart.jsx diff --git a/web/src/components/Overview/Monitor/index.jsx b/web/src/components/Overview/Monitor/index.jsx index 445d42f9..76b20163 100644 --- a/web/src/components/Overview/Monitor/index.jsx +++ b/web/src/components/Overview/Monitor/index.jsx @@ -39,8 +39,11 @@ const formatTimeout = (timeout) => { return timeout } +const TIMEOUT_CACHE_KEY = "monitor-timeout" + const Monitor = (props) => { const { + selectedCluster, formatState, getBreadcrumbList, StatisticBar, @@ -61,7 +64,7 @@ const Monitor = (props) => { timeFormatter: formatter.dates(1), }, timeInterval: formatTimeInterval(param?.timeInterval), - timeout: formatTimeout(param?.timeout), + timeout: formatTimeout(param?.timeout) || localStorage.getItem(TIMEOUT_CACHE_KEY) || '120s', param: param, }) ); @@ -104,6 +107,11 @@ const Monitor = (props) => { const breadcrumbList = getBreadcrumbList(state); + const isAgent = useMemo(() => { + const { monitor_configs = {} } = selectedCluster || {} + return monitor_configs?.node_stats?.enabled === false && monitor_configs?.index_stats?.enabled === false + }, [JSON.stringify(selectedCluster?.monitor_configs)]) + return (
@@ -111,7 +119,7 @@ const Monitor = (props) => {
-
+
{ showTimeout={true} timeout={state.timeout} onTimeSettingChange={(timeSetting) => { + localStorage.setItem(TIMEOUT_CACHE_KEY, timeSetting.timeout) setState({ ...state, timeInterval: timeSetting.timeInterval, @@ -139,16 +148,6 @@ const Monitor = (props) => { recentlyUsedRangesKey={'monitor'} />
-
@@ -178,6 +177,8 @@ const Monitor = (props) => { pane.component ) : ( item.enabled) .map((item) => { return { - name: item.name, - id: item.id, - endpoint: item.endpoint, - host: item.host, - version: item.version, + ...item, distribution: item.distribution || "elasticsearch", cluster_uuid: item.cluster_uuid || "", }; diff --git a/web/src/pages/Platform/Overview/Cluster/Monitor/advanced.jsx b/web/src/pages/Platform/Overview/Cluster/Monitor/advanced.jsx index 7456302b..a4b45446 100644 --- a/web/src/pages/Platform/Overview/Cluster/Monitor/advanced.jsx +++ b/web/src/pages/Platform/Overview/Cluster/Monitor/advanced.jsx @@ -1,6 +1,6 @@ import { Tabs } from "antd"; import { formatMessage } from "umi/locale"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import NodeMetric from "../../components/node_metric"; import IndexMetric from "../../components/index_metric"; import ClusterMetric from "../../components/cluster_metric"; @@ -10,11 +10,22 @@ import { ESPrefix } from "@/services/common"; const timezone = "local"; export default ({ + selectedCluster, clusterID, timeRange, handleTimeChange, bucketSize, + timeout, }) => { + + const isVersionGTE6 = useMemo(() => { + const main = selectedCluster?.version?.split('.')[0] + if (main && parseInt(main) >= 6) { + return true + } + return false + }, [selectedCluster?.version]) + const [param, setParam] = useState({ tab: "cluster", }); @@ -48,6 +59,20 @@ export default ({ handleTimeChange={handleTimeChange} fetchUrl={`${ESPrefix}/${clusterID}/cluster_metrics`} bucketSize={bucketSize} + timeout={timeout} + metrics={[ + 'cluster_health', + 'index_throughput', + 'search_throughput', + 'index_latency', + 'search_latency', + 'cluster_documents', + 'node_count', + 'cluster_indices', + 'circuit_breaker', + 'shard_count', + 'cluster_storage' + ]} /> !!item)} /> {/* { return ( { let clusterID = props.match.params?.cluster_id; if ( diff --git a/web/src/pages/Platform/Overview/Cluster/Monitor/overview.jsx b/web/src/pages/Platform/Overview/Cluster/Monitor/overview.jsx index 6b149c6b..2346d62d 100644 --- a/web/src/pages/Platform/Overview/Cluster/Monitor/overview.jsx +++ b/web/src/pages/Platform/Overview/Cluster/Monitor/overview.jsx @@ -8,15 +8,18 @@ export default ({ timeRange, handleTimeChange, bucketSize, + timeout, }) => { return ( ); } diff --git a/web/src/pages/Platform/Overview/Cluster/Monitor/statistic_bar.jsx b/web/src/pages/Platform/Overview/Cluster/Monitor/statistic_bar.jsx index db0823ab..597a7be0 100644 --- a/web/src/pages/Platform/Overview/Cluster/Monitor/statistic_bar.jsx +++ b/web/src/pages/Platform/Overview/Cluster/Monitor/statistic_bar.jsx @@ -23,6 +23,7 @@ const StatisticBar = ({ setSpinning, clusterAvailable, clusterMonitored, + onInfoChange }) => { const { loading, error, value } = useFetch( `${ESPrefix}/${clusterID}/metrics`, @@ -34,6 +35,12 @@ const StatisticBar = ({ setSpinning(loading); }, [loading]); + React.useEffect(() => { + if (onInfoChange) { + onInfoChange(value) + } + }, [JSON.stringify(value)]); + let overviewStatistic = []; if (value?.summary) { let rawStats = value.summary; diff --git a/web/src/pages/Platform/Overview/Indices/Monitor/advanced.jsx b/web/src/pages/Platform/Overview/Indices/Monitor/advanced.jsx index c2074a0b..7c15b218 100644 --- a/web/src/pages/Platform/Overview/Indices/Monitor/advanced.jsx +++ b/web/src/pages/Platform/Overview/Indices/Monitor/advanced.jsx @@ -10,7 +10,8 @@ export default ({ timeRange, handleTimeChange, shardID, - bucketSize + bucketSize, + timeout }) => { const [param, setParam] = useState({ show_top: false, @@ -26,6 +27,73 @@ export default ({ setParam={setParam} shardID={shardID} bucketSize={bucketSize} + timeout={timeout} + metrics={[ + [ + "operations", + [ + "indexing_rate", + "indexing_bytes", + "query_times", + "fetch_times", + "scroll_times", + "refresh_times", + "flush_times", + "merge_times" + ] + ], + [ + "latency", + [ + "indexing_latency", + "query_latency", + "fetch_latency", + "scroll_latency", + "refresh_latency", + "flush_latency", + "merge_latency" + ] + ], + [ + "storage", + [ + "index_storage", + "segment_count" + ] + ], + [ + "document", + [ + "doc_count", + "docs_deleted", + "doc_percent" + ] + ], + [ + "memory", + [ + "segment_doc_values_memory", + "segment_fields_memory", + "segment_memory", + "segment_terms_memory", + "segment_index_writer_memory", + "segment_term_vectors_memory" + ] + ], + [ + "cache", + [ + "query_cache", + "request_cache", + "fielddata_cache", + "query_cache_count", + "query_cache_hit", + "request_cache_hit", + "query_cache_miss", + "request_cache_miss" + ] + ] + ]} /> ); } diff --git a/web/src/pages/Platform/Overview/Indices/Monitor/index.jsx b/web/src/pages/Platform/Overview/Indices/Monitor/index.jsx index 5f693905..de479a79 100644 --- a/web/src/pages/Platform/Overview/Indices/Monitor/index.jsx +++ b/web/src/pages/Platform/Overview/Indices/Monitor/index.jsx @@ -6,8 +6,10 @@ import { formatMessage } from "umi/locale"; import Monitor from "@/components/Overview/Monitor"; import StatisticBar from "./statistic_bar"; import ShardStatisticBar from "./shard_statistic_bar"; +import { connect } from "dva"; -export default (props) => { +const Page = (props) => { + const { clusterStatus, selectedCluster } = props; const {shard_id} = props.location.query; const panes = React.useMemo(()=>{ const panes = [ @@ -26,6 +28,7 @@ export default (props) => { } return ( { return { ...state, @@ -80,3 +83,8 @@ export default (props) => { /> ); }; + +export default connect(({ global }) => ({ + selectedCluster: global.selectedCluster, + clusterStatus: global.clusterStatus, +}))(Page); diff --git a/web/src/pages/Platform/Overview/Indices/Monitor/overview.jsx b/web/src/pages/Platform/Overview/Indices/Monitor/overview.jsx index 05234fe5..e15a9292 100644 --- a/web/src/pages/Platform/Overview/Indices/Monitor/overview.jsx +++ b/web/src/pages/Platform/Overview/Indices/Monitor/overview.jsx @@ -5,12 +5,14 @@ import ClusterMetric from "../../components/cluster_metric"; const timezone = "local"; export default ({ + isAgent, clusterID, indexName, timeRange, handleTimeChange, shardID, bucketSize, + timeout }) => { let url = `${ESPrefix}/${clusterID}/index/${indexName}/metrics`; if(shardID){ @@ -24,6 +26,15 @@ export default ({ overview={1} fetchUrl={url} bucketSize={bucketSize} + timeout={timeout} + metrics={[ + "index_health", + "index_throughput", + "search_throughput", + "index_latency", + "search_latency", + isAgent ? "shard_state" : undefined, + ].filter((item) => !!item)} /> ); } diff --git a/web/src/pages/Platform/Overview/Node/Monitor/advanced.jsx b/web/src/pages/Platform/Overview/Node/Monitor/advanced.jsx index 88e0f46d..a4203aaf 100644 --- a/web/src/pages/Platform/Overview/Node/Monitor/advanced.jsx +++ b/web/src/pages/Platform/Overview/Node/Monitor/advanced.jsx @@ -1,4 +1,4 @@ -import { useState,useEffect } from "react"; +import { useState,useEffect, useMemo } from "react"; import { Tabs } from "antd"; import NodeMetric from "../../components/node_metric"; import QueueMetric from "../../components/queue_metric"; @@ -7,12 +7,23 @@ import { formatMessage } from "umi/locale"; const timezone = "local"; export default ({ + selectedCluster, clusterID, nodeID, timeRange, handleTimeChange, bucketSize, + timeout, }) => { + + const isVersionGTE6 = useMemo(() => { + const main = selectedCluster?.version?.split('.')[0] + if (main && parseInt(main) >= 6) { + return true + } + return false + }, [selectedCluster?.version]) + const [param, setParam] = useState({ show_top: false, node_name: nodeID, @@ -53,6 +64,137 @@ export default ({ param={param} setParam={setParam} bucketSize={bucketSize} + timeout={timeout} + metrics={[ + [ + "operations", + [ + "indexing_rate", + "indexing_bytes", + "query_rate", + "fetch_rate", + "scroll_rate", + "refresh_rate", + "flush_rate", + "merges_rate", + "scroll_open_contexts" + ] + ], + [ + "latency", + [ + "indexing_latency", + "query_latency", + "fetch_latency", + "scroll_latency", + "refresh_latency", + "flush_latency", + "merge_latency" + ] + ], + [ + "system", + [ + "cpu", + "disk", + "open_file", + "open_file_percent", + "os_cpu", + "os_load_average_1m", + "os_used_mem", + "os_used_swap" + ] + ], + [ + "circuit_breaker", + [ + "parent_breaker", + "accounting_breaker", + "fielddata_breaker", + "request_breaker", + "in_flight_requests_breaker", + "model_inference_breaker" + ] + ], + [ + "io", + [ + "total_io_operations", + "total_read_io_operations", + "total_write_io_operations" + ] + ], + [ + "transport", + [ + "transport_rx_bytes", + "transport_rx_rate", + "transport_tx_bytes", + "transport_tx_rate", + "transport_outbound_connections" + ] + ], + [ + "storage", + [ + "segment_count", + "index_storage" + ] + ], + [ + "document", + [ + "docs_count", + "docs_deleted" + ] + ], + [ + "http", + [ + "http_connect_num", + "http_rate" + ] + ], + [ + "JVM", + [ + "jvm_heap_used_percent", + "jvm_used_heap", + "jvm_mem_young_peak_used", + "jvm_mem_young_used", + "jvm_young_gc_latency", + "jvm_young_gc_rate", + "jvm_mem_old_peak_used", + "jvm_mem_old_used", + "jvm_old_gc_latency", + "jvm_old_gc_rate" + ] + ], + [ + "memory", + [ + "segment_doc_values_memory", + "segment_index_writer_memory", + "segment_memory", + "segment_stored_fields_memory", + "segment_term_vectors_memory", + "segment_terms_memory" + ] + ], + [ + "cache", + [ + "query_cache", + "request_cache", + "fielddata_cache", + "query_cache_count", + "query_cache_hit", + "request_cache_hit", + "query_cache_miss", + "request_cache_miss" + ] + ] + ]} /> !!item)} /> diff --git a/web/src/pages/Platform/Overview/Node/Monitor/index.jsx b/web/src/pages/Platform/Overview/Node/Monitor/index.jsx index 6617349b..56bb5a0d 100644 --- a/web/src/pages/Platform/Overview/Node/Monitor/index.jsx +++ b/web/src/pages/Platform/Overview/Node/Monitor/index.jsx @@ -5,16 +5,18 @@ import Shards from "./shards"; import { formatMessage } from "umi/locale"; import Monitor from "@/components/Overview/Monitor"; import StatisticBar from "./statistic_bar"; +import { connect } from "dva"; const panes = [ { title: "Overview", component: Overview, key: "overview" }, { title: "Advanced", component: Advanced, key: "advanced" }, { title: "Shards", component: Shards, key: "shards" }, ]; - -export default (props) => { +const Page = (props) => { + const { clusterStatus, selectedCluster } = props; return ( { return { ...state, @@ -54,3 +56,8 @@ export default (props) => { /> ); }; + +export default connect(({ global }) => ({ + selectedCluster: global.selectedCluster, + clusterStatus: global.clusterStatus, +}))(Page); \ No newline at end of file diff --git a/web/src/pages/Platform/Overview/Node/Monitor/overview.jsx b/web/src/pages/Platform/Overview/Node/Monitor/overview.jsx index fc41ee17..8c007819 100644 --- a/web/src/pages/Platform/Overview/Node/Monitor/overview.jsx +++ b/web/src/pages/Platform/Overview/Node/Monitor/overview.jsx @@ -1,24 +1,40 @@ import { ESPrefix } from "@/services/common"; import StatisticBar from "./statistic_bar"; import ClusterMetric from "../../components/cluster_metric"; +import { useMemo } from "react"; const timezone = "local"; export default ({ + isAgent, clusterID, nodeID, timeRange, handleTimeChange, bucketSize, + timeout, }) => { + return ( !!item)} /> ); } diff --git a/web/src/pages/Platform/Overview/components/MetricChart.jsx b/web/src/pages/Platform/Overview/components/MetricChart.jsx new file mode 100644 index 00000000..a93962b3 --- /dev/null +++ b/web/src/pages/Platform/Overview/components/MetricChart.jsx @@ -0,0 +1,319 @@ +import request from "@/utils/request"; +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 { + Axis, + Chart, + CurveType, + LineSeries, + BarSeries, + niceTimeFormatByDay, + Position, + ScaleType, + Settings, + timeFormatter, +} from "@elastic/charts"; +import { formatter, getFormatter, getNumFormatter } from "@/utils/format"; +import { CopyToClipboard } from "react-copy-to-clipboard"; +import moment from "moment"; +import { ESPrefix } from "@/services/common"; + +export default (props) => { + + const { + timezone, + timeRange, + timeout, + handleTimeChange, + fetchUrl, + metricKey, + title, + queryParams, + className, + style, + formatMetric + } = props; + + const [loading, setLoading] = useState(false) + + const [metric, setMetric] = useState() + + const [isInView, setIsInView] = useState(false); + + const [error, setError] = useState(); + + const observerRef = useRef({ isInView: false }) + + const containerRef = useRef(null) + + const firstFetchRef = useRef(true) + + const fetchData = async (queryParams, fetchUrl, metricKey, showLoading) => { + if (!observerRef.current.isInView || !fetchUrl) return; + setError() + if (firstFetchRef.current || showLoading) { + setLoading(true) + } + const res = await request(fetchUrl, { + method: 'GET', + queryParams: { + ...queryParams, + key: metricKey, + timeout + }, + ignoreTimeout: true + }, false, false) + if (res?.error?.reason) { + setError(res.error.reason) + } else if (res && !res.error) { + const { metrics = {} } = res || {}; + const metric = metrics[metricKey] + setMetric(formatMetric ? formatMetric(metric) : metric); + } + if (firstFetchRef.current || showLoading) { + setLoading(false) + firstFetchRef.current = false + } + } + + useEffect(() => { + observerRef.current.deps = cloneDeep([queryParams, fetchUrl, metricKey]) + fetchData(queryParams, fetchUrl, metricKey) + }, [JSON.stringify(queryParams), fetchUrl, metricKey]) + + useEffect(() => { + const observer = new IntersectionObserver( + entries => { + entries.forEach(entry => { + if (entry.isIntersecting) { + observerRef.current.isInView = true + if (JSON.stringify(observerRef.current.deps) !== JSON.stringify(observerRef.current.lastDeps)) { + observerRef.current.lastDeps = cloneDeep(observerRef.current.deps) + fetchData(...observerRef.current.deps) + } + } else { + observerRef.current.isInView = false + } + }); + }, + { + root: null, + threshold: 0, + } + ); + + if (containerRef.current) { + observer.observe(containerRef.current); + } + + return () => { + if (containerRef.current) { + observer.unobserve(containerRef.current); + } + }; + }, [isInView]); + + const chartRef = useRef(); + + const pointerUpdate = (event) => { + if (chartRef.current) { + chartRef.current.dispatchExternalPointerEvent(event); + } + }; + + const handleChartBrush = ({ x }) => { + if (!x) { + return; + } + let [from, to] = x; + if (typeof handleTimeChange == "function") { + if (to - from < 20 * 1000) { + from -= 10 * 1000; + to += 10 * 1000; + } + handleTimeChange({ + start: moment(from).toISOString(), + end: moment(to).toISOString(), + }); + } + }; + + const renderChart = () => { + if (loading) return
+ if (error) { + return ( +
+ +
+ ) + } + const axis = metric?.axis || []; + const lines = metric?.lines || []; + if (lines.every((item) => !item.data || item.data.length === 0)) { + return + } + return ( + + + `${formatter.full_dates(value)}`, + }} + debug={false} + /> + + {metricKey == "cluster_health" ? ( + Number(d).toFixed(0) + "%"} + /> + ) : null} + + {axis.map((item) => { + return ( + + ); + })} + + {lines.map((item) => { + if (item.type == "Bar") { + return ( + { + const g = splitAccessors.get("g"); + if ( + yAccessor === "y" + + ) { + if( ["red", "yellow", "green"].includes(g)){ + return g; + } + if(g == "online" || g == "available"){ + return "green"; + } + if(g == "offline" || g == "unavailable" || g == "N/A"){ + return "gray"; + } + + } + return null; + }} + /> + ); + } + return ( + + ); + })} + + ) + } + + const chartTitle = { title }; + const lines = metric?.lines || []; + if (metricKey == "cluster_health") { + chartTitle.units = "%"; + } else { + if (lines[0]?.metric) { + if (lines[0].metric.formatType.toLowerCase == "bytes") { + chartTitle.units = lines[0].metric.formatType; + } else { + chartTitle.units = lines[0].metric.units; + } + } + } + + return ( +
+ +
+ + {chartTitle.title} + {chartTitle.units ? `(${chartTitle.units})` : ""} + + { + + { + metric?.request && ( + + + message.success(formatMessage({id: "cluster.metrics.request.copy.success"}))} + /> + + + ) + } + + fetchData(...observerRef.current.deps, true)}/> + + + } +
+ {renderChart()} +
+
+ ); + } \ No newline at end of file diff --git a/web/src/pages/Platform/Overview/components/Metrics.scss b/web/src/pages/Platform/Overview/components/Metrics.scss index c72a1a39..dd0ccf8e 100644 --- a/web/src/pages/Platform/Overview/components/Metrics.scss +++ b/web/src/pages/Platform/Overview/components/Metrics.scss @@ -18,6 +18,13 @@ font-weight: 600; } +.copy { + cursor: pointer; + &:hover { + color: #1890ff; + } +} + .vizChartItem { background: white !important; diff --git a/web/src/pages/Platform/Overview/components/cluster_metric.jsx b/web/src/pages/Platform/Overview/components/cluster_metric.jsx index 8c53c261..44d96d8b 100644 --- a/web/src/pages/Platform/Overview/components/cluster_metric.jsx +++ b/web/src/pages/Platform/Overview/components/cluster_metric.jsx @@ -1,298 +1,73 @@ -import * as React from "react"; -import { - Axis, - Chart, - CurveType, - LineSeries, - BarSeries, - niceTimeFormatByDay, - Position, - ScaleType, - Settings, - timeFormatter, -} from "@elastic/charts"; -import useFetch from "@/lib/hooks/use_fetch"; -import { ESPrefix } from "@/services/common"; import styles from "./Metrics.scss"; -import { Spin, Radio, Select, Skeleton } from "antd"; -import { formatter, getFormatter, getNumFormatter } from "@/utils/format"; import "./node_metric.scss"; import { calculateBounds } from "@/components/vendor/data/common/query/timefilter"; -import moment from "moment"; +import MetricChart from "./MetricChart"; +import { useMemo } from "react"; import { formatMessage } from "umi/locale"; -import _ from "lodash"; +import { formatTimeRange } from "@/lib/elasticsearch/util"; -export default ({ - timezone, - timeRange, - handleTimeChange, - overview, - fetchUrl, - renderExtra, - bucketSize -}) => { - if (!fetchUrl) { +export default (props) => { + + const { fetchUrl, overview, metrics = [], renderExtra, timeRange, timeout, timezone, bucketSize, handleTimeChange } = props + + if (!fetchUrl || metrics.length === 0) { return null; } - const queryParams = React.useMemo(() => { - const bounds = calculateBounds({ - from: timeRange.min, - to: timeRange.max, - }); - let params = { - min: bounds.min.valueOf(), - max: bounds.max.valueOf(), - }; + + const queryParams = useMemo(() => { + const newParams = formatTimeRange(timeRange); if (overview) { - params.overview = overview; + newParams.overview = overview; } if (bucketSize) { - params.bucket_size = bucketSize + newParams.bucket_size = bucketSize } - return params; + return newParams; }, [timeRange, bucketSize]); - const { loading, error, value } = useFetch( - fetchUrl, - { - queryParams: queryParams, - }, - [queryParams, fetchUrl] - ); - - const metrics = React.useMemo(() => { - const { metrics = {} } = value || {}; - return Object.values(metrics) - .sort((a, b) => a.order - b.order) - .map((m) => { - let lines = m.lines || []; - m.lines = lines.map((line) => { - const data = line.data || []; - if (data.length > 1) { - line.data = line.data.slice(0, data.length - 1); - } - return line; - }); - return m; - }); - }, [value]); - - const chartRefs = React.useRef(); - React.useEffect(() => { - let refs = []; - Object.keys(metrics).map((m) => { - refs.push(React.createRef()); - }); - chartRefs.current = refs; - }, [metrics]); - - const pointerUpdate = (event) => { - chartRefs.current.forEach((ref) => { - if (ref.current) { - ref.current.dispatchExternalPointerEvent(event); - } - }); - }; - - const handleChartBrush = ({ x }) => { - if (!x) { - return; - } - let [from, to] = x; - if (typeof handleTimeChange == "function") { - if (to - from < 20 * 1000) { - from -= 10 * 1000; - to += 10 * 1000; - } - handleTimeChange({ - start: moment(from).toISOString(), - end: moment(to).toISOString(), - }); - } - }; - let refIdx = 0; - if (Object.keys(metrics).length == 0) { - return null; - } - + const extra = renderExtra ? renderExtra() : null; return (
- - {Object.keys(metrics).map((e, i) => { - let axis = metrics[e].axis; - let lines = metrics[e].lines; - if (lines.length == 0 || (lines && lines[0]?.data?.length == 0)) { - return null; - } - let disableHeaderFormat = false; - let headerUnit = ""; - let chartTitle = {}; - if (metrics[e].key == "cluster_health") { - chartTitle.units = "%"; - } else { - if (lines[0].metric.formatType.toLowerCase == "bytes") { - chartTitle.units = lines[0].metric.formatType; - } else { - chartTitle.units = lines[0].metric.units; + {metrics.map((metricKey, i) => ( + { + if (metric) { + const lines = metric.lines || [] + metric.lines = lines.map((line) => { + const data = line.data || []; + if (data.length > 1) { + line.data = line.data.slice(0, data.length - 1); + } + return line; + }); } - } - chartTitle.title = formatMessage({ - id: "cluster.metrics.axis." + metrics[e].key + ".title", - }); - - return ( -
-
- - {chartTitle.title} - {chartTitle.units ? `(${chartTitle.units})` : ""} - -
- - - `${formatter.full_dates(value)}${ - headerUnit ? ` ${headerUnit}` : "" - }`, - }} - debug={false} - /> - - {metrics[e].key == "cluster_health" ? ( - Number(d).toFixed(0) + "%"} - /> - ) : null} - - {axis.map((item) => { - return ( - - ); - })} - - {lines.map((item) => { - if (item.type == "Bar") { - return ( - { - const g = splitAccessors.get("g"); - if ( - yAccessor === "y" - - ) { - if( ["red", "yellow", "green"].includes(g)){ - return g; - } - if(g == "online" || g == "available"){ - return "green"; - } - if(g == "offline" || g == "unavailable" || g == "N/A"){ - return "gray"; - } - - } - return null; - }} - /> - ); - } - return ( - - ); - })} - -
- ); - })} - { - extra && ( -
- {extra} -
- ) - } -
+ return metric + }} + /> + ))} + { + extra && ( +
+ {extra} +
+ ) + }
); -}; +}; \ No newline at end of file diff --git a/web/src/pages/Platform/Overview/components/index_metric.jsx b/web/src/pages/Platform/Overview/components/index_metric.jsx index 49a967c4..44fab94b 100644 --- a/web/src/pages/Platform/Overview/components/index_metric.jsx +++ b/web/src/pages/Platform/Overview/components/index_metric.jsx @@ -1,51 +1,35 @@ -import * as React from "react"; -import { - Axis, - Chart, - CurveType, - LineSeries, - BarSeries, - niceTimeFormatByDay, - Position, - ScaleType, - Settings, - timeFormatter, -} from "@elastic/charts"; +import IndexSelect from "@/components/IndexSelect"; import useFetch from "@/lib/hooks/use_fetch"; import { ESPrefix } from "@/services/common"; -import styles from "./Metrics.scss"; -import { Spin, Radio, Select, Skeleton } from "antd"; -import { formatter, getFormatter, getNumFormatter } from "@/utils/format"; +import { Radio } from "antd"; import "./node_metric.scss"; -import { calculateBounds } from "@/components/vendor/data/common/query/timefilter"; -import moment from "moment"; import { formatMessage } from "umi/locale"; import MetricContainer from "./metric_container"; -import _ from "lodash"; -import IndexSelect from "@/components/IndexSelect"; +import { formatTimeRange } from "@/lib/elasticsearch/util"; +import NodeSelect from "@/components/NodeSelect"; import Anchor from "@/components/Anchor"; +import MetricChart from "./MetricChart"; +import { useMemo } from "react"; -const gorupOrder = [ - "operations", - "latency", - "storage", - "document", - "memory", - "cache", -]; -export default ({ - clusterID, - timezone, - timeRange, - handleTimeChange, - param, - setParam, - shardID, - bucketSize -}) => { - if (!clusterID) { +export default (props) => { + + const { + clusterID, + timezone, + timeRange, + handleTimeChange, + param, + setParam, + shardID, + bucketSize, + metrics = [], + timeout + } = props + + if (!clusterID || metrics.length == 0) { return null; } + const showTop = param.show_top ?? true; const topChange = (e) => { @@ -68,16 +52,8 @@ export default ({ }; }); }; - const queryParams = React.useMemo(() => { - const bounds = calculateBounds({ - from: timeRange.min, - to: timeRange.max, - }); - let newParams = { - min: bounds.min.valueOf(), - max: bounds.max.valueOf(), - }; - console.log(shardID) + const queryParams = useMemo(() => { + const newParams = formatTimeRange(timeRange); if(shardID){ newParams.shard_id = shardID; } @@ -92,73 +68,16 @@ export default ({ } return newParams; }, [param, timeRange, bucketSize]); - const { loading, error, value } = useFetch( - `${ESPrefix}/${clusterID}/index_metrics`, - { - queryParams: queryParams, - }, - [clusterID, queryParams] - ); - - const metrics = React.useMemo(() => { - const grpMetrics = _.groupBy(value?.metrics, "group"); - let metrics = {}; - Object.keys(grpMetrics).forEach((k) => { - metrics[k] = (grpMetrics[k] || []) - .sort((a, b) => a.order - b.order) - }); - return metrics; - }, [value]); - - const chartRefs = React.useRef(); - React.useEffect(() => { - let refs = []; - Object.values(metrics).map((m) => { - m.forEach(() => { - refs.push(React.createRef()); - }); - }); - chartRefs.current = refs; - }, [metrics]); const { value: indices } = useFetch( `${ESPrefix}/${clusterID}/_cat/indices`, {}, [clusterID] ); - const formatedIndices = React.useMemo(() => { + const formatedIndices = useMemo(() => { return Object.values(indices || []); }, [indices]); - const pointerUpdate = (event) => { - chartRefs.current.forEach((ref) => { - if (ref.current) { - ref.current.dispatchExternalPointerEvent(event); - } - }); - }; - - const handleChartBrush = ({ x }) => { - if (!x) { - return; - } - let [from, to] = x; - if (typeof handleTimeChange == "function") { - if (to - from < 20 * 1000) { - from -= 10 * 1000; - to += 10 * 1000; - } - handleTimeChange({ - start: moment(from).toISOString(), - end: moment(to).toISOString(), - }); - } - }; - let refIdx = 0; - if (Object.keys(metrics).length == 0) { - return null; - } - return (
{showTop ? ( @@ -189,179 +108,40 @@ export default ({
- - {gorupOrder.map((e, i) => { - if (!metrics[e]) { - return null; - } + {metrics.map((item, i) => { return ( -
+
- {metrics[e].map((metric) => { - let axis = metric.axis; - let lines = metric.lines; - if ( - lines.length == 0 || - (lines && lines[0]?.data?.length == 0) - ) { - return null; - } - let disableHeaderFormat = false; - let headerUnit = ""; - let chartTitle = {}; - if (lines[0].metric.formatType.toLowerCase == "bytes") { - chartTitle.units = lines[0].metric.formatType; - } else { - chartTitle.units = lines[0].metric.units; - } - chartTitle.title = formatMessage({ - id: - "cluster.metrics.index.axis." + metric.key + ".title", - }); - return ( -
-
- - {chartTitle.title} - {chartTitle.units ? `(${chartTitle.units})` : ""} - -
- - - `${formatter.full_dates(value)}${ - headerUnit ? ` ${headerUnit}` : "" - }`, - }} - debug={false} - /> - - {lines[0].type == "Bar" ? ( - Number(d).toFixed(0) + "%"} - /> - ) : null} - {axis.map((item) => { - return ( - - ); - })} - - {lines.map((item) => { - if (item.type == "Bar") { - return ( - { - const g = splitAccessors.get("g"); - if ( - yAccessor === "y" && - ["red", "yellow", "green"].includes(g) - ) { - return g; - } - return null; - }} - /> - ); - } - return ( - - ); - })} - -
- ); - })} + { + item[1].map((metricKey) => ( + + )) + }
); })} -
- + item[0])}>
); -}; +}; \ No newline at end of file diff --git a/web/src/pages/Platform/Overview/components/node_metric.jsx b/web/src/pages/Platform/Overview/components/node_metric.jsx index aa31320d..c4bd0eb9 100644 --- a/web/src/pages/Platform/Overview/components/node_metric.jsx +++ b/web/src/pages/Platform/Overview/components/node_metric.jsx @@ -1,63 +1,36 @@ -import * as React from "react"; -import { - Axis, - Chart, - CurveType, - LineSeries, - niceTimeFormatByDay, - Position, - ScaleType, - Settings, - timeFormatter, -} from "@elastic/charts"; import useFetch from "@/lib/hooks/use_fetch"; import { ESPrefix } from "@/services/common"; -import styles from "./Metrics.scss"; -import { Spin, Radio, Select, Skeleton, Row, Col, InputNumber } from "antd"; -import { formatter, getFormatter, getNumFormatter } from "@/utils/format"; +import { Radio } from "antd"; import "./node_metric.scss"; -import { calculateBounds } from "@/components/vendor/data/common/query/timefilter"; -import moment from "moment"; import { formatMessage } from "umi/locale"; import MetricContainer from "./metric_container"; -import _ from "lodash"; import { formatTimeRange } from "@/lib/elasticsearch/util"; import NodeSelect from "@/components/NodeSelect"; import Anchor from "@/components/Anchor"; +import MetricChart from "./MetricChart"; +import { useCallback, useMemo } from "react"; -const gorupOrder = [ - "operations", - "latency", - "system", - "circuit_breaker", - "io", - "transport", +export default (props) => { - "storage", - "document", - "http", - "JVM", - "memory", - "cache", -]; + const { + clusterID, + timezone, + timeRange, + handleTimeChange, + param, + setParam, + bucketSize, + timeout, + metrics = [] + } = props -export default ({ - clusterID, - timezone, - timeRange, - handleTimeChange, - param, - setParam, - bucketSize, -}) => { - // const [filter, setFilter] = React.useState({ - // top: "5", - // node_name: param?.transport, - // }); + if (!clusterID || metrics.length == 0) { + return null; + } const showTop = param.show_top ?? true; - const topChange = React.useCallback( + const topChange = useCallback( (e) => { // setFilter({ // node_name: undefined, @@ -74,7 +47,7 @@ export default ({ [param] ); - const nodeValueChange = React.useCallback( + const nodeValueChange = useCallback( (value) => { const nodeNames = value.map(item=>item.host); setParam((param) => { @@ -87,8 +60,15 @@ export default ({ }, [param] ); - const queryParams = React.useMemo(() => { - let newParams = formatTimeRange(timeRange); + + const { value: nodes } = useFetch( + `${ESPrefix}/${clusterID}/nodes/realtime`, + {}, + [clusterID] + ); + + const queryParams = useMemo(() => { + const newParams = formatTimeRange(timeRange); if (param.top) { newParams.top = param.top; } @@ -100,40 +80,7 @@ export default ({ } return newParams; }, [param, timeRange, bucketSize]); - const { loading, error, value } = useFetch( - `${ESPrefix}/${clusterID}/node_metrics`, - { - queryParams: queryParams, - }, - [clusterID, queryParams] - ); - const metrics = React.useMemo(() => { - const grpMetrics = _.groupBy(value?.metrics, "group"); - let metrics = {}; - Object.keys(grpMetrics).forEach((k) => { - metrics[k] = (grpMetrics[k] || []) - .sort((a, b) => a.order - b.order) - }); - return metrics; - }, [value]); - - const chartRefs = React.useRef(); - React.useEffect(() => { - let refs = []; - Object.values(metrics).map((m) => { - m.forEach(() => { - refs.push(React.createRef()); - }); - }); - chartRefs.current = refs; - }, [metrics]); - - const { value: nodes } = useFetch( - `${ESPrefix}/${clusterID}/nodes/realtime`, - {}, - [clusterID] - ); const formatedNodes = React.useMemo(() => { if (!nodes) { return []; @@ -146,36 +93,6 @@ export default ({ }); }, [nodes]); - const pointerUpdate = (event) => { - chartRefs.current.forEach((ref) => { - if (ref.current) { - ref.current.dispatchExternalPointerEvent(event); - } - }); - }; - - const handleChartBrush = ({ x }) => { - if (!x) { - return; - } - let [from, to] = x; - if (typeof handleTimeChange == "function") { - if (to - from < 20 * 1000) { - from -= 10 * 1000; - to += 10 * 1000; - } - handleTimeChange({ - start: moment(from).toISOString(), - end: moment(to).toISOString(), - }); - } - }; - - let refIdx = 0; - if (Object.keys(metrics).length == 0) { - return null; - } - return (
{showTop ? ( @@ -214,147 +131,39 @@ export default ({
- - {//Object.keys(metrics) - gorupOrder.map((e, i) => { - let hasData = (metrics[e] || []).some( - (m) => m.lines && m.lines[0]?.data.length > 0 - ); - if (!hasData) { - return null; - } + {metrics.map((item, i) => { return ( -
+
- {metrics[e].map((metric) => { - let axis = metric.axis; - let lines = metric.lines; - if ( - lines.length == 0 || - (lines && lines[0]?.data?.length == 0) - ) { - return null; - } - let disableHeaderFormat = false; - let headerUnit = ""; - let chartTitle = {}; - if (lines[0].metric.formatType.toLowerCase == "bytes") { - chartTitle.units = lines[0].metric.formatType; - } else { - chartTitle.units = lines[0].metric.units; - } - chartTitle.title = formatMessage({ - id: - "cluster.metrics.node.axis." + metric.key + ".title", - }); - return ( -
-
- - {chartTitle.title} - {chartTitle.units ? `(${chartTitle.units})` : ""} - -
- - - `${formatter.full_dates(value)}${ - headerUnit ? ` ${headerUnit}` : "" - }`, - }} - debug={false} - /> - - {axis.map((item) => { - return ( - - ); - })} - - {lines.map((item) => { - return ( - - ); - })} - -
- ); - })} + { + item[1].map((metricKey) => ( + + )) + }
); })} -
- + item[0])}>
); -}; +}; \ No newline at end of file diff --git a/web/src/pages/Platform/Overview/components/queue_metric.jsx b/web/src/pages/Platform/Overview/components/queue_metric.jsx index 5d3a2818..c790f7dd 100644 --- a/web/src/pages/Platform/Overview/components/queue_metric.jsx +++ b/web/src/pages/Platform/Overview/components/queue_metric.jsx @@ -1,52 +1,41 @@ -import * as React from "react"; -import { - Axis, - Chart, - CurveType, - LineSeries, - niceTimeFormatByDay, - Position, - ScaleType, - Settings, - timeFormatter, -} from "@elastic/charts"; import useFetch from "@/lib/hooks/use_fetch"; import { ESPrefix } from "@/services/common"; -import styles from "./Metrics.scss"; -import { Spin, Radio, Select, Skeleton, Row, Col } from "antd"; -import { formatter, getFormatter, getNumFormatter } from "@/utils/format"; +import { Radio } from "antd"; import "./node_metric.scss"; -import { calculateBounds } from "@/components/vendor/data/common/query/timefilter"; -import moment from "moment"; import { formatMessage } from "umi/locale"; import MetricContainer from "./metric_container"; -import _ from "lodash"; +import { formatTimeRange } from "@/lib/elasticsearch/util"; import NodeSelect from "@/components/NodeSelect"; import Anchor from "@/components/Anchor"; +import MetricChart from "./MetricChart"; +import { useCallback, useMemo } from "react"; -const gorupOrder = [ - "thread_pool_write", - "thread_pool_index", - "thread_pool_search", - "thread_pool_bulk", - "thread_pool_get", - "thread_pool_flush", - "thread_pool_refresh", - "thread_pool_force_merge", -]; +export default (props) => { + + const { + clusterID, + timezone, + timeRange, + handleTimeChange, + param, + setParam, + bucketSize, + metrics = [], + timeout + } = props + + if (!clusterID || metrics.length == 0) { + return null; + } -export default ({ - clusterID, - timezone, - timeRange, - handleTimeChange, - param, - setParam, - bucketSize, -}) => { const showTop = param.show_top ?? true; - const topChange = React.useCallback( + + const topChange = useCallback( (e) => { + // setFilter({ + // node_name: undefined, + // top: e.target.value, + // }); setParam((param) => { delete param["node_name"]; return { @@ -58,7 +47,7 @@ export default ({ [param] ); - const nodeValueChange = React.useCallback( + const nodeValueChange = useCallback( (value) => { const nodeNames = value.map(item=>item.host); setParam((param) => { @@ -71,15 +60,15 @@ export default ({ }, [param] ); - const queryParams = React.useMemo(() => { - const bounds = calculateBounds({ - from: timeRange.min, - to: timeRange.max, - }); - let newParams = { - min: bounds.min.valueOf(), - max: bounds.max.valueOf(), - }; + + const { value: nodes } = useFetch( + `${ESPrefix}/${clusterID}/nodes/realtime`, + {}, + [clusterID] + ); + + const queryParams = useMemo(() => { + const newParams = formatTimeRange(timeRange); if (param.top) { newParams.top = param.top; } @@ -91,40 +80,7 @@ export default ({ } return newParams; }, [param, timeRange, bucketSize]); - const { loading, error, value } = useFetch( - `${ESPrefix}/${clusterID}/queue_metrics`, - { - queryParams: queryParams, - }, - [clusterID, queryParams] - ); - const metrics = React.useMemo(() => { - const grpMetrics = _.groupBy(value?.metrics, "group"); - let metrics = {}; - Object.keys(grpMetrics).forEach((k) => { - metrics[k] = (grpMetrics[k] || []) - .sort((a, b) => a.order - b.order) - }); - return metrics; - }, [value]); - - const chartRefs = React.useRef(); - React.useEffect(() => { - let refs = []; - Object.values(metrics).map((m) => { - m.forEach(() => { - refs.push(React.createRef()); - }); - }); - chartRefs.current = refs; - }, [metrics]); - - const { value: nodes } = useFetch( - `${ESPrefix}/${clusterID}/nodes/realtime`, - {}, - [clusterID] - ); const formatedNodes = React.useMemo(() => { if (!nodes) { return []; @@ -137,208 +93,77 @@ export default ({ }); }, [nodes]); - const pointerUpdate = (event) => { - chartRefs.current.forEach((ref) => { - if (ref.current) { - ref.current.dispatchExternalPointerEvent(event); - } - }); - }; - - const handleChartBrush = ({ x }) => { - if (!x) { - return; - } - let [from, to] = x; - if (typeof handleTimeChange == "function") { - if (to - from < 20 * 1000) { - from -= 10 * 1000; - to += 10 * 1000; - } - handleTimeChange({ - start: moment(from).toISOString(), - end: moment(to).toISOString(), - }); - } - }; - - let refIdx = 0; - if (Object.keys(metrics).length == 0) { - return null; - } return (
- {showTop ?
-
-
-
- - - Top5 - - - Top10 - - - Top15 - - - Top20 - - -
-
- + {showTop ? ( +
+
+
+
+ + + Top5 + + + Top10 + + + Top15 + + + Top20 + + +
+
+ +
-
:null} + ) : ( + "" + )} +
- - {//Object.keys(metrics) - gorupOrder.map((e, i) => { - if (!metrics[e]) { - return null; - } + {metrics.map((item, i) => { return ( -
+
- {metrics[e].map((metric) => { - let axis = metric.axis; - let lines = metric.lines; - if ( - lines.length == 0 || - (lines && lines[0]?.data?.length == 0) - ) { - return null; - } - let disableHeaderFormat = false; - let headerUnit = ""; - let chartTitle = {}; - if (lines[0].metric.formatType.toLowerCase == "bytes") { - chartTitle.units = lines[0].metric.formatType; - } else { - chartTitle.units = lines[0].metric.units; - } - chartTitle.title = formatMessage({ - id: - "cluster.metrics.threadpool.axis." + - metric.key + - ".title", - }); - return ( -
-
- - {chartTitle.title} - {chartTitle.units ? `(${chartTitle.units})` : ""} - -
- - - `${formatter.full_dates(value)}${ - headerUnit ? ` ${headerUnit}` : "" - }`, - }} - debug={false} - /> - - {axis.map((item) => { - return ( - - ); - })} - - {lines.map((item) => { - return ( - - ); - })} - -
- ); - })} + { + item[1].map((metricKey) => ( + + )) + }
); })} -
- + item[0])}>
); -}; +}; \ No newline at end of file