完善monitor
This commit is contained in:
parent
57baf86fbf
commit
45d58895cf
@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="https://cdn.staticfile.org/element-ui/2.15.14/theme-chalk/index.min.css">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<link href="https://cdn.staticfile.org/element-ui/2.15.14/theme-chalk/index.min.css" rel="stylesheet">
|
||||
<title>Interface Monitor</title>
|
||||
<script src="https://cdn.staticfile.org/vue/2.7.14/vue.min.js"></script>
|
||||
<script src="https://cdn.staticfile.org/blueimp-md5/2.19.0/js/md5.min.js"></script>
|
||||
@ -28,17 +28,17 @@
|
||||
}
|
||||
|
||||
.el-form-item__content {
|
||||
margin-left: 20px !important;
|
||||
margin-left: 6px !important;
|
||||
}
|
||||
|
||||
.resu {
|
||||
margin-left: 30%;
|
||||
width: 70%;
|
||||
min-height: 1080px;
|
||||
margin-left: 360px;
|
||||
width: 1160px;
|
||||
min-height: 700px;
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 1000px;
|
||||
width: 1100px;
|
||||
height: 300px;
|
||||
}
|
||||
</style>
|
||||
@ -46,51 +46,70 @@
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
<div style="width:30%;position: fixed;padding: 20px 0 0 50px;">
|
||||
<div style="width:320px; position: fixed; padding: 20px 0 0 30px;">
|
||||
<el-form label-width="80px">
|
||||
<el-form-item label="Host:">
|
||||
<div style="width: 108%;">
|
||||
<el-select v-model="query.host" placeholder="请选择">
|
||||
<el-option v-for="item in data.host" :key="item" :label="item" :value="item"></el-option>
|
||||
<el-select @change="init()" placeholder="请选择" v-model="query.host">
|
||||
<el-option :key="item" :label="item" :value="item" v-for="item in data.host"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="开始时间:">
|
||||
<el-date-picker v-model="query.startTime" type="datetime" placeholder="选择开始时间">
|
||||
<el-date-picker @change="init()" placeholder="选择开始时间" type="datetime" v-model="query.startTime">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束时间:">
|
||||
<el-date-picker v-model="query.endTime" type="datetime" placeholder="选择结束时间">
|
||||
<el-date-picker @change="init()" placeholder="选择结束时间" type="datetime" v-model="query.endTime">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
<el-form-item label="IP:">
|
||||
<el-select v-model="query.ip" placeholder="请选择">
|
||||
<el-option v-for="item in data.ip" :key="item" :label="item" :value="item">
|
||||
<el-select placeholder="请选择" v-model="query.ip">
|
||||
<el-option :key="item" :label="item" :value="item" @change="init()" v-for="item in data.ip">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="接口:">
|
||||
<el-select v-model="query.name" placeholder="请选择">
|
||||
<el-option v-for="item in data.name" :key="item" :label="item" :value="item">
|
||||
<el-select @change="init(false)" placeholder="请选择" v-model="query.name">
|
||||
<el-option :key="item" :label="item" :value="item" v-for="item in data.name">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组方式:">
|
||||
<el-radio v-for="item in data.groupBy" v-model="query.groupBy" :label="item">{{ item }}</el-radio>
|
||||
<el-radio :label="item" @change="groupData()" v-for="item in data.groupBy" v-model="query.groupBy">{{ item }}</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div style="margin-left: 20%; display: flex;">
|
||||
<el-button type="primary" @click="listData()">查询曲线</el-button>
|
||||
<el-button type="primary" @click="groupData()">查询分组</el-button>
|
||||
<el-button @click="listData()" type="primary">查询曲线</el-button>
|
||||
<el-button @click="groupData()" type="primary">查询分组</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="resu">
|
||||
<div id="total" class="line"></div>
|
||||
<div id="average" class="line"></div>
|
||||
<div id="failure" class="line"></div>
|
||||
<div id="timeout" class="line"></div>
|
||||
<div id="line" v-show="data.showLine">
|
||||
<div class="line" id="total"></div>
|
||||
<div class="line" id="average"></div>
|
||||
<div class="line" id="failure"></div>
|
||||
<div class="line" id="timeout"></div>
|
||||
</div>
|
||||
<div id="table" v-show="data.showTable">
|
||||
<el-table :data="data.groupData" :default-sort = "{prop: 'total', order: 'descending'}" @sort-change="sortChange" height="700" >
|
||||
<el-table-column label="IP" prop="ip" v-if="query.groupBy === 'ip'" width="120"></el-table-column>
|
||||
<el-table-column label="接口" prop="interfaceName" v-if="query.groupBy === 'name'" width="230"></el-table-column>
|
||||
<el-table-column label="数量" prop="total" width="80" sortable='custom'></el-table-column>
|
||||
<el-table-column label="失败" prop="failure" width="80" sortable='custom'></el-table-column>
|
||||
<el-table-column label="平均" prop="averageTime" width="80" sortable='custom'></el-table-column>
|
||||
<el-table-column label="最大" prop="maxTime" width="80" sortable='custom'></el-table-column>
|
||||
<el-table-column label="最小" prop="minTime" width="80" sortable='custom'></el-table-column>
|
||||
<el-table-column label="10ms" prop="over10ms" width="80" sortable='custom'></el-table-column>
|
||||
<el-table-column label="50ms" prop="over50ms" width="80" sortable='custom'></el-table-column>
|
||||
<el-table-column label="100ms" prop="over100ms" width="88" sortable='custom'></el-table-column>
|
||||
<el-table-column label="500ms" prop="over500ms" width="88" sortable='custom'></el-table-column>
|
||||
<el-table-column label="1000ms" prop="over1000ms" width="96" sortable='custom'></el-table-column>
|
||||
<el-table-column label="5000ms" prop="over5000ms" width="96" sortable='custom'></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -176,20 +195,19 @@
|
||||
total: 0
|
||||
});
|
||||
}
|
||||
|
||||
current = current.add(5, 'minute');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function drawLine(data, div, field, filter) {
|
||||
function drawLine(data, title, div, field, filter) {
|
||||
if (data && data.length) {
|
||||
const xyData = data.map(v => [v.time, v[field]]);
|
||||
const myChart = echarts.init(document.getElementById(div));
|
||||
myChart.setOption({
|
||||
const chart = echarts.init(document.getElementById(div));
|
||||
chart.setOption({
|
||||
title: {
|
||||
text: field
|
||||
text: title
|
||||
},
|
||||
xAxis: {
|
||||
type: 'time',
|
||||
@ -206,6 +224,9 @@
|
||||
type: 'line',
|
||||
data: xyData,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
color: '#409eff' // 设置折线的颜色为红色
|
||||
},
|
||||
}
|
||||
]
|
||||
})
|
||||
@ -232,38 +253,44 @@
|
||||
data() {
|
||||
return {
|
||||
query: {
|
||||
host: "http://8.138.144.54:8080",
|
||||
host: "https://do.tgsys.sztg.com",
|
||||
// startTime: dayjs().format('YYYY-MM-DD 00:00:00'),
|
||||
// endTime: dayjs().add(1, 'day').format('YYYY-MM-DD 00:00:00'),
|
||||
startTime: dayjs().subtract(1, 'day').format('YYYY-MM-DD 00:00:00'),
|
||||
endTime: dayjs().format('YYYY-MM-DD 00:00:00'),
|
||||
startTime: dayjs().format('YYYY-MM-DD 00:00:00'),
|
||||
endTime: dayjs().add(1, 'day').format('YYYY-MM-DD 00:00:00'),
|
||||
ip: "",
|
||||
name: "",
|
||||
groupBy: "name",
|
||||
orderBy: "",
|
||||
},
|
||||
data: {
|
||||
host: ["http://8.138.144.54:8080"],
|
||||
host: ["https://do.tgsys.sztg.com", "http://8.138.144.54:8080", "http://127.0.0.1:8080"],
|
||||
ip: [],
|
||||
name: [],
|
||||
groupBy: ["name", "ip"],
|
||||
groupData: [],
|
||||
showLine: false,
|
||||
showTable: false,
|
||||
},
|
||||
monitor: [],
|
||||
interval: null,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
async init(loadName = true) {
|
||||
const ip = await API.listIP(this.query.host, this.query)
|
||||
if(ip){
|
||||
if (ip) {
|
||||
ip.unshift("全部")
|
||||
this.data.ip = ip;
|
||||
this.query.ip = ip[0];
|
||||
}
|
||||
const name = await API.listInterface(this.query.host, this.query)
|
||||
if(name){
|
||||
name.unshift("全部")
|
||||
this.data.name = name;
|
||||
this.query.name = name[0];
|
||||
if (loadName) {
|
||||
const name = await API.listInterface(this.query.host, this.query)
|
||||
if(name){
|
||||
name.unshift("全部")
|
||||
this.data.name = name;
|
||||
this.query.name = name[0];
|
||||
}
|
||||
}
|
||||
},
|
||||
async listData() {
|
||||
@ -271,24 +298,40 @@
|
||||
// console.log('before', data);
|
||||
data = fillTimeData(this.query.startTime, this.query.endTime, data);
|
||||
// console.log('after', data);
|
||||
drawLine(data, 'total', 'total');
|
||||
drawLine(data, 'average', 'averageTime');
|
||||
drawLine(data, 'failure', 'failure');
|
||||
drawLine(data, 'timeout', 'over5000ms');
|
||||
drawLine(data, '总流量', 'total', 'total');
|
||||
drawLine(data, '平均耗时', 'average', 'averageTime');
|
||||
drawLine(data, '失败流量', 'failure', 'failure');
|
||||
drawLine(data, '超时流量', 'timeout', 'over5000ms');
|
||||
this.data.showLine = true;
|
||||
this.data.showTable = false;
|
||||
},
|
||||
async groupData() {
|
||||
let groupData = await API.groupData(this.query.host, this.query);
|
||||
// groupData = groupData.filter(data => data.interfaceName && data.interfaceName.indexOf('/admin/monitor') === -1);
|
||||
this.data.groupData = groupData;
|
||||
this.data.showLine = false;
|
||||
this.data.showTable = true;
|
||||
},
|
||||
async sortChange({column, prop, order}) {
|
||||
console.log('sortChange', column, prop, order);
|
||||
this.query.orderBy = prop;
|
||||
this.query.order = order;
|
||||
console.log(this.query);
|
||||
this.groupData();
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.init();
|
||||
},
|
||||
watch: {
|
||||
'query.host': async function (newVal) {
|
||||
if (this.interval) {
|
||||
clearTimeout(this.interval)
|
||||
}
|
||||
this.interval = setTimeout(async () => {
|
||||
this.init();
|
||||
}, 100);
|
||||
},
|
||||
// 'query.host': async function (newVal) {
|
||||
// if (this.interval) {
|
||||
// clearTimeout(this.interval)
|
||||
// }
|
||||
// this.interval = setTimeout(async () => {
|
||||
// this.init();
|
||||
// }, 100);
|
||||
// },
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -19,6 +19,10 @@ public class MonitorQuery {
|
||||
|
||||
private String groupBy;
|
||||
|
||||
private String orderBy;
|
||||
|
||||
private String order;
|
||||
|
||||
public LocalDateTime getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
@ -66,4 +70,20 @@ public class MonitorQuery {
|
||||
public void setGroupBy(String groupBy) {
|
||||
this.groupBy = groupBy;
|
||||
}
|
||||
|
||||
public String getOrderBy() {
|
||||
return orderBy;
|
||||
}
|
||||
|
||||
public void setOrderBy(String orderBy) {
|
||||
this.orderBy = orderBy;
|
||||
}
|
||||
|
||||
public String getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(String order) {
|
||||
this.order = order;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
package com.syzb.monitor.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.hazelcast.core.HazelcastInstance;
|
||||
import com.syzb.common.handler.BizException;
|
||||
import com.syzb.common.result.ResponseStatus;
|
||||
@ -13,16 +16,16 @@ import com.syzb.monitor.entity.InterfaceMonitor;
|
||||
import com.syzb.monitor.mapper.InterfaceMonitorMapper;
|
||||
import com.syzb.monitor.query.MonitorQuery;
|
||||
import com.syzb.monitor.vo.MonitorResult;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@ -39,6 +42,23 @@ public class MonitorService {
|
||||
// 记录保存天数
|
||||
private static final int SAVE_DAY = 15;
|
||||
|
||||
// 固定排序字段和列关系,防止SQL注入
|
||||
private static Map<String, String> ORDER_BY_MAP = new HashMap<>();
|
||||
|
||||
static {
|
||||
ORDER_BY_MAP.put("total", "total");
|
||||
ORDER_BY_MAP.put("averageTime", "average_time");
|
||||
ORDER_BY_MAP.put("maxTime", "max_time");
|
||||
ORDER_BY_MAP.put("minTime", "min_time");
|
||||
ORDER_BY_MAP.put("failure", "failure");
|
||||
ORDER_BY_MAP.put("over10ms", "over_10ms");
|
||||
ORDER_BY_MAP.put("over50ms", "over_50ms");
|
||||
ORDER_BY_MAP.put("over100ms", "over_100ms");
|
||||
ORDER_BY_MAP.put("over500ms", "over_500ms");
|
||||
ORDER_BY_MAP.put("over1000ms", "over_1000ms");
|
||||
ORDER_BY_MAP.put("over5000ms", "over_5000ms");
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
private void loadHost() {
|
||||
host = hazelcastInstance.getCluster().getLocalMember().getAddress().getHost();
|
||||
@ -54,13 +74,32 @@ public class MonitorService {
|
||||
this.results.add(new MonitorResult(name, time, code));
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 * * * * ?")
|
||||
public void save() {
|
||||
List<InterfaceMonitor> list = this.statistic();
|
||||
interfaceMonitorMapper.insertBatchSomeColumn(list);
|
||||
// 使用自定义线程池+任务实现定时器,避免scheduledEnable条件,只跑admin服务
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
// 创建一个调度线程池
|
||||
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
|
||||
|
||||
// 每分钟 保存日志
|
||||
scheduler.scheduleAtFixedRate(() -> save(), 1, 1, TimeUnit.MINUTES);
|
||||
|
||||
// 每天 清理日子
|
||||
scheduler.scheduleAtFixedRate(() -> clear(), 0, 1, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 1 2 * * ?")
|
||||
// @Scheduled(cron = "0 * * * * ?")
|
||||
public void save() {
|
||||
// 5分钟执行一次
|
||||
if (LocalDateTime.now().getMinute() % 5 != 0) {
|
||||
return;
|
||||
}
|
||||
List<InterfaceMonitor> list = this.statistic();
|
||||
if (CollUtil.isNotEmpty(list)) {
|
||||
interfaceMonitorMapper.insertBatchSomeColumn(list);
|
||||
}
|
||||
}
|
||||
|
||||
// @Scheduled(cron = "0 1 2 * * ?")
|
||||
public void clear() {
|
||||
LocalDateTime time = LocalDate.now().minusDays(SAVE_DAY).atStartOfDay();
|
||||
LambdaQueryWrapper<InterfaceMonitor> wrapper = Wrappers.<InterfaceMonitor>lambdaQuery()
|
||||
@ -126,6 +165,11 @@ public class MonitorService {
|
||||
} else {
|
||||
throw new BizException(ResponseStatus.OUTSYS_ERROR);
|
||||
}
|
||||
String orderBy = query.getOrderBy();
|
||||
if (StrUtil.isNotEmpty(orderBy)) {
|
||||
orderBy = ORDER_BY_MAP.get(orderBy);
|
||||
}
|
||||
String order = query.getOrder();
|
||||
QueryWrapper<InterfaceMonitor> wrapper = Wrappers.<InterfaceMonitor>query()
|
||||
.select(groupColumn,
|
||||
"sum(total) as total", "avg(average_time) as average_time", "max(max_time) as max_time", "min(min_time) as min_time",
|
||||
@ -134,7 +178,9 @@ public class MonitorService {
|
||||
.ge("time", startTime)
|
||||
.lt("time", endTime)
|
||||
.groupBy(groupColumn)
|
||||
.orderByAsc(groupColumn);
|
||||
.orderByAsc(StrUtil.isEmpty(orderBy) || (!"ascending".equals(order) && !"descending".equals(order)), groupColumn)
|
||||
.orderByAsc(StrUtil.isNotEmpty(orderBy) && "ascending".equals(order), orderBy)
|
||||
.orderByDesc(StrUtil.isNotEmpty(orderBy) && "descending".equals(order), orderBy);
|
||||
return interfaceMonitorMapper.selectList(wrapper);
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user