badmfck-api-server 5.0.1 → 5.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apiServer/MonitorService.d.ts +14 -0
- package/dist/apiServer/MonitorService.js +114 -20
- package/dist/apiServer/documentation/index.html +43 -43
- package/dist/apiServer/helper/Validator.js +4 -0
- package/dist/apiServer/http/Http.d.ts +7 -0
- package/dist/apiServer/http/Http.js +33 -6
- package/dist/apiServer/monitor/index.html +72 -56
- package/package.json +1 -1
|
@@ -15,6 +15,11 @@ export declare const S_STAT_REGISTRATE_SERVICE: Signal<string>;
|
|
|
15
15
|
export declare const S_STAT_REGISTRATE_HTTP_REQUEST: Signal<{
|
|
16
16
|
url: string;
|
|
17
17
|
}>;
|
|
18
|
+
export declare const S_STAT_REGISTRATE_HTTP_RESULT: Signal<{
|
|
19
|
+
host: string;
|
|
20
|
+
ok: boolean;
|
|
21
|
+
ms: number;
|
|
22
|
+
}>;
|
|
18
23
|
export declare const S_STAT_REGISTRATE_SQL_REQUEST: Signal<{
|
|
19
24
|
success: boolean;
|
|
20
25
|
query: string;
|
|
@@ -39,6 +44,9 @@ type IMonitorSlot = {
|
|
|
39
44
|
successEp: Record<string, number>;
|
|
40
45
|
failEp: Record<string, number>;
|
|
41
46
|
http: Record<string, number>;
|
|
47
|
+
http_ok: Record<string, number>;
|
|
48
|
+
http_fail: Record<string, number>;
|
|
49
|
+
httpP95: Record<string, number>;
|
|
42
50
|
success_sql: Record<string, number>;
|
|
43
51
|
fail_sql: Record<string, number>;
|
|
44
52
|
};
|
|
@@ -49,6 +57,7 @@ export declare class MonitorService extends BaseService {
|
|
|
49
57
|
lastTimestamp: number | null;
|
|
50
58
|
private metricsDir;
|
|
51
59
|
private static MAX_WEEKS;
|
|
60
|
+
private static SCHEMA_VERSION;
|
|
52
61
|
private _persistReady;
|
|
53
62
|
private _flushedUpTo;
|
|
54
63
|
private _lastSeenWeekKey;
|
|
@@ -67,12 +76,17 @@ export declare class MonitorService extends BaseService {
|
|
|
67
76
|
static normalizeUrl(url: string): string;
|
|
68
77
|
private bumpCappedKey;
|
|
69
78
|
private onHttpRequestRegistrate;
|
|
79
|
+
private _latByMinute;
|
|
80
|
+
private static LAT_CAP_PER_MIN;
|
|
81
|
+
private onHttpResultRegistrate;
|
|
82
|
+
private finalizeLatency;
|
|
70
83
|
private onSQLRequestRegistrate;
|
|
71
84
|
private getSlot;
|
|
72
85
|
private liveFile;
|
|
73
86
|
private archiveFile;
|
|
74
87
|
private weekKey;
|
|
75
88
|
private atomicWrite;
|
|
89
|
+
private normalizeSlot;
|
|
76
90
|
private readJsonl;
|
|
77
91
|
private persistTick;
|
|
78
92
|
private rotate;
|
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.MonitorService = exports.REQ_STAT_WEEK = exports.REQ_STAT_WEEKS = exports.REQ_STAT = exports.S_STAT_REGISTRATE_SQL_REQUEST = exports.S_STAT_REGISTRATE_HTTP_REQUEST = exports.S_STAT_REGISTRATE_SERVICE = exports.S_STAT_REGISTRATE_CUSTOM_EVENT = exports.S_STAT_REGISTRATE_REQUEST = void 0;
|
|
39
|
+
exports.MonitorService = exports.REQ_STAT_WEEK = exports.REQ_STAT_WEEKS = exports.REQ_STAT = exports.S_STAT_REGISTRATE_SQL_REQUEST = exports.S_STAT_REGISTRATE_HTTP_RESULT = exports.S_STAT_REGISTRATE_HTTP_REQUEST = exports.S_STAT_REGISTRATE_SERVICE = exports.S_STAT_REGISTRATE_CUSTOM_EVENT = exports.S_STAT_REGISTRATE_REQUEST = void 0;
|
|
40
40
|
const badmfck_signal_1 = __importStar(require("badmfck-signal"));
|
|
41
41
|
const BaseService_1 = require("./BaseService");
|
|
42
42
|
const YYYYMMDDHH_1 = require("./helper/YYYYMMDDHH");
|
|
@@ -48,6 +48,7 @@ exports.S_STAT_REGISTRATE_REQUEST = new badmfck_signal_1.default();
|
|
|
48
48
|
exports.S_STAT_REGISTRATE_CUSTOM_EVENT = new badmfck_signal_1.default();
|
|
49
49
|
exports.S_STAT_REGISTRATE_SERVICE = new badmfck_signal_1.default();
|
|
50
50
|
exports.S_STAT_REGISTRATE_HTTP_REQUEST = new badmfck_signal_1.default();
|
|
51
|
+
exports.S_STAT_REGISTRATE_HTTP_RESULT = new badmfck_signal_1.default();
|
|
51
52
|
exports.S_STAT_REGISTRATE_SQL_REQUEST = new badmfck_signal_1.default();
|
|
52
53
|
exports.REQ_STAT = new badmfck_signal_1.Req(undefined, "REQ_STAT");
|
|
53
54
|
exports.REQ_STAT_WEEKS = new badmfck_signal_1.Req(undefined, "REQ_STAT_WEEKS");
|
|
@@ -59,6 +60,7 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
59
60
|
lastTimestamp = null;
|
|
60
61
|
metricsDir;
|
|
61
62
|
static MAX_WEEKS = 10;
|
|
63
|
+
static SCHEMA_VERSION = 2;
|
|
62
64
|
_persistReady = false;
|
|
63
65
|
_flushedUpTo = 0;
|
|
64
66
|
_lastSeenWeekKey = null;
|
|
@@ -73,6 +75,7 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
73
75
|
exports.REQ_STAT_WEEKS.listener = async () => this.listPersistedWeeks();
|
|
74
76
|
exports.REQ_STAT_WEEK.listener = async (req) => this.readPersistedWeek(req.weekKey);
|
|
75
77
|
exports.S_STAT_REGISTRATE_HTTP_REQUEST.subscribe((data) => this.onHttpRequestRegistrate(data));
|
|
78
|
+
exports.S_STAT_REGISTRATE_HTTP_RESULT.subscribe((data) => this.onHttpResultRegistrate(data));
|
|
76
79
|
exports.S_STAT_REGISTRATE_SQL_REQUEST.subscribe((data) => this.onSQLRequestRegistrate(data));
|
|
77
80
|
exports.S_STAT_REGISTRATE_REQUEST.subscribe((data) => this.onStatRegistrate(data));
|
|
78
81
|
if (this.metricsDir) {
|
|
@@ -124,30 +127,38 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
124
127
|
successEp: {},
|
|
125
128
|
failEp: {},
|
|
126
129
|
http: {},
|
|
130
|
+
http_ok: {},
|
|
131
|
+
http_fail: {},
|
|
132
|
+
httpP95: {},
|
|
127
133
|
success_sql: {},
|
|
128
134
|
fail_sql: {}
|
|
129
135
|
};
|
|
136
|
+
const sum = (dst, src) => {
|
|
137
|
+
if (!src)
|
|
138
|
+
return;
|
|
139
|
+
for (const [k, v] of Object.entries(src))
|
|
140
|
+
dst[k] = (dst[k] || 0) + v;
|
|
141
|
+
};
|
|
142
|
+
const maxInto = (dst, src) => {
|
|
143
|
+
if (!src)
|
|
144
|
+
return;
|
|
145
|
+
for (const [k, v] of Object.entries(src))
|
|
146
|
+
dst[k] = Math.max(dst[k] || 0, v);
|
|
147
|
+
};
|
|
130
148
|
chunk.forEach(s => {
|
|
131
149
|
agg.cpu += s.cpu;
|
|
132
150
|
agg.memory.rss += s.memory.rss;
|
|
133
151
|
agg.memory.heapUsed += s.memory.heapUsed;
|
|
134
152
|
agg.memory.heapTotal += s.memory.heapTotal;
|
|
135
153
|
agg.memory.external += s.memory.external;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
for (const [query, count] of Object.entries(s.success_sql)) {
|
|
146
|
-
agg.success_sql[query] = (agg.success_sql[query] || 0) + count;
|
|
147
|
-
}
|
|
148
|
-
for (const [query, count] of Object.entries(s.fail_sql)) {
|
|
149
|
-
agg.fail_sql[query] = (agg.fail_sql[query] || 0) + count;
|
|
150
|
-
}
|
|
154
|
+
sum(agg.successEp, s.successEp);
|
|
155
|
+
sum(agg.failEp, s.failEp);
|
|
156
|
+
sum(agg.http, s.http);
|
|
157
|
+
sum(agg.http_ok, s.http_ok);
|
|
158
|
+
sum(agg.http_fail, s.http_fail);
|
|
159
|
+
sum(agg.success_sql, s.success_sql);
|
|
160
|
+
sum(agg.fail_sql, s.fail_sql);
|
|
161
|
+
maxInto(agg.httpP95, s.httpP95);
|
|
151
162
|
});
|
|
152
163
|
const len = chunk.length;
|
|
153
164
|
agg.cpu = Number((agg.cpu / len).toFixed(2));
|
|
@@ -181,6 +192,7 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
181
192
|
const usage = process.cpuUsage();
|
|
182
193
|
const now = Date.now();
|
|
183
194
|
const yyyymmddhhmm = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(new Date(now), "yyyymmddhhmm"));
|
|
195
|
+
this.finalizeLatency(yyyymmddhhmm);
|
|
184
196
|
const slot = this.getSlot(yyyymmddhhmm);
|
|
185
197
|
if (this.lastCpuUsage && this.lastTimestamp) {
|
|
186
198
|
const diff = usage.user - this.lastCpuUsage.user;
|
|
@@ -239,6 +251,42 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
239
251
|
const slot = this.getSlot(yyyymmddhhmm);
|
|
240
252
|
this.bumpCappedKey(slot.http, MonitorService.normalizeUrl(data.url));
|
|
241
253
|
}
|
|
254
|
+
_latByMinute = new Map();
|
|
255
|
+
static LAT_CAP_PER_MIN = 3000;
|
|
256
|
+
onHttpResultRegistrate(data) {
|
|
257
|
+
const minuteKey = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(new Date(), "yyyymmddhhmm"));
|
|
258
|
+
const slot = this.getSlot(minuteKey);
|
|
259
|
+
this.bumpCappedKey(data.ok ? slot.http_ok : slot.http_fail, data.host);
|
|
260
|
+
let hosts = this._latByMinute.get(minuteKey);
|
|
261
|
+
if (!hosts) {
|
|
262
|
+
hosts = new Map();
|
|
263
|
+
this._latByMinute.set(minuteKey, hosts);
|
|
264
|
+
}
|
|
265
|
+
let arr = hosts.get(data.host);
|
|
266
|
+
if (!arr) {
|
|
267
|
+
arr = [];
|
|
268
|
+
hosts.set(data.host, arr);
|
|
269
|
+
}
|
|
270
|
+
if (arr.length < MonitorService.LAT_CAP_PER_MIN)
|
|
271
|
+
arr.push(data.ms);
|
|
272
|
+
}
|
|
273
|
+
finalizeLatency(currentMinute, includeCurrent = false) {
|
|
274
|
+
for (const [minuteKey, hosts] of this._latByMinute) {
|
|
275
|
+
if (includeCurrent ? minuteKey > currentMinute : minuteKey >= currentMinute)
|
|
276
|
+
continue;
|
|
277
|
+
const slot = this.slots.get(Math.floor(minuteKey / 100))?.get(minuteKey);
|
|
278
|
+
if (slot) {
|
|
279
|
+
for (const [host, samples] of hosts) {
|
|
280
|
+
if (!samples.length)
|
|
281
|
+
continue;
|
|
282
|
+
samples.sort((a, b) => a - b);
|
|
283
|
+
const idx = Math.min(samples.length - 1, Math.max(0, Math.ceil(0.95 * samples.length) - 1));
|
|
284
|
+
slot.httpP95[host] = samples[idx];
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
this._latByMinute.delete(minuteKey);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
242
290
|
onSQLRequestRegistrate(data) {
|
|
243
291
|
const date = new Date();
|
|
244
292
|
const yyyymmddhhmm = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(date, "yyyymmddhhmm"));
|
|
@@ -272,6 +320,9 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
272
320
|
successEp: {},
|
|
273
321
|
failEp: {},
|
|
274
322
|
http: {},
|
|
323
|
+
http_ok: {},
|
|
324
|
+
http_fail: {},
|
|
325
|
+
httpP95: {},
|
|
275
326
|
success_sql: {},
|
|
276
327
|
fail_sql: {}
|
|
277
328
|
};
|
|
@@ -300,6 +351,31 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
300
351
|
await promises_1.default.writeFile(tmp, content, "utf-8");
|
|
301
352
|
await promises_1.default.rename(tmp, file);
|
|
302
353
|
}
|
|
354
|
+
normalizeSlot(raw) {
|
|
355
|
+
if (!raw || typeof raw !== "object")
|
|
356
|
+
return null;
|
|
357
|
+
const key = Number(raw.yyyymmddhhii);
|
|
358
|
+
if (!Number.isFinite(key))
|
|
359
|
+
return null;
|
|
360
|
+
const num = (v) => (typeof v === "number" && Number.isFinite(v) ? v : 0);
|
|
361
|
+
const rec = (v) => {
|
|
362
|
+
const out = {};
|
|
363
|
+
if (v && typeof v === "object" && !Array.isArray(v))
|
|
364
|
+
for (const [k, val] of Object.entries(v))
|
|
365
|
+
if (typeof val === "number" && Number.isFinite(val))
|
|
366
|
+
out[k] = val;
|
|
367
|
+
return out;
|
|
368
|
+
};
|
|
369
|
+
const m = raw.memory && typeof raw.memory === "object" ? raw.memory : {};
|
|
370
|
+
return {
|
|
371
|
+
yyyymmddhhii: key,
|
|
372
|
+
cpu: num(raw.cpu),
|
|
373
|
+
memory: { rss: num(m.rss), heapUsed: num(m.heapUsed), heapTotal: num(m.heapTotal), external: num(m.external) },
|
|
374
|
+
successEp: rec(raw.successEp), failEp: rec(raw.failEp),
|
|
375
|
+
http: rec(raw.http), http_ok: rec(raw.http_ok), http_fail: rec(raw.http_fail), httpP95: rec(raw.httpP95),
|
|
376
|
+
success_sql: rec(raw.success_sql), fail_sql: rec(raw.fail_sql),
|
|
377
|
+
};
|
|
378
|
+
}
|
|
303
379
|
async readJsonl(file) {
|
|
304
380
|
const txt = await promises_1.default.readFile(file, "utf-8");
|
|
305
381
|
const out = [];
|
|
@@ -308,7 +384,9 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
308
384
|
if (!t)
|
|
309
385
|
continue;
|
|
310
386
|
try {
|
|
311
|
-
|
|
387
|
+
const s = this.normalizeSlot(JSON.parse(t));
|
|
388
|
+
if (s)
|
|
389
|
+
out.push(s);
|
|
312
390
|
}
|
|
313
391
|
catch { }
|
|
314
392
|
}
|
|
@@ -318,6 +396,7 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
318
396
|
if (!this.metricsDir)
|
|
319
397
|
return;
|
|
320
398
|
const nowKey = parseInt((0, YYYYMMDDHH_1.YYYYMMDDHH)(new Date(), "yyyymmddhhmm"));
|
|
399
|
+
this.finalizeLatency(nowKey, includeCurrentMinute);
|
|
321
400
|
const pending = [];
|
|
322
401
|
for (const dayMap of this.slots.values()) {
|
|
323
402
|
for (const slot of dayMap.values()) {
|
|
@@ -404,19 +483,28 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
404
483
|
const hourly = Array.from(byHour.entries())
|
|
405
484
|
.sort((a, b) => a[0] - b[0])
|
|
406
485
|
.map(([hourKey, arr]) => this.mergeSlots(hourKey, arr));
|
|
407
|
-
await this.atomicWrite(this.archiveFile(weekKey), JSON.stringify({ version:
|
|
486
|
+
await this.atomicWrite(this.archiveFile(weekKey), JSON.stringify({ version: MonitorService.SCHEMA_VERSION, weekKey, resolution: "hour", slots: hourly }));
|
|
408
487
|
await promises_1.default.unlink(live).catch(() => { });
|
|
409
488
|
}
|
|
410
489
|
mergeSlots(key, arr) {
|
|
411
490
|
const out = {
|
|
412
491
|
yyyymmddhhii: key, cpu: 0,
|
|
413
492
|
memory: { rss: 0, heapUsed: 0, heapTotal: 0, external: 0 },
|
|
414
|
-
successEp: {}, failEp: {}, http: {},
|
|
493
|
+
successEp: {}, failEp: {}, http: {}, http_ok: {}, http_fail: {}, httpP95: {},
|
|
494
|
+
success_sql: {}, fail_sql: {},
|
|
415
495
|
};
|
|
416
496
|
const add = (dst, src) => {
|
|
497
|
+
if (!src)
|
|
498
|
+
return;
|
|
417
499
|
for (const [k, v] of Object.entries(src))
|
|
418
500
|
dst[k] = (dst[k] || 0) + v;
|
|
419
501
|
};
|
|
502
|
+
const maxInto = (dst, src) => {
|
|
503
|
+
if (!src)
|
|
504
|
+
return;
|
|
505
|
+
for (const [k, v] of Object.entries(src))
|
|
506
|
+
dst[k] = Math.max(dst[k] || 0, v);
|
|
507
|
+
};
|
|
420
508
|
for (const s of arr) {
|
|
421
509
|
out.cpu += s.cpu;
|
|
422
510
|
out.memory.rss += s.memory.rss;
|
|
@@ -426,8 +514,11 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
426
514
|
add(out.successEp, s.successEp);
|
|
427
515
|
add(out.failEp, s.failEp);
|
|
428
516
|
add(out.http, s.http);
|
|
517
|
+
add(out.http_ok, s.http_ok);
|
|
518
|
+
add(out.http_fail, s.http_fail);
|
|
429
519
|
add(out.success_sql, s.success_sql);
|
|
430
520
|
add(out.fail_sql, s.fail_sql);
|
|
521
|
+
maxInto(out.httpP95, s.httpP95);
|
|
431
522
|
}
|
|
432
523
|
const n = arr.length || 1;
|
|
433
524
|
out.cpu = Number((out.cpu / n).toFixed(2));
|
|
@@ -506,7 +597,10 @@ class MonitorService extends BaseService_1.BaseService {
|
|
|
506
597
|
}
|
|
507
598
|
catch {
|
|
508
599
|
const txt = await promises_1.default.readFile(this.archiveFile(weekKey), "utf-8");
|
|
509
|
-
|
|
600
|
+
const rawSlots = JSON.parse(txt)?.slots;
|
|
601
|
+
slots = Array.isArray(rawSlots)
|
|
602
|
+
? rawSlots.map((r) => this.normalizeSlot(r)).filter((s) => s !== null)
|
|
603
|
+
: [];
|
|
510
604
|
}
|
|
511
605
|
}
|
|
512
606
|
catch {
|