zibri-cli 2.2.0 → 2.2.1
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/assets/public/assets.svg +3 -1
- package/assets/public/home.svg +3 -0
- package/assets/public/lib/chartjs-adapter-date-fns.js +7 -0
- package/assets/public/metrics.svg +3 -0
- package/assets/public/style.css +4 -127
- package/dist/commands/new/new.command.js +164 -26
- package/dist/commands/new/new.command.js.map +1 -1
- package/dist/encapsulation/fs.utilities.js +13 -3
- package/dist/encapsulation/fs.utilities.js.map +1 -1
- package/package.json +2 -2
- package/templates/components/base-page.tsx.tpl +24 -0
- package/templates/components/button.tsx.tpl +34 -0
- package/templates/components/card.tsx.tpl +13 -0
- package/templates/components/chart.tsx.tpl +19 -0
- package/templates/components/checkbox.tsx.tpl +24 -0
- package/templates/components/dialog.tsx.tpl +14 -0
- package/templates/components/empty-page.tsx.tpl +34 -0
- package/templates/components/file-entry.tsx.tpl +31 -0
- package/templates/components/form.tsx.tpl +17 -0
- package/templates/components/heading.tsx.tpl +19 -0
- package/templates/components/link.tsx.tpl +29 -0
- package/templates/components/metrics-status.tsx.tpl +92 -0
- package/templates/components/navbar.tsx.tpl +31 -0
- package/templates/components/network-chart.tsx.tpl +99 -0
- package/templates/components/request-duration-chart.tsx.tpl +83 -0
- package/templates/components/requests-per-second-chart.tsx.tpl +167 -0
- package/templates/components/resource-usage-chart.tsx.tpl +121 -0
- package/templates/components/textarea.tsx.tpl +23 -0
- package/templates/pages/assets.tsx.tpl +23 -0
- package/templates/pages/error.tsx.tpl +42 -0
- package/templates/pages/home.tsx.tpl +44 -0
- package/templates/pages/mailing-list-preferences.tsx.tpl +149 -0
- package/templates/pages/mailing-list-unsubscribe-confirmation.tsx.tpl +40 -0
- package/templates/pages/metrics.tsx.tpl +82 -0
- package/assets/public/socket.io.js +0 -4908
- package/templates/pages/assets.hbs +0 -25
- package/templates/pages/base-page.hbs +0 -15
- package/templates/pages/error.hbs +0 -41
- package/templates/pages/index.hbs +0 -20
- package/templates/pages/mailing-list-preferences.hbs +0 -152
- package/templates/pages/mailing-list-unsubscribe.hbs +0 -25
- package/templates/pages/metrics.hbs +0 -443
|
@@ -1,443 +0,0 @@
|
|
|
1
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
2
|
-
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns"></script>
|
|
3
|
-
|
|
4
|
-
<div style="height: 100%; width: 100%; color: whitesmoke; padding-left: 40px; padding-right: 40px;">
|
|
5
|
-
<h1 style="margin-top: 20px; margin-bottom: 20px;">{{name}} Metrics</h1>
|
|
6
|
-
<div style="display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 20px; margin-bottom: 20px; grid-auto-rows: 1fr;">
|
|
7
|
-
<div style="grid-column: span 2; display: flex; flex-direction: column; gap: 20px;">
|
|
8
|
-
<div style="display: flex; gap: 20px;">
|
|
9
|
-
<div class="metrics-container" style="flex: 1; display: flex; justify-content: center; align-items: center;">
|
|
10
|
-
<a href="/"><img src="/assets/logo.jpg" width="100" height="100"></a>
|
|
11
|
-
<a href="/" style="margin-left: auto; margin-right: auto;"><h2>{{name}}</h2></a>
|
|
12
|
-
</div>
|
|
13
|
-
<div class="metrics-container" style="flex: 1;">
|
|
14
|
-
<h2>Status</h2>
|
|
15
|
-
|
|
16
|
-
<div style="display: flex; justify-content: center; align-items: center; gap: 5px;">
|
|
17
|
-
<input id="automaticReload"
|
|
18
|
-
type="checkbox"
|
|
19
|
-
checked="true"
|
|
20
|
-
onchange="updateAutomaticReload(event)"
|
|
21
|
-
>
|
|
22
|
-
<label style="cursor: pointer;" for="automaticReload">Automatic reload</label>
|
|
23
|
-
</div>
|
|
24
|
-
|
|
25
|
-
<div style="width: fit-content; margin-left: auto; margin-right: auto; margin-bottom: 20px; margin-top: 20px;">
|
|
26
|
-
<div style="display: flex; justify-content: space-between; gap: 40px;">
|
|
27
|
-
<div>Version:</div>
|
|
28
|
-
<div>{{version}}</div>
|
|
29
|
-
</div>
|
|
30
|
-
<div style="display: flex; justify-content: space-between; gap: 40px;">
|
|
31
|
-
<div>Uptime:</div>
|
|
32
|
-
<div id="uptimeInfo">...</div>
|
|
33
|
-
</div>
|
|
34
|
-
<div style="display: flex; justify-content: space-between; gap: 40px;">
|
|
35
|
-
<div>Since:</div>
|
|
36
|
-
<div id="uptimeInfoSince">...</div>
|
|
37
|
-
</div>
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|
|
40
|
-
</div>
|
|
41
|
-
|
|
42
|
-
<div class="metrics-container" style="flex: 1;">
|
|
43
|
-
<h2 style="margin-bottom: 10px;">Request duration</h2>
|
|
44
|
-
<canvas id="requestDurationChart"></canvas>
|
|
45
|
-
</div>
|
|
46
|
-
</div>
|
|
47
|
-
|
|
48
|
-
<div class="metrics-container" style="grid-column: span 3;">
|
|
49
|
-
<h2>Requests per second</h2>
|
|
50
|
-
<canvas id="rpsChart"></canvas>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
<div style="display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 20px;">
|
|
54
|
-
<div class="metrics-container" style="margin-bottom: 40px;">
|
|
55
|
-
<h2>Resource Usage</h2>
|
|
56
|
-
<canvas id="resourceChart"></canvas>
|
|
57
|
-
</div>
|
|
58
|
-
<div class="metrics-container" style="margin-bottom: 40px;">
|
|
59
|
-
<h2>Network</h2>
|
|
60
|
-
<canvas id="networkChart"></canvas>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<script>
|
|
66
|
-
const primary = '#0e456f';
|
|
67
|
-
const secondary = '#00b4d8';
|
|
68
|
-
Chart.defaults.color = 'whitesmoke';
|
|
69
|
-
Chart.defaults.borderColor = 'whitesmoke';
|
|
70
|
-
Chart.defaults.backgroundColor = secondary;
|
|
71
|
-
Chart.defaults.scale.grid.color = 'rgba(200, 200, 200, 0.3)';
|
|
72
|
-
|
|
73
|
-
let automaticReload = true;
|
|
74
|
-
let snaps = [];
|
|
75
|
-
let rpsChart;
|
|
76
|
-
let requestDurationChart;
|
|
77
|
-
let resourceChart;
|
|
78
|
-
let networkChart;
|
|
79
|
-
|
|
80
|
-
window.addEventListener('load', () => {
|
|
81
|
-
loadSnapshots();
|
|
82
|
-
setInterval(loadSnapshots, 1000);
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
async function loadSnapshots() {
|
|
86
|
-
if (!automaticReload) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
const resp = await fetch('/metrics');
|
|
91
|
-
snaps = await resp.json();
|
|
92
|
-
updateData();
|
|
93
|
-
}
|
|
94
|
-
catch (err) {
|
|
95
|
-
console.error('failed to load metrics', err);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function updateData() {
|
|
100
|
-
renderRequestDurationChart();
|
|
101
|
-
renderRequestsPerSecondChart();
|
|
102
|
-
renderMemoryChart();
|
|
103
|
-
renderNetworkChart();
|
|
104
|
-
renderUptime();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function updateAutomaticReload(event) {
|
|
108
|
-
automaticReload = !automaticReload;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function renderUptime() {
|
|
112
|
-
const latest = snaps[snaps.length - 1];
|
|
113
|
-
if (!latest) return;
|
|
114
|
-
|
|
115
|
-
const startTimeMetric = latest.metrics.find(m => m.name === 'process_start_time_seconds');
|
|
116
|
-
if (!startTimeMetric) return;
|
|
117
|
-
|
|
118
|
-
const start = startTimeMetric.value * 1000;
|
|
119
|
-
const diffMs = Date.now() - start;
|
|
120
|
-
const diff = {
|
|
121
|
-
days: Math.floor(diffMs / 86400000),
|
|
122
|
-
hours: Math.floor((diffMs % 86400000) / 3600000),
|
|
123
|
-
minutes: Math.floor((diffMs % 3600000) / 60000),
|
|
124
|
-
seconds: Math.floor((diffMs % 60000) / 1000)
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const text = `${diff.days}d ${diff.hours}h ${diff.minutes}m ${diff.seconds}s`;
|
|
128
|
-
const sinceText = `${new Date(start).toLocaleDateString('de', { year: 'numeric', day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' })}`;
|
|
129
|
-
document.getElementById('uptimeInfo').textContent = text;
|
|
130
|
-
document.getElementById('uptimeInfoSince').textContent = sinceText;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function renderRequestsPerSecondChart() {
|
|
134
|
-
// 1) Build per‐snapshot RPS deltas by status class
|
|
135
|
-
const intervalSec = 5;
|
|
136
|
-
const seriesSuccess = [];
|
|
137
|
-
const seriesClientError = [];
|
|
138
|
-
const seriesServerError = [];
|
|
139
|
-
|
|
140
|
-
snaps.forEach((snap, i, all) => {
|
|
141
|
-
const t = new Date(snap.timestamp);
|
|
142
|
-
if (i === 0) {
|
|
143
|
-
seriesSuccess.push({ x: t, y: 0 });
|
|
144
|
-
seriesClientError.push({ x: t, y: 0 });
|
|
145
|
-
seriesServerError.push({ x: t, y: 0 });
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
const prev = all[i - 1].metrics;
|
|
149
|
-
const curr = snap.metrics;
|
|
150
|
-
// sum counts by class
|
|
151
|
-
const sumFor = (codeRx) => {
|
|
152
|
-
const sum = (metrics) =>
|
|
153
|
-
metrics
|
|
154
|
-
.filter(m => {
|
|
155
|
-
if (m.name !== 'http_requests_total') {
|
|
156
|
-
return false;
|
|
157
|
-
}
|
|
158
|
-
switch (codeRx) {
|
|
159
|
-
case 'success': {
|
|
160
|
-
return !m.labels.status_code.startsWith('5') && !m.labels.status_code.startsWith('4')
|
|
161
|
-
}
|
|
162
|
-
case 'client': {
|
|
163
|
-
return m.labels.status_code.startsWith('4');
|
|
164
|
-
}
|
|
165
|
-
case 'server': {
|
|
166
|
-
return m.labels.status_code.startsWith('5');
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
})
|
|
170
|
-
.reduce((acc, m) => acc + m.value, 0);
|
|
171
|
-
return (sum(curr) - sum(prev)) / intervalSec;
|
|
172
|
-
};
|
|
173
|
-
seriesSuccess.push({ x: t, y: sumFor('success') });
|
|
174
|
-
seriesClientError.push({ x: t, y: sumFor('client') });
|
|
175
|
-
seriesServerError.push({ x: t, y: sumFor('server') });
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
// build event‑loop lag series (in ms)
|
|
179
|
-
const lagSeries = snaps.map(snap => {
|
|
180
|
-
const m = snap.metrics.find(m => m.name === 'nodejs_eventloop_lag_seconds');
|
|
181
|
-
return {
|
|
182
|
-
x: new Date(snap.timestamp),
|
|
183
|
-
y: m ? m.value * 1000 : 0
|
|
184
|
-
};
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
const datasets = [
|
|
188
|
-
{
|
|
189
|
-
label: 'Event Loop Lag',
|
|
190
|
-
data: lagSeries,
|
|
191
|
-
fill: false,
|
|
192
|
-
yAxisID: 'y1',
|
|
193
|
-
backgroundColor: 'purple',
|
|
194
|
-
borderColor: 'purple'
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
label: 'Server Error',
|
|
198
|
-
data: seriesServerError,
|
|
199
|
-
fill: true,
|
|
200
|
-
backgroundColor: 'red',
|
|
201
|
-
borderColor: 'red'
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
label: 'Client Error',
|
|
205
|
-
data: seriesClientError,
|
|
206
|
-
fill: true,
|
|
207
|
-
borderColor: secondary
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
label: 'Success',
|
|
211
|
-
data: seriesSuccess,
|
|
212
|
-
fill: true,
|
|
213
|
-
backgroundColor: 'green',
|
|
214
|
-
borderColor: 'green'
|
|
215
|
-
}
|
|
216
|
-
];
|
|
217
|
-
|
|
218
|
-
if (rpsChart) {
|
|
219
|
-
rpsChart.data.datasets[0].data = datasets[0].data;
|
|
220
|
-
rpsChart.data.datasets[1].data = datasets[1].data;
|
|
221
|
-
rpsChart.data.datasets[2].data = datasets[2].data;
|
|
222
|
-
rpsChart.data.datasets[3].data = datasets[3].data;
|
|
223
|
-
rpsChart.update();
|
|
224
|
-
return;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// render RPS chart
|
|
228
|
-
rpsChart = new Chart(document.getElementById('rpsChart'), {
|
|
229
|
-
type: 'line',
|
|
230
|
-
data: { datasets },
|
|
231
|
-
options: {
|
|
232
|
-
scales: {
|
|
233
|
-
x: {
|
|
234
|
-
type: 'time',
|
|
235
|
-
time: {
|
|
236
|
-
unit: 'second',
|
|
237
|
-
displayFormats: {
|
|
238
|
-
second: 'HH:mm:ss'
|
|
239
|
-
}
|
|
240
|
-
},
|
|
241
|
-
grid: { display: false }
|
|
242
|
-
},
|
|
243
|
-
y: {
|
|
244
|
-
stacked: true,
|
|
245
|
-
beginAtZero: true,
|
|
246
|
-
title: { display: true, text: 'Requests/sec' },
|
|
247
|
-
grid: { display: true }
|
|
248
|
-
},
|
|
249
|
-
y1: {
|
|
250
|
-
position: 'right',
|
|
251
|
-
title: { display: true, text: 'Lag (ms)' },
|
|
252
|
-
ticks: { callback: v => `${v} ms` }
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
ticks: {
|
|
256
|
-
stepSize: 5,
|
|
257
|
-
source: 'auto'
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function renderRequestDurationChart() {
|
|
264
|
-
// Build histogram from the *latest* snapshot only
|
|
265
|
-
const latest = snaps[snaps.length - 1]?.metrics ?? [];
|
|
266
|
-
const sumsByLe = latest
|
|
267
|
-
.filter(m => m.name === 'http_request_duration_ms' && m.labels.le !== undefined)
|
|
268
|
-
.reduce((map,m) => {
|
|
269
|
-
const le = m.labels.le;
|
|
270
|
-
map[le] = (map[le] || 0) + m.value;
|
|
271
|
-
return map;
|
|
272
|
-
}, {});
|
|
273
|
-
const sorted = Object.entries(sumsByLe)
|
|
274
|
-
.map(([le,v]) => ({ le, v }))
|
|
275
|
-
.sort((a,b) => {
|
|
276
|
-
const na = a.le==='+Inf'? Infinity: +a.le;
|
|
277
|
-
const nb = b.le==='+Inf'? Infinity: +b.le;
|
|
278
|
-
return na-nb;
|
|
279
|
-
});
|
|
280
|
-
const perBucket = sorted.map((cur, i) => {
|
|
281
|
-
const prev = i > 0 ? sorted[i - 1].v : 0;
|
|
282
|
-
return { le: cur.le, v: cur.v - prev };
|
|
283
|
-
});
|
|
284
|
-
const hist = perBucket.reduce(
|
|
285
|
-
(acc,m) => {
|
|
286
|
-
acc.labels.push(`${m.le} ms`);
|
|
287
|
-
acc.values.push(m.v);
|
|
288
|
-
return acc;
|
|
289
|
-
},
|
|
290
|
-
{ labels:[], values:[] }
|
|
291
|
-
);
|
|
292
|
-
|
|
293
|
-
if (requestDurationChart) {
|
|
294
|
-
requestDurationChart.data.labels = hist.labels;
|
|
295
|
-
requestDurationChart.data.datasets[0].data = hist.values;
|
|
296
|
-
requestDurationChart.update();
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// render latency histogram
|
|
301
|
-
requestDurationChart = new Chart(document.getElementById('requestDurationChart'), {
|
|
302
|
-
type: 'bar',
|
|
303
|
-
data: { labels: hist.labels, datasets: [{ label: 'Count', data: hist.values }] }
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function renderMemoryChart() {
|
|
308
|
-
const data = snaps.map(snap => {
|
|
309
|
-
const mem = snap.metrics.find(m => m.name === 'process_resident_memory_bytes');
|
|
310
|
-
return {
|
|
311
|
-
x: new Date(snap.timestamp),
|
|
312
|
-
y: mem ? mem.value / 1024 / 1024 : 0 // MB
|
|
313
|
-
};
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
const userSeries = snaps.map(s => s.metrics.find(m => m.name==='process_cpu_user_seconds_total')?.value || 0);
|
|
317
|
-
const systemSeries = snaps.map(s => s.metrics.find(m => m.name==='process_cpu_system_seconds_total')?.value|| 0);
|
|
318
|
-
const times = snaps.map(s => new Date(s.timestamp).getTime()/1000); // seconds
|
|
319
|
-
const cpuSeries = times.map((t,i) => {
|
|
320
|
-
if (i === 0) {
|
|
321
|
-
return { t, pct: 0 };
|
|
322
|
-
}
|
|
323
|
-
const dt = t - times[i-1]; // interval in seconds (should be ~5)
|
|
324
|
-
const du = userSeries[i] - userSeries[i-1];
|
|
325
|
-
const ds = systemSeries[i] - systemSeries[i-1];
|
|
326
|
-
const usedSec = du + ds; // total CPU seconds used in that interval
|
|
327
|
-
const cores = navigator.hardwareConcurrency || 1;
|
|
328
|
-
const pct = (usedSec / dt / cores) * 100; // CPU% across all cores
|
|
329
|
-
return { x: new Date(t*1000), y: pct };
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
if (resourceChart) {
|
|
333
|
-
resourceChart.data.datasets[0].data = data;
|
|
334
|
-
resourceChart.data.datasets[1].data = cpuSeries;
|
|
335
|
-
resourceChart.update();
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
resourceChart = new Chart(document.getElementById('resourceChart'), {
|
|
340
|
-
type: 'line',
|
|
341
|
-
data: {
|
|
342
|
-
datasets: [
|
|
343
|
-
{
|
|
344
|
-
label: 'RAM (MB)',
|
|
345
|
-
data,
|
|
346
|
-
yAxisID: 'y',
|
|
347
|
-
borderColor: secondary,
|
|
348
|
-
},
|
|
349
|
-
{
|
|
350
|
-
label: 'CPU (%)',
|
|
351
|
-
data: cpuSeries,
|
|
352
|
-
yAxisID: 'yCPU',
|
|
353
|
-
borderColor: primary,
|
|
354
|
-
backgroundColor: primary
|
|
355
|
-
}
|
|
356
|
-
]
|
|
357
|
-
},
|
|
358
|
-
options: {
|
|
359
|
-
scales: {
|
|
360
|
-
x: {
|
|
361
|
-
type: 'time',
|
|
362
|
-
time: {
|
|
363
|
-
unit: 'second',
|
|
364
|
-
displayFormats: {
|
|
365
|
-
second: 'HH:mm:ss'
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
grid: {display: false }
|
|
369
|
-
},
|
|
370
|
-
y: {
|
|
371
|
-
ticks: {
|
|
372
|
-
callback: v => `${v} MB`
|
|
373
|
-
},
|
|
374
|
-
beginAtZero: true,
|
|
375
|
-
},
|
|
376
|
-
yCPU: {
|
|
377
|
-
position: 'right',
|
|
378
|
-
grid: { drawOnChartArea: false }, // don't duplicate grid lines
|
|
379
|
-
ticks: {
|
|
380
|
-
callback: v => `${v}%`
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
function renderNetworkChart() {
|
|
389
|
-
if (snaps.length < 2) {
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
const intervalSec = 5;
|
|
393
|
-
|
|
394
|
-
// extract series of cumulative values
|
|
395
|
-
const rxSeries = snaps.map(s => s.metrics.find(m => m.name==='network_bytes_received_total')?.value||0);
|
|
396
|
-
const txSeries = snaps.map(s => s.metrics.find(m => m.name==='network_bytes_transmitted_total')?.value||0);
|
|
397
|
-
const times = snaps.map(s => new Date(s.timestamp));
|
|
398
|
-
|
|
399
|
-
// build bytes/sec points
|
|
400
|
-
const dataRx = times.map((t,i) => {
|
|
401
|
-
if (i===0) return { x:t, y:0 };
|
|
402
|
-
return { x:t, y:(rxSeries[i]-rxSeries[i-1])/intervalSec };
|
|
403
|
-
});
|
|
404
|
-
const dataTx = times.map((t,i) => {
|
|
405
|
-
if (i===0) return { x:t, y:0 };
|
|
406
|
-
return { x:t, y:(txSeries[i]-txSeries[i-1])/intervalSec };
|
|
407
|
-
});
|
|
408
|
-
|
|
409
|
-
const datasets = [
|
|
410
|
-
{ label:'bytes received', data: dataRx, borderColor: secondary },
|
|
411
|
-
{ label:'bytes sent', data: dataTx, backgroundColor: primary, borderColor: primary },
|
|
412
|
-
];
|
|
413
|
-
|
|
414
|
-
if (networkChart) {
|
|
415
|
-
networkChart.data.datasets[0].data = datasets[0].data;
|
|
416
|
-
networkChart.data.datasets[1].data = datasets[1].data;
|
|
417
|
-
networkChart.update();
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
networkChart = new Chart(document.getElementById('networkChart'), {
|
|
422
|
-
type: 'line',
|
|
423
|
-
data: { datasets },
|
|
424
|
-
options: {
|
|
425
|
-
scales: {
|
|
426
|
-
x: {
|
|
427
|
-
type: 'time',
|
|
428
|
-
time: { unit:'second', displayFormats: { second:'HH:mm:ss' } },
|
|
429
|
-
grid: { display: false }
|
|
430
|
-
},
|
|
431
|
-
y: {
|
|
432
|
-
beginAtZero: true,
|
|
433
|
-
ticks: {
|
|
434
|
-
callback: v => `${v/1000} KB`
|
|
435
|
-
},
|
|
436
|
-
}
|
|
437
|
-
},
|
|
438
|
-
plugins:{ legend: { position:'top' } }
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
</script>
|