badmfck-api-server 4.1.53 → 4.1.58
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/APIService.d.ts +3 -4
- package/dist/apiServer/APIService.js +19 -9
- package/dist/apiServer/BaseEndpoint.d.ts +2 -2
- package/dist/apiServer/LocalRequest.d.ts +1 -1
- package/dist/apiServer/LogService.js +17 -7
- package/dist/apiServer/MonitorService.d.ts +0 -1
- package/dist/apiServer/MonitorService.js +17 -7
- package/dist/apiServer/MysqlService.d.ts +1 -2
- package/dist/apiServer/MysqlService.js +17 -7
- package/dist/apiServer/TimeframeService.js +17 -7
- package/dist/apiServer/db/MysqlAdapter.d.ts +1 -1
- package/dist/apiServer/db/MysqlAdapter.js +37 -12
- package/dist/apiServer/deployment/Activate.js +1 -2
- package/dist/apiServer/deployment/ConfigService.d.ts +26 -26
- package/dist/apiServer/deployment/ConfigService.js +17 -7
- package/dist/apiServer/deployment/Deploy.js +1 -2
- package/dist/apiServer/deployment/DeployerService.d.ts +10 -9
- package/dist/apiServer/deployment/DeployerService.js +18 -1
- package/dist/apiServer/deployment/cli.js +37 -14
- package/dist/apiServer/deployment/resolveConfig.js +2 -3
- package/dist/apiServer/documentation/index.html +25 -25
- package/dist/apiServer/external/ExternalService.js +17 -7
- package/dist/apiServer/external/MicroserviceClient.js +17 -7
- package/dist/apiServer/external/MicroserviceHost.js +17 -7
- package/dist/apiServer/external/MicroserviceHostController.js +17 -7
- package/dist/apiServer/helper/JSONStableStringify.js +1 -2
- package/dist/apiServer/helper/YYYYMMDDHH.js +2 -3
- package/dist/apiServer/helper/ZipUtils.d.ts +0 -2
- package/dist/apiServer/monitor/Monitor.d.ts +9 -1
- package/dist/apiServer/monitor/Monitor.js +28 -2
- package/dist/apiServer/monitor/ProcessMonitor.d.ts +61 -1
- package/dist/apiServer/monitor/ProcessMonitor.js +160 -0
- package/dist/apiServer/monitor/SystemInfo.d.ts +7 -8
- package/dist/apiServer/monitor/SystemInfo.js +1 -2
- package/dist/apiServer/monitor/index.html +53 -53
- package/dist/apiServer/structures/Interfaces.d.ts +0 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -1
- package/package.json +1 -1
|
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
37
|
};
|
|
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
37
|
};
|
|
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
37
|
};
|
|
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
36
|
exports.MicroserviceHostController = void 0;
|
|
27
37
|
const BaseEndpoint_1 = require("../BaseEndpoint");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.JSONStableStringify =
|
|
3
|
+
exports.JSONStableStringify = JSONStableStringify;
|
|
4
4
|
function JSONStableStringify(obj) {
|
|
5
5
|
if (obj === null || typeof obj !== "object") {
|
|
6
6
|
return JSON.stringify(obj);
|
|
@@ -13,4 +13,3 @@ function JSONStableStringify(obj) {
|
|
|
13
13
|
keys.map(k => JSON.stringify(k) + ":" + JSONStableStringify(obj[k])).join(",") +
|
|
14
14
|
"}");
|
|
15
15
|
}
|
|
16
|
-
exports.JSONStableStringify = JSONStableStringify;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.YYYYMMDDHH = YYYYMMDDHH;
|
|
4
|
+
exports.YYYYMMDDHH_toDate = YYYYMMDDHH_toDate;
|
|
4
5
|
function YYYYMMDDHH(date, format = "yyyymmddhh") {
|
|
5
6
|
const year = date.getFullYear();
|
|
6
7
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
@@ -20,7 +21,6 @@ function YYYYMMDDHH(date, format = "yyyymmddhh") {
|
|
|
20
21
|
return `${year}${month}${day}${hours}`;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
|
-
exports.YYYYMMDDHH = YYYYMMDDHH;
|
|
24
24
|
function YYYYMMDDHH_toDate(yyyymmddhh) {
|
|
25
25
|
const str = String(yyyymmddhh);
|
|
26
26
|
const year = parseInt(str.slice(0, 4), 10);
|
|
@@ -29,4 +29,3 @@ function YYYYMMDDHH_toDate(yyyymmddhh) {
|
|
|
29
29
|
const hours = parseInt(str.slice(8, 10), 10);
|
|
30
30
|
return new Date(year, month, day, hours);
|
|
31
31
|
}
|
|
32
|
-
exports.YYYYMMDDHH_toDate = YYYYMMDDHH_toDate;
|
|
@@ -6,6 +6,12 @@ export interface IUserAction {
|
|
|
6
6
|
action: string;
|
|
7
7
|
}
|
|
8
8
|
export declare const S_MONITOR_REGISTRATE_ACTION: Signal<IUserAction>;
|
|
9
|
+
export interface IRestartRequest {
|
|
10
|
+
by: string;
|
|
11
|
+
name: string | null;
|
|
12
|
+
pmId: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare const S_MONITOR_RESTART_REQUESTED: Signal<IRestartRequest>;
|
|
9
15
|
export declare class Monitor extends BaseEndpoint {
|
|
10
16
|
ignoreHttpLogging: boolean;
|
|
11
17
|
ignoreInDocumentation: boolean;
|
|
@@ -23,10 +29,12 @@ export declare class Monitor extends BaseEndpoint {
|
|
|
23
29
|
outgoingNetlog(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
24
30
|
sysinfo(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
25
31
|
procinfo(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
32
|
+
fdreport(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
33
|
+
restart(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
26
34
|
httpstats(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
27
35
|
inventory(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
28
36
|
metrics(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
29
37
|
connections(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
30
38
|
db(req: HTTPRequestVO): Promise<TransferPacketVO>;
|
|
31
|
-
checkAuthentication(req: HTTPRequestVO): Promise<
|
|
39
|
+
checkAuthentication(req: HTTPRequestVO): Promise<string>;
|
|
32
40
|
}
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Monitor = exports.S_MONITOR_REGISTRATE_ACTION = void 0;
|
|
6
|
+
exports.Monitor = exports.S_MONITOR_RESTART_REQUESTED = 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");
|
|
@@ -19,6 +19,7 @@ const DBService_1 = require("../DBService");
|
|
|
19
19
|
const SystemInfo_1 = require("./SystemInfo");
|
|
20
20
|
const ProcessMonitor_1 = require("./ProcessMonitor");
|
|
21
21
|
exports.S_MONITOR_REGISTRATE_ACTION = new badmfck_signal_1.Signal();
|
|
22
|
+
exports.S_MONITOR_RESTART_REQUESTED = new badmfck_signal_1.Signal();
|
|
22
23
|
const logMap = {
|
|
23
24
|
ALL: 10,
|
|
24
25
|
API: 19,
|
|
@@ -45,6 +46,10 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
|
|
|
45
46
|
{ ignoreInterceptor: true, endpoint: "outgoingnetlog", handler: this.outgoingNetlog },
|
|
46
47
|
{ ignoreInterceptor: true, endpoint: "sysinfo", handler: this.sysinfo },
|
|
47
48
|
{ ignoreInterceptor: true, endpoint: "procinfo", handler: this.procinfo },
|
|
49
|
+
{ ignoreInterceptor: true, endpoint: "fdreport", handler: this.fdreport },
|
|
50
|
+
{ ignoreInterceptor: true, endpoint: "restart", handler: this.restart, validationModel: {
|
|
51
|
+
confirm: ""
|
|
52
|
+
} },
|
|
48
53
|
{ ignoreInterceptor: true, endpoint: "inventory", handler: this.inventory },
|
|
49
54
|
{ ignoreInterceptor: true, endpoint: "httpstats", handler: this.httpstats },
|
|
50
55
|
{ ignoreInterceptor: true, endpoint: "metrics", handler: this.metrics, validationModel: {
|
|
@@ -140,6 +145,27 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
|
|
|
140
145
|
await this.checkAuthentication(req);
|
|
141
146
|
return { data: await ProcessMonitor_1.ProcessMonitor.getSnapshot() };
|
|
142
147
|
}
|
|
148
|
+
async fdreport(req) {
|
|
149
|
+
await this.checkAuthentication(req);
|
|
150
|
+
return { data: await ProcessMonitor_1.ProcessMonitor.getFdReport() };
|
|
151
|
+
}
|
|
152
|
+
async restart(req) {
|
|
153
|
+
const login = await this.checkAuthentication(req);
|
|
154
|
+
const id = ProcessMonitor_1.ProcessMonitor.getPm2Identity();
|
|
155
|
+
if (!id.managed)
|
|
156
|
+
throw { ...DefaultErrors_1.default.BAD_REQUEST, details: "Process is not managed by pm2 (no pm_id in env) — refusing to exit, nothing would restart it." };
|
|
157
|
+
if (req.data.confirm !== id.name)
|
|
158
|
+
throw { ...DefaultErrors_1.default.BAD_REQUEST, details: `Confirmation mismatch. To restart send { "confirm": "${id.name}" }.` };
|
|
159
|
+
exports.S_MONITOR_RESTART_REQUESTED.invoke({ by: login, name: id.name, pmId: id.pmId });
|
|
160
|
+
exports.S_MONITOR_REGISTRATE_ACTION.invoke({ action: `restart requested by "${login}" for pm2 "${id.name}" (id ${id.pmId})` });
|
|
161
|
+
(0, LogService_1.logInfo)(`Restart requested via monitor by "${login}" for pm2 process "${id.name}" (id ${id.pmId})`, "Monitor");
|
|
162
|
+
const graceMs = 750;
|
|
163
|
+
setTimeout(() => {
|
|
164
|
+
(0, LogService_1.logInfo)(`Exiting now for pm2 restart (requested by "${login}")`, "Monitor");
|
|
165
|
+
process.exit(0);
|
|
166
|
+
}, graceMs);
|
|
167
|
+
return { data: { restarting: true, name: id.name, pmId: id.pmId, by: login, graceMs } };
|
|
168
|
+
}
|
|
143
169
|
async httpstats(req) {
|
|
144
170
|
await this.checkAuthentication(req);
|
|
145
171
|
return { data: Http_1.Http.getHttpStats() };
|
|
@@ -196,7 +222,7 @@ class Monitor extends BaseEndpoint_1.BaseEndpoint {
|
|
|
196
222
|
for (let i of users) {
|
|
197
223
|
const hash = crypto_1.default.createHash("sha256").update(i.login + ":" + i.password).digest("hex");
|
|
198
224
|
if (hash === header) {
|
|
199
|
-
return;
|
|
225
|
+
return i.login;
|
|
200
226
|
}
|
|
201
227
|
}
|
|
202
228
|
throw { ...DefaultErrors_1.default.UNAUTHORIZED, details: "Invalid user" };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
export declare class ProcessMonitor {
|
|
3
2
|
private static _inited;
|
|
4
3
|
private static _eld;
|
|
@@ -10,7 +9,11 @@ export declare class ProcessMonitor {
|
|
|
10
9
|
private static _eluPrev;
|
|
11
10
|
private static _elu;
|
|
12
11
|
private static _processStartedAt;
|
|
12
|
+
private static _fdHistory;
|
|
13
|
+
private static _fdHistoryMax;
|
|
14
|
+
private static _fdSampleEveryMs;
|
|
13
15
|
static init(): void;
|
|
16
|
+
private static _sampleFdSeries;
|
|
14
17
|
private static _sampleCpu;
|
|
15
18
|
private static _sampleELU;
|
|
16
19
|
private static _mapGcKind;
|
|
@@ -34,6 +37,14 @@ export declare class ProcessMonitor {
|
|
|
34
37
|
startedAtUnix: number;
|
|
35
38
|
uptimeSec: number;
|
|
36
39
|
availableParallelism: number;
|
|
40
|
+
pm2: {
|
|
41
|
+
managed: boolean;
|
|
42
|
+
name: string | null;
|
|
43
|
+
pmId: string | null;
|
|
44
|
+
instanceId: string | null;
|
|
45
|
+
execPath: string | null;
|
|
46
|
+
cwd: string | null;
|
|
47
|
+
};
|
|
37
48
|
};
|
|
38
49
|
memory: {
|
|
39
50
|
rss: number;
|
|
@@ -99,6 +110,14 @@ export declare class ProcessMonitor {
|
|
|
99
110
|
limitSoft: number | null;
|
|
100
111
|
limitHard: number | null;
|
|
101
112
|
};
|
|
113
|
+
sockets: {
|
|
114
|
+
total: number;
|
|
115
|
+
byState: Record<string, number>;
|
|
116
|
+
topRemotes: Array<{
|
|
117
|
+
host: string;
|
|
118
|
+
established: number;
|
|
119
|
+
}>;
|
|
120
|
+
} | null;
|
|
102
121
|
io: Record<string, number> | null;
|
|
103
122
|
limits: Record<string, {
|
|
104
123
|
soft: string;
|
|
@@ -106,10 +125,51 @@ export declare class ProcessMonitor {
|
|
|
106
125
|
unit: string;
|
|
107
126
|
}> | null;
|
|
108
127
|
}>;
|
|
128
|
+
static getPm2Identity(): {
|
|
129
|
+
managed: boolean;
|
|
130
|
+
name: string | null;
|
|
131
|
+
pmId: string | null;
|
|
132
|
+
instanceId: string | null;
|
|
133
|
+
execPath: string | null;
|
|
134
|
+
cwd: string | null;
|
|
135
|
+
};
|
|
136
|
+
static getFdReport(): Promise<{
|
|
137
|
+
now: {
|
|
138
|
+
ts: number;
|
|
139
|
+
fdOpen: number | null;
|
|
140
|
+
limitSoft: number | null;
|
|
141
|
+
limitHard: number | null;
|
|
142
|
+
sockets: {
|
|
143
|
+
total: number;
|
|
144
|
+
byState: Record<string, number>;
|
|
145
|
+
topRemotes: Array<{
|
|
146
|
+
host: string;
|
|
147
|
+
established: number;
|
|
148
|
+
}>;
|
|
149
|
+
} | null;
|
|
150
|
+
};
|
|
151
|
+
sampleEveryMs: number;
|
|
152
|
+
historyMax: number;
|
|
153
|
+
history: {
|
|
154
|
+
ts: number;
|
|
155
|
+
fdOpen: number | null;
|
|
156
|
+
fdSoftLimit: number | null;
|
|
157
|
+
socketTotal: number | null;
|
|
158
|
+
byState: Record<string, number>;
|
|
159
|
+
topRemote: {
|
|
160
|
+
host: string;
|
|
161
|
+
established: number;
|
|
162
|
+
} | null;
|
|
163
|
+
rss: number;
|
|
164
|
+
}[];
|
|
165
|
+
}>;
|
|
109
166
|
private static _readProcStatus;
|
|
110
167
|
private static _readProcIo;
|
|
111
168
|
private static _readProcLimits;
|
|
112
169
|
private static _countFds;
|
|
170
|
+
private static _readSocketBreakdown;
|
|
171
|
+
private static _tcpState;
|
|
172
|
+
private static _parseHexAddr;
|
|
113
173
|
private static _parseLimitValue;
|
|
114
174
|
private static _toMsFinite;
|
|
115
175
|
private static _safePercentileMs;
|
|
@@ -25,6 +25,9 @@ class ProcessMonitor {
|
|
|
25
25
|
static _eluPrev = perf_hooks_1.performance.eventLoopUtilization();
|
|
26
26
|
static _elu = { idle: 0, active: 0, utilizationPct: 0 };
|
|
27
27
|
static _processStartedAt = Date.now() - process.uptime() * 1000;
|
|
28
|
+
static _fdHistory = [];
|
|
29
|
+
static _fdHistoryMax = 300;
|
|
30
|
+
static _fdSampleEveryMs = Math.max(1, parseInt(process.env.MONITOR_FD_SAMPLE_MINUTES || "10", 10) || 10) * 60 * 1000;
|
|
28
31
|
static init() {
|
|
29
32
|
if (this._inited)
|
|
30
33
|
return;
|
|
@@ -62,6 +65,28 @@ class ProcessMonitor {
|
|
|
62
65
|
const eluTimer = setInterval(() => this._sampleELU(), 5000);
|
|
63
66
|
if (eluTimer.unref)
|
|
64
67
|
eluTimer.unref();
|
|
68
|
+
this._sampleFdSeries().catch(() => { });
|
|
69
|
+
const fdTimer = setInterval(() => this._sampleFdSeries().catch(() => { }), this._fdSampleEveryMs);
|
|
70
|
+
if (fdTimer.unref)
|
|
71
|
+
fdTimer.unref();
|
|
72
|
+
}
|
|
73
|
+
static async _sampleFdSeries() {
|
|
74
|
+
const fdOpen = await this._countFds();
|
|
75
|
+
const sockets = await this._readSocketBreakdown();
|
|
76
|
+
const limits = await this._readProcLimits();
|
|
77
|
+
const softRaw = limits?.["Max open files"]?.soft;
|
|
78
|
+
const mem = process.memoryUsage();
|
|
79
|
+
this._fdHistory.push({
|
|
80
|
+
ts: Date.now(),
|
|
81
|
+
fdOpen,
|
|
82
|
+
fdSoftLimit: softRaw ? this._parseLimitValue(softRaw) : null,
|
|
83
|
+
socketTotal: sockets ? sockets.total : null,
|
|
84
|
+
byState: sockets ? sockets.byState : {},
|
|
85
|
+
topRemote: sockets && sockets.topRemotes.length > 0 ? sockets.topRemotes[0] : null,
|
|
86
|
+
rss: mem.rss,
|
|
87
|
+
});
|
|
88
|
+
if (this._fdHistory.length > this._fdHistoryMax)
|
|
89
|
+
this._fdHistory.shift();
|
|
65
90
|
}
|
|
66
91
|
static _sampleCpu() {
|
|
67
92
|
const now = process.cpuUsage();
|
|
@@ -137,6 +162,7 @@ class ProcessMonitor {
|
|
|
137
162
|
const fdOpen = await this._countFds();
|
|
138
163
|
const fdSoft = limitsParsed?.["Max open files"]?.soft;
|
|
139
164
|
const fdHard = limitsParsed?.["Max open files"]?.hard;
|
|
165
|
+
const sockets = await this._readSocketBreakdown();
|
|
140
166
|
const h = this._eld;
|
|
141
167
|
const eventLoop = h ? {
|
|
142
168
|
minMs: this._toMsFinite(h.min),
|
|
@@ -172,6 +198,7 @@ class ProcessMonitor {
|
|
|
172
198
|
availableParallelism: typeof os_1.default.availableParallelism === "function"
|
|
173
199
|
? os_1.default.availableParallelism()
|
|
174
200
|
: os_1.default.cpus().length,
|
|
201
|
+
pm2: this.getPm2Identity(),
|
|
175
202
|
},
|
|
176
203
|
memory: {
|
|
177
204
|
rss: mem.rss,
|
|
@@ -212,10 +239,44 @@ class ProcessMonitor {
|
|
|
212
239
|
limitSoft: fdSoft ? this._parseLimitValue(fdSoft) : null,
|
|
213
240
|
limitHard: fdHard ? this._parseLimitValue(fdHard) : null,
|
|
214
241
|
},
|
|
242
|
+
sockets,
|
|
215
243
|
io,
|
|
216
244
|
limits: limitsParsed,
|
|
217
245
|
};
|
|
218
246
|
}
|
|
247
|
+
static getPm2Identity() {
|
|
248
|
+
const env = process.env;
|
|
249
|
+
const pmId = env.pm_id ?? null;
|
|
250
|
+
return {
|
|
251
|
+
managed: pmId !== null && pmId !== "",
|
|
252
|
+
name: env.name ?? null,
|
|
253
|
+
pmId,
|
|
254
|
+
instanceId: env.NODE_APP_INSTANCE ?? null,
|
|
255
|
+
execPath: env.pm_exec_path ?? null,
|
|
256
|
+
cwd: env.pm_cwd ?? null,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
static async getFdReport() {
|
|
260
|
+
const [sockets, limits, fdOpen] = await Promise.all([
|
|
261
|
+
this._readSocketBreakdown(),
|
|
262
|
+
this._readProcLimits(),
|
|
263
|
+
this._countFds(),
|
|
264
|
+
]);
|
|
265
|
+
const softRaw = limits?.["Max open files"]?.soft;
|
|
266
|
+
const hardRaw = limits?.["Max open files"]?.hard;
|
|
267
|
+
return {
|
|
268
|
+
now: {
|
|
269
|
+
ts: Date.now(),
|
|
270
|
+
fdOpen,
|
|
271
|
+
limitSoft: softRaw ? this._parseLimitValue(softRaw) : null,
|
|
272
|
+
limitHard: hardRaw ? this._parseLimitValue(hardRaw) : null,
|
|
273
|
+
sockets,
|
|
274
|
+
},
|
|
275
|
+
sampleEveryMs: this._fdSampleEveryMs,
|
|
276
|
+
historyMax: this._fdHistoryMax,
|
|
277
|
+
history: this._fdHistory,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
219
280
|
static async _readProcStatus() {
|
|
220
281
|
try {
|
|
221
282
|
const txt = await promises_1.default.readFile(`/proc/${process.pid}/status`, "utf-8");
|
|
@@ -278,6 +339,105 @@ class ProcessMonitor {
|
|
|
278
339
|
return null;
|
|
279
340
|
}
|
|
280
341
|
}
|
|
342
|
+
static async _readSocketBreakdown() {
|
|
343
|
+
try {
|
|
344
|
+
const fdDir = `/proc/${process.pid}/fd`;
|
|
345
|
+
const fds = await promises_1.default.readdir(fdDir);
|
|
346
|
+
const inodes = new Set();
|
|
347
|
+
await Promise.all(fds.map(async (fd) => {
|
|
348
|
+
try {
|
|
349
|
+
const link = await promises_1.default.readlink(`${fdDir}/${fd}`);
|
|
350
|
+
const m = link.match(/^socket:\[(\d+)\]$/);
|
|
351
|
+
if (m)
|
|
352
|
+
inodes.add(m[1]);
|
|
353
|
+
}
|
|
354
|
+
catch { }
|
|
355
|
+
}));
|
|
356
|
+
if (inodes.size === 0)
|
|
357
|
+
return { total: 0, byState: {}, topRemotes: [] };
|
|
358
|
+
const byState = {};
|
|
359
|
+
const remoteEstab = {};
|
|
360
|
+
let total = 0;
|
|
361
|
+
for (const [file, isV6] of [["/proc/net/tcp", false], ["/proc/net/tcp6", true]]) {
|
|
362
|
+
let txt;
|
|
363
|
+
try {
|
|
364
|
+
txt = await promises_1.default.readFile(file, "utf-8");
|
|
365
|
+
}
|
|
366
|
+
catch {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
for (const line of txt.split("\n").slice(1)) {
|
|
370
|
+
const parts = line.trim().split(/\s+/);
|
|
371
|
+
if (parts.length < 10)
|
|
372
|
+
continue;
|
|
373
|
+
const inode = parts[9];
|
|
374
|
+
if (!inodes.has(inode))
|
|
375
|
+
continue;
|
|
376
|
+
total++;
|
|
377
|
+
const state = this._tcpState(parts[3]);
|
|
378
|
+
byState[state] = (byState[state] ?? 0) + 1;
|
|
379
|
+
if (state === "ESTABLISHED") {
|
|
380
|
+
const host = this._parseHexAddr((parts[2] ?? "").split(":")[0], isV6);
|
|
381
|
+
remoteEstab[host] = (remoteEstab[host] ?? 0) + 1;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const topRemotes = Object.entries(remoteEstab)
|
|
386
|
+
.map(([host, established]) => ({ host, established }))
|
|
387
|
+
.sort((a, b) => b.established - a.established)
|
|
388
|
+
.slice(0, 10);
|
|
389
|
+
return { total, byState, topRemotes };
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
static _tcpState(hex) {
|
|
396
|
+
switch ((hex || "").toUpperCase()) {
|
|
397
|
+
case "01": return "ESTABLISHED";
|
|
398
|
+
case "02": return "SYN_SENT";
|
|
399
|
+
case "03": return "SYN_RECV";
|
|
400
|
+
case "04": return "FIN_WAIT1";
|
|
401
|
+
case "05": return "FIN_WAIT2";
|
|
402
|
+
case "06": return "TIME_WAIT";
|
|
403
|
+
case "07": return "CLOSE";
|
|
404
|
+
case "08": return "CLOSE_WAIT";
|
|
405
|
+
case "09": return "LAST_ACK";
|
|
406
|
+
case "0A": return "LISTEN";
|
|
407
|
+
case "0B": return "CLOSING";
|
|
408
|
+
case "0C": return "NEW_SYN_RECV";
|
|
409
|
+
default: return "UNKNOWN(" + hex + ")";
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
static _parseHexAddr(hex, isV6) {
|
|
413
|
+
try {
|
|
414
|
+
if (!isV6) {
|
|
415
|
+
if (hex.length !== 8)
|
|
416
|
+
return hex;
|
|
417
|
+
return [
|
|
418
|
+
parseInt(hex.slice(6, 8), 16),
|
|
419
|
+
parseInt(hex.slice(4, 6), 16),
|
|
420
|
+
parseInt(hex.slice(2, 4), 16),
|
|
421
|
+
parseInt(hex.slice(0, 2), 16),
|
|
422
|
+
].join(".");
|
|
423
|
+
}
|
|
424
|
+
if (hex.length !== 32)
|
|
425
|
+
return hex;
|
|
426
|
+
const bytes = [];
|
|
427
|
+
for (let w = 0; w < 4; w++) {
|
|
428
|
+
const word = hex.slice(w * 8, w * 8 + 8);
|
|
429
|
+
for (let b = 3; b >= 0; b--)
|
|
430
|
+
bytes.push(parseInt(word.slice(b * 2, b * 2 + 2), 16));
|
|
431
|
+
}
|
|
432
|
+
const hextets = [];
|
|
433
|
+
for (let i = 0; i < 16; i += 2)
|
|
434
|
+
hextets.push((((bytes[i] << 8) | bytes[i + 1]) >>> 0).toString(16));
|
|
435
|
+
return hextets.join(":");
|
|
436
|
+
}
|
|
437
|
+
catch {
|
|
438
|
+
return hex;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
281
441
|
static _parseLimitValue(v) {
|
|
282
442
|
if (v === "unlimited")
|
|
283
443
|
return -1;
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
1
|
export declare function collectSystemInfo(): Promise<{
|
|
3
2
|
cpu: {
|
|
4
|
-
usage: {
|
|
5
|
-
userPct: number;
|
|
6
|
-
systemPct: number;
|
|
7
|
-
idlePct: number;
|
|
8
|
-
iowaitPct: number;
|
|
9
|
-
} | null;
|
|
10
3
|
count: number;
|
|
11
4
|
models: Record<string, number>;
|
|
12
5
|
loadAvg: number[];
|
|
13
6
|
platform: NodeJS.Platform;
|
|
14
7
|
arch: NodeJS.Architecture;
|
|
15
8
|
uptimeSec: number;
|
|
9
|
+
usage: {
|
|
10
|
+
userPct: number;
|
|
11
|
+
systemPct: number;
|
|
12
|
+
idlePct: number;
|
|
13
|
+
iowaitPct: number;
|
|
14
|
+
} | null;
|
|
16
15
|
};
|
|
17
16
|
memory: {
|
|
18
17
|
total: number;
|
|
@@ -61,9 +60,9 @@ export declare function collectSystemInfo(): Promise<{
|
|
|
61
60
|
error: string;
|
|
62
61
|
processes?: undefined;
|
|
63
62
|
} | {
|
|
63
|
+
error?: undefined;
|
|
64
64
|
installed: boolean;
|
|
65
65
|
processes: any;
|
|
66
|
-
error?: undefined;
|
|
67
66
|
} | {
|
|
68
67
|
installed: boolean;
|
|
69
68
|
error: string;
|
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.collectSystemInfo =
|
|
6
|
+
exports.collectSystemInfo = collectSystemInfo;
|
|
7
7
|
const os_1 = __importDefault(require("os"));
|
|
8
8
|
const child_process_1 = require("child_process");
|
|
9
9
|
const util_1 = require("util");
|
|
@@ -215,4 +215,3 @@ async function collectSystemInfo() {
|
|
|
215
215
|
pm2: pm2Data,
|
|
216
216
|
};
|
|
217
217
|
}
|
|
218
|
-
exports.collectSystemInfo = collectSystemInfo;
|