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