badmfck-api-server 5.0.1 → 5.0.3

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.
@@ -595,11 +595,12 @@ class APIService extends BaseService_1.BaseService {
595
595
  checkDataLength(data, result, lvl) {
596
596
  let json = "";
597
597
  try {
598
- json = JSON.stringify(data, (k, v) => {
598
+ const s = JSON.stringify(data, (k, v) => {
599
599
  if (typeof v === 'string' && v.length > 255)
600
600
  return v.slice(0, 255) + `...(${v.length})`;
601
601
  return v;
602
602
  });
603
+ json = typeof s === "string" ? s : "";
603
604
  }
604
605
  catch (e) {
605
606
  json = "[unserializable data]";
@@ -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
- for (const [path, count] of Object.entries(s.successEp)) {
137
- agg.successEp[path] = (agg.successEp[path] || 0) + count;
138
- }
139
- for (const [path, count] of Object.entries(s.failEp)) {
140
- agg.failEp[path] = (agg.failEp[path] || 0) + count;
141
- }
142
- for (const [url, count] of Object.entries(s.http)) {
143
- agg.http[url] = (agg.http[url] || 0) + count;
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
- out.push(JSON.parse(t));
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: 1, weekKey, resolution: "hour", slots: hourly }));
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: {}, success_sql: {}, fail_sql: {},
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
- slots = (JSON.parse(txt)?.slots ?? []);
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 {