From 652be94621f844f958db19f73987fbe7c6f4b4a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Majdand=C5=BEi=C4=87?= Date: Wed, 15 Nov 2023 10:46:48 +0100 Subject: [PATCH] Implement metrics --- .vscode/settings.json | 10 ++++++++++ client.js | 24 ++++++++++++++++++++++-- metrics/circularBuffer.js | 30 ++++++++++++++++++++++++++++++ metrics/metricManager.js | 24 ++++++++++++++++++++++++ metrics/metrics.js | 39 +++++++++++++++++++++++++++++++++++++++ package.json | 1 + pnpm-lock.yaml | 30 ++++++++++++++++++++++++++++-- 7 files changed, 154 insertions(+), 4 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 metrics/circularBuffer.js create mode 100644 metrics/metricManager.js create mode 100644 metrics/metrics.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..db7e31f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "sqltools.connections": [ + { + "previewLimit": 50, + "driver": "SQLite", + "name": "1", + "database": "E:\\tmp\\output.db" + } + ] +} diff --git a/client.js b/client.js index bc47302..7c5fc61 100644 --- a/client.js +++ b/client.js @@ -5,6 +5,7 @@ const NanoTimer = require("nanotimer"); const { createBaseLogger, createSessionLogger } = require("./logger"); const { verifyDefaults, verifyExists } = require("./utils"); const { clientOptions } = require("./cliOptions"); +const { MetricManager } = require("./metrics/metricManager"); const logger = createBaseLogger(); 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++) { const sessionLogger = createSessionLogger(i); sessionLogger.info(`Connecting to ${options.host}:${options.port}...`); @@ -101,12 +113,20 @@ for (let i = 0; i < options.sessions; i++) { sessionLogger.info( `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); // TODO: Add error message for invalid systemid and password session.on("deliver_sm", function (pdu) { - sessionLogger.info("Got deliver_sm, replying..."); - session.send(pdu.response()); + rxMetrics.AddEvent(); + // sessionLogger.info("Got deliver_sm, replying..."); + setTimeout(() => { + session.send(pdu.response()); + txMetrics.AddEvent(); + }, 2000); + // session.send(pdu.response()); + // txMetrics.AddEvent(); }); session.on("enquire_link", function (pdu) { session.send(pdu.response()); diff --git a/metrics/circularBuffer.js b/metrics/circularBuffer.js new file mode 100644 index 0000000..b23e185 --- /dev/null +++ b/metrics/circularBuffer.js @@ -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 }; diff --git a/metrics/metricManager.js b/metrics/metricManager.js new file mode 100644 index 0000000..c613364 --- /dev/null +++ b/metrics/metricManager.js @@ -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 }; diff --git a/metrics/metrics.js b/metrics/metrics.js new file mode 100644 index 0000000..85cf4f3 --- /dev/null +++ b/metrics/metrics.js @@ -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 }; diff --git a/package.json b/package.json index 3420819..bd8e99b 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "author": "", "license": "ISC", "dependencies": { + "cli-progress": "^3.12.0", "command-line-args": "^5.2.1", "command-line-usage": "^7.0.1", "nanotimer": "^0.3.15", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1ab18aa..16ff980 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,6 +5,9 @@ settings: excludeLinksFromLockfile: false dependencies: + cli-progress: + specifier: ^3.12.0 + version: 3.12.0 command-line-args: specifier: ^5.2.1 version: 5.2.1 @@ -166,7 +169,6 @@ packages: /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} @@ -222,6 +224,13 @@ packages: ansi-styles: 4.3.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: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: @@ -318,6 +327,10 @@ packages: esutils: 2.0.3 dev: true + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: false + /enabled@2.0.0: resolution: {integrity: sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==} dev: false @@ -569,6 +582,11 @@ packages: engines: {node: '>=0.10.0'} 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: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -839,6 +857,15 @@ packages: engines: {node: '>=10'} 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: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: @@ -850,7 +877,6 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - dev: true /strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}