Skip to content

Commit

Permalink
Merge pull request #2847 from andrewbaldwin44/feature/echarts-axis
Browse files Browse the repository at this point in the history
Web UI Should use Built-In Echarts Time Axis
  • Loading branch information
cyberw authored Aug 14, 2024
2 parents a139155 + 001e876 commit 5cb7370
Show file tree
Hide file tree
Showing 24 changed files with 162 additions and 116 deletions.
15 changes: 9 additions & 6 deletions locust/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -904,18 +904,21 @@ def sort_stats(stats: dict[Any, S]) -> list[S]:

def update_stats_history(runner: Runner) -> None:
stats = runner.stats
timestamp = format_utc_timestamp(time.time())
current_response_time_percentiles = {
f"response_time_percentile_{percentile}": stats.total.get_current_response_time_percentile(percentile) or 0
f"response_time_percentile_{percentile}": [
timestamp,
stats.total.get_current_response_time_percentile(percentile) or 0,
]
for percentile in PERCENTILES_TO_CHART
}

r = {
**current_response_time_percentiles,
"time": format_utc_timestamp(time.time()),
"current_rps": stats.total.current_rps or 0,
"current_fail_per_sec": stats.total.current_fail_per_sec or 0,
"total_avg_response_time": proper_round(stats.total.avg_response_time, digits=2),
"user_count": runner.user_count or 0,
"current_rps": [timestamp, stats.total.current_rps or 0],
"current_fail_per_sec": [timestamp, stats.total.current_fail_per_sec or 0],
"total_avg_response_time": [timestamp, proper_round(stats.total.avg_response_time, digits=2)],
"user_count": [timestamp, runner.user_count or 0],
}
stats.history.append(r)

Expand Down
2 changes: 1 addition & 1 deletion locust/webui/auth.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="./assets/favicon.ico" />
<link rel="icon" href="/assets/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />

Expand Down
2 changes: 1 addition & 1 deletion locust/webui/dev.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="./assets/favicon.ico" />
<link rel="icon" href="./assets/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />

Expand Down
4 changes: 3 additions & 1 deletion locust/webui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
"prepack": "json -f package.json -I -e \"delete this.devDependencies; delete this.dependencies; delete this.scripts\"",
"scripts": {
"dev": "vite",
"dev:watch": "vite build --watch",
"watch": "npm-run-all --parallel watch:ui watch:report",
"build:ui": "vite build",
"build:lib": "vite build --config vite.lib.config.ts",
"build:report": "vite build --config vite.report.config.ts",
"watch:ui": "vite build --watch",
"watch:report": "vite build --config vite.lib.config.ts --watch",
"build": "yarn clean && npm-run-all --parallel build:ui build:report build:lib",
"clean": "rimraf dist",
"lint": "eslint './src/**/*.{ts,tsx}'",
Expand Down
2 changes: 1 addition & 1 deletion locust/webui/report.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{% raw %}
<head>
<meta charset="utf-8" />
<link rel="icon" href="./assets/favicon.ico" />
<link rel="icon" href="../../assets/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />

Expand Down
8 changes: 4 additions & 4 deletions locust/webui/src/assets/Logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@ export default function Logo({
xmlns='http://www.w3.org/2000/svg'
>
<path
clip-rule='evenodd'
clipRule='evenodd'
d='M36.6942 18.9754L48.9434 6.62621L47.341 5.01205L35.0918 17.3612L36.6942 18.9754Z'
fill='#B8EE4B'
fill-rule='evenodd'
fillRule='evenodd'
/>
<path
clip-rule='evenodd'
clipRule='evenodd'
d='M40.954 20.093L53.0301 8.01687L51.4159 6.40271L39.3398 18.4788L40.954 20.093Z'
fill='#B8EE4B'
fillRule='evenodd'
/>
<path
clip-rule='evenodd'
clipRule='evenodd'
d='M0 28.1761L39.9453 9.82391L43.7047 9.82392L47.4641 13.5833V19.9629L43.819 23.608L39.2012 18.9279L38.0252 20.0882L47.7932 29.988H41.7525L35.4796 23.7151L20.9615 29.988H0V28.1761Z'
fill='#B8EE4B'
fillRule='evenodd'
Expand Down
2 changes: 2 additions & 0 deletions locust/webui/src/components/Form/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ export default function Select({
multiple = false,
defaultValue,
sx,
...inputProps
}: ISelect) {
return (
<FormControl sx={sx}>
<InputLabel htmlFor={name} shrink>
{label}
</InputLabel>
<MuiSelect
{...inputProps}
defaultValue={defaultValue || (multiple && options) || options[0]}
id={name}
label={label}
Expand Down
9 changes: 2 additions & 7 deletions locust/webui/src/components/LineChart/LineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import { useEffect, useState, useRef } from 'react';
import { init, dispose, ECharts, connect } from 'echarts';

import { CHART_THEME } from 'components/LineChart/LineChart.constants';
import {
ILineChartTimeAxis,
ILineChart,
ILineChartMarkers,
} from 'components/LineChart/LineChart.types';
import { ILineChart, ILineChartMarkers } from 'components/LineChart/LineChart.types';
import {
createMarkLine,
createOptions,
Expand All @@ -15,7 +11,7 @@ import {
} from 'components/LineChart/LineChart.utils';
import { useSelector } from 'redux/hooks';

interface IBaseChartType extends ILineChartTimeAxis, ILineChartMarkers {}
interface IBaseChartType extends ILineChartMarkers {}

export default function LineChart<ChartType extends IBaseChartType>({
charts,
Expand Down Expand Up @@ -71,7 +67,6 @@ export default function LineChart<ChartType extends IBaseChartType>({
const isChartDataDefined = lines.every(({ key }) => !!charts[key]);
if (chart && isChartDataDefined) {
chart.setOption({
xAxis: { data: charts.time },
series: lines.map(({ key, yAxisIndex, ...echartsOptions }, index) => ({
...echartsOptions,
data: charts[key],
Expand Down
6 changes: 1 addition & 5 deletions locust/webui/src/components/LineChart/LineChart.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ export interface ILineChartZoomEvent {
batch?: { start: number; end: number }[];
}

export interface ILineChartTimeAxis {
time: string[];
}

export interface ILineChartMarkers {
markers?: string[];
}
Expand All @@ -33,5 +29,5 @@ export interface ILineChartTooltipFormatterParams {
axisValue: string;
color: string;
seriesName: string;
value: number;
value: string | number | [string, number];
}
48 changes: 40 additions & 8 deletions locust/webui/src/components/LineChart/LineChart.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import {

import { CHART_THEME } from 'components/LineChart/LineChart.constants';
import {
ILineChartTimeAxis,
ILineChart,
ILineChartZoomEvent,
ILineChartTooltipFormatterParams,
} from 'components/LineChart/LineChart.types';
import { swarmTemplateArgs } from 'constants/swarm';
import { ICharts } from 'types/ui.types';
import { formatLocaleString, formatLocaleTime } from 'utils/date';
import { formatLocaleString } from 'utils/date';
import { padStart } from 'utils/number';

export const getSeriesData = <ChartType>({
charts,
Expand Down Expand Up @@ -56,7 +57,31 @@ const createYAxis = <ChartType>({
} as YAXisComponentOption;
};

export const createOptions = <ChartType extends ILineChartTimeAxis>({
const formatTimeAxis = (value: string) => {
const date = new Date(value);

return [
padStart(date.getHours(), 2),
padStart(date.getMinutes(), 2),
padStart(date.getSeconds(), 2),
].join(':');
};

const renderChartTooltipValue = <ChartType>({
chartValueFormatter,
value,
}: {
chartValueFormatter: ILineChart<ChartType>['chartValueFormatter'];
value: ILineChartTooltipFormatterParams['value'];
}) => {
if (chartValueFormatter) {
return chartValueFormatter(value);
}

return Array.isArray(value) ? value[1] : value;
};

export const createOptions = <ChartType>({
charts,
title,
lines,
Expand Down Expand Up @@ -85,7 +110,10 @@ export const createOptions = <ChartType extends ILineChartTimeAxis>({
${tooltipText}
<br>
<span style="color:${color};">
${seriesName}:&nbsp${chartValueFormatter ? chartValueFormatter(value) : value}
${seriesName}:&nbsp${renderChartTooltipValue<ChartType>({
chartValueFormatter,
value,
})}
</span>
`,
'',
Expand All @@ -97,15 +125,18 @@ export const createOptions = <ChartType extends ILineChartTimeAxis>({
borderWidth: 0,
},
xAxis: {
type: 'category',
axisLabel: { formatter: formatLocaleTime },
data: charts.time,
type: 'time',
startValue: swarmTemplateArgs.startTime,
axisLabel: {
formatter: formatTimeAxis,
},
},
grid: { left: 40, right: splitAxis ? 40 : 10 },
grid: { left: 60, right: 40 },
yAxis: createYAxis({ splitAxis, yAxisLabels }),
series: getSeriesData<ChartType>({ charts, lines, scatterplot }),
color: colors,
toolbox: {
right: 10,
feature: {
dataZoom: {
show: true,
Expand Down Expand Up @@ -134,6 +165,7 @@ export const createMarkLine = <ChartType extends Pick<ICharts, 'markers'>>(
symbol: 'none',
label: {
formatter: (params: DefaultLabelFormatterCallbackParams) => `Run #${params.dataIndex + 1}`,
padding: [0, 0, 8, 0],
},
lineStyle: { color: isDarkMode ? CHART_THEME.DARK.axisColor : CHART_THEME.LIGHT.axisColor },
data: (charts.markers || []).map((timeMarker: string) => ({ xAxis: timeMarker })),
Expand Down
38 changes: 24 additions & 14 deletions locust/webui/src/components/LineChart/tests/LineChart.mocks.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,36 @@
import { ICharts } from 'types/ui.types';
import { formatLocaleTime } from 'utils/date';

export type MockChartType = Pick<ICharts, 'currentRps' | 'currentFailPerSec' | 'time'>;
export type MockChartType = Pick<ICharts, 'currentRps' | 'currentFailPerSec'>;

export const mockChartLines = [
{ name: 'RPS', key: 'currentRps' as keyof MockChartType },
{ name: 'Failures/s', key: 'currentFailPerSec' as keyof MockChartType },
];

export const mockCharts = {
currentRps: [3, 3.1, 3.27, 3.62, 4.19],
currentFailPerSec: [0, 0, 0, 0, 0],
time: [
'Tue, 06 Aug 2024 11:33:02 GMT',
'Tue, 06 Aug 2024 11:33:08 GMT',
'Tue, 06 Aug 2024 11:33:10 GMT',
'Tue, 06 Aug 2024 11:33:12 GMT',
'Tue, 06 Aug 2024 11:33:14 GMT',
export const mockCharts: MockChartType = {
currentRps: [
['Tue, 06 Aug 2024 11:33:02 GMT', 3],
['Tue, 06 Aug 2024 11:33:08 GMT', 3.1],
['Tue, 06 Aug 2024 11:33:10 GMT', 3.27],
['Tue, 06 Aug 2024 11:33:12 GMT', 3.62],
['Tue, 06 Aug 2024 11:33:14 GMT', 4.19],
],
currentFailPerSec: [
['Tue, 06 Aug 2024 11:33:02 GMT', 0],
['Tue, 06 Aug 2024 11:33:08 GMT', 0],
['Tue, 06 Aug 2024 11:33:10 GMT', 0],
['Tue, 06 Aug 2024 11:33:12 GMT', 0],
['Tue, 06 Aug 2024 11:33:14 GMT', 0],
],
};

export const mockFormattedTimeAxis = mockCharts.time.map(formatLocaleTime);
export const mockTimestamps = [
'Tue, 06 Aug 2024 11:33:02 GMT',
'Tue, 06 Aug 2024 11:33:08 GMT',
'Tue, 06 Aug 2024 11:33:10 GMT',
'Tue, 06 Aug 2024 11:33:12 GMT',
'Tue, 06 Aug 2024 11:33:14 GMT',
];

export const mockSeriesData = [
{ type: 'line', name: 'RPS', data: mockCharts.currentRps, symbolSize: 4 },
Expand All @@ -37,12 +47,12 @@ export const mockTooltipParams = [
axisValue: 'Tue, 06 Aug 2024 11:33:02 GMT',
color: '#ff0',
seriesName: 'RPS',
value: 1,
value: ['Tue, 06 Aug 2024 11:33:08 GMT', 1] as [string, number],
},
{
axisValue: 'Tue, 06 Aug 2024 11:33:08 GMT',
color: '#0ff',
seriesName: 'User',
value: 10,
value: ['Tue, 06 Aug 2024 11:33:12 GMT', 10] as [string, number],
},
];
Loading

0 comments on commit 5cb7370

Please sign in to comment.