From fe3eb23def39089f552e556aec82de34562eba32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lud=C4=9Bk=20Nov=C3=BD?= <13610612+ludeknovy@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:52:22 +0200 Subject: [PATCH 1/4] Add comparison chart integration to item-detail component --- src/app/_services/chart-service-utils.ts | 3 + src/app/graphs/item-detail.ts | 3 +- .../item-detail/item-detail.component.html | 30 +++++ src/app/item-detail/item-detail.component.ts | 121 +++++++++++++----- 4 files changed, 121 insertions(+), 36 deletions(-) diff --git a/src/app/_services/chart-service-utils.ts b/src/app/_services/chart-service-utils.ts index 7cf255cb..8dab6c12 100644 --- a/src/app/_services/chart-service-utils.ts +++ b/src/app/_services/chart-service-utils.ts @@ -95,6 +95,9 @@ export interface ChartLine { labels: Map; overall: Map; scatter: Map + threadsPerThreadGroup: Map + statusCodes:Map; + } export interface LabelChartLine { diff --git a/src/app/graphs/item-detail.ts b/src/app/graphs/item-detail.ts index 29af6e25..232a720a 100644 --- a/src/app/graphs/item-detail.ts +++ b/src/app/graphs/item-detail.ts @@ -282,7 +282,8 @@ export const networkLineSettings: any = { export const scatterChart = { chart: { type: "scatter", - zoomType: "xy" + zoomType: "xy", + marginTop: 50, }, title: { text: "" diff --git a/src/app/item-detail/item-detail.component.html b/src/app/item-detail/item-detail.component.html index dc726020..33699f65 100644 --- a/src/app/item-detail/item-detail.component.html +++ b/src/app/item-detail/item-detail.component.html @@ -401,6 +401,16 @@
Overall Chart [(update)]="updateOverallChartFlag" style="width: 100%; height: 350px; display: block;"> + + + +
+ + +
+ @@ -419,6 +429,16 @@
[(update)]="updateScatterChartFlag" style="width: 100%; height: 350px; display: block;"> + + + + +
+ + +
@@ -437,6 +457,16 @@
Status Codes Chart
[(update)]="updateChartFlag" style="width: 100%; height: 350px; display: block;"> + + + + +
+ + +
diff --git a/src/app/item-detail/item-detail.component.ts b/src/app/item-detail/item-detail.component.ts index 65df30fc..08536242 100644 --- a/src/app/item-detail/item-detail.component.ts +++ b/src/app/item-detail/item-detail.component.ts @@ -17,19 +17,21 @@ import { Metrics } from "./metrics"; import { AnalyzeChartService } from "../analyze-chart.service"; import { getValidationResults } from "../utils/showZeroErrorTolerance"; import { ItemChartService } from "../_services/item-chart.service"; +import { ComparisonChartService } from "../_services/comparison-chart.service"; +import { ChartLines } from "../_services/chart-service-utils"; exporting(Highcharts); @Component({ - selector: "app-item-detail", - templateUrl: "./item-detail.component.html", - styleUrls: ["./item-detail.component.scss", "../shared-styles.css"], + selector: 'app-item-detail', + templateUrl: './item-detail.component.html', + styleUrls: ['./item-detail.component.scss', '../shared-styles.css'], providers: [DecimalPipe] }) export class ItemDetailComponent implements OnInit, OnDestroy { - @ViewChild("overallChart") componentRef; + @ViewChild('overallChart') componentRef; Highcharts: typeof Highcharts = Highcharts; chart: Highcharts.Chart; @@ -84,7 +86,21 @@ export class ItemDetailComponent implements OnInit, OnDestroy { zeroErrorValidation: null, minTestDurationValidation: null, }; - + comparisonChartOptionsDefault = { + overallChart: null, + threadsPerThreadGroup: null, + scatterChartOptions: null, + statusChartOptions: null, + }; + comparisonLabelChartOptions = { + overallChart: null, + threadsPerThreadGroup: null, + scatterChartOptions: null, + statusChartOptions: null, + }; + comparisonChart: Highcharts.Chart; + updateComparisonChartFlag; + comparisonChartCallback; constructor( private route: ActivatedRoute, @@ -94,11 +110,15 @@ export class ItemDetailComponent implements OnInit, OnDestroy { private toastr: ToastrService, private analyzeChartService: AnalyzeChartService, private itemChartService: ItemChartService, + private comparisonChartService: ComparisonChartService, ) { this.Math = Math; this.overallChartCallback = chart => { this.overallChart = chart; }; + this.comparisonChartCallback = chart => { + this.comparisonChart = chart; + }; } @@ -132,12 +152,14 @@ export class ItemDetailComponent implements OnInit, OnDestroy { this.itemChartService.setCurrentPlot(this.itemData.plot); this.selectedPlotSubscription(); this.plotRangeSubscription(); + this.comparisonSubscription(); this.calculateTotalRequests(); const validations = this.showValidationWarning(this.itemData); this.validations = { zeroErrorValidation: validations.zeroErrorToleranceValidation, minTestDurationValidation: validations.minTestDurationValidation }; + this; this.spinner.hide(); }); this.analyzeChartService.currentData.subscribe(data => { @@ -154,41 +176,18 @@ export class ItemDetailComponent implements OnInit, OnDestroy { ngOnDestroy() { this.toastr.clear(); + this.comparisonChartService.resetPlot() } private selectedPlotSubscription() { this.itemChartService.selectedPlot$.subscribe((value) => { this.chartLines = value.chartLines; - if (this.chartLines) { - const overallChartSeries = Array.from(this.chartLines?.overall?.values()); - if (this.chartLines.threadsPerThreadGroup.has(Metrics.Threads)) { - this.threadsPerThreadGroup = this.chartLines.threadsPerThreadGroup.get(Metrics.Threads); - } - - - this.overallChartOptions.series = JSON.parse(JSON.stringify(overallChartSeries)); - const scatterResponseTimeData = value.chartLines.scatter.get(Metrics.ResponseTimeRaw); - - if (this.chartLines?.scatter?.has(Metrics.ResponseTimeRaw)) { - this.scatterChartOptions = scatterChart; - this.scatterChartOptions.series = [{ - data: scatterResponseTimeData, name: "Response Time", marker: { - radius: 1 - }, - }]; - this.updateScatterChartFlag = true; - } - - if (this.chartLines?.statusCodes?.has(Metrics.StatusCodeInTime)) { - // initialize the chart options only when there are the status codes data - this.statusChartOptions = { - ...commonGraphSettings("") - }; - const statusCodesLines = this.chartLines?.statusCodes.get(Metrics.StatusCodeInTime); - this.statusChartOptions.series = JSON.parse(JSON.stringify(statusCodesLines.data)); - } - } - + const chartOptions = this.prepareChartOptions(value) + this.threadsPerThreadGroup = chartOptions.threadsPerThreadGroup + this.overallChartOptions = chartOptions.overallChart + this.statusChartOptions = chartOptions.statusChartOptions + this.scatterChartOptions = chartOptions.scatterChartOptions + this.updateScatterChartFlag = true; this.updateChartFlag = true; }); } @@ -203,6 +202,58 @@ export class ItemDetailComponent implements OnInit, OnDestroy { }); } + private comparisonSubscription() { + this.comparisonChartService.selectedPlot$.subscribe((plot) => { + if (!plot) { + if (this.comparisonChart) { + this.comparisonLabelChartOptions = this.comparisonChartOptionsDefault; + this.comparisonChart = null; + } + return; + } + this.comparisonLabelChartOptions = this.prepareChartOptions(plot) + this.updateChartFlag = true; + this.updateScatterChartFlag = true; + }); + } + + private prepareChartOptions(plot: ChartLines) { + const chartOptions = this.comparisonChartOptionsDefault + if (plot.chartLines) { + const overallChartSeries = Array.from(this.chartLines?.overall?.values()); + if (plot.chartLines.threadsPerThreadGroup.has(Metrics.Threads)) { + chartOptions.threadsPerThreadGroup = plot.chartLines.threadsPerThreadGroup.get(Metrics.Threads); + } + + chartOptions.overallChart = { + ...overallChartSettings("ms"), + series: JSON.parse(JSON.stringify(overallChartSeries)) + }; + + + const scatterResponseTimeData = plot.chartLines.scatter.get(Metrics.ResponseTimeRaw); + + if (plot.chartLines?.scatter?.has(Metrics.ResponseTimeRaw)) { + chartOptions.scatterChartOptions = scatterChart; + chartOptions.scatterChartOptions.series = [{ + data: scatterResponseTimeData, name: "Response Time", marker: { + radius: 1 + }, + }]; + } + + if (plot.chartLines?.statusCodes?.has(Metrics.StatusCodeInTime)) { + // initialize the chart options only when there are the status codes data + chartOptions.statusChartOptions = { + ...commonGraphSettings("") + }; + const statusCodesLines = plot.chartLines?.statusCodes.get(Metrics.StatusCodeInTime); + chartOptions.statusChartOptions.series = JSON.parse(JSON.stringify(statusCodesLines.data)); + } + return chartOptions; + } + } + private updateMinMaxOfCharts(min, max) { if (min && max) { this.plotRangeMin = min; From faf0db41a2c986d47834fac796de603976db55ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lud=C4=9Bk=20Nov=C3=BD?= <13610612+ludeknovy@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:33:00 +0200 Subject: [PATCH 2/4] Add date range to charts and refactor chart options Integrated start and end date parameters into the charts to allow for date range filtering. Refactored chart option handling by introducing the `ItemChartOption` class, improving code organization and readability. --- src/app/_services/comparison-chart.service.ts | 36 ++++--- src/app/graphs/item-detail.ts | 6 +- src/app/item-detail/item-chart-option.ts | 23 +++++ .../item-detail/item-detail.component.html | 22 +++-- src/app/item-detail/item-detail.component.ts | 97 ++++++++++--------- .../label-chart/label-chart.component.ts | 4 +- .../stats-compare/stats-compare.component.ts | 8 +- 7 files changed, 125 insertions(+), 71 deletions(-) create mode 100644 src/app/item-detail/item-chart-option.ts diff --git a/src/app/_services/comparison-chart.service.ts b/src/app/_services/comparison-chart.service.ts index 37f066cb..3174f414 100644 --- a/src/app/_services/comparison-chart.service.ts +++ b/src/app/_services/comparison-chart.service.ts @@ -1,19 +1,19 @@ import { Injectable } from "@angular/core"; import { BehaviorSubject } from "rxjs"; -import { ChartLines, getChartLines } from "./chart-service-utils"; +import { ChartLine, getChartLines } from "./chart-service-utils"; @Injectable({ - providedIn: "root" + providedIn: 'root' }) export class ComparisonChartService { - private plot$ = new BehaviorSubject({ chartLines: null }); - private histogramPlot$ = new BehaviorSubject<{ responseTimePerLabelDistribution: []}>(null); + private plot$ = new BehaviorSubject({ chartLines: null, startDate: null, endDate: null }); + private histogramPlot$ = new BehaviorSubject<{ responseTimePerLabelDistribution: [] }>(null); private interval$ = new BehaviorSubject(null); selectedPlot$ = this.plot$.asObservable(); - histogram$ = this.histogramPlot$.asObservable() + histogram$ = this.histogramPlot$.asObservable(); setInterval(interval) { @@ -25,21 +25,31 @@ export class ComparisonChartService { } resetPlot() { - this.plot$.next(null) + this.plot$.next(null); } - setComparisonPlot(defaultPlot, extraPlots) { + setComparisonPlot(defaultPlot: ChartLine, extraPlots, startDate, endDate) { this.interval$.subscribe(interval => { - let comparisonPlot = null + let comparisonPlot: ChartLine = null; if (!interval || interval === "Auto") { - comparisonPlot = defaultPlot + comparisonPlot = defaultPlot; } else { - const extraPlotIntervalData = extraPlots?.find(extraPlot => extraPlot.interval === interval)?.data - comparisonPlot = extraPlotIntervalData || defaultPlot + const extraPlotIntervalData = extraPlots?.find(extraPlot => extraPlot.interval === interval)?.data; + comparisonPlot = extraPlotIntervalData || defaultPlot; } - this.plot$.next(comparisonPlot ? getChartLines(comparisonPlot): null) - }) + this.plot$.next({ + chartLines: comparisonPlot ? getChartLines(comparisonPlot).chartLines : null, + startDate: startDate ? new Date(startDate) : null, + endDate: endDate ? new Date(endDate) : null, + }); + }); } } + +export interface ComparisonChartLines { + chartLines?: ChartLine; + startDate: Date; + endDate: Date; +} diff --git a/src/app/graphs/item-detail.ts b/src/app/graphs/item-detail.ts index 232a720a..d5649c74 100644 --- a/src/app/graphs/item-detail.ts +++ b/src/app/graphs/item-detail.ts @@ -279,7 +279,7 @@ export const networkLineSettings: any = { name: Metrics.Network }; -export const scatterChart = { +export const scatterChart = () => ({ chart: { type: "scatter", zoomType: "xy", @@ -297,6 +297,8 @@ export const scatterChart = { }, xAxis: { showLastLabel: true, + min: null, + max: null, type: "datetime", legend: { enabled: false, @@ -339,4 +341,4 @@ export const scatterChart = { } } }, -}; +}); diff --git a/src/app/item-detail/item-chart-option.ts b/src/app/item-detail/item-chart-option.ts new file mode 100644 index 00000000..d1e8081c --- /dev/null +++ b/src/app/item-detail/item-chart-option.ts @@ -0,0 +1,23 @@ +export class ItemChartOption { + public overallChart = null + public threadsPerThreadGroup = null + public scatterChartOptions = null + public statusChartOptions = null + + setChartsOptions(options: { overallChart: any, threadsPerThreadGroup: any, scatterChartOptions: any, statusChartOptions: any }) { + if (!options) { + return + } + this.overallChart = options.overallChart + this.threadsPerThreadGroup = options.threadsPerThreadGroup + this.scatterChartOptions = options.scatterChartOptions + this.statusChartOptions = options.statusChartOptions + } + + resetChartOptions() { + this.overallChart = null + this.threadsPerThreadGroup = null + this.scatterChartOptions = null + this.statusChartOptions = null + } +} diff --git a/src/app/item-detail/item-detail.component.html b/src/app/item-detail/item-detail.component.html index 33699f65..d76e5db0 100644 --- a/src/app/item-detail/item-detail.component.html +++ b/src/app/item-detail/item-detail.component.html @@ -402,11 +402,11 @@
Overall Chart style="width: 100%; height: 350px; display: block;"> - + -
+
-
@@ -430,14 +430,18 @@
style="width: 100%; height: 350px; display: block;"> - -
- + + +
+ + +
@@ -458,12 +462,12 @@
Status Codes Chart
style="width: 100%; height: 350px; display: block;"> - + -
+
-
diff --git a/src/app/item-detail/item-detail.component.ts b/src/app/item-detail/item-detail.component.ts index 08536242..712f0490 100644 --- a/src/app/item-detail/item-detail.component.ts +++ b/src/app/item-detail/item-detail.component.ts @@ -18,7 +18,8 @@ import { AnalyzeChartService } from "../analyze-chart.service"; import { getValidationResults } from "../utils/showZeroErrorTolerance"; import { ItemChartService } from "../_services/item-chart.service"; import { ComparisonChartService } from "../_services/comparison-chart.service"; -import { ChartLines } from "../_services/chart-service-utils"; +import { ChartLine } from "../_services/chart-service-utils"; +import { ItemChartOption } from "./item-chart-option"; exporting(Highcharts); @@ -86,21 +87,13 @@ export class ItemDetailComponent implements OnInit, OnDestroy { zeroErrorValidation: null, minTestDurationValidation: null, }; - comparisonChartOptionsDefault = { - overallChart: null, - threadsPerThreadGroup: null, - scatterChartOptions: null, - statusChartOptions: null, - }; - comparisonLabelChartOptions = { - overallChart: null, - threadsPerThreadGroup: null, - scatterChartOptions: null, - statusChartOptions: null, - }; + comparisonItemChartOptions = new ItemChartOption() comparisonChart: Highcharts.Chart; - updateComparisonChartFlag; + scatterChart: Highcharts.Chart; + updateComparisonChartFlag = false; + updateComparisonScatterChartFlag = false; comparisonChartCallback; + comparisonScatterChartCallback constructor( private route: ActivatedRoute, @@ -119,6 +112,9 @@ export class ItemDetailComponent implements OnInit, OnDestroy { this.comparisonChartCallback = chart => { this.comparisonChart = chart; }; + this.comparisonScatterChartCallback = chart => { + this.scatterChart = chart; + }; } @@ -182,7 +178,7 @@ export class ItemDetailComponent implements OnInit, OnDestroy { private selectedPlotSubscription() { this.itemChartService.selectedPlot$.subscribe((value) => { this.chartLines = value.chartLines; - const chartOptions = this.prepareChartOptions(value) + const chartOptions = this.prepareChartOptions(value.chartLines) this.threadsPerThreadGroup = chartOptions.threadsPerThreadGroup this.overallChartOptions = chartOptions.overallChart this.statusChartOptions = chartOptions.statusChartOptions @@ -204,54 +200,67 @@ export class ItemDetailComponent implements OnInit, OnDestroy { private comparisonSubscription() { this.comparisonChartService.selectedPlot$.subscribe((plot) => { + this.comparisonItemChartOptions.resetChartOptions(); if (!plot) { - if (this.comparisonChart) { - this.comparisonLabelChartOptions = this.comparisonChartOptionsDefault; - this.comparisonChart = null; - } + this.comparisonChart = null; return; + } else { + this.comparisonItemChartOptions.setChartsOptions(this.prepareChartOptions(plot.chartLines, plot.startDate, plot.endDate)); } - this.comparisonLabelChartOptions = this.prepareChartOptions(plot) - this.updateChartFlag = true; - this.updateScatterChartFlag = true; + this.updateComparisonChartFlag = true; + this.updateComparisonScatterChartFlag = true }); } - private prepareChartOptions(plot: ChartLines) { - const chartOptions = this.comparisonChartOptionsDefault - if (plot.chartLines) { - const overallChartSeries = Array.from(this.chartLines?.overall?.values()); - if (plot.chartLines.threadsPerThreadGroup.has(Metrics.Threads)) { - chartOptions.threadsPerThreadGroup = plot.chartLines.threadsPerThreadGroup.get(Metrics.Threads); - } + private prepareChartOptions(plot: ChartLine, startDate?: Date, endDate?: Date) { + const chartOptions = { + overallChart: null, + threadsPerThreadGroup: null, + scatterChartOptions: null, + statusChartOptions: null, + } + if (plot) { + if(plot.overall) { + const overallChartSeries = Array.from(plot.overall?.values()); + if (plot.threadsPerThreadGroup.has(Metrics.Threads)) { + chartOptions.threadsPerThreadGroup = plot.threadsPerThreadGroup.get(Metrics.Threads); + } - chartOptions.overallChart = { - ...overallChartSettings("ms"), - series: JSON.parse(JSON.stringify(overallChartSeries)) - }; + chartOptions.overallChart = { + ...overallChartSettings("ms"), + series: JSON.parse(JSON.stringify(overallChartSeries)) + }; + } + const scatterResponseTimeData = plot.scatter.get(Metrics.ResponseTimeRaw); + if (scatterResponseTimeData) { + chartOptions.scatterChartOptions = { + ...scatterChart(), + series: [{ + data: scatterResponseTimeData, name: "Response Time", marker: { + radius: 1 + }, + }] + }; + if (startDate && endDate) { + chartOptions.scatterChartOptions.xAxis.min = startDate.getTime() + chartOptions.scatterChartOptions.xAxis.max = endDate.getTime() + } - const scatterResponseTimeData = plot.chartLines.scatter.get(Metrics.ResponseTimeRaw); - if (plot.chartLines?.scatter?.has(Metrics.ResponseTimeRaw)) { - chartOptions.scatterChartOptions = scatterChart; - chartOptions.scatterChartOptions.series = [{ - data: scatterResponseTimeData, name: "Response Time", marker: { - radius: 1 - }, - }]; } - if (plot.chartLines?.statusCodes?.has(Metrics.StatusCodeInTime)) { + if (plot?.statusCodes?.has(Metrics.StatusCodeInTime)) { // initialize the chart options only when there are the status codes data chartOptions.statusChartOptions = { ...commonGraphSettings("") }; - const statusCodesLines = plot.chartLines?.statusCodes.get(Metrics.StatusCodeInTime); + const statusCodesLines = plot?.statusCodes.get(Metrics.StatusCodeInTime); chartOptions.statusChartOptions.series = JSON.parse(JSON.stringify(statusCodesLines.data)); } - return chartOptions; } + + return chartOptions; } private updateMinMaxOfCharts(min, max) { diff --git a/src/app/item-detail/label-chart/label-chart.component.ts b/src/app/item-detail/label-chart/label-chart.component.ts index f0a0b76d..efe9d527 100644 --- a/src/app/item-detail/label-chart/label-chart.component.ts +++ b/src/app/item-detail/label-chart/label-chart.component.ts @@ -150,7 +150,9 @@ export class LabelChartComponent implements OnChanges { this.labelCharts.set("Histogram", responseTimeDistribution(histogram.values)); } else { const histogram = histogramData.find(data => data.label === this.label); - this.comparisonLabelCharts.set("Histogram", responseTimeDistribution(histogram.values)); + if (histogram && histogram.data) { + this.comparisonLabelCharts.set("Histogram", responseTimeDistribution(histogram.values)); + } } } diff --git a/src/app/item-detail/stats-compare/stats-compare.component.ts b/src/app/item-detail/stats-compare/stats-compare.component.ts index dc2904bf..bda48747 100644 --- a/src/app/item-detail/stats-compare/stats-compare.component.ts +++ b/src/app/item-detail/stats-compare/stats-compare.component.ts @@ -84,7 +84,9 @@ export class StatsCompareComponent implements OnInit { environment: _.environment, plot: _.plot, histogramPlotData: _.histogramPlotData, - extraPlotData: _.extraPlotData + extraPlotData: _.extraPlotData, + startDate: _.overview.startDate, + endDate: _.overview.endDate, }); this.page = 0; this.modalService.dismissAll(); @@ -103,13 +105,15 @@ export class StatsCompareComponent implements OnInit { plot: _.plot, histogramPlotData: _.histogramPlotData, extraPlotData: _.extraPlotData, + startDate: _.overview.startDate, + endDate: _.overview.endDate, id })); } itemToCompare(data) { this.resetStatsData(); - this.comparisonChartService.setComparisonPlot(data.plot, data.extraPlotData); + this.comparisonChartService.setComparisonPlot(data.plot, data.extraPlotData, data.startDate, data.endDate); this.comparisonChartService.setHistogramPlot(data.histogramPlotData); this.comparingData = data; this.comparedMetadata = { id: data.id, maxVu: data.maxVu }; From f82fb45aa50e7188e5f8f8c6150e949421ea994c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lud=C4=9Bk=20Nov=C3=BD?= <13610612+ludeknovy@users.noreply.github.com> Date: Sat, 28 Sep 2024 15:34:54 +0200 Subject: [PATCH 3/4] Monitoring comparison chart --- src/app/_services/chart-service-utils.ts | 12 +++- src/app/_services/comparison-chart.service.ts | 5 +- src/app/_services/item-chart.service.ts | 5 +- .../chart-interval.component.ts | 2 +- src/app/item-detail/item-detail.component.ts | 2 +- src/app/item-detail/metrics.ts | 1 + .../monitoring-stats.component.css | 4 ++ .../monitoring-stats.component.html | 11 +++ .../monitoring-stats.component.ts | 49 +++++++++----- .../stats-compare/stats-compare.component.ts | 9 +-- src/app/items.service.model.ts | 67 +++++++++---------- 11 files changed, 102 insertions(+), 65 deletions(-) diff --git a/src/app/_services/chart-service-utils.ts b/src/app/_services/chart-service-utils.ts index 8dab6c12..680a51be 100644 --- a/src/app/_services/chart-service-utils.ts +++ b/src/app/_services/chart-service-utils.ts @@ -1,8 +1,9 @@ import { errorLineSettings, networkLineSettings, threadLineSettings, throughputLineSettings } from "../graphs/item-detail"; import { bytesToMbps } from "../item-detail/calculations"; import { Metrics } from "../item-detail/metrics"; +import { MonitoringData } from "../items.service.model"; -export const getChartLines = (plot): ChartLines => { +export const getChartLines = (plot, monitoringPlot: MonitoringData[]): ChartLines => { const { threads, overallTimeResponse, overallThroughput, overAllFailRate, overAllNetworkV2, @@ -20,6 +21,7 @@ export const getChartLines = (plot): ChartLines => { labels: new Map(), statusCodes: new Map(), scatter: new Map(), + monitoring: new Map(), }; if (overAllNetworkV2) { @@ -48,6 +50,10 @@ export const getChartLines = (plot): ChartLines => { chartLines.scatter.set(Metrics.ResponseTimeRaw, scatterPlotData) } + if (monitoringPlot && monitoringPlot.length > 0) { + chartLines.monitoring.set(Metrics.Monitoring, monitoringPlot) + } + if (networkV2) { const networkMbps = networkV2.map((_) => { _.data = _.data.map(__ => [__[0], bytesToMbps(__[1])]); @@ -83,7 +89,6 @@ export const getChartLines = (plot): ChartLines => { chartLines.labels.set(Metrics.Throughput, throughput.map((label) => ({ ...label, suffix: " reqs/s" }))); return { chartLines }; - }; @@ -97,11 +102,12 @@ export interface ChartLine { scatter: Map threadsPerThreadGroup: Map statusCodes:Map; + monitoring: Map } - export interface LabelChartLine { name: string data: [], suffix: string } + diff --git a/src/app/_services/comparison-chart.service.ts b/src/app/_services/comparison-chart.service.ts index 3174f414..8d93f6d7 100644 --- a/src/app/_services/comparison-chart.service.ts +++ b/src/app/_services/comparison-chart.service.ts @@ -1,6 +1,7 @@ import { Injectable } from "@angular/core"; import { BehaviorSubject } from "rxjs"; import { ChartLine, getChartLines } from "./chart-service-utils"; +import { MonitoringData } from "../items.service.model"; @Injectable({ providedIn: 'root' @@ -29,7 +30,7 @@ export class ComparisonChartService { } - setComparisonPlot(defaultPlot: ChartLine, extraPlots, startDate, endDate) { + setComparisonPlot(defaultPlot: ChartLine, extraPlots, startDate, endDate, monitoring: MonitoringData[]) { this.interval$.subscribe(interval => { let comparisonPlot: ChartLine = null; if (!interval || interval === "Auto") { @@ -39,7 +40,7 @@ export class ComparisonChartService { comparisonPlot = extraPlotIntervalData || defaultPlot; } this.plot$.next({ - chartLines: comparisonPlot ? getChartLines(comparisonPlot).chartLines : null, + chartLines: comparisonPlot ? getChartLines(comparisonPlot, monitoring).chartLines : null, startDate: startDate ? new Date(startDate) : null, endDate: endDate ? new Date(endDate) : null, }); diff --git a/src/app/_services/item-chart.service.ts b/src/app/_services/item-chart.service.ts index 615ef1e7..2c5a5d32 100644 --- a/src/app/_services/item-chart.service.ts +++ b/src/app/_services/item-chart.service.ts @@ -1,6 +1,7 @@ import { Injectable } from "@angular/core"; import { BehaviorSubject } from "rxjs"; import { ChartLines, getChartLines } from "./chart-service-utils"; +import { MonitoringData } from "../items.service.model"; @Injectable({ providedIn: "root" @@ -13,8 +14,8 @@ export class ItemChartService { selectedPlot$ = this.plot.asObservable(); plotRange$ = this.plotRange.asObservable(); - setCurrentPlot(plot) { - this.plot.next(getChartLines(plot)); + setCurrentPlot(plot, monitoringPlot: MonitoringData[]) { + this.plot.next(getChartLines(plot, monitoringPlot)); } setPlotRange(plotRange: PlotRange) { diff --git a/src/app/item-detail/chart-interval/chart-interval.component.ts b/src/app/item-detail/chart-interval/chart-interval.component.ts index 508a026e..8dbfb2c3 100644 --- a/src/app/item-detail/chart-interval/chart-interval.component.ts +++ b/src/app/item-detail/chart-interval/chart-interval.component.ts @@ -29,7 +29,7 @@ export class ChartIntervalComponent implements OnInit { } else { newPlotData = this.intervals.extraIntervals.find(interval => interval.interval === inputInterval)?.data } - this.itemChartService.setCurrentPlot(newPlotData) + this.itemChartService.setCurrentPlot(newPlotData, null) } } diff --git a/src/app/item-detail/item-detail.component.ts b/src/app/item-detail/item-detail.component.ts index 712f0490..39a0d906 100644 --- a/src/app/item-detail/item-detail.component.ts +++ b/src/app/item-detail/item-detail.component.ts @@ -145,7 +145,7 @@ export class ItemDetailComponent implements OnInit, OnDestroy { .subscribe((results) => { this.itemData = results; this.monitoringAlerts(); - this.itemChartService.setCurrentPlot(this.itemData.plot); + this.itemChartService.setCurrentPlot(this.itemData.plot, this.itemData.monitoring.cpu.data); this.selectedPlotSubscription(); this.plotRangeSubscription(); this.comparisonSubscription(); diff --git a/src/app/item-detail/metrics.ts b/src/app/item-detail/metrics.ts index f57e7549..51c16285 100644 --- a/src/app/item-detail/metrics.ts +++ b/src/app/item-detail/metrics.ts @@ -14,4 +14,5 @@ export enum Metrics { ResponseTimeRaw = "Raw Response Time", LatencyAvg = "Latency [avg]", ConnectAvg = "Connection [avg]", + Monitoring = "Monitoring" } diff --git a/src/app/item-detail/monitoring-stats/monitoring-stats.component.css b/src/app/item-detail/monitoring-stats/monitoring-stats.component.css index db9e4665..f1d7a1a9 100644 --- a/src/app/item-detail/monitoring-stats/monitoring-stats.component.css +++ b/src/app/item-detail/monitoring-stats/monitoring-stats.component.css @@ -16,3 +16,7 @@ .card { margin-top: 20px; } + +.compare-chart{ + margin-top: 3rem; +} diff --git a/src/app/item-detail/monitoring-stats/monitoring-stats.component.html b/src/app/item-detail/monitoring-stats/monitoring-stats.component.html index df506f4b..d7511f39 100644 --- a/src/app/item-detail/monitoring-stats/monitoring-stats.component.html +++ b/src/app/item-detail/monitoring-stats/monitoring-stats.component.html @@ -9,6 +9,17 @@
Monitoring
[(update)]="updateFlag" [constructorType]="chartConstructor" style="width: auto; height: 400px; display: block;"> + + + + +
+ +
+ +
diff --git a/src/app/item-detail/monitoring-stats/monitoring-stats.component.ts b/src/app/item-detail/monitoring-stats/monitoring-stats.component.ts index 000de2e4..0c7485d1 100644 --- a/src/app/item-detail/monitoring-stats/monitoring-stats.component.ts +++ b/src/app/item-detail/monitoring-stats/monitoring-stats.component.ts @@ -1,14 +1,16 @@ import { Component, OnInit, Input, } from "@angular/core"; -import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; import * as Highcharts from "highcharts"; import { monitoringGraphSettings } from "src/app/graphs/monitoring"; import { from } from "rxjs"; +import { ComparisonChartService } from "../../_services/comparison-chart.service"; +import { MonitoringData } from "../../items.service.model"; +import { Metrics } from "../metrics"; @Component({ - selector: "app-monitoring-stats", - templateUrl: "./monitoring-stats.component.html", - styleUrls: ["./monitoring-stats.component.css", "../item-detail.component.scss", "../../shared-styles.css"] + selector: 'app-monitoring-stats', + templateUrl: './monitoring-stats.component.html', + styleUrls: ['./monitoring-stats.component.css', '../item-detail.component.scss', '../../shared-styles.css'] }) export class MonitoringStatsComponent implements OnInit { Highcharts: typeof Highcharts = Highcharts; @@ -16,26 +18,44 @@ export class MonitoringStatsComponent implements OnInit { chartConstructor = "chart"; chartCallback; updateFlag = false; + updateComparisonChartFlag = false; + monitoringComparisonChartOptions; chart; - constructor( - private modalService: NgbModal, + constructor( + private comparisonChartService: ComparisonChartService, ) { this.chartCallback = chart => { this.chart = chart; }; } - @Input() data: [{ name: string, timestamp: Date, avgCpu: number, avgMem: number }]; + @Input() data: MonitoringData[]; ngOnInit() { - this.prepareChart() + const series = this.prepareChartSeries(this.data); + from(new Promise(resolve => setTimeout(resolve, 50))).subscribe((val: any) => { + this.monitoringChartOptions = { + ...monitoringGraphSettings(), series: series + }; + this.updateFlag = true; + }); + this.comparisonChartService.selectedPlot$.subscribe(plot => { + if (plot && plot?.chartLines?.monitoring.has(Metrics.Monitoring)) { + const comparisonSeries = this.prepareChartSeries(plot?.chartLines.monitoring.get(Metrics.Monitoring)); + this.monitoringComparisonChartOptions = { + ...monitoringGraphSettings(), + series: comparisonSeries, + }; + this.updateComparisonChartFlag = true; + } + }); } - prepareChart() { - const workers = Array.from(new Set(this.data.map(data => data.name))); - const series = workers.map((worker) => this.data + prepareChartSeries(data: MonitoringData[]) { + const workers = Array.from(new Set(data.map(data => data.name))); + return workers.map((worker) => data .filter(data => data.name === worker) .reduce((acc, current) => { acc.data.cpu.push([current.timestamp, current.avgCpu]); @@ -45,12 +65,5 @@ export class MonitoringStatsComponent implements OnInit { }, { data: { cpu: [], mem: [] }, name: null })) .map((worker) => [{ data: worker.data.cpu, name: worker.name + " - cpu" }, { data: worker.data.mem, name: worker.name + " - mem" }]) .flat(); - - from(new Promise(resolve => setTimeout(resolve, 50))).subscribe((val: any) => { - this.monitoringChartOptions = { - ...monitoringGraphSettings(), series: series - }; - this.updateFlag = true; - }); } } diff --git a/src/app/item-detail/stats-compare/stats-compare.component.ts b/src/app/item-detail/stats-compare/stats-compare.component.ts index bda48747..122c28a7 100644 --- a/src/app/item-detail/stats-compare/stats-compare.component.ts +++ b/src/app/item-detail/stats-compare/stats-compare.component.ts @@ -1,8 +1,8 @@ -import { Component, OnInit, Output, EventEmitter, Input } from "@angular/core"; +import { Component, OnInit, Input } from "@angular/core"; import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; import { ItemsService } from "src/app/items.service"; import { ActivatedRoute, Params } from "@angular/router"; -import { Observable, Subscription } from "rxjs"; +import { Observable } from "rxjs"; import { IScenarios, ItemDetail, Items } from "src/app/items.service.model"; import { ItemsApiService } from "src/app/items-api.service"; import { ComparisonChartService } from "../../_services/comparison-chart.service"; @@ -87,6 +87,7 @@ export class StatsCompareComponent implements OnInit { extraPlotData: _.extraPlotData, startDate: _.overview.startDate, endDate: _.overview.endDate, + monitoring: _.monitoring, }); this.page = 0; this.modalService.dismissAll(); @@ -107,13 +108,14 @@ export class StatsCompareComponent implements OnInit { extraPlotData: _.extraPlotData, startDate: _.overview.startDate, endDate: _.overview.endDate, + monitoring: _.monitoring, id })); } itemToCompare(data) { this.resetStatsData(); - this.comparisonChartService.setComparisonPlot(data.plot, data.extraPlotData, data.startDate, data.endDate); + this.comparisonChartService.setComparisonPlot(data.plot, data.extraPlotData, data.startDate, data.endDate, data.monitoring?.cpu?.data); this.comparisonChartService.setHistogramPlot(data.histogramPlotData); this.comparingData = data; this.comparedMetadata = { id: data.id, maxVu: data.maxVu }; @@ -149,7 +151,6 @@ export class StatsCompareComponent implements OnInit { } loadScenario(event) { - console.log(event.target.value) const scenario = event.target.value this.selectedScenario = scenario this.itemsService.fetchItems(this.params.projectName, scenario, { limit: LIMIT, offset: 0 }); diff --git a/src/app/items.service.model.ts b/src/app/items.service.model.ts index 31bb4161..2af41cda 100644 --- a/src/app/items.service.model.ts +++ b/src/app/items.service.model.ts @@ -31,7 +31,7 @@ export interface ItemDetail { zeroErrorToleranceEnabled: boolean; reportStatus: ReportStatus; monitoring: { - cpu: { data: { name: string, cpu: number, timestamp: number }[], max?: number } + cpu: { data: MonitoringData[], max?: number } }; baseId: string; name: string; @@ -62,9 +62,9 @@ export interface ItemDetail { userSettings: { requestStats: RequestStats }; - errorSummary: ErrorSummary - status: string - minTestDuration: number + errorSummary: ErrorSummary; + status: string; + minTestDuration: number; } interface TopMetricsSettings { @@ -78,7 +78,7 @@ interface TopMetricsSettings { percentile: boolean; // legacy setting percentile90: boolean; percentile95: boolean; - percentile99: boolean + percentile99: boolean; } interface ItemOverview { @@ -146,9 +146,9 @@ export interface ItemStatistics { satisfaction?: number toleration?: number }; - medianResponseTime?: number - bytesPerSecond?: number - bytesSentPerSecond?: number + medianResponseTime?: number; + bytesPerSecond?: number; + bytesSentPerSecond?: number; } interface ResponseMessageFailure { @@ -156,16 +156,6 @@ interface ResponseMessageFailure { responseMessage: string; } -interface MonitoringData { - "bytes-recv"?: string; - "bytes-sent"?: string; - "conn-all"?: string; - cpu?: string; - "diskSpace"?: string; - mem?: string; - ts?: string; -} - export interface ScenarioTrendsData { overview: { avgConnect: number; @@ -338,37 +328,46 @@ export interface ThresholdResult { errorRate: { diffValue: number, passed: boolean - } + }; percentile: { diffValue: number, passed: boolean - } + }; throughput: { diffValue: number, passed: boolean - } + }; } export interface ErrorSummary { - groupedErrors: Errors[] - topErrorsByLabel: Top5Errors[] + groupedErrors: Errors[]; + topErrorsByLabel: Top5Errors[]; } interface Errors { - count: number - statusCode: string - responseMessage: string - failureMessage: string + count: number; + statusCode: string; + responseMessage: string; + failureMessage: string; } interface Top5Errors { - label: string - error1: LabelError - error2: LabelError - error3: LabelError - error4: LabelError - error5: LabelError + label: string; + error1: LabelError; + error2: LabelError; + error3: LabelError; + error4: LabelError; + error5: LabelError; } -interface LabelError { count: number; error: string } +interface LabelError { + count: number; + error: string; +} +export interface MonitoringData { + name: string, + avgCpu: number, + avgMem?: number, + timestamp: number +} From 3a27fe11b18f1940e9eeb3d35ba7bc15e331bd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lud=C4=9Bk=20Nov=C3=BD?= <13610612+ludeknovy@users.noreply.github.com> Date: Sat, 28 Sep 2024 18:59:09 +0200 Subject: [PATCH 4/4] tests fixes --- .../analyze-charts/analyze-charts.component.spec.ts | 9 ++++++++- .../label-chart/label-chart.component.spec.ts | 5 ++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/app/item-detail/analyze-charts/analyze-charts.component.spec.ts b/src/app/item-detail/analyze-charts/analyze-charts.component.spec.ts index e610ecbd..ffe8ef5b 100644 --- a/src/app/item-detail/analyze-charts/analyze-charts.component.spec.ts +++ b/src/app/item-detail/analyze-charts/analyze-charts.component.spec.ts @@ -33,7 +33,14 @@ describe("AnalyzeChartsComponent", () => { beforeEach(() => { fixture = TestBed.createComponent(AnalyzeChartsComponent); component = fixture.componentInstance; - component.chartLines = { labels: new Map([["test", [{ name: "test", data: [], suffix: " ms" }]]]), overall: new Map(), scatter: new Map() }; + component.chartLines = { + labels: new Map([["test", [{ name: "test", data: [], suffix: " ms" }]]]), + overall: new Map(), + scatter: new Map(), + threadsPerThreadGroup: new Map(), + monitoring: new Map(), + statusCodes: new Map(), + }; component.params = { projectName: "test-project", scenarioName: "test-scenario", id: "test-item" }; fixture.detectChanges(); }); diff --git a/src/app/item-detail/label-chart/label-chart.component.spec.ts b/src/app/item-detail/label-chart/label-chart.component.spec.ts index 766f7577..e88158f8 100644 --- a/src/app/item-detail/label-chart/label-chart.component.spec.ts +++ b/src/app/item-detail/label-chart/label-chart.component.spec.ts @@ -24,7 +24,10 @@ describe("LabelChartComponent", () => { component.chartLines = { labels: new Map([[Metrics.Network, [{ name: "name", suffix: "mbps", data: [] }] ]]), overall: new Map([[Metrics.Threads, { name: "virtual-users", data: [] }]]), - scatter: new Map([[Metrics.ResponseTimeRaw, [] ]]) + scatter: new Map([[Metrics.ResponseTimeRaw, [] ]]), + threadsPerThreadGroup: new Map([[Metrics.Threads, []]]), + monitoring: new Map([[Metrics.Monitoring, []]]), + statusCodes: new Map([[Metrics.StatusCodeInTime, { name: "name", data: [] }]]) }; fixture.detectChanges(); });