diff --git a/framework/sdk-parent/sdk/src/main/java/io/metersphere/dto/ResourcePoolOperationInfo.java b/framework/sdk-parent/sdk/src/main/java/io/metersphere/dto/ResourcePoolOperationInfo.java index 8b60278e8d..2698f336ba 100644 --- a/framework/sdk-parent/sdk/src/main/java/io/metersphere/dto/ResourcePoolOperationInfo.java +++ b/framework/sdk-parent/sdk/src/main/java/io/metersphere/dto/ResourcePoolOperationInfo.java @@ -1,6 +1,7 @@ package io.metersphere.dto; import lombok.Data; +import org.apache.commons.lang3.StringUtils; import java.util.HashMap; import java.util.Map; @@ -13,6 +14,10 @@ public class ResourcePoolOperationInfo { Map nodeOperationInfos = new HashMap<>(); public void addNodeOperationInfo(String taskResourceId, String ip, String port, String cpuUsage, int runningTask) { + if(StringUtils.isBlank(cpuUsage)) { + //节点下如果获取不到cpu使用率,判断为没有查询到该节点的数据。 + return; + } NodeOperationInfo nodeOperationInfo = new NodeOperationInfo(); nodeOperationInfo.setIp(ip); nodeOperationInfo.setPort(port); diff --git a/framework/sdk-parent/sdk/src/main/java/io/metersphere/service/PrometheusService.java b/framework/sdk-parent/sdk/src/main/java/io/metersphere/service/PrometheusService.java index 48090a2c79..ba79d283ff 100644 --- a/framework/sdk-parent/sdk/src/main/java/io/metersphere/service/PrometheusService.java +++ b/framework/sdk-parent/sdk/src/main/java/io/metersphere/service/PrometheusService.java @@ -84,7 +84,7 @@ public class PrometheusService { } String cpuUsage = null; - int runningTask = 0; + int runningTask = -1; for (TestResource testResource : testResourcePoolDTO.getResources()) { String config = testResource.getConfiguration(); @@ -97,7 +97,9 @@ public class PrometheusService { String cpuUsageQL = this.generatePromQL(new String[]{"system_cpu_usage"}, nodeId); LogUtil.debug(host + "/api/v1/query?query=" + cpuUsageQL); String cpuUsageDouble = this.runPromQL(headers, host, cpuUsageQL); - cpuUsage = decimalFormat.format(Double.parseDouble(cpuUsageDouble) * 100) + "%"; + if(StringUtils.isNotBlank(cpuUsageDouble)){ + cpuUsage = decimalFormat.format(Double.parseDouble(cpuUsageDouble) * 100) + "%"; + } } // 查询任务数 diff --git a/performance-test/backend/src/main/java/io/metersphere/controller/ShareController.java b/performance-test/backend/src/main/java/io/metersphere/controller/ShareController.java index affeac5874..480fe7adbd 100644 --- a/performance-test/backend/src/main/java/io/metersphere/controller/ShareController.java +++ b/performance-test/backend/src/main/java/io/metersphere/controller/ShareController.java @@ -132,6 +132,13 @@ public class ShareController { return performanceReportService.getReportErrorsTOP5(reportId); } + @GetMapping("/performance/report/content/errors_samples/{shareId}/{reportId}") + public SamplesRecord getErrorSamples(@PathVariable String shareId, @PathVariable String reportId) { + shareInfoService.validateExpired(shareId); + return performanceReportService.getErrorSamples(reportId); + } + + @GetMapping("/performance/report/log/resource/{shareId}/{reportId}") public List getResourceIds(@PathVariable String shareId, @PathVariable String reportId) { shareInfoService.validateExpired(shareId); diff --git a/performance-test/backend/src/main/java/io/metersphere/plan/dto/TestPlanLoadCaseDTO.java b/performance-test/backend/src/main/java/io/metersphere/plan/dto/TestPlanLoadCaseDTO.java index 2020ceb238..a64fa3d477 100644 --- a/performance-test/backend/src/main/java/io/metersphere/plan/dto/TestPlanLoadCaseDTO.java +++ b/performance-test/backend/src/main/java/io/metersphere/plan/dto/TestPlanLoadCaseDTO.java @@ -44,6 +44,7 @@ public class TestPlanLoadCaseDTO extends TestPlanLoadCaseWithBLOBs { private List reportLogResource; private List reportResource; private List metricData; + private SamplesRecord errorSamples; private List resourcePools; } } diff --git a/performance-test/backend/src/main/java/io/metersphere/plan/service/TestPlanLoadCaseService.java b/performance-test/backend/src/main/java/io/metersphere/plan/service/TestPlanLoadCaseService.java index 7e56434213..fa2ce80d42 100644 --- a/performance-test/backend/src/main/java/io/metersphere/plan/service/TestPlanLoadCaseService.java +++ b/performance-test/backend/src/main/java/io/metersphere/plan/service/TestPlanLoadCaseService.java @@ -605,7 +605,8 @@ public class TestPlanLoadCaseService { response.setReportErrors(reportErrors); List reportErrorsTop5 = performanceReportService.getReportErrorsTOP5(reportId); response.setReportErrorsTop5(reportErrorsTop5); - + SamplesRecord samplesRecord = performanceReportService.getErrorSamples(reportId); + response.setErrorSamples(samplesRecord); // 日志详情 List reportLogResource = performanceReportService.getReportLogResource(reportId); if (org.apache.commons.collections.CollectionUtils.isNotEmpty(reportLogResource)) { diff --git a/performance-test/frontend/src/api/load-test.js b/performance-test/frontend/src/api/load-test.js index ea2765fb9b..c2bb7722b5 100644 --- a/performance-test/frontend/src/api/load-test.js +++ b/performance-test/frontend/src/api/load-test.js @@ -112,6 +112,9 @@ export function getPerformanceReportErrorSamples(reportId) { return get('/performance/report/content/errors_samples/' + reportId); } +export function getSharePerformanceReportErrorSamples(shareId, reportId) { + return get('/share/performance/report/content/errors_samples/' + shareId + '/' + reportId); +} export function getSharePerformanceReportErrorsTop5(shareId, reportId) { return get('/share/performance/report/content/errors_top5/' + shareId + '/' + reportId); } diff --git a/performance-test/frontend/src/template/report/performance/share/LoadCaseReportView.vue b/performance-test/frontend/src/template/report/performance/share/LoadCaseReportView.vue index 9fecec2466..c087834b77 100644 --- a/performance-test/frontend/src/template/report/performance/share/LoadCaseReportView.vue +++ b/performance-test/frontend/src/template/report/performance/share/LoadCaseReportView.vue @@ -121,10 +121,17 @@ :plan-report-template="planReportTemplate" :share-id="shareId" ref="requestStatistics"/> - + + + + + + + + @@ -178,14 +185,22 @@ import MonitorCard from "../../../../business/report/components/MonitorCard"; import MsReportTestDetails from '../../../../business/report/components/TestDetails'; import ProjectEnvironmentDialog from "../../../../business/report/components/ProjectEnvironmentDialog"; import MsTag from "metersphere-frontend/src/components/MsTag"; -import {getPerformanceReport, getPerformanceReportTime, getSharePerformanceReport, getSharePerformanceReportTime} from "../../../../api/load-test"; +import { + getPerformanceReport, + getPerformanceReportErrorSamples, + getPerformanceReportTime, + getSharePerformanceReport, getSharePerformanceReportErrorSamples, + getSharePerformanceReportTime +} from "../../../../api/load-test"; import MsTestConfiguration from "../../../../business/report/components/TestConfiguration"; import {getTestProInfo, stopTest} from "../../../../api/report"; +import SamplesTabs from "@/business/report/components/samples/SamplesTabs.vue"; export default { name: "LoadCaseReportView", components: { + SamplesTabs, MsTestConfiguration, MonitorCard, MsPerformanceReportExport, @@ -221,8 +236,10 @@ export default { websocket: null, dialogFormVisible: false, reportExportVisible: false, + haveErrorSamples: false, testPlan: {testResourcePoolId: null}, show: true, + errorSamples: {}, test: {testResourcePoolId: null}, }; }, @@ -482,6 +499,7 @@ export default { .then(({data}) => { this.handleInit(data); }); + this.checkSampleResults(this.reportId); } else { this.loading = getPerformanceReport(this.reportId) .then(({data}) => { @@ -489,6 +507,17 @@ export default { }); } }, + checkSampleResults(reportId) { + getSharePerformanceReportErrorSamples(this.shareId,reportId) + .then(res => { + if (res.data) { + this.errorSamples = res.data; + this.haveErrorSamples = true; + } else { + this.haveErrorSamples = false; + } + }); + }, handleInit(data) { if (data) { this.allProjectEnvMap = data.projectEnvMap; diff --git a/test-track/backend/src/main/java/io/metersphere/dto/SamplesRecord.java b/test-track/backend/src/main/java/io/metersphere/dto/SamplesRecord.java new file mode 100644 index 0000000000..8e365104f5 --- /dev/null +++ b/test-track/backend/src/main/java/io/metersphere/dto/SamplesRecord.java @@ -0,0 +1,13 @@ +package io.metersphere.dto; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Data +public class SamplesRecord { + //请求名称 - 错误类型 - 错误请求 + private Map>> samples; + private Map> sampleCount; +} \ No newline at end of file diff --git a/test-track/backend/src/main/java/io/metersphere/dto/TestPlanLoadCaseDTO.java b/test-track/backend/src/main/java/io/metersphere/dto/TestPlanLoadCaseDTO.java index b7761720de..c441e3ddf7 100644 --- a/test-track/backend/src/main/java/io/metersphere/dto/TestPlanLoadCaseDTO.java +++ b/test-track/backend/src/main/java/io/metersphere/dto/TestPlanLoadCaseDTO.java @@ -46,6 +46,7 @@ public class TestPlanLoadCaseDTO extends TestPlanLoadCaseWithBLOBs { private List reportLogResource; private List reportResource; private List metricData; + private SamplesRecord errorSamples; private List resourcePools; } } diff --git a/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanReportService.java b/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanReportService.java index ca1acb2e23..ef3df2be94 100644 --- a/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanReportService.java +++ b/test-track/backend/src/main/java/io/metersphere/plan/service/TestPlanReportService.java @@ -359,7 +359,10 @@ public class TestPlanReportService { returnDTO.setUiScenarioIdMap(uiScenarioIdMap); } - if (runInfoDTO != null && testPlanReport == null) { + if (testPlanReport == null) { + if(runInfoDTO == null){ + runInfoDTO = new TestPlanReportRunInfoDTO(); + } if (!saveRequest.isApiCaseIsExecuting() && !saveRequest.isScenarioIsExecuting()) { //如果没有接口用例以及场景运行,执行配置中所选的资源池配置置空,避免报告显示资源池时给用户造成困扰; runModeConfigDTO.setResourcePoolId(null); diff --git a/test-track/frontend/src/business/plan/view/comonents/load/PerformanceLoadConfig.vue b/test-track/frontend/src/business/plan/view/comonents/load/PerformanceLoadConfig.vue index 644aa36950..8b6577ccdb 100644 --- a/test-track/frontend/src/business/plan/view/comonents/load/PerformanceLoadConfig.vue +++ b/test-track/frontend/src/business/plan/view/comonents/load/PerformanceLoadConfig.vue @@ -11,6 +11,11 @@ :label="item.name" :disabled="!item.performance" :value="item.id"> + @@ -253,6 +258,8 @@ import {getQuotaValidResourcePools} from "@/api/remote/resource-pool"; import {testPlanLoadCaseGetLoadConfig} from "@/api/remote/plan/test-plan-load-case"; import {loadTestGetJmxContent} from "@/api/remote/load/performance"; import {findThreadGroup} from "@/business/plan/view/comonents/load/ThreadGroup"; +import NodeOperationLabel from "metersphere-frontend/src/components/resource-pool/NodeOperationLabel"; +import {getNodeOperationInfo} from "@/api/project"; const HANDLER = "handler"; const THREAD_GROUP_TYPE = "tgType"; @@ -287,7 +294,7 @@ const hexToRgb = function (hex) { export default { name: "PerformanceLoadConfig", - components: {MsChart}, + components: {MsChart,NodeOperationLabel}, props: { test: { type: Object @@ -323,6 +330,7 @@ export default { autoStopDelay: 30, isReadOnly: false, rampUpTimeVisible: true, + nodeOperationInfo: {}, }; }, computed: { @@ -356,6 +364,20 @@ export default { }, }, methods: { + refreshNodeOperation() { + let nodeOperationInfoRequest = {nodeIds: []}; + this.resourcePools.forEach(item => { + nodeOperationInfoRequest.nodeIds.push(item.id); + }); + + getNodeOperationInfo(nodeOperationInfoRequest) + .then(response => { + this.parseNodeOperationStatus(response.data); + }); + }, + nodeInfo(nodeId) { + return this.nodeOperationInfo[nodeId]; + }, getResourcePools() { this.loading = true; getQuotaValidResourcePools() @@ -366,9 +388,25 @@ export default { if (response.data.filter(p => p.id === this.resourcePool).length === 0) { this.resourcePool = null; } + let nodeOperationInfoRequest = {nodeIds: []}; + this.resourcePools.forEach(item => { + nodeOperationInfoRequest.nodeIds.push(item.id); + }); + + getNodeOperationInfo(nodeOperationInfoRequest) + .then(response => { + this.parseNodeOperationStatus(response.data); + }); + this.resourcePoolChange(); }); }, + parseNodeOperationStatus(nodeOperationData) { + this.nodeOperationInfo = {}; + nodeOperationData.forEach(item => { + this.nodeOperationInfo[item.id] = item; + }); + }, getLoadConfig() { testPlanLoadCaseGetLoadConfig(this.loadCaseId) .then((response) => { diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/component/LoadAllResult.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/component/LoadAllResult.vue index b524f929ab..d7d09d35bd 100644 --- a/test-track/frontend/src/business/plan/view/comonents/report/detail/component/LoadAllResult.vue +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/component/LoadAllResult.vue @@ -93,7 +93,6 @@ export default { }, methods: { getReport(row) { - if (this.isTemplate) { if (row.response) { this.showResponse = true; diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/LoadCaseReportView.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/LoadCaseReportView.vue index 198f32ed3e..5daa3be4f0 100644 --- a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/LoadCaseReportView.vue +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/LoadCaseReportView.vue @@ -64,7 +64,11 @@ :plan-report-template="planReportTemplate" :share-id="shareId" ref="requestStatistics"/> - + + + + + @@ -83,9 +87,7 @@ - - @@ -104,9 +106,9 @@ import MsReportTestDetails from './TestDetails'; import ProjectEnvironmentDialog from "./ProjectEnvironmentDialog"; import MsTag from "metersphere-frontend/src/components/MsTag"; import MsTestConfiguration from "./TestConfiguration"; +import SamplesTabs from "./samples/SamplesTabs"; - -export default { +export default { name: "LoadCaseReportView", components: { MsTestConfiguration, @@ -120,6 +122,7 @@ export default { MsMainContainer, ProjectEnvironmentDialog, MsTag, + SamplesTabs, }, data() { return { @@ -145,6 +148,8 @@ export default { testPlan: {testResourcePoolId: null}, show: true, test: {testResourcePoolId: null}, + haveErrorSamples: false, + errorSamples: {}, }; }, props: { @@ -163,6 +168,16 @@ export default { this.init(); } }, + created(){ + if (this.planReportTemplate) { + if (this.planReportTemplate.errorSamples) { + this.errorSamples = this.planReportTemplate.errorSamples; + this.haveErrorSamples = true; + } else { + this.haveErrorSamples = false; + } + } + }, computed: { showProjectEnv() { return this.projectEnvMap && JSON.stringify(this.projectEnvMap) !== '{}'; @@ -253,13 +268,21 @@ export default { }); }, init() { + alert(1); this.clearData(); if (this.planReportTemplate) { this.handleInit(this.planReportTemplate); } }, + handleInit(data) { if (data) { + if (data.errorSamples) { + this.errorSamples = data.errorSamples; + this.haveErrorSamples = true; + } else { + this.haveErrorSamples = false; + } this.allProjectEnvMap = data.projectEnvMap; this.isProjectEnvShowMore(data.projectEnvMap); this.status = data.status; diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/ErrorSamplesTable.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/ErrorSamplesTable.vue new file mode 100644 index 0000000000..205c546890 --- /dev/null +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/ErrorSamplesTable.vue @@ -0,0 +1,149 @@ + + + + diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/SamplesDrawer.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/SamplesDrawer.vue new file mode 100644 index 0000000000..402ebea7b8 --- /dev/null +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/SamplesDrawer.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/SamplesTabs.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/SamplesTabs.vue new file mode 100644 index 0000000000..6f3a0e835c --- /dev/null +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/SamplesTabs.vue @@ -0,0 +1,68 @@ + + + + diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/AssertionResults.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/AssertionResults.vue new file mode 100644 index 0000000000..8ba9f1187c --- /dev/null +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/AssertionResults.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/MsCodeEdit.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/MsCodeEdit.vue new file mode 100644 index 0000000000..4f08fcf8a7 --- /dev/null +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/MsCodeEdit.vue @@ -0,0 +1,140 @@ + + + + + diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/MsDropdown.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/MsDropdown.vue new file mode 100644 index 0000000000..7ff49490ba --- /dev/null +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/MsDropdown.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/RequestMetric.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/RequestMetric.vue new file mode 100644 index 0000000000..4c8d2d991b --- /dev/null +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/RequestMetric.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/RequestResultTail.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/RequestResultTail.vue new file mode 100644 index 0000000000..7349eecb1c --- /dev/null +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/RequestResultTail.vue @@ -0,0 +1,136 @@ + + + + + diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/ResponseResult.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/ResponseResult.vue new file mode 100644 index 0000000000..47507aa36d --- /dev/null +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/ResponseResult.vue @@ -0,0 +1,314 @@ + + + + + diff --git a/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/SqlResultTable.vue b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/SqlResultTable.vue new file mode 100644 index 0000000000..58ac10f70f --- /dev/null +++ b/test-track/frontend/src/business/plan/view/comonents/report/detail/load/samples/compnent/SqlResultTable.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/test-track/frontend/src/i18n/lang/en-US.js b/test-track/frontend/src/i18n/lang/en-US.js index 827afc432f..1efef01de3 100644 --- a/test-track/frontend/src/i18n/lang/en-US.js +++ b/test-track/frontend/src/i18n/lang/en-US.js @@ -63,6 +63,9 @@ const message = { } }, plan: { + error_samples: 'Error samples', + all_samples: 'All samples', + response_3_samples: 'The first three pieces of data', batch_delete_tip: "Do you want to continue deleting the test plan?", relevance_case_success: "Relevance success" }, diff --git a/test-track/frontend/src/i18n/lang/zh-CN.js b/test-track/frontend/src/i18n/lang/zh-CN.js index 28e692c32e..7a8b749755 100644 --- a/test-track/frontend/src/i18n/lang/zh-CN.js +++ b/test-track/frontend/src/i18n/lang/zh-CN.js @@ -63,6 +63,9 @@ const message = { }, }, plan: { + error_samples: '错误请求', + all_samples: '所有请求', + response_3_samples: '默认抽样前3个请求的响应数据', batch_delete_tip: "批量删除测试计划,是否继续?", relevance_case_success: "已添加至测试计划" }, diff --git a/test-track/frontend/src/i18n/lang/zh-TW.js b/test-track/frontend/src/i18n/lang/zh-TW.js index 27d64bac7f..c3dc5569d8 100644 --- a/test-track/frontend/src/i18n/lang/zh-TW.js +++ b/test-track/frontend/src/i18n/lang/zh-TW.js @@ -63,6 +63,9 @@ const message = { } }, plan: { + error_samples: '錯誤請求', + all_samples: '所有請求', + response_3_samples: '默認抽樣前3個請求的響應數據', batch_delete_tip: "批量刪除測試計劃,是否繼續?", relevance_case_success: "已添加至測試計劃" },