|
26 | 26 | type resourceUsage = {
|
27 | 27 | readonly timestamp: Date;
|
28 | 28 | readonly value: number;
|
| 29 | + readonly instance: string; |
29 | 30 | }[];
|
30 | 31 |
|
31 | 32 | const interval = $derived(page.url.searchParams.get('interval') ?? '7d');
|
32 | 33 |
|
| 34 | + const visualizationColors: string[] = [ |
| 35 | + '#FFC166', |
| 36 | + '#99DEAD', |
| 37 | + '#CCE1FF', |
| 38 | + '#C0B2D2', |
| 39 | + '#66CBEC', |
| 40 | + '#99C4DD' |
| 41 | + ]; |
| 42 | +
|
33 | 43 | function options(
|
34 | 44 | data: resourceUsage,
|
35 | 45 | request: number,
|
36 | 46 | limit?: number,
|
37 |
| - color: string = '#000000', |
38 | 47 | valueFormatter: (value: number) => string = (value: number) =>
|
39 | 48 | value == null ? '-' : value.toLocaleString('en-GB', { maximumFractionDigits: 4 })
|
40 | 49 | ): EChartsOption {
|
41 | 50 | const safeData = data ?? [];
|
42 | 51 |
|
43 |
| - return { |
| 52 | + const uniqueTimestamps = Array.from(new Set(safeData.map((d) => d.timestamp.getTime()))); |
| 53 | +
|
| 54 | + const opts = { |
44 | 55 | animation: false,
|
45 | 56 | tooltip: {
|
46 | 57 | trigger: 'axis',
|
|
49 | 60 | `<div style="height: 8px; width: 8px; border-radius: 50%; background-color: ${color};"></div>`;
|
50 | 61 | const div = document.createElement('div');
|
51 | 62 |
|
52 |
| - const [usage, request, limit] = value; |
53 |
| - if (Array.isArray(usage.value) && Array.isArray(request.value)) { |
54 |
| - div.innerHTML = ` |
55 |
| - <div>${format(usage.value.at(0) as number, 'dd/MM/yyyy HH:mm')}</div> |
| 63 | + div.innerHTML = ` |
| 64 | + <div>${format( |
| 65 | + Array.isArray(value) && Array.isArray(value[0]?.value) |
| 66 | + ? (value[0].value[0] as number) |
| 67 | + : 0, |
| 68 | + 'dd/MM/yyyy HH:mm' |
| 69 | + )}</div> |
56 | 70 | <hr style="border: none; height: 1px; background-color: var(--a-border-subtle);" />
|
57 | 71 | <div style="display: grid; grid-template-columns: auto auto; column-gap: 0.5rem;">
|
58 |
| - <div style="display: flex; align-items: center; gap: 0.25rem;">${dot(color)}${usage.seriesName}:</div><div style="text-align: right;">${valueFormatter(usage.value.at(1) as number)}</div> |
59 |
| - <div style="display: flex; align-items: center; gap: 0.25rem;">${dot(requestColor)}${request.seriesName}:</div><div style="text-align: right;">${valueFormatter(request.value.at(1) as number)}</div> |
60 |
| - ${Array.isArray(limit?.value) ? `<div style="display: flex; align-items: center; gap: 0.25rem;">${dot(limitColor)}${limit.seriesName}:</div><div style="text-align: right;">${valueFormatter(limit.value.at(1) as number)}</div>` : ''} |
| 72 | + ${value |
| 73 | + .map( |
| 74 | + (v) => |
| 75 | + `<div style="display: flex; align-items: center; gap: 0.25rem;">${dot( |
| 76 | + v.color?.toString() ?? '' |
| 77 | + )}${v.seriesName}:</div><div style="text-align: right;">${valueFormatter( |
| 78 | + (Array.isArray(v.value) ? v.value[1] : null) as number |
| 79 | + )}</div>` |
| 80 | + ) |
| 81 | + .join('')} |
61 | 82 | </div>
|
62 | 83 | `;
|
63 |
| - } |
64 | 84 | return div;
|
65 | 85 | },
|
66 | 86 | axisPointer: {
|
|
80 | 100 | }
|
81 | 101 | },
|
82 | 102 | series: [
|
83 |
| - { |
84 |
| - name: 'Usage', |
| 103 | + ...Array.from(new Set(safeData.map((d) => d.instance))).map((instance, index) => ({ |
| 104 | + name: `Usage - ${instance}`, |
85 | 105 | type: 'line',
|
86 |
| - data: safeData.map((d) => [d.timestamp.getTime(), d.value]), |
| 106 | + data: safeData |
| 107 | + .filter((d) => d.instance === instance) |
| 108 | + .map((d) => [d.timestamp.getTime(), d.value]), |
87 | 109 | showSymbol: false,
|
88 |
| - color, |
| 110 | + color: visualizationColors[index % visualizationColors.length], |
89 | 111 | areaStyle: {
|
90 | 112 | opacity: 0.2
|
91 | 113 | }
|
92 |
| - }, |
| 114 | + })), |
93 | 115 | {
|
94 |
| - data: safeData.map((d) => [d.timestamp.getTime(), request]), |
| 116 | + data: uniqueTimestamps.map((timestamp) => [timestamp, request]), |
95 | 117 | type: 'line',
|
96 | 118 | name: 'Requested',
|
97 | 119 | showSymbol: false,
|
|
108 | 130 | ]
|
109 | 131 | }
|
110 | 132 | },
|
111 |
| - ...(limit |
112 |
| - ? [ |
113 |
| - { |
114 |
| - data: safeData.map((d) => [d.timestamp.getTime(), limit]), |
115 |
| - type: 'line', |
116 |
| - name: 'Limit', |
117 |
| - showSymbol: false, |
118 |
| - color: limitColor, |
119 |
| - lineStyle: { color: limitColor }, |
120 |
| - markLine: { |
121 |
| - symbol: 'none', |
122 |
| - data: [ |
123 |
| - { |
124 |
| - yAxis: limit, |
125 |
| - label: { formatter: 'Limit', position: 'end', color: limitColor }, |
126 |
| - lineStyle: { type: 'solid', color: 'transparent' } |
127 |
| - } |
128 |
| - ] |
129 |
| - } |
| 133 | + limit |
| 134 | + ? { |
| 135 | + data: uniqueTimestamps.map((timestamp) => [timestamp, limit]), |
| 136 | + type: 'line', |
| 137 | + name: 'Limit', |
| 138 | + showSymbol: false, |
| 139 | + color: limitColor, |
| 140 | + lineStyle: { color: limitColor }, |
| 141 | + markLine: { |
| 142 | + symbol: 'none', |
| 143 | + data: [ |
| 144 | + { |
| 145 | + yAxis: limit, |
| 146 | + label: { formatter: 'Limit', position: 'end', color: limitColor }, |
| 147 | + lineStyle: { type: 'solid', color: 'transparent' } |
| 148 | + } |
| 149 | + ] |
130 | 150 | }
|
131 |
| - ] |
132 |
| - : []) |
| 151 | + } |
| 152 | + : null |
133 | 153 | ]
|
134 | 154 | } as EChartsOption;
|
| 155 | +
|
| 156 | + return opts; |
135 | 157 | }
|
136 | 158 |
|
137 | 159 | const limitColor = '#DE2E2E';
|
138 |
| - const usageMemColor = '#8269A2'; |
139 |
| - const usageCPUColor = '#FF9100'; |
140 | 160 | const requestColor = '#3386E0';
|
141 | 161 | </script>
|
142 | 162 |
|
|
203 | 223 | <EChart
|
204 | 224 | options={options(
|
205 | 225 | utilization.memory_series.map((d) => {
|
206 |
| - return { timestamp: d.timestamp, value: d.value / 1024 / 1024 / 1024 }; |
| 226 | + return { |
| 227 | + timestamp: d.timestamp, |
| 228 | + value: d.value / 1024 / 1024 / 1024, |
| 229 | + instance: d.instance |
| 230 | + }; |
207 | 231 | }),
|
208 | 232 | utilization.requested_memory / 1024 / 1024 / 1024,
|
209 | 233 | utilization.limit_memory ? utilization.limit_memory / 1024 / 1024 / 1024 : undefined,
|
210 |
| - usageMemColor, |
211 | 234 | (value) =>
|
212 | 235 | value == null
|
213 | 236 | ? '-'
|
|
260 | 283 | utilization.cpu_series,
|
261 | 284 | utilization.requested_cpu,
|
262 | 285 | utilization.limit_cpu ? utilization.limit_cpu : undefined,
|
263 |
| - usageCPUColor, |
264 | 286 | (value: number) =>
|
265 | 287 | value == null
|
266 | 288 | ? '-'
|
|
0 commit comments