feat(性能测试): 测试报告页面添加监控Tab
This commit is contained in:
parent
814b5129eb
commit
1807ff3538
|
@ -0,0 +1,5 @@
|
||||||
|
package io.metersphere.performance.base;
|
||||||
|
|
||||||
|
public enum MonitorStatus {
|
||||||
|
NOT, NORMAL, ABNORMAL
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package io.metersphere.performance.controller;
|
||||||
|
|
||||||
|
import io.metersphere.performance.dto.MetricData;
|
||||||
|
import io.metersphere.performance.service.MetricQueryService;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/metric")
|
||||||
|
public class MetricQueryController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MetricQueryService metricService;
|
||||||
|
|
||||||
|
@GetMapping("/query/{id}")
|
||||||
|
public List<MetricData> queryMetric(@PathVariable("id") String reportId) {
|
||||||
|
return metricService.queryMetric(reportId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package io.metersphere.performance.controller.request;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class MetricDataRequest {
|
||||||
|
private String seriesName;
|
||||||
|
private String promQL;
|
||||||
|
private String instance;
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package io.metersphere.performance.controller.request;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class MetricQuery {
|
||||||
|
|
||||||
|
public static Map<String, String> getMetricQueryMap() {
|
||||||
|
return new HashMap<String, String>(16) {{
|
||||||
|
// 指标名:promQL
|
||||||
|
put("cpu", "100 - (avg by (instance) (irate(node_cpu_seconds_total{instance='%1$s', mode='idle'}[1m])) * 100)");
|
||||||
|
put("disk", "100 - node_filesystem_free_bytes{instance='%1$s',fstype!~'rootfs|selinuxfs|autofs|rpc_pipefs|tmpfs|udev|none|devpts|sysfs|debugfs|fuse.*'} / node_filesystem_size_bytes{instance='%1$s',fstype!~'rootfs|selinuxfs|autofs|rpc_pipefs|tmpfs|udev|none|devpts|sysfs|debugfs|fuse.*'} * 100");
|
||||||
|
put("memory", "(node_memory_MemTotal_bytes{instance='%1$s'} - node_memory_MemFree_bytes{instance='%1$s'}) / node_memory_MemTotal_bytes{instance='%1$s'} * 100");
|
||||||
|
put("netIn", "sum by (instance) (irate(node_network_receive_bytes_total{instance='%1$s',device!~'bond.*?|lo'}[1m])/128)");
|
||||||
|
put("netOut", "sum by (instance) (irate(node_network_transmit_bytes_total{instance='%1$s',device!~'bond.*?|lo'}[1m])/128)");
|
||||||
|
}};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package io.metersphere.performance.controller.request;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class MetricRequest {
|
||||||
|
private List<MetricDataRequest> metricDataQueries = new ArrayList<>();
|
||||||
|
private long startTime;
|
||||||
|
private long endTime;
|
||||||
|
private int step = 15;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package io.metersphere.performance.dto;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class MetricData {
|
||||||
|
private String uniqueLabel;
|
||||||
|
private String seriesName;
|
||||||
|
private List<Double> values;
|
||||||
|
private List<String> timestamps;
|
||||||
|
private String instance;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package io.metersphere.performance.dto;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class Monitor {
|
||||||
|
private String name;
|
||||||
|
private String environmentId;
|
||||||
|
private String environmentName;
|
||||||
|
private String ip;
|
||||||
|
private Integer port;
|
||||||
|
private String description;
|
||||||
|
private String monitorStatus;
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
package io.metersphere.performance.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.alibaba.nacos.client.utils.StringUtils;
|
||||||
|
import io.metersphere.base.domain.LoadTestReportWithBLOBs;
|
||||||
|
import io.metersphere.base.domain.LoadTestWithBLOBs;
|
||||||
|
import io.metersphere.base.mapper.LoadTestMapper;
|
||||||
|
import io.metersphere.base.mapper.LoadTestReportMapper;
|
||||||
|
import io.metersphere.commons.exception.MSException;
|
||||||
|
import io.metersphere.commons.utils.DateUtils;
|
||||||
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
|
import io.metersphere.performance.base.ReportTimeInfo;
|
||||||
|
import io.metersphere.performance.controller.request.MetricDataRequest;
|
||||||
|
import io.metersphere.performance.controller.request.MetricQuery;
|
||||||
|
import io.metersphere.performance.controller.request.MetricRequest;
|
||||||
|
import io.metersphere.performance.dto.MetricData;
|
||||||
|
import io.metersphere.performance.dto.Monitor;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public class MetricQueryService {
|
||||||
|
|
||||||
|
|
||||||
|
private String prometheusHost = "http://192.168.1.8:9090";
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RestTemplate restTemplate;
|
||||||
|
@Resource
|
||||||
|
private LoadTestReportMapper loadTestReportMapper;
|
||||||
|
@Resource
|
||||||
|
private LoadTestMapper loadTestMapper;
|
||||||
|
@Resource
|
||||||
|
private ReportService reportService;
|
||||||
|
|
||||||
|
|
||||||
|
public List<MetricData> queryMetricData(MetricRequest metricRequest) {
|
||||||
|
List<MetricData> metricDataList = new ArrayList<>();
|
||||||
|
long endTime = metricRequest.getEndTime();
|
||||||
|
long startTime = metricRequest.getStartTime();
|
||||||
|
int step = metricRequest.getStep();
|
||||||
|
long reliableEndTime;
|
||||||
|
if (endTime > System.currentTimeMillis()) {
|
||||||
|
reliableEndTime = System.currentTimeMillis();
|
||||||
|
} else {
|
||||||
|
reliableEndTime = endTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional.ofNullable(metricRequest.getMetricDataQueries()).ifPresent(metricDataQueries -> metricDataQueries.forEach(query -> {
|
||||||
|
String promQL = query.getPromQL();
|
||||||
|
promQL = String.format(promQL, query.getInstance());
|
||||||
|
if (StringUtils.isEmpty(promQL)) {
|
||||||
|
MSException.throwException("promQL is null");
|
||||||
|
} else {
|
||||||
|
Optional.of(queryPrometheusMetric(promQL, query.getSeriesName(), startTime, reliableEndTime, step, query.getInstance())).ifPresent(metricDataList::addAll);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return metricDataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private List<MetricData> queryPrometheusMetric(String promQL, String seriesName, long startTime, long endTime, int step, String instance) {
|
||||||
|
DecimalFormat df = new DecimalFormat("#.###");
|
||||||
|
String start = df.format(startTime / 1000.0);
|
||||||
|
String end = df.format(endTime / 1000.0);
|
||||||
|
JSONObject response = restTemplate.getForObject(prometheusHost + "/api/v1/query_range?query={promQL}&start={start}&end={end}&step={step}", JSONObject.class, promQL, start, end, step);
|
||||||
|
return handleResult(seriesName, response, instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<MetricData> handleResult(String seriesName, JSONObject response, String instance) {
|
||||||
|
List<MetricData> list = new ArrayList<>();
|
||||||
|
|
||||||
|
Map<String, Set<String>> labelMap = new HashMap<>();
|
||||||
|
|
||||||
|
if (response != null && StringUtils.equals(response.getString("status"), "success")) {
|
||||||
|
JSONObject data = response.getJSONObject("data");
|
||||||
|
JSONArray result = data.getJSONArray("result");
|
||||||
|
|
||||||
|
if (result.size() > 1) {
|
||||||
|
result.forEach(rObject -> {
|
||||||
|
JSONObject resultObject = new JSONObject((Map)rObject);
|
||||||
|
// JSONObject resultObject = JSONObject.parseObject(rObject.toString());
|
||||||
|
JSONObject metrics = resultObject.getJSONObject("metric");
|
||||||
|
|
||||||
|
if (metrics != null && metrics.size() > 0) {
|
||||||
|
for (Map.Entry<String, Object> entry : metrics.entrySet())
|
||||||
|
labelMap.computeIfAbsent(entry.getKey(), k -> new HashSet<>()).add(entry.getValue().toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<String> uniqueLabelKey = labelMap.entrySet().stream().filter(entry -> entry.getValue().size() == result.size()).map(Map.Entry::getKey).findFirst();
|
||||||
|
|
||||||
|
result.forEach(rObject -> {
|
||||||
|
MetricData metricData = new MetricData();
|
||||||
|
List<String> timestamps = new ArrayList<>();
|
||||||
|
List<Double> values = new ArrayList<>();
|
||||||
|
|
||||||
|
JSONObject resultObject = new JSONObject((Map)rObject);
|
||||||
|
JSONObject metrics = resultObject.getJSONObject("metric");
|
||||||
|
JSONArray jsonArray = resultObject.getJSONArray("values");
|
||||||
|
jsonArray.forEach(value -> {
|
||||||
|
JSONArray ja = JSONObject.parseArray(value.toString());
|
||||||
|
Double timestamp = ja.getDouble(0);
|
||||||
|
try {
|
||||||
|
timestamps.add(DateUtils.getTimeString((long) (timestamp * 1000)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
values.add(ja.getDouble(1));
|
||||||
|
});
|
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(values)) {
|
||||||
|
metricData.setValues(values);
|
||||||
|
metricData.setTimestamps(timestamps);
|
||||||
|
metricData.setSeriesName(seriesName);
|
||||||
|
metricData.setInstance(instance);
|
||||||
|
uniqueLabelKey.ifPresent(s -> metricData.setUniqueLabel(metrics.getString(s)));
|
||||||
|
list.add(metricData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<MetricData> queryMetric(String reportId) {
|
||||||
|
LoadTestReportWithBLOBs report = loadTestReportMapper.selectByPrimaryKey(reportId);
|
||||||
|
String testId = report.getTestId();
|
||||||
|
LoadTestWithBLOBs loadTestWithBLOBs = loadTestMapper.selectByPrimaryKey(testId);
|
||||||
|
String advancedConfiguration = loadTestWithBLOBs.getAdvancedConfiguration();
|
||||||
|
JSONObject jsonObject = JSON.parseObject(advancedConfiguration);
|
||||||
|
JSONArray monitorParams = jsonObject.getJSONArray("monitorParams");
|
||||||
|
if (monitorParams == null) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
List<MetricDataRequest> list = new ArrayList<>();
|
||||||
|
for (int i = 0; i < monitorParams.size(); i++) {
|
||||||
|
Monitor monitor = monitorParams.getObject(i, Monitor.class);
|
||||||
|
String instance = monitor.getIp() + ":" + monitor.getPort();
|
||||||
|
getRequest(instance, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReportTimeInfo reportTimeInfo = reportService.getReportTimeInfo(reportId);
|
||||||
|
MetricRequest metricRequest = new MetricRequest();
|
||||||
|
metricRequest.setMetricDataQueries(list);
|
||||||
|
try {
|
||||||
|
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
||||||
|
Date startTime = df.parse(reportTimeInfo.getStartTime());
|
||||||
|
Date endTime = df.parse(reportTimeInfo.getEndTime());
|
||||||
|
metricRequest.setStartTime(startTime.getTime());
|
||||||
|
metricRequest.setEndTime(endTime.getTime());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtil.error(e, e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryMetricData(metricRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getRequest(String instance, List<MetricDataRequest> list) {
|
||||||
|
Map<String, String> map = MetricQuery.getMetricQueryMap();
|
||||||
|
Set<String> set = map.keySet();
|
||||||
|
set.forEach(s -> {
|
||||||
|
MetricDataRequest request = new MetricDataRequest();
|
||||||
|
String promQL = map.get(s);
|
||||||
|
request.setPromQL(promQL);
|
||||||
|
request.setSeriesName(s);
|
||||||
|
request.setInstance(instance);
|
||||||
|
list.add(request);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
package io.metersphere.performance.service;
|
package io.metersphere.performance.service;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import io.metersphere.base.domain.*;
|
import io.metersphere.base.domain.*;
|
||||||
import io.metersphere.base.mapper.*;
|
import io.metersphere.base.mapper.*;
|
||||||
import io.metersphere.base.mapper.ext.ExtLoadTestMapper;
|
import io.metersphere.base.mapper.ext.ExtLoadTestMapper;
|
||||||
|
@ -17,10 +19,13 @@ import io.metersphere.controller.request.QueryScheduleRequest;
|
||||||
import io.metersphere.controller.request.ScheduleRequest;
|
import io.metersphere.controller.request.ScheduleRequest;
|
||||||
import io.metersphere.dto.DashboardTestDTO;
|
import io.metersphere.dto.DashboardTestDTO;
|
||||||
import io.metersphere.dto.LoadTestDTO;
|
import io.metersphere.dto.LoadTestDTO;
|
||||||
|
import io.metersphere.dto.NodeDTO;
|
||||||
import io.metersphere.dto.ScheduleDao;
|
import io.metersphere.dto.ScheduleDao;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.job.sechedule.PerformanceTestJob;
|
import io.metersphere.job.sechedule.PerformanceTestJob;
|
||||||
|
import io.metersphere.performance.base.MonitorStatus;
|
||||||
import io.metersphere.performance.dto.LoadTestExportJmx;
|
import io.metersphere.performance.dto.LoadTestExportJmx;
|
||||||
|
import io.metersphere.performance.dto.Monitor;
|
||||||
import io.metersphere.performance.engine.Engine;
|
import io.metersphere.performance.engine.Engine;
|
||||||
import io.metersphere.performance.engine.EngineFactory;
|
import io.metersphere.performance.engine.EngineFactory;
|
||||||
import io.metersphere.performance.engine.producer.LoadTestProducer;
|
import io.metersphere.performance.engine.producer.LoadTestProducer;
|
||||||
|
@ -44,6 +49,7 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
|
@ -81,6 +87,8 @@ public class PerformanceTestService {
|
||||||
private TestResourcePoolMapper testResourcePoolMapper;
|
private TestResourcePoolMapper testResourcePoolMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private LoadTestProducer loadTestProducer;
|
private LoadTestProducer loadTestProducer;
|
||||||
|
@Resource
|
||||||
|
private TestResourceMapper testResourceMapper;
|
||||||
|
|
||||||
public List<LoadTestDTO> list(QueryTestPlanRequest request) {
|
public List<LoadTestDTO> list(QueryTestPlanRequest request) {
|
||||||
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
|
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
|
||||||
|
@ -204,6 +212,36 @@ public class PerformanceTestService {
|
||||||
}
|
}
|
||||||
|
|
||||||
final LoadTestWithBLOBs loadTest = new LoadTestWithBLOBs();
|
final LoadTestWithBLOBs loadTest = new LoadTestWithBLOBs();
|
||||||
|
|
||||||
|
String poolId = request.getTestResourcePoolId();
|
||||||
|
TestResourceExample testResourceExample = new TestResourceExample();
|
||||||
|
testResourceExample.createCriteria().andTestResourcePoolIdEqualTo(poolId);
|
||||||
|
List<TestResource> testResources = testResourceMapper.selectByExampleWithBLOBs(testResourceExample);
|
||||||
|
String advancedConfiguration = request.getAdvancedConfiguration();
|
||||||
|
JSONObject jsonObject = JSON.parseObject(advancedConfiguration);
|
||||||
|
List<Monitor> list = new ArrayList<>();
|
||||||
|
|
||||||
|
if (!CollectionUtils.isEmpty(testResources)) {
|
||||||
|
AtomicInteger index = new AtomicInteger(1);
|
||||||
|
testResources.forEach(testResource -> {
|
||||||
|
String configuration = testResource.getConfiguration();
|
||||||
|
NodeDTO nodeDTO = JSON.parseObject(configuration, NodeDTO.class);
|
||||||
|
Monitor monitor = new Monitor();
|
||||||
|
monitor.setName("名称" + index.getAndIncrement());
|
||||||
|
monitor.setDescription("默认生成");
|
||||||
|
monitor.setIp(nodeDTO.getIp());
|
||||||
|
monitor.setPort(9100);
|
||||||
|
monitor.setMonitorStatus(MonitorStatus.NORMAL.name());
|
||||||
|
list.add(monitor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CollectionUtils.isEmpty(list)) {
|
||||||
|
jsonObject.put("monitorParams", list);
|
||||||
|
}
|
||||||
|
|
||||||
|
advancedConfiguration = JSON.toJSONString(jsonObject);
|
||||||
|
|
||||||
loadTest.setUserId(SessionUtils.getUser().getId());
|
loadTest.setUserId(SessionUtils.getUser().getId());
|
||||||
loadTest.setId(UUID.randomUUID().toString());
|
loadTest.setId(UUID.randomUUID().toString());
|
||||||
loadTest.setName(request.getName());
|
loadTest.setName(request.getName());
|
||||||
|
@ -212,7 +250,7 @@ public class PerformanceTestService {
|
||||||
loadTest.setUpdateTime(System.currentTimeMillis());
|
loadTest.setUpdateTime(System.currentTimeMillis());
|
||||||
loadTest.setTestResourcePoolId(request.getTestResourcePoolId());
|
loadTest.setTestResourcePoolId(request.getTestResourcePoolId());
|
||||||
loadTest.setLoadConfiguration(request.getLoadConfiguration());
|
loadTest.setLoadConfiguration(request.getLoadConfiguration());
|
||||||
loadTest.setAdvancedConfiguration(request.getAdvancedConfiguration());
|
loadTest.setAdvancedConfiguration(advancedConfiguration);
|
||||||
loadTest.setStatus(PerformanceTestStatus.Saved.name());
|
loadTest.setStatus(PerformanceTestStatus.Saved.name());
|
||||||
loadTest.setNum(getNextNum(request.getProjectId()));
|
loadTest.setNum(getNextNum(request.getProjectId()));
|
||||||
loadTestMapper.insert(loadTest);
|
loadTestMapper.insert(loadTest);
|
||||||
|
|
|
@ -81,6 +81,9 @@
|
||||||
<el-tab-pane :label="$t('report.test_log_details')">
|
<el-tab-pane :label="$t('report.test_log_details')">
|
||||||
<ms-report-log-details :report="report"/>
|
<ms-report-log-details :report="report"/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="监控详情">
|
||||||
|
<monitor-card :report="report"/>
|
||||||
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -117,11 +120,13 @@ import html2canvas from 'html2canvas';
|
||||||
import MsPerformanceReportExport from "./PerformanceReportExport";
|
import MsPerformanceReportExport from "./PerformanceReportExport";
|
||||||
import {Message} from "element-ui";
|
import {Message} from "element-ui";
|
||||||
import SameTestReports from "@/business/components/performance/report/components/SameTestReports";
|
import SameTestReports from "@/business/components/performance/report/components/SameTestReports";
|
||||||
|
import MonitorCard from "@/business/components/performance/report/components/MonitorCard";
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "PerformanceReportView",
|
name: "PerformanceReportView",
|
||||||
components: {
|
components: {
|
||||||
|
MonitorCard,
|
||||||
SameTestReports,
|
SameTestReports,
|
||||||
MsPerformanceReportExport,
|
MsPerformanceReportExport,
|
||||||
MsReportErrorLog,
|
MsReportErrorLog,
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
<template>
|
||||||
|
<div v-loading="result.loading">
|
||||||
|
<el-tabs type="border-card" :stretch="true">
|
||||||
|
<el-tab-pane v-for="item in instances" :key="item" :label="item" class="logging-content">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="10" :offset="2">
|
||||||
|
<ms-chart ref="chart1" :options="getCpuOption(item)" :autoresize="true"></ms-chart>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="10" :offset="2">
|
||||||
|
<ms-chart ref="chart2" :options="getMemoryOption(item)" :autoresize="true"></ms-chart>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="10" :offset="2">
|
||||||
|
<ms-chart ref="chart3" :options="getDiskOption(item)" :autoresize="true"></ms-chart>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="10" :offset="2">
|
||||||
|
<ms-chart ref="chart4" :options="getNetInOption(item)" :autoresize="true"></ms-chart>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="10" :offset="2">
|
||||||
|
<ms-chart ref="chart3" :options="getNetOutOption(item)" :autoresize="true"></ms-chart>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import MsChart from "@/business/components/common/chart/MsChart";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "MonitorCard",
|
||||||
|
props: ['report'],
|
||||||
|
components: {MsChart},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
resource: [],
|
||||||
|
logContent: [],
|
||||||
|
result: {},
|
||||||
|
id: '',
|
||||||
|
loading: false,
|
||||||
|
instances: [],
|
||||||
|
data: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getResource() {
|
||||||
|
this.$get("/metric/query/" + this.report.id, result => {
|
||||||
|
if (result) {
|
||||||
|
let data = result.data;
|
||||||
|
this.data = data;
|
||||||
|
let set = new Set()
|
||||||
|
data.map(d => set.add(d.instance));
|
||||||
|
this.instances = Array.from(set);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getCpuOption(id) {
|
||||||
|
let xAxis = [];
|
||||||
|
let yAxis = [];
|
||||||
|
this.data.map(d => {
|
||||||
|
if (d.instance === id && d.seriesName === 'cpu') {
|
||||||
|
xAxis = d.timestamps;
|
||||||
|
yAxis = d.values;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let option = {
|
||||||
|
title: {
|
||||||
|
left: 'center',
|
||||||
|
text: 'CPU',
|
||||||
|
textStyle: {
|
||||||
|
color: '#99743C'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xAxis
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: yAxis,
|
||||||
|
type: 'line'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
},
|
||||||
|
getDiskOption(id) {
|
||||||
|
let xAxis = [];
|
||||||
|
let yAxis = [];
|
||||||
|
this.data.map(d => {
|
||||||
|
if (d.instance === id && d.seriesName === 'disk') {
|
||||||
|
xAxis = d.timestamps;
|
||||||
|
yAxis = d.values;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let option = {
|
||||||
|
title: {
|
||||||
|
left: 'center',
|
||||||
|
text: 'Disk',
|
||||||
|
textStyle: {
|
||||||
|
color: '#99743C'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xAxis
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: yAxis,
|
||||||
|
type: 'line'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
},
|
||||||
|
getNetInOption(id) {
|
||||||
|
let xAxis = [];
|
||||||
|
let yAxis = [];
|
||||||
|
this.data.map(d => {
|
||||||
|
if (d.instance === id && d.seriesName === 'netIn') {
|
||||||
|
xAxis = d.timestamps;
|
||||||
|
yAxis = d.values;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let option = {
|
||||||
|
title: {
|
||||||
|
left: 'center',
|
||||||
|
text: 'NetIn',
|
||||||
|
textStyle: {
|
||||||
|
color: '#99743C'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xAxis
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: yAxis,
|
||||||
|
type: 'line'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
},
|
||||||
|
getNetOutOption(id) {
|
||||||
|
let xAxis = [];
|
||||||
|
let yAxis = [];
|
||||||
|
this.data.map(d => {
|
||||||
|
if (d.instance === id && d.seriesName === 'netOut') {
|
||||||
|
xAxis = d.timestamps;
|
||||||
|
yAxis = d.values;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let option = {
|
||||||
|
title: {
|
||||||
|
left: 'center',
|
||||||
|
text: 'NetOut',
|
||||||
|
textStyle: {
|
||||||
|
color: '#99743C'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xAxis
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: yAxis,
|
||||||
|
type: 'line'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
},
|
||||||
|
getMemoryOption(id) {
|
||||||
|
let xAxis = [];
|
||||||
|
let yAxis = [];
|
||||||
|
this.data.map(d => {
|
||||||
|
if (d.instance === id && d.seriesName === 'memory') {
|
||||||
|
xAxis = d.timestamps;
|
||||||
|
yAxis = d.values;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let option = {
|
||||||
|
title: {
|
||||||
|
left: 'center',
|
||||||
|
text: 'Memory',
|
||||||
|
textStyle: {
|
||||||
|
color: '#99743C'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
data: xAxis
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value'
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: yAxis,
|
||||||
|
type: 'line'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
report: {
|
||||||
|
handler(val) {
|
||||||
|
if (!val.status || !val.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let status = val.status;
|
||||||
|
this.id = val.id;
|
||||||
|
if (status === "Completed" || status === "Running") {
|
||||||
|
this.getResource();
|
||||||
|
} else {
|
||||||
|
this.resource = [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -22,49 +22,19 @@
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<!-- <h4 style="margin-left: 80px;">认证配置</h4>-->
|
|
||||||
<!-- <el-form-item label="IP" prop="name">-->
|
|
||||||
<!-- <el-input v-model="form.ip" autocomplete="off"/>-->
|
|
||||||
<!-- </el-form-item>-->
|
|
||||||
<!-- <el-form-item label="用户名" prop="description">-->
|
|
||||||
<!-- <el-input v-model="form.username" autocomplete="off"/>-->
|
|
||||||
<!-- </el-form-item>-->
|
|
||||||
<!-- <el-form-item label="密码" prop="description">-->
|
|
||||||
<!-- <el-input v-model="form.password" autocomplete="off"/>-->
|
|
||||||
<!-- </el-form-item>-->
|
|
||||||
|
|
||||||
<h4 style="margin-left: 80px;">监控配置</h4>
|
<h4 style="margin-left: 80px;">监控配置</h4>
|
||||||
<el-form-item label="地址" prop="host">
|
<el-row>
|
||||||
<el-input v-model="form.host" autocomplete="off"/>
|
<el-col :span="12">
|
||||||
</el-form-item>
|
<el-form-item label="IP" prop="ip">
|
||||||
<div v-for="(item,index) in monitorList " :key="index">
|
<el-input v-model="form.ip" autocomplete="off"/>
|
||||||
<el-row>
|
</el-form-item>
|
||||||
<el-col :span="8">
|
</el-col>
|
||||||
<el-form-item label="指标名" prop="indicator">
|
<el-col :span="12">
|
||||||
<el-input v-model="item.indicator" autocomplete="off"/>
|
<el-form-item label="Port">
|
||||||
</el-form-item>
|
<el-input-number v-model="form.port" :min="1" :max="65535"/>
|
||||||
</el-col>
|
</el-form-item>
|
||||||
<el-col :span="12">
|
</el-col>
|
||||||
<el-form-item label="表达式" prop="expression">
|
</el-row>
|
||||||
<el-input v-model="item.expression" autocomplete="off"/>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
<el-col :offset="1" :span="2">
|
|
||||||
<span class="box">
|
|
||||||
<el-button @click="addMonitorConfig" type="success" size="mini" circle>
|
|
||||||
<font-awesome-icon :icon="['fas', 'plus']"/>
|
|
||||||
</el-button>
|
|
||||||
</span>
|
|
||||||
<span class="box">
|
|
||||||
<el-button @click="delMonitorConfig" type="danger" size="mini" circle>
|
|
||||||
<font-awesome-icon :icon="['fas', 'minus']"/>
|
|
||||||
</el-button>
|
|
||||||
</span>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-form-item label="描述" prop="description">
|
<el-form-item label="描述" prop="description">
|
||||||
<el-input v-model="form.description" autocomplete="off"/>
|
<el-input v-model="form.description" autocomplete="off"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -115,19 +85,10 @@ export default {
|
||||||
methods: {
|
methods: {
|
||||||
open(data, index) {
|
open(data, index) {
|
||||||
this.index = '';
|
this.index = '';
|
||||||
this.monitorList = [
|
|
||||||
{
|
|
||||||
indicator: '',
|
|
||||||
expression: '',
|
|
||||||
}
|
|
||||||
];
|
|
||||||
this.dialogVisible = true;
|
this.dialogVisible = true;
|
||||||
if (data) {
|
if (data) {
|
||||||
const copy = JSON.parse(JSON.stringify(data));
|
const copy = JSON.parse(JSON.stringify(data));
|
||||||
this.form = copy;
|
this.form = copy;
|
||||||
if (copy.monitorConfig) {
|
|
||||||
this.monitorList = JSON.parse(copy.monitorConfig);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (index !== '' && index !== undefined) {
|
if (index !== '' && index !== undefined) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
@ -140,13 +101,6 @@ export default {
|
||||||
update() {
|
update() {
|
||||||
this.$refs.monitorForm.validate(valid => {
|
this.$refs.monitorForm.validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
this.form.monitorConfig = JSON.stringify(this.monitorList);
|
|
||||||
// let authConfig = {
|
|
||||||
// "ip": this.form.ip,
|
|
||||||
// "username": this.form.username,
|
|
||||||
// "password": this.form.password,
|
|
||||||
// };
|
|
||||||
// this.form.authConfig = JSON.stringify(authConfig);
|
|
||||||
this.list.splice(this.index, 1, this.form);
|
this.list.splice(this.index, 1, this.form);
|
||||||
this.$emit("update:list", this.list);
|
this.$emit("update:list", this.list);
|
||||||
} else {
|
} else {
|
||||||
|
@ -158,13 +112,6 @@ export default {
|
||||||
create() {
|
create() {
|
||||||
this.$refs.monitorForm.validate(valid => {
|
this.$refs.monitorForm.validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
this.form.monitorConfig = JSON.stringify(this.monitorList);
|
|
||||||
// let authConfig = {
|
|
||||||
// "ip": this.form.ip,
|
|
||||||
// "username": this.form.username,
|
|
||||||
// "password": this.form.password,
|
|
||||||
// };
|
|
||||||
// this.form.authConfig = JSON.stringify(authConfig);
|
|
||||||
this.form.loadTestId = this.testId;
|
this.form.loadTestId = this.testId;
|
||||||
this.form.authStatus = CONFIG_TYPE.NOT;
|
this.form.authStatus = CONFIG_TYPE.NOT;
|
||||||
this.form.monitorStatus = CONFIG_TYPE.NOT;
|
this.form.monitorStatus = CONFIG_TYPE.NOT;
|
||||||
|
@ -176,26 +123,6 @@ export default {
|
||||||
})
|
})
|
||||||
this.dialogVisible = false;
|
this.dialogVisible = false;
|
||||||
},
|
},
|
||||||
convertConfig() {
|
|
||||||
let config = [];
|
|
||||||
if (this.form.monitorConfig) {
|
|
||||||
config = JSON.parse(this.form.monitorConfig);
|
|
||||||
}
|
|
||||||
this.monitorList = config;
|
|
||||||
},
|
|
||||||
addMonitorConfig() {
|
|
||||||
this.monitorList.push({
|
|
||||||
indicator: '',
|
|
||||||
expression: ''
|
|
||||||
});
|
|
||||||
},
|
|
||||||
delMonitorConfig(index) {
|
|
||||||
if (this.monitorList.length > 1) {
|
|
||||||
this.monitorList.splice(index, 1);
|
|
||||||
} else {
|
|
||||||
this.$warning("不能删除当前节点");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
change(data) {
|
change(data) {
|
||||||
let env = this.environments.find(env => env.id === data);
|
let env = this.environments.find(env => env.id === data);
|
||||||
this.form.environmentName = env ? env.name : "";
|
this.form.environmentName = env ? env.name : "";
|
||||||
|
|
Loading…
Reference in New Issue