Implement metrics

This commit is contained in:
2023-11-15 10:46:48 +01:00
parent 8ad7bbdebb
commit 652be94621
7 changed files with 154 additions and 4 deletions

10
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,10 @@
{
"sqltools.connections": [
{
"previewLimit": 50,
"driver": "SQLite",
"name": "1",
"database": "E:\\tmp\\output.db"
}
]
}

View File

@@ -5,6 +5,7 @@ const NanoTimer = require("nanotimer");
const { createBaseLogger, createSessionLogger } = require("./logger"); const { createBaseLogger, createSessionLogger } = require("./logger");
const { verifyDefaults, verifyExists } = require("./utils"); const { verifyDefaults, verifyExists } = require("./utils");
const { clientOptions } = require("./cliOptions"); const { clientOptions } = require("./cliOptions");
const { MetricManager } = require("./metrics/metricManager");
const logger = createBaseLogger(); const logger = createBaseLogger();
const options = commandLineArgs(clientOptions); const options = commandLineArgs(clientOptions);
@@ -78,6 +79,17 @@ function startInterval(session, sessionLogger) {
); );
} }
const metricManager = new MetricManager();
// async function main() {
// const test1 = metricManager.AddMetrics("test");
// for (let i = 0; i < 1e5; i++) {
// test1.AddEvent();
// test1.UpdateBar();
// await setTimeout(() => {}, 200);
// }
// }
// main();
for (let i = 0; i < options.sessions; i++) { for (let i = 0; i < options.sessions; i++) {
const sessionLogger = createSessionLogger(i); const sessionLogger = createSessionLogger(i);
sessionLogger.info(`Connecting to ${options.host}:${options.port}...`); sessionLogger.info(`Connecting to ${options.host}:${options.port}...`);
@@ -101,12 +113,20 @@ for (let i = 0; i < options.sessions; i++) {
sessionLogger.info( sessionLogger.info(
`Successfully bound, sending ${options.messagecount} messages '${options.source}'->'${options.destination}' ('${options.message}')` `Successfully bound, sending ${options.messagecount} messages '${options.source}'->'${options.destination}' ('${options.message}')`
); );
const rxMetrics = metricManager.AddMetrics(`Session-${i}-RX`);
const txMetrics = metricManager.AddMetrics(`Session-${i}-TX`);
startInterval(session, sessionLogger); startInterval(session, sessionLogger);
// TODO: Add error message for invalid systemid and password // TODO: Add error message for invalid systemid and password
session.on("deliver_sm", function (pdu) { session.on("deliver_sm", function (pdu) {
sessionLogger.info("Got deliver_sm, replying..."); rxMetrics.AddEvent();
// sessionLogger.info("Got deliver_sm, replying...");
setTimeout(() => {
session.send(pdu.response()); session.send(pdu.response());
txMetrics.AddEvent();
}, 2000);
// session.send(pdu.response());
// txMetrics.AddEvent();
}); });
session.on("enquire_link", function (pdu) { session.on("enquire_link", function (pdu) {
session.send(pdu.response()); session.send(pdu.response());

30
metrics/circularBuffer.js Normal file
View File

@@ -0,0 +1,30 @@
class CircularBuffer {
constructor(size) {
this.buffer = new Array(size);
this.size = size;
this.head = 0;
this.tail = 0;
}
push(item) {
this.buffer[this.head] = item;
this.head = (this.head + 1) % this.size;
if (this.head === this.tail) {
this.tail = (this.tail + 1) % this.size;
}
}
toArray() {
const result = [];
let current = this.tail;
for (let i = 0; i < this.size; i++) {
if (this.buffer[current] !== undefined) {
result.push(this.buffer[current]);
}
current = (current + 1) % this.size;
}
return result;
}
}
module.exports = { CircularBuffer };

24
metrics/metricManager.js Normal file
View File

@@ -0,0 +1,24 @@
const cliProgress = require("cli-progress");
const { Metric } = require("./metrics");
class MetricManager {
constructor() {
this.metricBufferSize = 1000;
this.multibar = new cliProgress.MultiBar(
{
clearOnComplete: false,
barCompleteChar: "\u2588",
barIncompleteChar: "\u2591",
format: " {bar} | {name} | {value}/{total}",
},
cliProgress.Presets.shades_grey
);
}
AddMetrics(name) {
const metric = new Metric(name, this.multibar, this.metricBufferSize);
return metric;
}
}
module.exports = { MetricManager };

39
metrics/metrics.js Normal file
View File

@@ -0,0 +1,39 @@
const { CircularBuffer } = require("./circularBuffer");
class Metric {
constructor(barName, multibar, bufferSize) {
this.multibar = multibar;
this.pbar = multibar.create(0, 0);
this.pbar.update(0, { name: barName });
this.buffer = new CircularBuffer(bufferSize);
this.maxRate = 0;
setInterval(this.UpdateBar.bind(this), 100);
}
AddEvent() {
const timestamp = Date.now();
this.buffer.push({ timestamp, count: 1 });
}
GetRate() {
const entries = this.buffer.toArray();
const currentTime = Date.now();
const interval = entries.length > 1 ? currentTime - entries[0].timestamp : 1;
const totalRX = entries.reduce((sum, entry) => sum + entry.count, 0);
return Math.round((totalRX / interval) * 100000) / 100;
}
UpdateBar() {
const eps = this.GetRate();
if (eps > this.maxRate) {
this.pbar.total = eps;
this.maxRate = eps;
}
this.pbar.update(eps);
this.multibar.update();
}
}
module.exports = { Metric };

View File

@@ -14,6 +14,7 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"cli-progress": "^3.12.0",
"command-line-args": "^5.2.1", "command-line-args": "^5.2.1",
"command-line-usage": "^7.0.1", "command-line-usage": "^7.0.1",
"nanotimer": "^0.3.15", "nanotimer": "^0.3.15",

30
pnpm-lock.yaml generated
View File

@@ -5,6 +5,9 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
cli-progress:
specifier: ^3.12.0
version: 3.12.0
command-line-args: command-line-args:
specifier: ^5.2.1 specifier: ^5.2.1
version: 5.2.1 version: 5.2.1
@@ -166,7 +169,6 @@ packages:
/ansi-regex@5.0.1: /ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
dev: true
/ansi-styles@4.3.0: /ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
@@ -222,6 +224,13 @@ packages:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
supports-color: 7.2.0 supports-color: 7.2.0
/cli-progress@3.12.0:
resolution: {integrity: sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==}
engines: {node: '>=4'}
dependencies:
string-width: 4.2.3
dev: false
/color-convert@1.9.3: /color-convert@1.9.3:
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
dependencies: dependencies:
@@ -318,6 +327,10 @@ packages:
esutils: 2.0.3 esutils: 2.0.3
dev: true dev: true
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: false
/enabled@2.0.0: /enabled@2.0.0:
resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==}
dev: false dev: false
@@ -569,6 +582,11 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
/is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
dev: false
/is-glob@4.0.3: /is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -839,6 +857,15 @@ packages:
engines: {node: '>=10'} engines: {node: '>=10'}
dev: false dev: false
/string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
dev: false
/string_decoder@1.3.0: /string_decoder@1.3.0:
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
dependencies: dependencies:
@@ -850,7 +877,6 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dependencies: dependencies:
ansi-regex: 5.0.1 ansi-regex: 5.0.1
dev: true
/strip-json-comments@3.1.1: /strip-json-comments@3.1.1:
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}