fix cluster metrics ui

This commit is contained in:
medcl 2021-03-05 00:23:49 +08:00
parent 21301f52e8
commit 494bb8cdf2
6 changed files with 249 additions and 143 deletions

View File

@ -18,7 +18,7 @@ export default [
authority: ['admin', 'user'], authority: ['admin', 'user'],
routes: [ routes: [
// cluster // cluster
{ path: '/', redirect: '/cluster/overview' }, { path: '/', redirect: '/cluster/overview/' },
{ {
path: '/cluster', path: '/cluster',
name: 'cluster', name: 'cluster',
@ -26,24 +26,35 @@ export default [
routes: [ routes: [
// { path: '/', redirect: '/platform/gateway' }, // { path: '/', redirect: '/platform/gateway' },
{ {
path: '/cluster/overview', path: '/cluster/overview/',
name: 'overview', name: 'overview',
component: './Cluster/Overview', component: './Cluster/Overview',
}, { }, {
path: '/cluster/overview/:elasticsearch',
name: 'overview',
component: './Cluster/Overview',
hideInMenu: true,
},
{
path: '/cluster/monitoring/:name', path: '/cluster/monitoring/:name',
name: 'cluster', name: 'cluster',
component: './Cluster/ClusterMonitor', component: './Cluster/ClusterMonitor',
hideInMenu: true, hideInMenu: true,
}, { }, {
path: '/cluster/metrics', path: '/cluster/metrics/',
name: 'monitoring', name: 'monitoring',
component: './Cluster/Metrics', component: './Cluster/Metrics',
}, { }, {
path: '/cluster/logging', path: '/cluster/metrics/:elasticsearch',
name: 'monitoring',
component: './Cluster/Metrics',
hideInMenu: true,
}, {
path: '/cluster/logs/',
name: 'logging', name: 'logging',
component: './Cluster/SearchMonitor', component: './Cluster/SearchMonitor',
},{ },{
path: '/cluster/settings', path: '/cluster/settings/',
name: 'settings', name: 'settings',
component: './Cluster/Settings/Base', component: './Cluster/Settings/Base',
routes: [ routes: [

View File

@ -6,10 +6,25 @@ import styles from './index.less';
import RightContent from './RightContent'; import RightContent from './RightContent';
import DropdownSelect from './DropdownSelect' import DropdownSelect from './DropdownSelect'
import router from "umi/router";
const path=require('path');
export default class GlobalHeader extends PureComponent { export default class GlobalHeader extends PureComponent {
componentWillUnmount() {
constructor(props) {
super(props);
}
componentDidMount() {
}
componentWillUnmount() {
this.triggerResizeEvent.cancel(); this.triggerResizeEvent.cancel();
} }
/* eslint-disable*/ /* eslint-disable*/
@Debounce(600) @Debounce(600)
triggerResizeEvent() { triggerResizeEvent() {
@ -44,7 +59,17 @@ export default class GlobalHeader extends PureComponent {
onChange={(item)=>{ onChange={(item)=>{
this.props.handleSaveGlobalState({ this.props.handleSaveGlobalState({
selectedCluster: item selectedCluster: item
}) });
const path1=this.props.location.pathname
if (path1[path1.length-1] !=='/'){
const currentPath=path.dirname(path1);
router.replace(currentPath+'/'+item.id);
}else{
router.replace(path1+item.id);
}
//location.reload()
}} }}
size={56} size={56}
fetchData={ fetchData={

View File

@ -13,6 +13,7 @@ export default {
clusterVisible: true, clusterVisible: true,
clusterList: [], clusterList: [],
selectedCluster: {name:"Select cluster", id: ""}, selectedCluster: {name:"Select cluster", id: ""},
selectedClusterID: "",
}, },
effects: { effects: {
@ -122,6 +123,7 @@ export default {
return { return {
...state, ...state,
selectedCluster: state.clusterList[idx], selectedCluster: state.clusterList[idx],
selectedClusterID: state.clusterList[idx].id,
} }
} }
return state; return state;

View File

@ -4,6 +4,7 @@ import {formatMessage} from 'umi/locale';
import {Button, Card, Col, DatePicker, Dropdown, Icon, Input, InputNumber, Row, Select, Statistic} from 'antd'; import {Button, Card, Col, DatePicker, Dropdown, Icon, Input, InputNumber, Row, Select, Statistic} from 'antd';
import moment from 'moment'; import moment from 'moment';
import {DateTime} from 'luxon'; import {DateTime} from 'luxon';
import router from "umi/router";
import numeral from 'numeral'; import numeral from 'numeral';
@ -21,6 +22,9 @@ import {
} from "@elastic/charts"; } from "@elastic/charts";
import styles from './Metrics.less'; import styles from './Metrics.less';
import { Spin, Alert } from 'antd';
const {RangePicker} = DatePicker; const {RangePicker} = DatePicker;
@ -140,7 +144,8 @@ const vstyle = {
@connect(({clusterMonitor,global}) => ({ @connect(({clusterMonitor,global}) => ({
clusterMonitor, clusterMonitor,
selectedCluster: global.selectedCluster selectedCluster: global.selectedCluster,
clusterList: global.clusterList
})) }))
class ClusterMonitor extends PureComponent { class ClusterMonitor extends PureComponent {
@ -151,6 +156,8 @@ class ClusterMonitor extends PureComponent {
} }
state = { state = {
spinning: false,
clusterID:null,
timeRange: { timeRange: {
min: moment().subtract(1, 'h').toISOString(), min: moment().subtract(1, 'h').toISOString(),
max: moment().toISOString() max: moment().toISOString()
@ -158,7 +165,24 @@ class ClusterMonitor extends PureComponent {
lastSeconds: 3600, lastSeconds: 3600,
qsVisible: false, qsVisible: false,
} }
componentWillReceiveProps(newProps) {
}
fetchData = () => { fetchData = () => {
console.log("fetching data ing."+this.state.clusterID)
if (this.state.clusterID===undefined||this.state.clusterID===""||this.state.clusterID===null){
return
}
this.setState({
spinning:true,
})
fetchDataCount++; fetchDataCount++;
//console.log(fetchDataCount, moment().diff(startTime)/1000); //console.log(fetchDataCount, moment().diff(startTime)/1000);
const {dispatch} = this.props; const {dispatch} = this.props;
@ -184,24 +208,58 @@ class ClusterMonitor extends PureComponent {
timeMask = 'YY-MM-DD' timeMask = 'YY-MM-DD'
} }
this.setState({timeScale: {min: timeRange.min, max: timeRange.max, mask: timeMask}}); this.setState({timeScale: {min: timeRange.min, max: timeRange.max, mask: timeMask}});
// console.log(this.props.selectedCluster)
dispatch({ dispatch({
type: 'clusterMonitor/fetchClusterMetrics', type: 'clusterMonitor/fetchClusterMetrics',
payload: { payload: {
timeRange: timeRange, timeRange: timeRange,
cluster_id:this.props.selectedCluster?this.props.selectedCluster.id:'' cluster_id:this.state.clusterID
}, },
}); }).then(()=>{
this.setState({
spinning:false,
})
})
} }
componentWillUnmount() { componentWillUnmount() {
clearInterval(tv1); clearInterval(tv1);
} }
componentDidUpdate(prevProps, prevState, snapshot) {
// console.log(this.props.selectedCluster)
// console.log(this.state.clusterID)
if (this.props.selectedCluster.id!==this.state.clusterID){
console.log("cluster changed")
this.setState({ clusterID:this.props.selectedCluster.id }, () => {
//TODO 处理 cancel 事件,先把当前还在执行中的请求结束,避免更新完成之后,被晚到的之前的请求给覆盖了。
this.fetchData();
});
}
}
componentDidMount() { componentDidMount() {
const {match, location} = this.props; const {match, location} = this.props;
const queryESID=this.props.match.params.elasticsearch;
if (queryESID !== null&&queryESID !== undefined){
this.state.clusterID=queryESID
const {dispatch} = this.props;
dispatch({
type: 'global/changeClusterById',
payload: {
id: queryESID
}
})
}else{
//alert("cluster ID is not set");
return
}
console.log("selectedCluster:"+this.state.clusterID)
let min = location.query.start || '2020-12-10 15:00'; let min = location.query.start || '2020-12-10 15:00';
let max = location.query.end || '2020-12-10 16:00'; let max = location.query.end || '2020-12-10 16:00';
@ -221,7 +279,8 @@ class ClusterMonitor extends PureComponent {
// tv1 = setInterval(()=>{ // tv1 = setInterval(()=>{
// this.fetchData(); // this.fetchData();
// }, 10000); // }, 10000);
this.autoRefresh();
//this.autoRefresh();
} }
autoRefresh(durationInSeconds) { autoRefresh(durationInSeconds) {
@ -427,7 +486,7 @@ class ClusterMonitor extends PureComponent {
// <Menu.Item key="5"> */} // <Menu.Item key="5"> */}
<Input.Group compact> <Input.Group compact>
<Button style={{cursor: "default"}}>刷新间隔</Button> <Button style={{cursor: "default"}}>刷新间隔</Button>
<InputNumber min={1} defaultValue={10} ref={el => this.refreshNum = el}/> <InputNumber min={-1} defaultValue={-1} ref={el => this.refreshNum = el}/>
<Select defaultValue="seconds" ref={el => this.refreshUnit = el}> <Select defaultValue="seconds" ref={el => this.refreshUnit = el}>
<Select.Option value="seconds"></Select.Option> <Select.Option value="seconds"></Select.Option>
<Select.Option value="minutes"></Select.Option> <Select.Option value="minutes"></Select.Option>
@ -443,137 +502,140 @@ class ClusterMonitor extends PureComponent {
); );
return ( return (
<div> <Spin spinning={this.state.spinning} tip="Loading...">
<div style={{background: "#fff", padding: "5px", marginBottom: 5}}> <div>
<Input.Group compact>
<Dropdown overlay={menu} <div style={{background: "#fff", padding: "5px", marginBottom: 5}}>
onVisibleChange={this.handleQSVisibleChange} <Input.Group compact>
visible={this.state.qsVisible} <Dropdown overlay={menu}
> onVisibleChange={this.handleQSVisibleChange}
<Button> visible={this.state.qsVisible}
快速选择 <Icon type="clock-circle"/> >
<Button>
快速选择 <Icon type="clock-circle"/>
</Button>
</Dropdown>
<RangePicker
showTime={{format: 'HH:mm'}}
format="YYYY-MM-DD HH:mm"
placeholder={['开始时间', '结束时间']}
defaultValue={[moment().subtract(1, 'h'), moment()]}
value={this.state.pickerValue}
onChange={this.onTimeChange}
onOk={this.onTimeOk}
/>
<Button type="primary" onClick={this.fetchData}>
刷新
</Button> </Button>
</Dropdown> </Input.Group>
<RangePicker </div>
showTime={{format: 'HH:mm'}}
format="YYYY-MM-DD HH:mm" <Card
placeholder={['开始时间', '结束时间']} //title={this.state.clusterID?this.state.clusterID:''}
defaultValue={[moment().subtract(1, 'h'), moment()]} style={{marginBottom: 5}}>
value={this.state.pickerValue} <Row>
onChange={this.onTimeChange} <Col md={2} xs={4}>
onOk={this.onTimeOk} <Statistic valueStyle={vstyle} title="集群名称" value={clusterStats.cluster_name}/>
/> </Col>
<Button type="primary" onClick={this.fetchData}> <Col md={2} xs={4}>
刷新 <Statistic valueStyle={vstyle} title="在线时长" value={clusterStats.uptime}/>
</Button> </Col>
</Input.Group> <Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="集群版本" value={clusterStats.version}/>
</Col>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="健康情况" value={clusterStats.status}
prefix={<HealthCircle color={clusterStats.status}/>}/>
</Col>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="节点数" value={clusterStats.nodes_count}/>
</Col>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="索引数" value={clusterStats.indices_count}/>
</Col>
<Col md={3} xs={4}>
<Statistic valueStyle={vstyle} title="分片数"
value={clusterStats.primary_shards + '/' + clusterStats.unassigned_shards + '/' + clusterStats.total_shards}/>
</Col>
<Col md={3} xs={4}>
<Statistic valueStyle={vstyle} title="文档数" value={clusterStats.document_count}/>
</Col>
<Col md={3} xs={4}>
<Statistic valueStyle={vstyle} title="存储空间"
value={clusterStats.used_store_bytes + '/' + clusterStats.max_store_bytes}/>
</Col>
<Col md={3} xs={4}>
<Statistic valueStyle={vstyle} title="JVM 内存"
value={clusterStats.used_jvm_bytes + '/' + clusterStats.max_jvm_bytes}/>
</Col>
</Row>
</Card>
{
Object.keys(clusterMetrics).map((e, i) => {
let axis = clusterMetrics[e].axis
let lines = clusterMetrics[e].lines
let disableHeaderFormat = false
let headerUnit = ""
return (
<div className={styles.vizChartContainer}>
<Chart size={[, 200]} className={styles.vizChartItem}>
<Settings theme={theme} showLegend legendPosition={Position.Top}
tooltip={{
headerFormatter: disableHeaderFormat
? undefined
: ({value}) => `${formatter.full_dates(value)}${headerUnit ? ` ${headerUnit}` : ''}`,
}}
debug={false}/>
<Axis id="{e}-bottom" position={Position.Bottom} showOverlappingTicks
labelFormat={formatter.dates}
tickFormat={formatter.dates}
/>
{
axis.map((item) => {
return <Axis
id={e + '-' + item.id}
showGridLines={item.showGridLines}
groupId={item.group}
title={formatMessage({id: 'dashboard.charts.title.' + e + '.axis.' + item.title})}
position={item.position}
ticks={item.ticks}
labelFormat={getFormatter(item.formatType, item.labelFormat)}
tickFormat={getFormatter(item.formatType, item.tickFormat)}
/>
})
}
{
lines.map((item) => {
return <LineSeries
id={item.metric.label}
groupId={item.metric.group}
timeZone={timezone}
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
xAccessor={0}
tickFormat={getFormatter(item.metric.formatType, item.metric.tickFormat, item.metric.units)}
yAccessors={[1]}
data={item.data}
curve={CurveType.CURVE_MONOTONE_X}
/>
})
}
</Chart>
</div>
)
})
}
</div> </div>
</Spin>
<Card
// title={this.props.selectedCluster?this.props.selectedCluster.name:''}
style={{marginBottom: 5}}>
<Row>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="集群名称" value={clusterStats.cluster_name}/>
</Col>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="在线时长" value={clusterStats.uptime}/>
</Col>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="集群版本" value={clusterStats.version}/>
</Col>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="健康情况" value={clusterStats.status}
prefix={<HealthCircle color={clusterStats.status}/>}/>
</Col>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="节点数" value={clusterStats.nodes_count}/>
</Col>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="索引数" value={clusterStats.indices_count}/>
</Col>
<Col md={3} xs={4}>
<Statistic valueStyle={vstyle} title="分片数"
value={clusterStats.primary_shards + '/' + clusterStats.unassigned_shards + '/' + clusterStats.total_shards}/>
</Col>
<Col md={3} xs={4}>
<Statistic valueStyle={vstyle} title="文档数" value={clusterStats.document_count}/>
</Col>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="存储空间"
value={clusterStats.used_store_bytes + '/' + clusterStats.max_store_bytes}/>
</Col>
<Col md={2} xs={4}>
<Statistic valueStyle={vstyle} title="JVM 内存"
value={clusterStats.used_jvm_bytes + '/' + clusterStats.max_jvm_bytes}/>
</Col>
</Row>
</Card>
{
Object.keys(clusterMetrics).map((e, i) => {
let axis = clusterMetrics[e].axis
let lines = clusterMetrics[e].lines
let disableHeaderFormat = false
let headerUnit = ""
return (
<div className={styles.vizChartContainer}>
<Chart size={[, 200]} className={styles.vizChartItem}>
<Settings theme={theme} showLegend legendPosition={Position.Top}
tooltip={{
headerFormatter: disableHeaderFormat
? undefined
: ({value}) => `${formatter.full_dates(value)}${headerUnit ? ` ${headerUnit}` : ''}`,
}}
debug={false}/>
<Axis id="{e}-bottom" position={Position.Bottom} showOverlappingTicks
labelFormat={formatter.dates}
tickFormat={formatter.dates}
/>
{
axis.map((item) => {
return <Axis
id={e + '-' + item.id}
showGridLines={item.showGridLines}
groupId={item.group}
title={formatMessage({id: 'dashboard.charts.title.' + e + '.axis.' + item.title})}
position={item.position}
ticks={item.ticks}
labelFormat={getFormatter(item.formatType, item.labelFormat)}
tickFormat={getFormatter(item.formatType, item.tickFormat)}
/>
})
}
{
lines.map((item) => {
return <LineSeries
id={item.metric.label}
groupId={item.metric.group}
timeZone={timezone}
xScaleType={ScaleType.Time}
yScaleType={ScaleType.Linear}
xAccessor={0}
tickFormat={getFormatter(item.metric.formatType, item.metric.tickFormat, item.metric.units)}
yAccessors={[1]}
data={item.data}
curve={CurveType.CURVE_MONOTONE_X}
/>
})
}
</Chart>
</div>
)
})
}
</div>
); );
} }
} }

View File

@ -24,6 +24,7 @@ let HealthCircle = (props)=>{
})) }))
class Overview extends React.Component { class Overview extends React.Component {
state = { state = {
data: [{id:"JFpIbacZQamv9hkgQEDZ2Q", name:"single-es", endpoint:"http://localhost:9200", health: "green", version: "7.10.0", uptime:"320883955"}] data: [{id:"JFpIbacZQamv9hkgQEDZ2Q", name:"single-es", endpoint:"http://localhost:9200", health: "green", version: "7.10.0", uptime:"320883955"}]
} }
@ -142,6 +143,10 @@ class Overview extends React.Component {
render() { render() {
// useEffect(() => {
// console.log('Listening: ', name);
// }, [name]);
return ( return (
<div> <div>
<Button type="primary" onClick={this.handleChangeClusterById}>change cluster</Button> <Button type="primary" onClick={this.handleChangeClusterById}>change cluster</Button>

View File

@ -19,6 +19,7 @@ export default {
yield put({type: 'saveData', payload: clusterMetrics}) yield put({type: 'saveData', payload: clusterMetrics})
if(callback && typeof callback == 'function'){ if(callback && typeof callback == 'function'){
callback(clusterMetrics); callback(clusterMetrics);
console.log("finished call:"+params.cluster_id);
} }
}, },
*fetchClusterNodeStats({callback}, {call, put}){ *fetchClusterNodeStats({callback}, {call, put}){