Path: blob/master/src/packages/conat/monitor/tables.ts
1452 views
/*1Displaying ASCII art tables in the terminal to understand Conat state.23We will also have similar functionality in the web app. Both are a good idea to4have for various reasons.567*/89import { AsciiTable3 } from "ascii-table3";10import { type Client } from "@cocalc/conat/core/client";11import { field_cmp, human_readable_size } from "@cocalc/util/misc";12import dayjs from "dayjs";13import duration from "dayjs/plugin/duration";1415dayjs.extend(duration);1617function formatCompactDuration(ms: number): string {18const d = dayjs.duration(ms);1920const hours = d.hours();21const minutes = d.minutes();22const seconds = d.seconds();2324let out = "";25if (d.asDays() >= 1) out += `${Math.floor(d.asDays())}d`;26if (d.asHours() % 24 >= 1) out += `${hours}h`;27if (d.asMinutes() % 60 >= 1) out += `${minutes}m`;28out += `${seconds}s`;29return out;30}3132interface Options {33client: Client;34maxWait?: number;35maxMessages?: number;36}3738// cd packages/backend; pnpm conat-connections39export async function usage({ client, maxWait = 3000, maxMessages }: Options) {40const sys = client.callMany("sys.conat.server", { maxWait, maxMessages });41const data = await sys.usage();42const rows: any[] = [];43const perServerRows: any[] = [];44let total = 0;45for await (const X of data) {46for (const server in X) {47const { perUser, total: total0 } = X[server];48perServerRows.push([server, total0]);49total += total0;5051for (const user in perUser) {52rows.push([server, user, perUser[user]]);53}54}55}56rows.sort(field_cmp("2"));57rows.push(["", "", ""]);58rows.push(["TOTAL", "", total]);5960const tablePerUser = new AsciiTable3(`${total} Connections`)61.setHeading("Server", "User", "Connections")62.addRowMatrix(rows);63const tablePerServer = new AsciiTable3(`Connections Per Server`)64.setHeading("Server", "Connections")65.addRowMatrix(perServerRows);6667tablePerUser.setStyle("unicode-round");68tablePerServer.setStyle("unicode-round");6970return [tablePerServer, tablePerUser];71}7273export async function stats({ client, maxWait = 3000, maxMessages }: Options) {74const sys = client.callMany("sys.conat.server", { maxWait, maxMessages });75const data = await sys.stats();7677const rows: any[] = [];78const cols = 10;79const totals = Array(cols).fill(0);80for await (const X of data) {81for (const server in X) {82const stats = X[server];83for (const id in stats) {84const x = stats[id];85let user;86if (x.user?.error) {87user = x.user.error;88} else {89user = JSON.stringify(x.user).slice(1, -1);90}91const uptime = formatCompactDuration(Date.now() - x.connected);92rows.push([93id,94user,95server,96x.address,97uptime,98x.recv.messages,99x.send.messages,100human_readable_size(x.recv.bytes),101human_readable_size(x.send.bytes),102x.subs,103]);104totals[cols - 5] += x.recv.messages;105totals[cols - 4] += x.send.messages;106totals[cols - 3] += x.recv.bytes;107totals[cols - 2] += x.send.bytes;108totals[cols - 1] += x.subs;109}110}111}112rows.sort(field_cmp(`${cols - 1}`));113rows.push(Array(cols).fill(""));114rows.push([115"TOTALS",116`Total for ${rows.length - 1} connections:`,117...Array(cols - 7).fill(""),118totals[cols - 5],119totals[cols - 4],120human_readable_size(totals[cols - 3]),121human_readable_size(totals[cols - 2]),122totals[cols - 1],123]);124125const table = new AsciiTable3(`${rows.length - 2} Conat Connections`)126.setHeading(127"ID",128"User",129"Server",130"Address",131"Uptime",132"In Msgs",133"Out Msgs",134"In Bytes",135"Out Bytes",136"Subs",137)138.addRowMatrix(rows);139140table.setStyle("unicode-round");141return [table];142}143144export async function showUsersAndStats({145client,146maxWait = 3000,147maxMessages,148}: Options) {149let s;150if (maxMessages) {151s = `for up ${maxMessages} servers `;152} else {153s = "";154}155console.log(`Gather data ${s}for up to ${maxWait / 1000} seconds...\n\n`);156const X = [stats, usage];157const tables: any[] = [];158const f = async (i) => {159for (const table of await X[i]({ client, maxWait, maxMessages })) {160tables.push(table);161}162};163await Promise.all([f(0), f(1)]);164for (const table of tables) {165console.log(table.toString());166}167}168169170