badmfck-api-server 4.1.25 → 4.1.27

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.
@@ -13,6 +13,60 @@ export type THttpErr<E = any> = {
13
13
  details?: any;
14
14
  };
15
15
  export type THttpResult<T, E = any> = THttpOk<T> | THttpErr<E>;
16
+ export type THttpOutgoingLogItem = {
17
+ id: number;
18
+ created: number;
19
+ time: number;
20
+ method: HttpMethod;
21
+ url: string;
22
+ status?: number;
23
+ ok: boolean;
24
+ attempts: number;
25
+ request: any;
26
+ response: any;
27
+ error?: string;
28
+ };
29
+ export type THttpHostStats = {
30
+ host: string;
31
+ count: number;
32
+ errors: number;
33
+ timeouts: number;
34
+ retries: number;
35
+ avgMs: number;
36
+ minMs: number;
37
+ maxMs: number;
38
+ totalTimeMs: number;
39
+ inFlight: number;
40
+ byStatus: {
41
+ s2xx: number;
42
+ s3xx: number;
43
+ s4xx: number;
44
+ s5xx: number;
45
+ other: number;
46
+ networkErrors: number;
47
+ };
48
+ lastRequestAt: number;
49
+ };
50
+ export type THttpStatusDistribution = {
51
+ s2xx: number;
52
+ s3xx: number;
53
+ s4xx: number;
54
+ s5xx: number;
55
+ other: number;
56
+ networkErrors: number;
57
+ timeouts: number;
58
+ };
59
+ export type THttpStatsSnapshot = {
60
+ totalRequests: number;
61
+ inFlight: {
62
+ current: number;
63
+ peak: number;
64
+ byHost: Record<string, number>;
65
+ };
66
+ statusDistribution: THttpStatusDistribution;
67
+ perHost: THttpHostStats[];
68
+ topSlow: THttpOutgoingLogItem[];
69
+ };
16
70
  export type TRetryPolicy = {
17
71
  enabled?: boolean;
18
72
  maxAttempts?: number;
@@ -71,6 +125,27 @@ export declare class Http {
71
125
  private static _activeConnectionsByHost;
72
126
  private static _countedSockets;
73
127
  private static _socketHostKey;
128
+ private static _outgoingLog;
129
+ private static _nextOutgoingLogID;
130
+ private static _outgoingLogLimit;
131
+ private static _truncate;
132
+ private static _pushOutgoingLog;
133
+ static getOutgoingLogSnapshot(): THttpOutgoingLogItem[];
134
+ private static _totalRequests;
135
+ private static _inFlight;
136
+ private static _peakInFlight;
137
+ private static _inFlightByHost;
138
+ private static _statusDist;
139
+ private static _perHost;
140
+ private static _topSlow;
141
+ private static _topSlowLimit;
142
+ private static _hostFromUrl;
143
+ private static _isTimeoutError;
144
+ private static _incInFlight;
145
+ private static _decInFlight;
146
+ private static _updateStats;
147
+ private static _pushToTopSlow;
148
+ static getHttpStats(): THttpStatsSnapshot;
74
149
  private static ensureInit;
75
150
  static configure(partial: Partial<THttpConfig>): void;
76
151
  static createAgent(opts?: ConstructorParameters<typeof Agent>[0]): Agent;
@@ -88,6 +88,179 @@ class Http {
88
88
  static _activeConnectionsByHost = new Map();
89
89
  static _countedSockets = new WeakSet();
90
90
  static _socketHostKey = new WeakMap();
91
+ static _outgoingLog = [];
92
+ static _nextOutgoingLogID = 0;
93
+ static _outgoingLogLimit = 500;
94
+ static _truncate(v) {
95
+ if (v === undefined || v === null)
96
+ return v;
97
+ if (Buffer.isBuffer(v))
98
+ return `[Buffer ${v.length} bytes]`;
99
+ let json = "";
100
+ try {
101
+ json = JSON.stringify(v, (_, val) => {
102
+ if (typeof val === "string" && val.length > 255)
103
+ return val.slice(0, 255) + `...(${val.length})`;
104
+ if (Buffer.isBuffer(val))
105
+ return `[Buffer ${val.length} bytes]`;
106
+ return val;
107
+ });
108
+ }
109
+ catch {
110
+ return "[unserializable]";
111
+ }
112
+ return json.length > 500 ? json.slice(0, 500) + `...(total ${json.length})` : json;
113
+ }
114
+ static _pushOutgoingLog(item) {
115
+ const entry = { id: this._nextOutgoingLogID++, ...item };
116
+ this._outgoingLog.push(entry);
117
+ if (this._outgoingLog.length > this._outgoingLogLimit)
118
+ this._outgoingLog.shift();
119
+ if (this._nextOutgoingLogID > Number.MAX_SAFE_INTEGER - 1000)
120
+ this._nextOutgoingLogID = 0;
121
+ return entry;
122
+ }
123
+ static getOutgoingLogSnapshot() {
124
+ return this._outgoingLog.slice();
125
+ }
126
+ static _totalRequests = 0;
127
+ static _inFlight = 0;
128
+ static _peakInFlight = 0;
129
+ static _inFlightByHost = new Map();
130
+ static _statusDist = {
131
+ s2xx: 0, s3xx: 0, s4xx: 0, s5xx: 0, other: 0, networkErrors: 0, timeouts: 0,
132
+ };
133
+ static _perHost = new Map();
134
+ static _topSlow = [];
135
+ static _topSlowLimit = 20;
136
+ static _hostFromUrl(url) {
137
+ try {
138
+ return new URL(url).host || "unknown";
139
+ }
140
+ catch {
141
+ return "unknown";
142
+ }
143
+ }
144
+ static _isTimeoutError(err) {
145
+ if (!err || typeof err !== "object")
146
+ return false;
147
+ const e = err;
148
+ if (e.name === "AbortError")
149
+ return true;
150
+ if (typeof e.code === "string" && e.code.includes("TIMEOUT"))
151
+ return true;
152
+ return false;
153
+ }
154
+ static _incInFlight(host) {
155
+ this._inFlight++;
156
+ if (this._inFlight > this._peakInFlight)
157
+ this._peakInFlight = this._inFlight;
158
+ this._inFlightByHost.set(host, (this._inFlightByHost.get(host) ?? 0) + 1);
159
+ }
160
+ static _decInFlight(host) {
161
+ this._inFlight = Math.max(0, this._inFlight - 1);
162
+ const prev = this._inFlightByHost.get(host) ?? 0;
163
+ const next = Math.max(0, prev - 1);
164
+ if (next === 0)
165
+ this._inFlightByHost.delete(host);
166
+ else
167
+ this._inFlightByHost.set(host, next);
168
+ }
169
+ static _updateStats(host, entry, isTimeout) {
170
+ this._totalRequests++;
171
+ if (typeof entry.status === "number") {
172
+ const s = entry.status;
173
+ if (s >= 200 && s < 300)
174
+ this._statusDist.s2xx++;
175
+ else if (s >= 300 && s < 400)
176
+ this._statusDist.s3xx++;
177
+ else if (s >= 400 && s < 500)
178
+ this._statusDist.s4xx++;
179
+ else if (s >= 500 && s < 600)
180
+ this._statusDist.s5xx++;
181
+ else
182
+ this._statusDist.other++;
183
+ }
184
+ else if (entry.error) {
185
+ this._statusDist.networkErrors++;
186
+ if (isTimeout)
187
+ this._statusDist.timeouts++;
188
+ }
189
+ let h = this._perHost.get(host);
190
+ if (!h) {
191
+ h = {
192
+ host,
193
+ count: 0, errors: 0, timeouts: 0, retries: 0,
194
+ avgMs: 0, minMs: Infinity, maxMs: 0, totalTimeMs: 0,
195
+ inFlight: 0,
196
+ byStatus: { s2xx: 0, s3xx: 0, s4xx: 0, s5xx: 0, other: 0, networkErrors: 0 },
197
+ lastRequestAt: 0,
198
+ };
199
+ this._perHost.set(host, h);
200
+ }
201
+ h.count++;
202
+ h.totalTimeMs += entry.time;
203
+ h.avgMs = Math.round((h.totalTimeMs / h.count) * 10) / 10;
204
+ if (entry.time < h.minMs)
205
+ h.minMs = entry.time;
206
+ if (entry.time > h.maxMs)
207
+ h.maxMs = entry.time;
208
+ if (!entry.ok)
209
+ h.errors++;
210
+ if (isTimeout)
211
+ h.timeouts++;
212
+ h.retries += Math.max(0, entry.attempts - 1);
213
+ h.lastRequestAt = Date.now();
214
+ if (typeof entry.status === "number") {
215
+ const s = entry.status;
216
+ if (s >= 200 && s < 300)
217
+ h.byStatus.s2xx++;
218
+ else if (s >= 300 && s < 400)
219
+ h.byStatus.s3xx++;
220
+ else if (s >= 400 && s < 500)
221
+ h.byStatus.s4xx++;
222
+ else if (s >= 500 && s < 600)
223
+ h.byStatus.s5xx++;
224
+ else
225
+ h.byStatus.other++;
226
+ }
227
+ else if (entry.error) {
228
+ h.byStatus.networkErrors++;
229
+ }
230
+ }
231
+ static _pushToTopSlow(entry) {
232
+ if (this._topSlow.length < this._topSlowLimit) {
233
+ this._topSlow.push(entry);
234
+ this._topSlow.sort((a, b) => b.time - a.time);
235
+ return;
236
+ }
237
+ const slowest_of_top = this._topSlow[this._topSlow.length - 1];
238
+ if (entry.time > slowest_of_top.time) {
239
+ this._topSlow.push(entry);
240
+ this._topSlow.sort((a, b) => b.time - a.time);
241
+ this._topSlow.pop();
242
+ }
243
+ }
244
+ static getHttpStats() {
245
+ const perHost = Array.from(this._perHost.values())
246
+ .map(h => ({
247
+ ...h,
248
+ minMs: Number.isFinite(h.minMs) ? h.minMs : 0,
249
+ inFlight: this._inFlightByHost.get(h.host) ?? 0,
250
+ }))
251
+ .sort((a, b) => b.count - a.count);
252
+ return {
253
+ totalRequests: this._totalRequests,
254
+ inFlight: {
255
+ current: this._inFlight,
256
+ peak: this._peakInFlight,
257
+ byHost: Object.fromEntries(this._inFlightByHost),
258
+ },
259
+ statusDistribution: { ...this._statusDist },
260
+ perHost,
261
+ topSlow: this._topSlow.slice(),
262
+ };
263
+ }
91
264
  static ensureInit() {
92
265
  if (this._inited)
93
266
  return;
@@ -230,6 +403,7 @@ class Http {
230
403
  static async request(opts) {
231
404
  MonitorService_1.S_STAT_REGISTRATE_HTTP_REQUEST.invoke({ url: opts.url });
232
405
  this.ensureInit();
406
+ const overallStarted = Date.now();
233
407
  const cfg = this.config;
234
408
  const retryCfg = {
235
409
  enabled: opts.retry?.enabled ?? cfg.retry?.enabled ?? true,
@@ -263,7 +437,30 @@ class Http {
263
437
  }
264
438
  const responseType = opts.responseType ?? "json";
265
439
  const throwOnJsonParseError = opts.throwOnJsonParseError ?? false;
440
+ let attemptsMade = 0;
441
+ const host = Http._hostFromUrl(url);
442
+ Http._incInFlight(host);
443
+ const finish = (result, networkErr) => {
444
+ Http._decInFlight(host);
445
+ const entry = Http._pushOutgoingLog({
446
+ created: overallStarted,
447
+ time: Date.now() - overallStarted,
448
+ method: opts.method,
449
+ url,
450
+ status: result.status,
451
+ ok: result.ok,
452
+ attempts: attemptsMade,
453
+ request: Http._truncate(opts.body),
454
+ response: Http._truncate(result.ok ? result.data : result.error),
455
+ error: networkErr ? String(networkErr?.message ?? networkErr) : undefined,
456
+ });
457
+ const isTimeout = Http._isTimeoutError(networkErr);
458
+ Http._updateStats(host, entry, isTimeout);
459
+ Http._pushToTopSlow(entry);
460
+ return result;
461
+ };
266
462
  for (let attempt = 1; attempt <= retryCfg.maxAttempts; attempt++) {
463
+ attemptsMade = attempt;
267
464
  const started = Date.now();
268
465
  cfg.onRequest?.({ method: opts.method, url, attempt });
269
466
  const controller = new AbortController();
@@ -307,7 +504,7 @@ class Http {
307
504
  }
308
505
  }
309
506
  if (status >= 200 && status < 300) {
310
- return { ok: true, status, data: parsed, headers: hdrs };
507
+ return finish({ ok: true, status, data: parsed, headers: hdrs });
311
508
  }
312
509
  const retryable = retryCfg.enabled &&
313
510
  isRetryableStatus(status, retryCfg.retryOnStatuses) &&
@@ -317,7 +514,7 @@ class Http {
317
514
  await sleep(backoff);
318
515
  continue;
319
516
  }
320
- return { ok: false, status, error: parsed, details: { headers: hdrs } };
517
+ return finish({ ok: false, status, error: parsed, details: { headers: hdrs } });
321
518
  }
322
519
  catch (err) {
323
520
  const ms = Date.now() - started;
@@ -331,13 +528,13 @@ class Http {
331
528
  await sleep(backoff);
332
529
  continue;
333
530
  }
334
- return { ok: false, error: err, details: { network: true } };
531
+ return finish({ ok: false, error: err, details: { network: true } }, err);
335
532
  }
336
533
  finally {
337
534
  clearTimeout(t);
338
535
  }
339
536
  }
340
- return { ok: false, error: "UNKNOWN" };
537
+ return finish({ ok: false, error: "UNKNOWN" });
341
538
  }
342
539
  static get(url, opts) {
343
540
  return this.request({ ...(opts || {}), method: "GET", url });
@@ -20,6 +20,11 @@ export declare class Monitor extends BaseEndpoint {
20
20
  changeLogLevel(req: HTTPRequestVO): Promise<TransferPacketVO>;
21
21
  logs(req: HTTPRequestVO): Promise<TransferPacketVO>;
22
22
  netlog(req: HTTPRequestVO): Promise<TransferPacketVO>;
23
+ outgoingNetlog(req: HTTPRequestVO): Promise<TransferPacketVO>;
24
+ sysinfo(req: HTTPRequestVO): Promise<TransferPacketVO>;
25
+ procinfo(req: HTTPRequestVO): Promise<TransferPacketVO>;
26
+ httpstats(req: HTTPRequestVO): Promise<TransferPacketVO>;
27
+ inventory(req: HTTPRequestVO): Promise<TransferPacketVO>;
23
28
  metrics(req: HTTPRequestVO): Promise<TransferPacketVO>;
24
29
  connections(req: HTTPRequestVO): Promise<TransferPacketVO>;
25
30
  db(req: HTTPRequestVO): Promise<TransferPacketVO>;
@@ -7,6 +7,7 @@ exports.Monitor = exports.S_MONITOR_REGISTRATE_ACTION = void 0;
7
7
  const badmfck_signal_1 = require("badmfck-signal");
8
8
  const APIService_1 = require("../APIService");
9
9
  const BaseEndpoint_1 = require("../BaseEndpoint");
10
+ const BaseService_1 = require("../BaseService");
10
11
  const LogService_1 = require("../LogService");
11
12
  const crypto_1 = __importDefault(require("crypto"));
12
13
  const DefaultErrors_1 = __importDefault(require("../structures/DefaultErrors"));
@@ -15,6 +16,8 @@ const path_1 = __importDefault(require("path"));
15
16
  const Http_1 = require("../http/Http");
16
17
  const MonitorService_1 = require("../MonitorService");
17
18
  const DBService_1 = require("../DBService");
19
+ const SystemInfo_1 = require("./SystemInfo");
20
+ const ProcessMonitor_1 = require("./ProcessMonitor");
18
21
  exports.S_MONITOR_REGISTRATE_ACTION = new badmfck_signal_1.Signal();
19
22
  const logMap = {
20
23
  ALL: 10,
@@ -39,6 +42,11 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
39
42
  { ignoreInterceptor: true, endpoint: "html", handler: this.html },
40
43
  { ignoreInterceptor: true, endpoint: "log", handler: this.logs },
41
44
  { ignoreInterceptor: true, endpoint: "netlog", handler: this.netlog },
45
+ { ignoreInterceptor: true, endpoint: "outgoingnetlog", handler: this.outgoingNetlog },
46
+ { ignoreInterceptor: true, endpoint: "sysinfo", handler: this.sysinfo },
47
+ { ignoreInterceptor: true, endpoint: "procinfo", handler: this.procinfo },
48
+ { ignoreInterceptor: true, endpoint: "inventory", handler: this.inventory },
49
+ { ignoreInterceptor: true, endpoint: "httpstats", handler: this.httpstats },
42
50
  { ignoreInterceptor: true, endpoint: "metrics", handler: this.metrics, validationModel: {
43
51
  range: "",
44
52
  $__range_values: ["week", "day", "hour"]
@@ -120,6 +128,48 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
120
128
  const log = await APIService_1.REQ_HTTP_LOG.request(req.data);
121
129
  return { data: log };
122
130
  }
131
+ async outgoingNetlog(req) {
132
+ await this.checkAuthentication(req);
133
+ return { data: Http_1.Http.getOutgoingLogSnapshot() };
134
+ }
135
+ async sysinfo(req) {
136
+ await this.checkAuthentication(req);
137
+ return { data: await (0, SystemInfo_1.collectSystemInfo)() };
138
+ }
139
+ async procinfo(req) {
140
+ await this.checkAuthentication(req);
141
+ return { data: await ProcessMonitor_1.ProcessMonitor.getSnapshot() };
142
+ }
143
+ async httpstats(req) {
144
+ await this.checkAuthentication(req);
145
+ return { data: Http_1.Http.getHttpStats() };
146
+ }
147
+ async inventory(req) {
148
+ await this.checkAuthentication(req);
149
+ const now = Date.now();
150
+ const services = BaseService_1.BaseService.allInstances.map(s => ({
151
+ className: s.constructor.name,
152
+ name: s.getName(),
153
+ initialized: s.initializedAt !== null,
154
+ createdAt: s.createdAt,
155
+ initializedAt: s.initializedAt,
156
+ applicationReadyAt: s.applicationReadyAt,
157
+ uptimeSec: s.initializedAt ? Math.round((now - s.initializedAt) / 1000) : null,
158
+ }));
159
+ const endpoints = BaseEndpoint_1.BaseEndpoint.allInstances.map(e => e.getInventory());
160
+ return {
161
+ data: {
162
+ services,
163
+ endpoints,
164
+ counts: {
165
+ services: services.length,
166
+ endpoints: endpoints.length,
167
+ totalHandlers: endpoints.reduce((sum, e) => sum + e.handlerCount, 0),
168
+ },
169
+ entrypoint: BaseEndpoint_1.BaseEndpoint.getEntryPoint(),
170
+ }
171
+ };
172
+ }
123
173
  async metrics(req) {
124
174
  await this.checkAuthentication(req);
125
175
  const result = await MonitorService_1.REQ_STAT.request(req.data);
@@ -0,0 +1,117 @@
1
+ /// <reference types="node" />
2
+ export declare class ProcessMonitor {
3
+ private static _inited;
4
+ private static _eld;
5
+ private static _gcObserver;
6
+ private static _gc;
7
+ private static _prevCpu;
8
+ private static _prevCpuAt;
9
+ private static _cpuPct;
10
+ private static _eluPrev;
11
+ private static _elu;
12
+ private static _processStartedAt;
13
+ static init(): void;
14
+ private static _sampleCpu;
15
+ private static _sampleELU;
16
+ private static _mapGcKind;
17
+ static getSnapshot(): Promise<{
18
+ identity: {
19
+ pid: number;
20
+ ppid: number;
21
+ title: string;
22
+ argv: string[];
23
+ execPath: string;
24
+ cwd: string;
25
+ nodeVersion: string;
26
+ v8Version: string;
27
+ platform: NodeJS.Platform;
28
+ arch: NodeJS.Architecture;
29
+ hostname: string;
30
+ user: {
31
+ uid: number;
32
+ gid: number;
33
+ } | null;
34
+ startedAtUnix: number;
35
+ uptimeSec: number;
36
+ availableParallelism: number;
37
+ };
38
+ memory: {
39
+ rss: number;
40
+ heapTotal: number;
41
+ heapUsed: number;
42
+ external: number;
43
+ arrayBuffers: any;
44
+ heap: {
45
+ sizeLimit: number;
46
+ usedHeap: number;
47
+ totalAvailable: number;
48
+ totalPhysical: number;
49
+ mallocedMemory: number;
50
+ peakMallocedMemory: number;
51
+ numberOfNativeContexts: number;
52
+ numberOfDetachedContexts: number;
53
+ };
54
+ heapSpaces: {
55
+ name: string;
56
+ size: number;
57
+ used: number;
58
+ available: number;
59
+ physical: number;
60
+ }[];
61
+ procStatus: Record<string, number> | null;
62
+ };
63
+ cpu: {
64
+ userPct: number;
65
+ systemPct: number;
66
+ userMicros: number;
67
+ systemMicros: number;
68
+ resource: any;
69
+ };
70
+ eventLoop: {
71
+ minMs: number;
72
+ maxMs: number;
73
+ meanMs: number;
74
+ stddevMs: number;
75
+ p50Ms: number;
76
+ p90Ms: number;
77
+ p99Ms: number;
78
+ utilizationPct: number;
79
+ activeHandles: number;
80
+ activeRequests: number;
81
+ spotLagMs: number;
82
+ } | null;
83
+ gc: {
84
+ totalCount: number;
85
+ totalTimeMs: number;
86
+ maxPauseMs: number;
87
+ byKind: Record<string, {
88
+ count: number;
89
+ timeMs: number;
90
+ }>;
91
+ lastPauses: {
92
+ kind: string;
93
+ ms: number;
94
+ timestamp: number;
95
+ }[];
96
+ };
97
+ fileDescriptors: {
98
+ open: number | null;
99
+ limitSoft: number | null;
100
+ limitHard: number | null;
101
+ };
102
+ io: Record<string, number> | null;
103
+ limits: Record<string, {
104
+ soft: string;
105
+ hard: string;
106
+ unit: string;
107
+ }> | null;
108
+ }>;
109
+ private static _readProcStatus;
110
+ private static _readProcIo;
111
+ private static _readProcLimits;
112
+ private static _countFds;
113
+ private static _parseLimitValue;
114
+ private static _toMsFinite;
115
+ private static _safePercentileMs;
116
+ private static _safeCount;
117
+ }