@jfvilas/plugin-kwirth-metrics 0.13.4 → 0.13.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -30
- package/dist/components/Chart.esm.js +224 -0
- package/dist/components/Chart.esm.js.map +1 -0
- package/dist/components/EntityKwirthMetricsContent.esm.js +21 -137
- package/dist/components/EntityKwirthMetricsContent.esm.js.map +1 -1
- package/dist/components/MenuChart.esm.js +29 -0
- package/dist/components/MenuChart.esm.js.map +1 -0
- package/dist/components/Options.esm.js +1 -2
- package/dist/components/Options.esm.js.map +1 -1
- package/dist/index.d.ts +11 -2
- package/dist/index.esm.js +0 -4
- package/dist/index.esm.js.map +1 -1
- package/dist/version.esm.js +4 -0
- package/dist/version.esm.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,44 +74,63 @@ If everyting is correctly configured and tagged, the user should see a list of c
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
## Configuration: Entity Pages
|
|
77
|
-
1. Add the KwirthMetrics plugin as a tab in your Entity pages:
|
|
78
77
|
|
|
79
|
-
|
|
78
|
+
#### 1. Add the KwirthMetrics plugin as a tab in your Entity pages:
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
// In packages/app/src/components/catalog/EntityPage.tsx
|
|
83
|
-
import { EntityKwirthMetricsContent, isKwirthAvailable } from '@jfvilas/plugin-kwirth-metrics';
|
|
84
|
-
```
|
|
80
|
+
Firstly, import the plugin module.
|
|
85
81
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
82
|
+
```typescript
|
|
83
|
+
// In packages/app/src/components/catalog/EntityPage.tsx
|
|
84
|
+
import { EntityKwirthMetricsContent, isKwirthAvailable } from '@jfvilas/plugin-kwirth-metrics';
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Then, add a tab to your EntityPage (the 'if' is optional, you can keep the 'KwirthMetrics' tab always visible if you prefer to do it that way).
|
|
88
|
+
|
|
89
|
+
```jsx
|
|
90
|
+
// Note: Add to any other Pages as well (e.g. defaultEntityPage or webSiteEntityPage, for example)
|
|
91
|
+
const serviceEntityPage = (
|
|
92
|
+
<EntityLayout>
|
|
93
|
+
{/* other tabs... */}
|
|
94
|
+
<EntityLayout.Route if={isKwirthAvailable} path="/kwirthmetrics" title="KwirthMetrics">
|
|
95
|
+
<EntityKwirthMetricsContent allMetrics={true} enableRestart={false} />
|
|
96
|
+
</EntityLayout.Route>
|
|
97
|
+
</EntityLayout>
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
You can setup some default *viewing* options on the `EntityKwirthMetricsContent` component, so, when the entity loads the default options will be set. These options are:
|
|
99
102
|
- `depth`
|
|
100
103
|
- `width`
|
|
101
104
|
- `interval`
|
|
102
105
|
- `chart`
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
106
|
+
(The meaning of these properties are explained at the end of this document)
|
|
107
|
+
|
|
108
|
+
If you want to setup a long-running histogram as a default chart for your entities you should setup your entity page like this:
|
|
109
|
+
|
|
110
|
+
```jsx
|
|
111
|
+
...
|
|
112
|
+
<EntityLayout.Route if={isKwirthAvailable} path="/kwirthmetrics" title="KwirthMetrics">
|
|
113
|
+
<EntityKwirthMetricsContent allMetrics={true} enableRestart={false} depth={100} chart={'bar'}/>
|
|
114
|
+
</EntityLayout.Route>
|
|
115
|
+
...
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
You can also set some behaviour options for the plugin:
|
|
119
|
+
- `hideVersion` (optional `boolean`) if set to `true`, version information updates will not be shown.
|
|
120
|
+
- `excludeContainers` (optional `string[]`), an array of container names that will be excluded from visualization. For example, if you have pods that include sidecars, you can exclude sidecard log messages using this property. What follows is an example on how to use these properties:
|
|
121
|
+
- `defaultMetrics` (optional `boolean`) if set to `true`, version information updates will not be shown.
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
```jsx
|
|
125
|
+
...
|
|
126
|
+
<EntityLayout.Route if={isKwirthAvailable} path="/kwirthlog" title="KwirthLog">
|
|
127
|
+
<EntityKwirthLogContent hideVersion excludeContainers={['istio-proxy', 'nginx']} defaultMetrics={['kwirth_container_cpu_percentage', 'kwirth_container_memory_percentage']} />
|
|
128
|
+
</EntityLayout.Route>
|
|
129
|
+
...
|
|
130
|
+
```
|
|
113
131
|
|
|
114
|
-
2. Label your catalog-info
|
|
132
|
+
#### 2. Label your catalog-info
|
|
133
|
+
Use one of these startegies:
|
|
115
134
|
|
|
116
135
|
- **Strategy 1: one-to-one**. Add `backstage.io/kubernetes-id` annotation to your `catalog-info.yaml` for the entities deployed to Kubernetes you want to work with on Backstage. This is the same annotation that the Kubernetes core plugin uses, so, maybe you already have added it to your components. Exmaple:
|
|
117
136
|
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Alert, Stack, Typography, Card, CardContent, Tooltip as Tooltip$1, IconButton } from '@mui/material';
|
|
3
|
+
import { ResponsiveContainer, Treemap, Tooltip, PieChart, Legend, Pie, Cell, BarChart, CartesianGrid, XAxis, YAxis, Bar, LabelList, AreaChart, Area, LineChart, Line } from 'recharts';
|
|
4
|
+
import { MenuChart, MenuChartOption } from './MenuChart.esm.js';
|
|
5
|
+
import { MoreVert } from '@material-ui/icons';
|
|
6
|
+
|
|
7
|
+
const METRICSCOLOURS = [
|
|
8
|
+
"#6e5bb8",
|
|
9
|
+
// morado oscuro
|
|
10
|
+
"#4a9076",
|
|
11
|
+
// verde oscuro
|
|
12
|
+
"#b56c52",
|
|
13
|
+
// naranja oscuro
|
|
14
|
+
"#7f6b97",
|
|
15
|
+
// color lavanda oscuro
|
|
16
|
+
"#b0528f",
|
|
17
|
+
// rosa oscuro
|
|
18
|
+
"#b0b052",
|
|
19
|
+
// amarillo oscuro
|
|
20
|
+
"#b05252",
|
|
21
|
+
// rojo oscuro
|
|
22
|
+
"#5285b0",
|
|
23
|
+
// azul oscuro
|
|
24
|
+
"#a38ad6",
|
|
25
|
+
// morado pastel
|
|
26
|
+
"#89c1a0",
|
|
27
|
+
// verde pastel
|
|
28
|
+
"#e4a28a",
|
|
29
|
+
// naranja pastel
|
|
30
|
+
"#b09dbd",
|
|
31
|
+
// lavanda pastel
|
|
32
|
+
"#e2a4c6",
|
|
33
|
+
// rosa pastel
|
|
34
|
+
"#c5c89e",
|
|
35
|
+
// amarillo pastel
|
|
36
|
+
"#e2a4a4",
|
|
37
|
+
// rojo pastel
|
|
38
|
+
"#90b7e2",
|
|
39
|
+
// azul pastel
|
|
40
|
+
"#f8d5e1",
|
|
41
|
+
// rosa claro pastel
|
|
42
|
+
"#b2d7f0",
|
|
43
|
+
// azul muy claro pastel
|
|
44
|
+
"#f7e1b5",
|
|
45
|
+
// amarillo muy claro pastel
|
|
46
|
+
"#d0f0c0",
|
|
47
|
+
// verde muy claro pastel
|
|
48
|
+
"#f5b0a1",
|
|
49
|
+
// coral pastel
|
|
50
|
+
"#d8a7db",
|
|
51
|
+
// lavanda muy claro pastel
|
|
52
|
+
"#f4c2c2",
|
|
53
|
+
// rosa suave pastel
|
|
54
|
+
"#e6c7b9",
|
|
55
|
+
// marron claro pastel
|
|
56
|
+
"#f0e2b6",
|
|
57
|
+
// crema pastel
|
|
58
|
+
"#a7c7e7",
|
|
59
|
+
// azul palido pastel
|
|
60
|
+
"#f5e6a5",
|
|
61
|
+
// amarillo palido pastel
|
|
62
|
+
"#e3c8f5",
|
|
63
|
+
// lilas pastel
|
|
64
|
+
"#d0c4e8",
|
|
65
|
+
// lila palido pastel
|
|
66
|
+
"#b8d8b8",
|
|
67
|
+
// verde claro pastel
|
|
68
|
+
"#d2ebfa",
|
|
69
|
+
// azul muy claro pastel
|
|
70
|
+
"#f1c1d2"
|
|
71
|
+
// rosa bebe pastel
|
|
72
|
+
];
|
|
73
|
+
const Chart = (props) => {
|
|
74
|
+
const [anchorMenuChart, setAnchorMenuChart] = useState(null);
|
|
75
|
+
const [chartType, setChartType] = useState(props.viewConfig?.chartType ? props.viewConfig.chartType : props.chartType);
|
|
76
|
+
const [stack, setStack] = useState(props.viewConfig?.stack ? props.viewConfig.stack : props.stack);
|
|
77
|
+
const [tooltip, setTooltip] = useState(props.viewConfig?.tooltip ? props.viewConfig.tooltip : props.tooltip);
|
|
78
|
+
const [labels, setLabels] = useState(props.viewConfig?.labels ? props.viewConfig.labels : props.labels);
|
|
79
|
+
let result;
|
|
80
|
+
let height = 300;
|
|
81
|
+
let dataSummarized;
|
|
82
|
+
const mergeSeries = (names, series) => {
|
|
83
|
+
if (!names || names.length === 0) return [];
|
|
84
|
+
let resultSeries = [];
|
|
85
|
+
for (var i = 0; i < series[0].length; i++) {
|
|
86
|
+
var item = {};
|
|
87
|
+
for (var j = 0; j < series.length; j++) {
|
|
88
|
+
if (series[j][i]) {
|
|
89
|
+
item["timestamp"] = series[0][i].timestamp;
|
|
90
|
+
item[names[j]] = series[j][i].value;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
resultSeries.push(item);
|
|
94
|
+
}
|
|
95
|
+
return resultSeries;
|
|
96
|
+
};
|
|
97
|
+
const CustomizedContent = (props2) => {
|
|
98
|
+
const { root, depth, x, y, width, height: height2, index, name } = props2;
|
|
99
|
+
return /* @__PURE__ */ React.createElement("g", null, /* @__PURE__ */ React.createElement(
|
|
100
|
+
"rect",
|
|
101
|
+
{
|
|
102
|
+
x,
|
|
103
|
+
y,
|
|
104
|
+
width,
|
|
105
|
+
height: height2,
|
|
106
|
+
style: {
|
|
107
|
+
fill: depth < 2 ? METRICSCOLOURS[Math.floor(index / root.children.length * 6)] : "#ffffff00",
|
|
108
|
+
stroke: "#fff",
|
|
109
|
+
strokeWidth: 2 / (depth + 1e-10),
|
|
110
|
+
strokeOpacity: 1 / (depth + 1e-10)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
), depth === 1 ? /* @__PURE__ */ React.createElement("text", { x: x + width / 2, y: y + height2 / 2 + 7, textAnchor: "middle", fill: "#fff", fontSize: 14, fontFamily: "Roboto, Helvetica, Arial, sans-serif" }, name) : null);
|
|
114
|
+
};
|
|
115
|
+
const menuChartOptionSelected = (opt, data) => {
|
|
116
|
+
setAnchorMenuChart(null);
|
|
117
|
+
switch (opt) {
|
|
118
|
+
case MenuChartOption.Stack:
|
|
119
|
+
setStack(!stack);
|
|
120
|
+
break;
|
|
121
|
+
case MenuChartOption.Remove:
|
|
122
|
+
break;
|
|
123
|
+
case MenuChartOption.Export:
|
|
124
|
+
if (!props.names?.length || !props.series?.length) return;
|
|
125
|
+
const headers = ["timestamp", ...props.names];
|
|
126
|
+
const timestamps = props.series[0].map((point) => point.timestamp);
|
|
127
|
+
const rows = timestamps.map((timestamp, idx) => {
|
|
128
|
+
const values = props.series.map((serie) => serie[idx]?.value ?? "");
|
|
129
|
+
return [timestamp, ...values];
|
|
130
|
+
});
|
|
131
|
+
const separator = ",";
|
|
132
|
+
const csvContent = headers.join(separator) + "\n" + rows.map((r) => r.join(separator)).join("\n");
|
|
133
|
+
const blob = new Blob(["\uFEFF" + csvContent], { type: "text/csv;charset=utf-8;" });
|
|
134
|
+
const link = document.createElement("a");
|
|
135
|
+
link.href = URL.createObjectURL(blob);
|
|
136
|
+
link.download = `${props.metricDefinition.metric}.csv`;
|
|
137
|
+
link.click();
|
|
138
|
+
break;
|
|
139
|
+
case MenuChartOption.Tooltip:
|
|
140
|
+
setTooltip(!tooltip);
|
|
141
|
+
break;
|
|
142
|
+
case MenuChartOption.Labels:
|
|
143
|
+
setLabels(!labels);
|
|
144
|
+
break;
|
|
145
|
+
case MenuChartOption.Default:
|
|
146
|
+
if (props.onSetDefault) {
|
|
147
|
+
props.onSetDefault(props.metricDefinition.metric, {
|
|
148
|
+
displayName: props.metricDefinition.metric,
|
|
149
|
+
chartType,
|
|
150
|
+
stack,
|
|
151
|
+
tooltip,
|
|
152
|
+
labels
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
default:
|
|
157
|
+
setChartType(data);
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const renderLabel = (data) => {
|
|
162
|
+
var values = props.series.map((s) => s[data.index]);
|
|
163
|
+
var total = values.reduce((acc, value) => acc + value.value, 0);
|
|
164
|
+
return /* @__PURE__ */ React.createElement("text", { x: data.x + data.width / 3.5, y: data.y - 10 }, total.toPrecision(3).replace(/0+$/, "").replace(/\.+$/, ""));
|
|
165
|
+
};
|
|
166
|
+
switch (chartType) {
|
|
167
|
+
case "value" /* ValueChart */:
|
|
168
|
+
result = /* @__PURE__ */ React.createElement("div", { style: { padding: "30px", height: height * 0.8, alignItems: "center", justifyContent: "center", display: "flex" } }, /* @__PURE__ */ React.createElement(Stack, { direction: "row", alignItems: "center", justifyContent: "center", spacing: 2, sx: { flexWrap: "wrap" } }, props.series.map((serie, index) => {
|
|
169
|
+
let value = serie[serie.length - 1].value;
|
|
170
|
+
if (!value) return;
|
|
171
|
+
let valueStr = value.toString();
|
|
172
|
+
if (value) {
|
|
173
|
+
valueStr = value.toFixed(3);
|
|
174
|
+
if (value > 10) valueStr = value.toFixed(2);
|
|
175
|
+
if (value > 100) valueStr = value.toFixed(1);
|
|
176
|
+
if (value > 1e3) valueStr = value.toFixed(0);
|
|
177
|
+
}
|
|
178
|
+
return /* @__PURE__ */ React.createElement(Stack, { key: index, direction: "column" }, /* @__PURE__ */ React.createElement(Typography, { mt: 2, textAlign: "center", width: "100%", fontSize: Math.min(48, 192 / props.series.length), color: props.series.length === 1 ? props.colour : METRICSCOLOURS[index] }, valueStr), /* @__PURE__ */ React.createElement(Typography, { textAlign: "center", width: "100%", fontSize: 12, color: props.series.length === 1 ? props.colour : METRICSCOLOURS[index] }, props.names[index]));
|
|
179
|
+
})));
|
|
180
|
+
break;
|
|
181
|
+
case "line" /* LineChart */:
|
|
182
|
+
result = /* @__PURE__ */ React.createElement(LineChart, { data: mergeSeries(props.names, props.series) }, /* @__PURE__ */ React.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React.createElement(XAxis, { dataKey: "timestamp", fontSize: 8 }), /* @__PURE__ */ React.createElement(YAxis, null), tooltip && /* @__PURE__ */ React.createElement(Tooltip, null), /* @__PURE__ */ React.createElement(Legend, null), props.series.map((_serie, index) => /* @__PURE__ */ React.createElement(Line, { key: index, name: props.names[index], type: "monotone", dataKey: props.names[index], stroke: props.series.length === 1 ? props.colour : METRICSCOLOURS[index], activeDot: { r: 8 } })));
|
|
183
|
+
break;
|
|
184
|
+
case "area" /* AreaChart */:
|
|
185
|
+
result = /* @__PURE__ */ React.createElement(AreaChart, { data: mergeSeries(props.names, props.series) }, /* @__PURE__ */ React.createElement("defs", null, props.series.map((_serie, index) => {
|
|
186
|
+
return /* @__PURE__ */ React.createElement("linearGradient", { key: index, id: `color${props.series.length === 1 ? props.colour : METRICSCOLOURS[index]}`, x1: "0", y1: "0", x2: "0", y2: "1" }, /* @__PURE__ */ React.createElement("stop", { offset: "7%", stopColor: props.series.length === 1 ? props.colour : METRICSCOLOURS[index], stopOpacity: 0.8 }), /* @__PURE__ */ React.createElement("stop", { offset: "93%", stopColor: props.series.length === 1 ? props.colour : METRICSCOLOURS[index], stopOpacity: 0 }));
|
|
187
|
+
})), /* @__PURE__ */ React.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React.createElement(XAxis, { dataKey: "timestamp", fontSize: 8 }), /* @__PURE__ */ React.createElement(YAxis, null), tooltip && /* @__PURE__ */ React.createElement(Tooltip, null), /* @__PURE__ */ React.createElement(Legend, null), props.series.map((_serie, index) => /* @__PURE__ */ React.createElement(Area, { key: index, name: props.names[index], type: "monotone", ...stack ? { stackId: "1" } : {}, dataKey: props.names[index], stroke: props.series.length === 1 ? props.colour : METRICSCOLOURS[index], fill: `url(#color${props.series.length === 1 ? props.colour : METRICSCOLOURS[index]})` })));
|
|
188
|
+
break;
|
|
189
|
+
case "bar" /* BarChart */:
|
|
190
|
+
result = /* @__PURE__ */ React.createElement(BarChart, { data: mergeSeries(props.names, props.series) }, /* @__PURE__ */ React.createElement(CartesianGrid, { strokeDasharray: "3 3" }), /* @__PURE__ */ React.createElement(XAxis, { dataKey: "timestamp", fontSize: 8 }), /* @__PURE__ */ React.createElement(YAxis, null), tooltip && /* @__PURE__ */ React.createElement(Tooltip, null), /* @__PURE__ */ React.createElement(Legend, null), props.series.map(
|
|
191
|
+
(_serie, index) => /* @__PURE__ */ React.createElement(Bar, { key: index, name: props.names[index], ...stack ? { stackId: "1" } : {}, dataKey: props.names[index], stroke: props.series.length === 1 ? props.colour : METRICSCOLOURS[index], fill: props.series.length === 1 ? props.colour : METRICSCOLOURS[index] }, index === props.series.length - 1 && props.series.length > 1 && labels ? /* @__PURE__ */ React.createElement(LabelList, { dataKey: props.names[index], position: "insideTop", content: renderLabel }) : null)
|
|
192
|
+
));
|
|
193
|
+
break;
|
|
194
|
+
case "pie" /* PieChart */:
|
|
195
|
+
dataSummarized = props.names.map((name, index) => {
|
|
196
|
+
return { name, value: props.series[index].reduce((ac, val) => ac + val.value, 0) };
|
|
197
|
+
});
|
|
198
|
+
result = /* @__PURE__ */ React.createElement(PieChart, null, tooltip && /* @__PURE__ */ React.createElement(Tooltip, null), /* @__PURE__ */ React.createElement(Legend, { layout: "vertical", align: "right", verticalAlign: "middle" }), /* @__PURE__ */ React.createElement(Pie, { key: "asd", data: dataSummarized, dataKey: "value", fill: METRICSCOLOURS[0], innerRadius: 0, outerRadius: 90 }, dataSummarized.map((_entry, index) => /* @__PURE__ */ React.createElement(Cell, { key: `cell-${index}`, fill: METRICSCOLOURS[index % METRICSCOLOURS.length] }))));
|
|
199
|
+
break;
|
|
200
|
+
case "treemap" /* TreemapChart */:
|
|
201
|
+
dataSummarized = props.names.map((name, index) => {
|
|
202
|
+
return { name, value: props.series[index].reduce((ac, val) => ac + val.value, 0) };
|
|
203
|
+
});
|
|
204
|
+
result = /* @__PURE__ */ React.createElement("div", { style: { paddingLeft: "32px", height: height * 0.8, alignItems: "center", justifyContent: "center", display: "flex" } }, /* @__PURE__ */ React.createElement(ResponsiveContainer, { width: "100%" }, /* @__PURE__ */ React.createElement(Treemap, { data: dataSummarized, dataKey: "value", nameKey: "name", aspectRatio: 4 / 3, stroke: "#ffffff", fill: "#6e5bb8", content: React.createElement(CustomizedContent) }, tooltip && /* @__PURE__ */ React.createElement(Tooltip, null))));
|
|
205
|
+
break;
|
|
206
|
+
default:
|
|
207
|
+
result = /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, "Unsupported chart type '", props.chartType, "'");
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
let title = props.metricDefinition.metric.replaceAll("_", " ");
|
|
211
|
+
title = title[0].toLocaleUpperCase() + title.substring(1);
|
|
212
|
+
title = title.replaceAll("cpu", "CPU");
|
|
213
|
+
title = title.replaceAll(" fs ", " FS ");
|
|
214
|
+
title = title.replaceAll(" io ", " IO ");
|
|
215
|
+
title = title.replaceAll("oom", "OOM");
|
|
216
|
+
title = title.replaceAll("nvm", "NVM");
|
|
217
|
+
title = title.replaceAll("rss", "RSS");
|
|
218
|
+
title = title.replaceAll("failcnt", "fail count");
|
|
219
|
+
title = title.replaceAll("mbps", "Mbps");
|
|
220
|
+
return /* @__PURE__ */ React.createElement(Card, { sx: { paddingRight: "32px", width: "100%" } }, /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Stack, { direction: "column", alignItems: "center", width: "100%", sx: { mb: "32px" } }, /* @__PURE__ */ React.createElement(Stack, { direction: "row", alignItems: "center" }, /* @__PURE__ */ React.createElement(Tooltip$1, { key: "tooltip" + props.metricDefinition.metric + JSON.stringify(props.names), title: /* @__PURE__ */ React.createElement(Typography, { style: { fontSize: 12 } }, /* @__PURE__ */ React.createElement("b", null, props.metricDefinition.metric), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null), props.metricDefinition.help) }, /* @__PURE__ */ React.createElement(Typography, { align: "center" }, title)), /* @__PURE__ */ React.createElement(IconButton, { onClick: (event) => setAnchorMenuChart(event.currentTarget) }, /* @__PURE__ */ React.createElement(MoreVert, { fontSize: "small" })), anchorMenuChart && /* @__PURE__ */ React.createElement(MenuChart, { onClose: () => setAnchorMenuChart(null), optionSelected: menuChartOptionSelected, anchorMenu: anchorMenuChart, selected: chartType, stacked: stack, tooltip, labels, numSeries: props.numSeries, setDefault: false })), /* @__PURE__ */ React.createElement("div", { style: { width: "100%" } }, /* @__PURE__ */ React.createElement(ResponsiveContainer, { width: "100%", height, key: props.metricDefinition.metric + JSON.stringify(props.names) }, result)))));
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export { Chart, METRICSCOLOURS };
|
|
224
|
+
//# sourceMappingURL=Chart.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Chart.esm.js","sources":["../../src/components/Chart.tsx"],"sourcesContent":["import React, { useState } from 'react'\r\nimport { Alert, Card, CardContent, Stack, Typography } from '@mui/material'\r\nimport { Area, AreaChart, Bar, BarChart, CartesianGrid, Cell, LabelList, Legend, Line, LineChart, Pie, PieChart, ResponsiveContainer, Tooltip, Treemap, XAxis, YAxis } from 'recharts'\r\nimport { Tooltip as MUITooltip, IconButton } from '@mui/material'\r\nimport { MenuChart, MenuChartOption } from './MenuChart'\r\nimport { MoreVert } from '@material-ui/icons'\r\nimport { TreemapNode } from 'recharts/types/util/types'\r\nimport { MetricDefinition } from '@jfvilas/plugin-kwirth-common'\r\n\r\nexport interface IMetricViewConfig {\r\n displayName: string\r\n chartType: ChartType\r\n tooltip: boolean\r\n labels: boolean\r\n stack: boolean\r\n}\r\n\r\nenum ChartType {\r\n LineChart='line',\r\n BarChart='bar',\r\n AreaChart='area',\r\n ValueChart='value',\r\n PieChart='pie',\r\n TreemapChart='treemap',\r\n}\r\n\r\nexport interface ISample {\r\n timestamp:string\r\n value:number\r\n}\r\n\r\nexport const METRICSCOLOURS = [\r\n \"#6e5bb8\", // morado oscuro\r\n \"#4a9076\", // verde oscuro\r\n \"#b56c52\", // naranja oscuro\r\n \"#7f6b97\", // color lavanda oscuro\r\n \"#b0528f\", // rosa oscuro\r\n \"#b0b052\", // amarillo oscuro\r\n \"#b05252\", // rojo oscuro\r\n \"#5285b0\", // azul oscuro\r\n \"#a38ad6\", // morado pastel\r\n \"#89c1a0\", // verde pastel\r\n \"#e4a28a\", // naranja pastel\r\n \"#b09dbd\", // lavanda pastel\r\n \"#e2a4c6\", // rosa pastel\r\n \"#c5c89e\", // amarillo pastel\r\n \"#e2a4a4\", // rojo pastel\r\n \"#90b7e2\", // azul pastel\r\n \"#f8d5e1\", // rosa claro pastel\r\n \"#b2d7f0\", // azul muy claro pastel\r\n \"#f7e1b5\", // amarillo muy claro pastel\r\n \"#d0f0c0\", // verde muy claro pastel\r\n \"#f5b0a1\", // coral pastel\r\n \"#d8a7db\", // lavanda muy claro pastel\r\n \"#f4c2c2\", // rosa suave pastel\r\n \"#e6c7b9\", // marron claro pastel\r\n \"#f0e2b6\", // crema pastel\r\n \"#a7c7e7\", // azul palido pastel\r\n \"#f5e6a5\", // amarillo palido pastel\r\n \"#e3c8f5\", // lilas pastel\r\n \"#d0c4e8\", // lila palido pastel\r\n \"#b8d8b8\", // verde claro pastel\r\n \"#d2ebfa\", // azul muy claro pastel\r\n \"#f1c1d2\" // rosa bebe pastel\r\n]\r\n\r\nexport interface IChartProps {\r\n metricDefinition: MetricDefinition,\r\n names: string[],\r\n series: ISample[][],\r\n colour: string,\r\n chartType: ChartType,\r\n stack: boolean\r\n tooltip: boolean\r\n labels: boolean\r\n numSeries: number\r\n viewConfig : IMetricViewConfig\r\n onSetDefault?: (name:string, mvc: IMetricViewConfig) => void\r\n}\r\n\r\nexport const Chart: React.FC<IChartProps> = (props:IChartProps) => {\r\n const [anchorMenuChart, setAnchorMenuChart] = useState<null | HTMLElement>(null)\r\n const [chartType, setChartType] = useState<ChartType>(props.viewConfig?.chartType? props.viewConfig.chartType : props.chartType)\r\n const [stack, setStack] = useState<boolean>(props.viewConfig?.stack? props.viewConfig.stack : props.stack)\r\n const [tooltip, setTooltip] = useState<boolean>(props.viewConfig?.tooltip? props.viewConfig.tooltip : props.tooltip)\r\n const [labels, setLabels] = useState<boolean>(props.viewConfig?.labels? props.viewConfig.labels : props.labels)\r\n\r\n let result\r\n let height=300\r\n let dataSummarized:any[]\r\n\r\n const mergeSeries = (names:string[], series:ISample[][]) => {\r\n // names is an array of names of series\r\n // series is an array of arrays of samples\r\n // example:\r\n // [default, ingress-nginx]\r\n // [ [ {timestamp:'dad',value:1}, {timestamp:'dad',value:2} ], [ {timestamp:'dad',value:4}, {timestamp:'dad',value:0} ] ]\r\n if (!names || names.length===0) return []\r\n let resultSeries = []\r\n\r\n for (var i=0; i<series[0].length; i++) {\r\n var item: { [key: string]: string|number } = {}\r\n for (var j=0; j<series.length; j++ ) {\r\n if (series[j][i]) {\r\n item['timestamp'] = series[0][i].timestamp\r\n item[names[j]] = series[j][i].value\r\n }\r\n }\r\n resultSeries.push(item)\r\n }\r\n\r\n // result is:\r\n // [ \r\n // {timestamp: '09:16:27', default: 0.21, ingress-nginx: 0.93}\r\n // {timestamp: '09:16:32', default: 0.5, ingress-nginx: 0.04}\r\n // ]\r\n return resultSeries\r\n }\r\n\r\n const CustomizedContent: React.FC<TreemapNode> = (props) => {\r\n const { root, depth, x, y, width, height, index, name } = props\r\n\r\n return (\r\n <g>\r\n <rect x={x} y={y} width={width} height={height}\r\n style={{\r\n fill: depth < 2 ? METRICSCOLOURS[Math.floor((index / root.children.length) * 6)] : '#ffffff00',\r\n stroke: '#fff',\r\n strokeWidth: 2 / (depth + 1e-10),\r\n strokeOpacity: 1 / (depth + 1e-10),\r\n }}\r\n />\r\n {depth === 1 ? (\r\n <text x={x + width / 2} y={y + height / 2 + 7} textAnchor=\"middle\" fill=\"#fff\" fontSize={14} fontFamily='Roboto, Helvetica, Arial, sans-serif'>\r\n {name}\r\n </text>\r\n ) : null}\r\n\r\n </g>\r\n )\r\n }\r\n\r\n const menuChartOptionSelected = (opt:MenuChartOption, data:any) => {\r\n setAnchorMenuChart(null)\r\n switch (opt) {\r\n case MenuChartOption.Stack:\r\n setStack(!stack)\r\n break\r\n case MenuChartOption.Remove:\r\n //+++ pending implementation on parent\r\n break\r\n case MenuChartOption.Export:\r\n if (!props.names?.length || !props.series?.length) return\r\n\r\n const headers = [\"timestamp\", ...props.names]\r\n const timestamps = props.series[0].map(point => point.timestamp)\r\n const rows = timestamps.map((timestamp, idx) => {\r\n const values = props.series.map(serie => serie[idx]?.value ?? \"\")\r\n return [timestamp, ...values];\r\n })\r\n const separator = \",\";\r\n const csvContent = headers.join(separator) + \"\\n\" + rows.map(r => r.join(separator)).join(\"\\n\")\r\n const blob = new Blob([\"\\uFEFF\" + csvContent], { type: \"text/csv;charset=utf-8;\" })\r\n \r\n const link = document.createElement(\"a\")\r\n link.href = URL.createObjectURL(blob)\r\n link.download = `${props.metricDefinition.metric}.csv`\r\n link.click()\r\n break\r\n case MenuChartOption.Tooltip:\r\n setTooltip(!tooltip)\r\n break\r\n case MenuChartOption.Labels:\r\n setLabels(!labels)\r\n break\r\n case MenuChartOption.Default:\r\n if (props.onSetDefault) {\r\n props.onSetDefault(props.metricDefinition.metric, {\r\n displayName: props.metricDefinition.metric,\r\n chartType: chartType,\r\n stack: stack,\r\n tooltip: tooltip,\r\n labels: labels\r\n })\r\n }\r\n break\r\n default:\r\n setChartType(data as ChartType)\r\n break\r\n } \r\n }\r\n\r\n const renderLabel = (data:any) => {\r\n var values:any[] = props.series.map (s => s[data.index])\r\n var total:number = values.reduce((acc,value) => acc+value.value, 0)\r\n return <text x={data.x + data.width/3.5} y={data.y-10}>{total.toPrecision(3).replace(/0+$/, '').replace(/\\.+$/, '')}</text>\r\n }\r\n\r\n switch (chartType) {\r\n case ChartType.ValueChart:\r\n result = (\r\n <div style={{padding:'30px', height:height*0.8, alignItems:'center', justifyContent:'center', display:'flex'}}>\r\n <Stack direction={'row'} alignItems={'center'} justifyContent={'center'} spacing={2} sx={{ flexWrap: 'wrap'}}>\r\n { props.series.map( (serie,index) => {\r\n let value = serie[serie.length-1].value\r\n if (!value) return //+++\r\n let valueStr = value.toString()\r\n if (value) {\r\n valueStr = value.toFixed(3)\r\n if (value>10) valueStr=value.toFixed(2)\r\n if (value>100) valueStr=value.toFixed(1)\r\n if (value>1000) valueStr=value.toFixed(0)\r\n }\r\n return (\r\n <Stack key={index} direction={'column'}>\r\n <Typography mt={2} textAlign={'center'} width={'100%'} fontSize={Math.min(48, 192/props.series.length)} color={props.series.length===1?props.colour:METRICSCOLOURS[index]}>\r\n {valueStr}\r\n </Typography>\r\n <Typography textAlign={'center'} width={'100%'} fontSize={12} color={props.series.length===1?props.colour:METRICSCOLOURS[index]}>\r\n {props.names[index]}\r\n </Typography>\r\n </Stack>\r\n )\r\n })}\r\n </Stack>\r\n </div>\r\n )\r\n break\r\n case ChartType.LineChart:\r\n result = (\r\n <LineChart data={mergeSeries(props.names, props.series)}>\r\n <CartesianGrid strokeDasharray='3 3'/>\r\n <XAxis dataKey='timestamp' fontSize={8}/>\r\n <YAxis/>\r\n { tooltip && <Tooltip /> }\r\n <Legend/>\r\n { props.series.map ((_serie,index) => <Line key={index} name={props.names[index]} type='monotone' dataKey={props.names[index]} stroke={props.series.length===1?props.colour:METRICSCOLOURS[index]} activeDot={{ r: 8 }} />) }\r\n </LineChart>\r\n )\r\n break\r\n case ChartType.AreaChart:\r\n result = (\r\n <AreaChart data={mergeSeries(props.names, props.series)}>\r\n <defs>\r\n {\r\n props.series.map( (_serie,index) => {\r\n return (\r\n <linearGradient key={index} id={`color${props.series.length===1?props.colour:METRICSCOLOURS[index]}`} x1='0' y1='0' x2='0' y2='1'>\r\n <stop offset='7%' stopColor={props.series.length===1?props.colour:METRICSCOLOURS[index]} stopOpacity={0.8}/>\r\n <stop offset='93%' stopColor={props.series.length===1?props.colour:METRICSCOLOURS[index]} stopOpacity={0}/>\r\n </linearGradient>\r\n )\r\n })\r\n }\r\n </defs>\r\n <CartesianGrid strokeDasharray='3 3'/>\r\n <XAxis dataKey='timestamp' fontSize={8}/>\r\n <YAxis />\r\n { tooltip && <Tooltip /> }\r\n <Legend/>\r\n { props.series.map ((_serie,index) => \r\n <Area key={index} name={props.names[index]} type='monotone' {...(stack? {stackId:'1'}:{})} dataKey={props.names[index]} stroke={props.series.length===1?props.colour:METRICSCOLOURS[index]} fill={`url(#color${props.series.length===1?props.colour:METRICSCOLOURS[index]})`}/> )\r\n }\r\n </AreaChart>\r\n )\r\n break\r\n case ChartType.BarChart:\r\n result = (\r\n <BarChart data={mergeSeries(props.names, props.series)}>\r\n <CartesianGrid strokeDasharray='3 3'/>\r\n <XAxis dataKey='timestamp' fontSize={8}/>\r\n <YAxis />\r\n { tooltip && <Tooltip /> }\r\n <Legend/>\r\n { props.series.map ((_serie,index) =>\r\n <Bar key={index} name={props.names[index]} {...(stack? {stackId:'1'}:{})} dataKey={props.names[index]} stroke={props.series.length===1?props.colour:METRICSCOLOURS[index]} fill={props.series.length===1?props.colour:METRICSCOLOURS[index]}>\r\n { index === props.series.length-1 && props.series.length > 1 && labels ? <LabelList dataKey={props.names[index]} position='insideTop' content={renderLabel}/> : null }\r\n </Bar>\r\n )}\r\n </BarChart>\r\n )\r\n break\r\n case ChartType.PieChart:\r\n dataSummarized= props.names.map( (name,index) => {\r\n return { name, value:(props.series[index] as ISample[]).reduce((ac,val) => ac+val.value,0)}\r\n })\r\n result = (\r\n <PieChart>\r\n { tooltip && <Tooltip /> }\r\n <Legend layout='vertical' align='right' verticalAlign='middle'/>\r\n <Pie key={'asd'} data={dataSummarized} dataKey={'value'} fill={METRICSCOLOURS[0]} innerRadius={0} outerRadius={90}>\r\n {dataSummarized.map((_entry, index) => (\r\n <Cell key={`cell-${index}`} fill={METRICSCOLOURS[index % METRICSCOLOURS.length]} />\r\n ))}\r\n </Pie>\r\n </PieChart>\r\n )\r\n break\r\n case ChartType.TreemapChart:\r\n dataSummarized = props.names.map( (name,index) => {\r\n return { name, value:(props.series[index] as ISample[]).reduce((ac,val) => ac+val.value,0)}\r\n })\r\n result = (\r\n <div style={{paddingLeft:'32px', height:height*0.8, alignItems:'center', justifyContent:'center', display:'flex'}}>\r\n <ResponsiveContainer width='100%'>\r\n <Treemap data={dataSummarized} dataKey='value' nameKey='name' aspectRatio={4 / 3} stroke=\"#ffffff\" fill=\"#6e5bb8\" content={React.createElement(CustomizedContent)}>\r\n { tooltip && <Tooltip /> }\r\n </Treemap>\r\n </ResponsiveContainer>\r\n </div>\r\n )\r\n break\r\n default:\r\n result = <Alert severity='error'>Unsupported chart type '{props.chartType}'</Alert>\r\n break\r\n }\r\n\r\n let title = props.metricDefinition.metric.replaceAll('_',' ')\r\n title = title[0].toLocaleUpperCase()+ title.substring(1)\r\n title = title.replaceAll('cpu', 'CPU')\r\n title = title.replaceAll(' fs ', ' FS ')\r\n title = title.replaceAll(' io ', ' IO ')\r\n title = title.replaceAll('oom', 'OOM')\r\n title = title.replaceAll('nvm', 'NVM')\r\n title = title.replaceAll('rss', 'RSS')\r\n title = title.replaceAll('failcnt', 'fail count')\r\n title = title.replaceAll('mbps', 'Mbps')\r\n\r\n // +++ review\r\n return (\r\n <Card sx={{paddingRight:'32px', width:'100%'}}>\r\n <CardContent>\r\n <Stack direction='column' alignItems='center' width='100%' sx={{mb:'32px'}}>\r\n <Stack direction={'row'} alignItems={'center'}>\r\n <MUITooltip key={'tooltip'+props.metricDefinition.metric+JSON.stringify(props.names)} title={<Typography style={{fontSize:12}}><b>{props.metricDefinition.metric}</b><br/><br/>{props.metricDefinition.help}</Typography>}>\r\n <Typography align='center'>{title}</Typography>\r\n </MUITooltip>\r\n <IconButton onClick={(event) => setAnchorMenuChart(event.currentTarget)}><MoreVert fontSize='small'/></IconButton> \r\n { anchorMenuChart && <MenuChart onClose={() => setAnchorMenuChart(null)} optionSelected={menuChartOptionSelected} anchorMenu={anchorMenuChart} selected={chartType} stacked={stack} tooltip={tooltip} labels={labels} numSeries={props.numSeries} setDefault={false}/>}\r\n </Stack>\r\n <div style={{width:'100%'}}>\r\n <ResponsiveContainer width='100%' height={height} key={props.metricDefinition.metric+JSON.stringify(props.names)}>\r\n {result}\r\n </ResponsiveContainer>\r\n </div>\r\n </Stack>\r\n </CardContent>\r\n </Card>\r\n )\r\n}\r\n"],"names":["props","height","MUITooltip"],"mappings":"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"}
|