Path: blob/master/src/packages/conat/monitor/tables.ts
1710 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";14import { sysApiMany } from "@cocalc/conat/core/sys";1516dayjs.extend(duration);1718function formatCompactDuration(ms: number): string {19const d = dayjs.duration(ms);2021const hours = d.hours();22const minutes = d.minutes();23const seconds = d.seconds();2425let out = "";26if (d.asDays() >= 1) out += `${Math.floor(d.asDays())}d`;27if (d.asHours() % 24 >= 1) out += `${hours}h`;28if (d.asMinutes() % 60 >= 1) out += `${minutes}m`;29out += `${seconds}s`;30return out;31}3233interface Options {34client: Client;35maxWait?: number;36maxMessages?: number;37}3839// cd packages/backend; pnpm conat-connections40export async function usage({ client, maxWait = 3000, maxMessages }: Options) {41const sys = sysApiMany(client, {42maxWait,43maxMessages,44});45const data = await sys.usage();46const rows: any[] = [];47const perServerRows: any[] = [];48let total = 0;49for await (const X of data) {50for (const server in X) {51const { perUser, total: total0 } = X[server];52perServerRows.push([server, total0]);53total += total0;5455for (const user in perUser) {56rows.push([server, user, perUser[user]]);57}58}59}60rows.sort(field_cmp("2"));61rows.push(["", "", ""]);62rows.push(["TOTAL", "", total]);6364const tablePerUser = new AsciiTable3(`${total} Connections`)65.setHeading("Server", "User", "Connections")66.addRowMatrix(rows);67const tablePerServer = new AsciiTable3(`Connections Per Server`)68.setHeading("Server", "Connections")69.addRowMatrix(perServerRows);7071tablePerUser.setStyle("unicode-round");72tablePerServer.setStyle("unicode-round");7374return [tablePerServer, tablePerUser];75}7677export async function stats({ client, maxWait = 3000, maxMessages }: Options) {78const sys = sysApiMany(client, {79maxWait,80maxMessages,81});82const data = await sys.stats();8384const rows: any[] = [];85const cols = 10;86const totals = Array(cols).fill(0);87for await (const X of data) {88for (const server in X) {89const stats = X[server];90for (const id in stats) {91const x = stats[id];92let user;93if (x.user?.error) {94user = x.user.error;95} else {96user = JSON.stringify(x.user).slice(1, -1);97}98const uptime = x.connected99? formatCompactDuration(Date.now() - x.connected)100: 0;101rows.push([102id,103user,104server,105x.address,106uptime,107x.recv.messages,108x.send.messages,109human_readable_size(x.recv.bytes),110human_readable_size(x.send.bytes),111x.subs,112]);113totals[cols - 5] += x.recv.messages;114totals[cols - 4] += x.send.messages;115totals[cols - 3] += x.recv.bytes;116totals[cols - 2] += x.send.bytes;117totals[cols - 1] += x.subs;118}119}120}121rows.sort(field_cmp(`${cols - 1}`));122rows.push(Array(cols).fill(""));123rows.push([124"TOTALS",125`Total for ${rows.length - 1} connections:`,126...Array(cols - 7).fill(""),127totals[cols - 5],128totals[cols - 4],129human_readable_size(totals[cols - 3]),130human_readable_size(totals[cols - 2]),131totals[cols - 1],132]);133134const table = new AsciiTable3(`${rows.length - 2} Conat Connections`)135.setHeading(136"ID",137"User",138"Server",139"Address",140"Uptime",141"In Msgs",142"Out Msgs",143"In Bytes",144"Out Bytes",145"Subs",146)147.addRowMatrix(rows);148149table.setStyle("unicode-round");150return [table];151}152153export async function showUsersAndStats({154client,155maxWait = 3000,156maxMessages,157}: Options) {158let s;159if (maxMessages) {160s = `for up ${maxMessages} servers `;161} else {162s = "";163}164console.log(`Gather data ${s}for up to ${maxWait / 1000} seconds...\n\n`);165const X = [usage, stats];166const tables: any[] = [];167const f = async (i) => {168for (const table of await X[i]({ client, maxWait, maxMessages })) {169tables.push(table);170}171};172await Promise.all([f(0), f(1)]);173for (const table of tables) {174console.log(table.toString());175}176}177178179