@arcblock/pm2-prom-module 2.6.1

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/core/pm2.js ADDED
@@ -0,0 +1,311 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.startPm2Connect = void 0;
16
+ const pm2_1 = __importDefault(require("pm2"));
17
+ const app_1 = require("./app");
18
+ const utils_1 = require("../utils");
19
+ const cpu_1 = require("../utils/cpu");
20
+ const metrics_1 = require("../metrics");
21
+ const app_2 = require("../metrics/app");
22
+ const logger_1 = require("../utils/logger");
23
+ const docker_1 = require("../utils/docker");
24
+ const WORKER_CHECK_INTERVAL = 1000;
25
+ const SHOW_STAT_INTERVAL = 10000;
26
+ const APPS = {};
27
+ const isMonitoringApp = (app) => {
28
+ const pm2_env = app.pm2_env;
29
+ if (pm2_env.axm_options.isModule ||
30
+ !app.name ||
31
+ app.pm_id === undefined // pm_id might be zero
32
+ ) {
33
+ return false;
34
+ }
35
+ return true;
36
+ };
37
+ const updateAppPidsData = (workingApp, pidData) => {
38
+ workingApp.updatePid({
39
+ id: pidData.id,
40
+ memory: pidData.memory,
41
+ cpu: pidData.cpu || 0,
42
+ pmId: pidData.pmId,
43
+ restartCount: pidData.restartCount,
44
+ createdAt: pidData.createdAt,
45
+ metrics: pidData.metrics,
46
+ status: pidData.status,
47
+ });
48
+ };
49
+ const detectActiveApps = () => {
50
+ const logger = (0, logger_1.getLogger)();
51
+ pm2_1.default.list((err, apps) => {
52
+ if (err)
53
+ return console.error(err.stack || err);
54
+ const pidsMonit = {};
55
+ const mapAppPids = {};
56
+ const activePM2Ids = new Set();
57
+ apps.forEach((appInstance) => {
58
+ var _a;
59
+ const pm2_env = appInstance.pm2_env;
60
+ const appName = appInstance.name;
61
+ if (!isMonitoringApp(appInstance) || !appName || appInstance.pm_id === undefined) {
62
+ return;
63
+ }
64
+ // Fill all apps pids
65
+ if (!mapAppPids[appName]) {
66
+ mapAppPids[appName] = {
67
+ pids: [],
68
+ restartsSum: 0,
69
+ };
70
+ }
71
+ mapAppPids[appName].restartsSum =
72
+ mapAppPids[appName].restartsSum + Number(pm2_env.restart_time || 0);
73
+ // Get the last app instance status
74
+ mapAppPids[appName].status = (_a = appInstance.pm2_env) === null || _a === void 0 ? void 0 : _a.status;
75
+ if (appInstance.pid && appInstance.pm_id !== undefined) {
76
+ mapAppPids[appName].pids.push(appInstance.pid);
77
+ // Fill active pm2 apps id to collect internal statistic
78
+ if (pm2_env.status === 'online') {
79
+ activePM2Ids.add(appInstance.pm_id);
80
+ }
81
+ // Fill monitoring data
82
+ pidsMonit[appInstance.pid] = {
83
+ cpu: 0,
84
+ memory: 0,
85
+ pmId: appInstance.pm_id,
86
+ id: appInstance.pid,
87
+ restartCount: pm2_env.restart_time || 0,
88
+ createdAt: pm2_env.created_at || 0,
89
+ metrics: pm2_env.axm_monitor,
90
+ status: pm2_env.status,
91
+ };
92
+ }
93
+ });
94
+ Object.keys(APPS).forEach((appName) => {
95
+ const processingApp = mapAppPids[appName];
96
+ // Filters apps which do not have active pids
97
+ if (!processingApp) {
98
+ logger.debug(`Delete ${appName} because it not longer exists`);
99
+ const workingApp = APPS[appName];
100
+ const instances = workingApp.getActivePm2Ids();
101
+ // Clear app metrics
102
+ (0, app_2.deleteAppMetrics)(appName);
103
+ // Clear all metrics in prom-client because an app is not exists anymore
104
+ (0, metrics_1.deletePromAppMetrics)(appName, instances);
105
+ delete APPS[appName];
106
+ }
107
+ else {
108
+ const workingApp = APPS[appName];
109
+ if (workingApp) {
110
+ const activePids = processingApp.pids;
111
+ const removedPids = workingApp.removeNotActivePids(activePids);
112
+ if (removedPids.length) {
113
+ const removedIntances = removedPids.map((entry) => entry.pmId);
114
+ logger.debug(`App ${appName} clear metrics. Removed PIDs ${removedIntances.toString()}`);
115
+ (0, metrics_1.deletePromAppInstancesMetrics)(appName, removedIntances);
116
+ if (!activePids.length) {
117
+ // Delete app metrics because it does not have active PIDs anymore
118
+ logger.debug(`App ${appName} does not have active PIDs. Clear app metrics`);
119
+ (0, app_2.deleteAppMetrics)(appName);
120
+ }
121
+ }
122
+ const pidsRestartsSum = workingApp
123
+ .getRestartCount()
124
+ .reduce((accum, item) => accum + item.value, 0);
125
+ if (processingApp.restartsSum > pidsRestartsSum) {
126
+ // Reset metrics when active restart app bigger then active app
127
+ // This logic exist to prevent autoscaling problems if we use only !==
128
+ logger.debug(`App ${appName} has been restarted. Clear app metrics`);
129
+ (0, app_2.deleteAppMetrics)(appName);
130
+ }
131
+ }
132
+ }
133
+ });
134
+ // Create instances for new apps
135
+ for (const [appName, entry] of Object.entries(mapAppPids)) {
136
+ if (!APPS[appName]) {
137
+ APPS[appName] = new app_1.App(appName);
138
+ }
139
+ const workingApp = APPS[appName];
140
+ if (workingApp) {
141
+ // Update status
142
+ workingApp.updateStatus(entry.status);
143
+ }
144
+ }
145
+ // Collect statistic from apps. Do it after all APPS created
146
+ if (activePM2Ids.size > 0) {
147
+ // logger.debug(`Collect app metrics from PIDs ${Array.from(activePM2Ids)}`);
148
+ sendCollectStaticticBusEvent(Array.from(activePM2Ids));
149
+ }
150
+ // Update metric with available apps
151
+ metrics_1.metricAvailableApps === null || metrics_1.metricAvailableApps === void 0 ? void 0 : metrics_1.metricAvailableApps.set(Object.keys(APPS).length);
152
+ // Get all pids to monit
153
+ const pids = Object.keys(pidsMonit);
154
+ // Get real pids data.
155
+ // !ATTENTION! Can not use PM2 app.monit because of incorrect values of CPU usage
156
+ (0, cpu_1.getPidsUsage)(pids)
157
+ .then((stats) => __awaiter(void 0, void 0, void 0, function* () {
158
+ // Fill data for all pids
159
+ if (stats && Object.keys(stats).length) {
160
+ for (const [pid, stat] of Object.entries(stats)) {
161
+ const pidId = Number(pid);
162
+ if (pidId && pidsMonit[pidId]) {
163
+ pidsMonit[pidId].cpu = Math.round(stat.cpu * 10) / 10;
164
+ pidsMonit[pidId].memory = stat.memory;
165
+ }
166
+ }
167
+ }
168
+ // Get docker stats
169
+ // @ts-expect-error
170
+ const dockerApps = apps.filter((app) => { var _a; return (_a = app.pm2_env) === null || _a === void 0 ? void 0 : _a.BLOCKLET_DOCKER_NAME; });
171
+ yield (0, docker_1.getDockerStats)(
172
+ // @ts-expect-error
173
+ dockerApps.map((x) => { var _a; return (_a = x.pm2_env) === null || _a === void 0 ? void 0 : _a.BLOCKLET_DOCKER_NAME; })).then((stats) => {
174
+ stats.map((stat, i) => {
175
+ const entry = mapAppPids[dockerApps[i].name];
176
+ if (entry) {
177
+ entry.pids.forEach((pid) => {
178
+ pidsMonit[pid].cpu = stat.cpuUsage;
179
+ pidsMonit[pid].memory = stat.memoryUsage;
180
+ });
181
+ }
182
+ });
183
+ });
184
+ for (const [appName, entry] of Object.entries(mapAppPids)) {
185
+ const workingApp = APPS[appName];
186
+ if (workingApp) {
187
+ // Update pids data
188
+ entry.pids.forEach((pidId) => {
189
+ const monit = pidsMonit[pidId];
190
+ if (monit) {
191
+ updateAppPidsData(workingApp, monit);
192
+ }
193
+ });
194
+ // Collect metrics
195
+ processWorkingApp(workingApp);
196
+ }
197
+ }
198
+ }))
199
+ .catch((err) => {
200
+ console.error(err.stack || err);
201
+ });
202
+ // get docker stats
203
+ // @ts-ignore
204
+ });
205
+ };
206
+ const startPm2Connect = (conf) => {
207
+ const logger = (0, logger_1.getLogger)();
208
+ pm2_1.default.connect((err) => {
209
+ var _a;
210
+ if (err)
211
+ return console.error(err.stack || err);
212
+ const additionalMetrics = app_1.PM2_METRICS.map((entry) => {
213
+ return {
214
+ key: (0, utils_1.toUndescore)(entry.name),
215
+ description: `${entry.name}. Unit "${entry.unit}"`,
216
+ };
217
+ });
218
+ if (additionalMetrics.length) {
219
+ (0, metrics_1.initDynamicGaugeMetricClients)(additionalMetrics);
220
+ }
221
+ detectActiveApps();
222
+ // Collect statistic from running apps
223
+ pm2_1.default.launchBus((err, bus) => {
224
+ if (err)
225
+ return console.error(err.stack || err);
226
+ logger.debug('Start bus listener');
227
+ bus.on('process:msg', (packet) => {
228
+ if (packet.process &&
229
+ packet.raw &&
230
+ packet.raw.topic === 'pm2-prom-module:metrics' &&
231
+ packet.raw.data) {
232
+ const { name, pm_id } = packet.process;
233
+ /*logger.debug(
234
+ `Got message from app=${name} and pid=${pm_id}. Message=${JSON.stringify(
235
+ packet.raw.data
236
+ )}`
237
+ );*/
238
+ if (name && APPS[name] && packet.raw.data.metrics) {
239
+ (0, app_2.processAppMetrics)(conf, {
240
+ pmId: pm_id,
241
+ appName: name,
242
+ appResponse: packet.raw.data,
243
+ });
244
+ }
245
+ }
246
+ });
247
+ });
248
+ // Start timer to update available apps
249
+ setInterval(() => {
250
+ detectActiveApps();
251
+ }, (_a = conf.app_check_interval) !== null && _a !== void 0 ? _a : WORKER_CHECK_INTERVAL);
252
+ if (conf.debug) {
253
+ setInterval(() => {
254
+ if (Object.keys(APPS).length) {
255
+ for (const [, app] of Object.entries(APPS)) {
256
+ const cpuValues = app.getCpuThreshold().map((entry) => entry.value);
257
+ const memory = Math.round(app.getTotalUsedMemory() / 1024 / 1024);
258
+ const CPU = cpuValues.length ? cpuValues.toString() : '0';
259
+ (0, logger_1.getLogger)().debug(`App "${app.getName()}" has ${app.getActiveWorkersCount()} worker(s). CPU: ${CPU}, Memory: ${memory}MB`);
260
+ }
261
+ }
262
+ else {
263
+ (0, logger_1.getLogger)().debug(`No apps available`);
264
+ }
265
+ }, SHOW_STAT_INTERVAL);
266
+ }
267
+ });
268
+ };
269
+ exports.startPm2Connect = startPm2Connect;
270
+ function processWorkingApp(workingApp) {
271
+ const labels = { app: workingApp.getName() };
272
+ metrics_1.metricAppInstances === null || metrics_1.metricAppInstances === void 0 ? void 0 : metrics_1.metricAppInstances.set(labels, workingApp.getActiveWorkersCount());
273
+ metrics_1.metricAppAverageMemory === null || metrics_1.metricAppAverageMemory === void 0 ? void 0 : metrics_1.metricAppAverageMemory.set(labels, workingApp.getAverageUsedMemory());
274
+ metrics_1.metricAppTotalMemory === null || metrics_1.metricAppTotalMemory === void 0 ? void 0 : metrics_1.metricAppTotalMemory.set(labels, workingApp.getTotalUsedMemory());
275
+ metrics_1.metricAppAverageCpu === null || metrics_1.metricAppAverageCpu === void 0 ? void 0 : metrics_1.metricAppAverageCpu.set(labels, workingApp.getAverageCpu());
276
+ metrics_1.metricAppUptime === null || metrics_1.metricAppUptime === void 0 ? void 0 : metrics_1.metricAppUptime.set(labels, workingApp.getUptime());
277
+ metrics_1.metricAppStatus === null || metrics_1.metricAppStatus === void 0 ? void 0 : metrics_1.metricAppStatus.set(labels, workingApp.getStatus());
278
+ workingApp.getCurrentPidsCpu().forEach((entry) => {
279
+ metrics_1.metricAppPidsCpuLast === null || metrics_1.metricAppPidsCpuLast === void 0 ? void 0 : metrics_1.metricAppPidsCpuLast.set(Object.assign(Object.assign({}, labels), { instance: entry.pmId }), entry.value);
280
+ });
281
+ workingApp.getCpuThreshold().forEach((entry) => {
282
+ metrics_1.metricAppPidsCpuThreshold === null || metrics_1.metricAppPidsCpuThreshold === void 0 ? void 0 : metrics_1.metricAppPidsCpuThreshold.set(Object.assign(Object.assign({}, labels), { instance: entry.pmId }), entry.value);
283
+ });
284
+ workingApp.getCurrentPidsMemory().forEach((entry) => {
285
+ metrics_1.metricAppPidsMemory === null || metrics_1.metricAppPidsMemory === void 0 ? void 0 : metrics_1.metricAppPidsMemory.set(Object.assign(Object.assign({}, labels), { instance: entry.pmId }), entry.value);
286
+ });
287
+ workingApp.getRestartCount().forEach((entry) => {
288
+ metrics_1.metricAppRestartCount === null || metrics_1.metricAppRestartCount === void 0 ? void 0 : metrics_1.metricAppRestartCount.set(Object.assign(Object.assign({}, labels), { instance: entry.pmId }), entry.value);
289
+ });
290
+ workingApp.getPidPm2Metrics().forEach((entry) => {
291
+ Object.keys(entry.metrics).forEach((metricKey) => {
292
+ if (metrics_1.dynamicGaugeMetricClients[metricKey]) {
293
+ metrics_1.dynamicGaugeMetricClients[metricKey].set(Object.assign(Object.assign({}, labels), { instance: entry.pmId }), entry.metrics[metricKey]);
294
+ }
295
+ });
296
+ });
297
+ }
298
+ function sendCollectStaticticBusEvent(pm2Ids) {
299
+ // Request available metrics from all running apps
300
+ pm2Ids.forEach((pm2id) => {
301
+ pm2_1.default.sendDataToProcessId(pm2id, {
302
+ topic: 'pm2-prom-module:collect',
303
+ data: {},
304
+ // Required fields by pm2 but we do not use them
305
+ id: pm2id,
306
+ }, (err) => {
307
+ if (err)
308
+ return console.error(`pm2-prom-module: sendDataToProcessId ${err.stack || err}`);
309
+ });
310
+ });
311
+ }
package/index.js ADDED
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ // @ts-ignore
16
+ const pmx_1 = __importDefault(require("pmx"));
17
+ const http_1 = require("http");
18
+ const net_1 = __importDefault(require("net"));
19
+ const fs_1 = __importDefault(require("fs"));
20
+ const pm2_1 = require("./core/pm2");
21
+ const logger_1 = require("./utils/logger");
22
+ const metrics_1 = require("./metrics");
23
+ const DEFAULT_PREFIX = 'pm2';
24
+ const startPromServer = (prefix, moduleConfig) => {
25
+ (0, metrics_1.initMetrics)(prefix);
26
+ const serviceName = moduleConfig.service_name;
27
+ const port = Number(moduleConfig.port);
28
+ const hostname = moduleConfig.hostname;
29
+ const unixSocketPath = moduleConfig.unix_socket_path;
30
+ const promServer = (0, http_1.createServer)((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
31
+ const mergedRegistry = (0, metrics_1.combineAllRegistries)(Boolean(moduleConfig.aggregate_app_metrics));
32
+ mergedRegistry.setDefaultLabels({ serviceName });
33
+ res.setHeader('Content-Type', mergedRegistry.contentType);
34
+ res.end(yield mergedRegistry.metrics());
35
+ return;
36
+ }));
37
+ const listenCallback = () => {
38
+ const listenValue = promServer.address();
39
+ let listenString = '';
40
+ if (typeof listenValue === 'string') {
41
+ listenString = listenValue;
42
+ }
43
+ else {
44
+ listenString = `${listenValue === null || listenValue === void 0 ? void 0 : listenValue.address}:${listenValue === null || listenValue === void 0 ? void 0 : listenValue.port}`;
45
+ }
46
+ console.log(`Metrics server is available on ${listenString}`);
47
+ };
48
+ if (unixSocketPath) {
49
+ promServer.on('error', function (promServerError) {
50
+ if (promServerError.code == 'EADDRINUSE') {
51
+ console.log(`Listen error: "${promServerError.message}". Try to remove socket...`);
52
+ const clientSocket = new net_1.default.Socket();
53
+ clientSocket.on('error', function (clientSocketError) {
54
+ if (clientSocketError.code == 'ECONNREFUSED') {
55
+ console.log(`Remove old socket ${unixSocketPath}`);
56
+ fs_1.default.unlinkSync(unixSocketPath);
57
+ promServer.listen(unixSocketPath);
58
+ }
59
+ });
60
+ clientSocket.connect({ path: unixSocketPath }, function () {
61
+ console.log('Server running, giving up...');
62
+ process.exit();
63
+ });
64
+ }
65
+ });
66
+ promServer.listen(unixSocketPath, listenCallback);
67
+ }
68
+ else {
69
+ promServer.listen(port, hostname, listenCallback);
70
+ }
71
+ };
72
+ pmx_1.default.initModule({
73
+ widget: {
74
+ el: {
75
+ probes: true,
76
+ actions: true,
77
+ },
78
+ block: {
79
+ actions: false,
80
+ issues: true,
81
+ meta: true,
82
+ },
83
+ },
84
+ }, function (err, conf) {
85
+ var _a, _b;
86
+ if (err)
87
+ return console.error(err.stack || err);
88
+ const moduleConfig = conf.module_conf;
89
+ (0, logger_1.initLogger)({ isDebug: moduleConfig.debug });
90
+ (0, pm2_1.startPm2Connect)(moduleConfig);
91
+ startPromServer((_a = moduleConfig.prefix) !== null && _a !== void 0 ? _a : DEFAULT_PREFIX, moduleConfig);
92
+ pmx_1.default.configureModule({
93
+ human_info: [
94
+ ['Status', 'Module enabled'],
95
+ ['Debug', moduleConfig.debug ? 'Enabled' : 'Disabled'],
96
+ [
97
+ 'Aggregate apps metrics',
98
+ moduleConfig.aggregate_app_metrics ? 'Enabled' : 'Disabled',
99
+ ],
100
+ ['Port', moduleConfig.port],
101
+ ['Service name', moduleConfig.service_name ? moduleConfig.service_name : `N/A`],
102
+ ['Check interval', `${moduleConfig.app_check_interval} ms`],
103
+ ['Prefix', (_b = moduleConfig.prefix) !== null && _b !== void 0 ? _b : DEFAULT_PREFIX],
104
+ ],
105
+ });
106
+ });
package/metrics/app.js ADDED
@@ -0,0 +1,193 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getAppRegistry = exports.processAppMetrics = exports.deleteAppMetrics = void 0;
7
+ const prom_client_1 = __importDefault(require("prom-client"));
8
+ const logger_1 = require("../utils/logger");
9
+ const histogram_1 = require("./prom/histogram");
10
+ const summary_1 = require("./prom/summary");
11
+ const dynamicAppMetrics = {};
12
+ const DEFAULT_LABELS = ['app', 'instance'];
13
+ const parseLabels = (values) => {
14
+ const labels = new Set();
15
+ values.forEach((entry) => {
16
+ Object.keys(entry.labels).forEach((label) => {
17
+ labels.add(String(label));
18
+ });
19
+ });
20
+ return Array.from(labels);
21
+ };
22
+ const createMetricByType = (metric, labels) => {
23
+ switch (metric.type) {
24
+ case "counter" /* MetricType.Counter */: {
25
+ const metricEntry = new prom_client_1.default.Counter({
26
+ name: metric.name,
27
+ help: metric.help,
28
+ aggregator: metric.aggregator,
29
+ labelNames: [...DEFAULT_LABELS, ...labels],
30
+ registers: [],
31
+ });
32
+ return metricEntry;
33
+ }
34
+ case "gauge" /* MetricType.Gauge */: {
35
+ const metricEntry = new prom_client_1.default.Gauge({
36
+ name: metric.name,
37
+ help: metric.help,
38
+ aggregator: metric.aggregator,
39
+ labelNames: [...DEFAULT_LABELS, ...labels],
40
+ registers: [],
41
+ });
42
+ return metricEntry;
43
+ }
44
+ case "histogram" /* MetricType.Histogram */: {
45
+ const filteredMetrics = labels.filter((entry) => entry !== 'le');
46
+ const metricEntry = new histogram_1.IHistogram({
47
+ name: metric.name,
48
+ help: metric.help,
49
+ aggregator: metric.aggregator,
50
+ labelNames: [...DEFAULT_LABELS, ...filteredMetrics],
51
+ registers: [],
52
+ });
53
+ return metricEntry;
54
+ }
55
+ case "summary" /* MetricType.Summary */: {
56
+ const filteredMetrics = labels.filter((entry) => entry !== 'quantile');
57
+ const metricEntry = new summary_1.ISummary({
58
+ name: metric.name,
59
+ help: metric.help,
60
+ aggregator: metric.aggregator,
61
+ labelNames: [...DEFAULT_LABELS, ...filteredMetrics],
62
+ registers: [],
63
+ });
64
+ return metricEntry;
65
+ }
66
+ default:
67
+ return null;
68
+ }
69
+ };
70
+ const createRegistryMetrics = (registry) => {
71
+ const logger = (0, logger_1.getLogger)();
72
+ const metrics = {};
73
+ for (const [appName, appEntry] of Object.entries(dynamicAppMetrics)) {
74
+ for (const [metricName, pidEntry] of Object.entries(appEntry)) {
75
+ for (const [pm2id, metric] of Object.entries(pidEntry)) {
76
+ if (!metrics[metricName]) {
77
+ const parsedLabels = parseLabels(metric.values);
78
+ const newMetricStore = createMetricByType(metric, parsedLabels);
79
+ if (newMetricStore) {
80
+ metrics[metricName] = newMetricStore;
81
+ }
82
+ }
83
+ const createdMetric = metrics[metricName];
84
+ if (!createdMetric) {
85
+ logger.error(`Unsupported metric type ${metric.type} for ${metricName}`);
86
+ }
87
+ else {
88
+ // Register metric
89
+ registry.registerMetric(createdMetric);
90
+ const defaultLabels = {
91
+ app: appName,
92
+ instance: pm2id,
93
+ };
94
+ // Fill data
95
+ switch (metric.type) {
96
+ case "counter" /* MetricType.Counter */: {
97
+ metric.values.forEach((entry) => {
98
+ try {
99
+ createdMetric.inc(Object.assign(Object.assign({}, entry.labels), defaultLabels), entry.value);
100
+ }
101
+ catch (error) {
102
+ logger.error(error);
103
+ }
104
+ });
105
+ break;
106
+ }
107
+ case "gauge" /* MetricType.Gauge */: {
108
+ metric.values.forEach((entry) => {
109
+ try {
110
+ createdMetric.inc(Object.assign(Object.assign({}, entry.labels), defaultLabels), entry.value);
111
+ }
112
+ catch (error) {
113
+ logger.error(error);
114
+ }
115
+ });
116
+ break;
117
+ }
118
+ case "histogram" /* MetricType.Histogram */: {
119
+ createdMetric.setValues(defaultLabels, metric.values);
120
+ break;
121
+ }
122
+ case "summary" /* MetricType.Summary */: {
123
+ createdMetric.setValues(defaultLabels, metric.values);
124
+ break;
125
+ }
126
+ default:
127
+ break;
128
+ }
129
+ }
130
+ }
131
+ }
132
+ }
133
+ };
134
+ const getAggregatedMetrics = () => {
135
+ const metrics = [];
136
+ for (const [appName, appEntry] of Object.entries(dynamicAppMetrics)) {
137
+ for (const [_metricName, pidEntry] of Object.entries(appEntry)) {
138
+ const pidMetrics = [];
139
+ for (const [_pm2id, metric] of Object.entries(pidEntry)) {
140
+ const metricWithApp = Object.assign({}, metric);
141
+ metricWithApp.values = metricWithApp.values.map((entry) => {
142
+ entry.labels['app'] = appName;
143
+ return entry;
144
+ });
145
+ pidMetrics.push(metricWithApp);
146
+ }
147
+ metrics.push(pidMetrics);
148
+ }
149
+ }
150
+ return prom_client_1.default.AggregatorRegistry.aggregate(metrics);
151
+ };
152
+ const deleteAppMetrics = (appName) => {
153
+ const logger = (0, logger_1.getLogger)();
154
+ if (dynamicAppMetrics[appName]) {
155
+ logger.debug(`Remove AppMetrics for app ${appName}`);
156
+ delete dynamicAppMetrics[appName];
157
+ }
158
+ };
159
+ exports.deleteAppMetrics = deleteAppMetrics;
160
+ const processAppMetrics = (_config, data) => {
161
+ if (!Array.isArray(data.appResponse.metrics)) {
162
+ return;
163
+ }
164
+ data.appResponse.metrics.forEach((entry) => {
165
+ if (Array.isArray(entry.values) && entry.values.length) {
166
+ const metricName = entry.name;
167
+ if (!dynamicAppMetrics[data.appName]) {
168
+ dynamicAppMetrics[data.appName] = {};
169
+ }
170
+ const appKey = dynamicAppMetrics[data.appName][metricName];
171
+ if (!appKey) {
172
+ dynamicAppMetrics[data.appName][metricName] = {};
173
+ }
174
+ const pm2id = String(data.pmId);
175
+ dynamicAppMetrics[data.appName][metricName][pm2id] = entry;
176
+ }
177
+ });
178
+ };
179
+ exports.processAppMetrics = processAppMetrics;
180
+ const getAppRegistry = (needAggregate) => {
181
+ if (Object.keys(dynamicAppMetrics).length) {
182
+ if (needAggregate) {
183
+ return getAggregatedMetrics();
184
+ }
185
+ else {
186
+ const registry = new prom_client_1.default.Registry();
187
+ createRegistryMetrics(registry);
188
+ return registry;
189
+ }
190
+ }
191
+ return undefined;
192
+ };
193
+ exports.getAppRegistry = getAppRegistry;