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