@adalo/metrics 0.1.56 → 0.1.57
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/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +11 -0
- package/lib/index.js.map +1 -1
- package/lib/metricsQueueRedisClient.d.ts +27 -0
- package/lib/metricsQueueRedisClient.d.ts.map +1 -0
- package/lib/metricsQueueRedisClient.js +181 -0
- package/lib/metricsQueueRedisClient.js.map +1 -0
- package/lib/metricsRedisClient.d.ts +28 -20
- package/lib/metricsRedisClient.d.ts.map +1 -1
- package/lib/metricsRedisClient.js +33 -118
- package/lib/metricsRedisClient.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/metricsQueueRedisClient.js +222 -0
- package/src/metricsRedisClient.js +36 -163
package/lib/index.d.ts
CHANGED
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA;AACpC,cAAc,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,sBAAsB,CAAA;AACpC,cAAc,2BAA2B,CAAA;AACzC,cAAc,cAAc,CAAA"}
|
package/lib/index.js
CHANGED
|
@@ -25,6 +25,17 @@ Object.keys(_metricsRedisClient).forEach(function (key) {
|
|
|
25
25
|
}
|
|
26
26
|
});
|
|
27
27
|
});
|
|
28
|
+
var _metricsQueueRedisClient = require("./metricsQueueRedisClient");
|
|
29
|
+
Object.keys(_metricsQueueRedisClient).forEach(function (key) {
|
|
30
|
+
if (key === "default" || key === "__esModule") return;
|
|
31
|
+
if (key in exports && exports[key] === _metricsQueueRedisClient[key]) return;
|
|
32
|
+
Object.defineProperty(exports, key, {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
get: function () {
|
|
35
|
+
return _metricsQueueRedisClient[key];
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
28
39
|
var _redisUtils = require("./redisUtils");
|
|
29
40
|
Object.keys(_redisUtils).forEach(function (key) {
|
|
30
41
|
if (key === "default" || key === "__esModule") return;
|
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["_metricsClient","require","Object","keys","forEach","key","exports","defineProperty","enumerable","get","_metricsRedisClient","_redisUtils"],"sources":["../src/index.ts"],"sourcesContent":["export * from './metricsClient'\nexport * from './metricsRedisClient'\nexport * from './redisUtils'\n"],"mappings":";;;;;AAAA,IAAAA,cAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,cAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,cAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAT,cAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AACA,IAAAK,mBAAA,GAAAT,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAO,mBAAA,EAAAN,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAK,mBAAA,CAAAL,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,mBAAA,CAAAL,GAAA;IAAA;EAAA;AAAA;AACA,IAAAM,
|
|
1
|
+
{"version":3,"file":"index.js","names":["_metricsClient","require","Object","keys","forEach","key","exports","defineProperty","enumerable","get","_metricsRedisClient","_metricsQueueRedisClient","_redisUtils"],"sources":["../src/index.ts"],"sourcesContent":["export * from './metricsClient'\nexport * from './metricsRedisClient'\nexport * from './metricsQueueRedisClient'\nexport * from './redisUtils'\n"],"mappings":";;;;;AAAA,IAAAA,cAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,cAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,cAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAT,cAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AACA,IAAAK,mBAAA,GAAAT,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAO,mBAAA,EAAAN,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAK,mBAAA,CAAAL,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,mBAAA,CAAAL,GAAA;IAAA;EAAA;AAAA;AACA,IAAAM,wBAAA,GAAAV,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAQ,wBAAA,EAAAP,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAM,wBAAA,CAAAN,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAE,wBAAA,CAAAN,GAAA;IAAA;EAAA;AAAA;AACA,IAAAO,WAAA,GAAAX,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAS,WAAA,EAAAR,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAO,WAAA,CAAAP,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAG,WAAA,CAAAP,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QueueRedisMetricsClient extends MetricsClient to collect
|
|
3
|
+
* Redis and Bee Queue metrics periodically and push them to Prometheus Pushgateway.
|
|
4
|
+
*
|
|
5
|
+
* @extends RedisMetricsClient
|
|
6
|
+
*/
|
|
7
|
+
export class QueueRedisMetricsClient extends RedisMetricsClient {
|
|
8
|
+
getConfiguredQueueNames: () => string[];
|
|
9
|
+
startupValidation: () => boolean;
|
|
10
|
+
/** Cache for queue objects to avoid multiple connections */
|
|
11
|
+
queueCache: Map<any, any>;
|
|
12
|
+
/** Gauge for queue jobs by status */
|
|
13
|
+
queueJobsGauge: import("prom-client").Gauge<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Collect metrics for a single Bee Queue and set gauges
|
|
16
|
+
* @param {string} queueName - Name of the queue
|
|
17
|
+
* @returns {Promise<void>}
|
|
18
|
+
*/
|
|
19
|
+
collectSingleQueueMetrics: (queueName: string) => Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* Collect metrics for all queues and Redis, then push to Pushgateway
|
|
22
|
+
* @returns {Promise<void>}
|
|
23
|
+
*/
|
|
24
|
+
collectQueueMetrics: () => Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
import { RedisMetricsClient } from "./metricsRedisClient";
|
|
27
|
+
//# sourceMappingURL=metricsQueueRedisClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricsQueueRedisClient.d.ts","sourceRoot":"","sources":["../src/metricsQueueRedisClient.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH;IAgEI,wCAAsD;IACtD,iCAA0C;IAE1C,4DAA4D;IAC5D,0BAA2B;IAE3B,qCAAqC;IACrC,oDAIE;IAKJ;;;;OAIG;IACH,uCAHW,MAAM,KACJ,QAAQ,IAAI,CAAC,CAkDzB;IAED;;;OAGG;IACH,2BAFa,QAAQ,IAAI,CAAC,CAsCzB;CAmCF"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const Queue = require('bee-queue');
|
|
4
|
+
const {
|
|
5
|
+
RedisMetricsClient
|
|
6
|
+
} = require('.');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* QueueRedisMetricsClient extends MetricsClient to collect
|
|
10
|
+
* Redis and Bee Queue metrics periodically and push them to Prometheus Pushgateway.
|
|
11
|
+
*
|
|
12
|
+
* @extends RedisMetricsClient
|
|
13
|
+
*/
|
|
14
|
+
class QueueRedisMetricsClient extends RedisMetricsClient {
|
|
15
|
+
/**
|
|
16
|
+
* @param {Object} options
|
|
17
|
+
* @param {any} options.redisClient - Redis client instance (required)
|
|
18
|
+
* @param {string} [options.appName] - Application name (from MetricsClient)
|
|
19
|
+
* @param {string} [options.dynoId] - Dyno/instance ID (from MetricsClient)
|
|
20
|
+
* @param {string} [options.processType] - Process type (from MetricsClient)
|
|
21
|
+
* @param {boolean} [options.enabled] - Enable metrics collection (from MetricsClient)
|
|
22
|
+
* @param {boolean} [options.logValues] - Log metrics values (from MetricsClient)
|
|
23
|
+
* @param {string} [options.pushgatewayUrl] - PushGateway URL (from MetricsClient)
|
|
24
|
+
* @param {string} [options.pushgatewaySecret] - PushGateway secret token (from MetricsClient)
|
|
25
|
+
* @param {number} [options.intervalSec] - Interval in seconds for pushing metrics (from MetricsClient)
|
|
26
|
+
* @param {boolean} [options.removeOldMetrics] - Remove old metrics by service (from MetricsClient)
|
|
27
|
+
* @param {boolean} [options.scripDefaultMetrics] - Skip default metrics creation (from MetricsClient)
|
|
28
|
+
* @param {function} [options.startupValidation] - Function to validate startup (from MetricsClient)
|
|
29
|
+
*/
|
|
30
|
+
constructor({
|
|
31
|
+
redisClient,
|
|
32
|
+
metricsConfig = {}
|
|
33
|
+
} = {}) {
|
|
34
|
+
const getConfiguredQueueNames = () => {
|
|
35
|
+
if (!process.env.METRICS_APP_REDIS_BQ) {
|
|
36
|
+
throw new Error('No queues configured for monitoring. Set METRICS_APP_REDIS_BQ with comma-separated queue names');
|
|
37
|
+
}
|
|
38
|
+
const allQueues = process.env.METRICS_APP_REDIS_BQ.split(',').map(q => q.trim()).filter(Boolean);
|
|
39
|
+
if (allQueues.length === 0) {
|
|
40
|
+
throw new Error('METRICS_APP_REDIS_BQ is empty or contains only whitespace. ' + 'Example: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"');
|
|
41
|
+
}
|
|
42
|
+
return [...new Set(allQueues)];
|
|
43
|
+
};
|
|
44
|
+
const startupValidation = () => {
|
|
45
|
+
try {
|
|
46
|
+
const queueNames = getConfiguredQueueNames();
|
|
47
|
+
console.info(`[queue-metrics] Queue & Redis metrics collection starting for ${queueNames.length} queues`);
|
|
48
|
+
return true;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error(`[queue-metrics] ❌ Cannot start: ${error.message}`);
|
|
51
|
+
console.error(`[queue-metrics] 💡 Example: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"`);
|
|
52
|
+
console.error(`[queue-metrics] 💡 Optional: METRICS_QUEUE_INTERVAL_SEC="10"`);
|
|
53
|
+
console.error(`[queue-metrics] Skipping queue metrics collection`);
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
super({
|
|
58
|
+
...metricsConfig,
|
|
59
|
+
redisClient,
|
|
60
|
+
startupValidation
|
|
61
|
+
});
|
|
62
|
+
this.getConfiguredQueueNames = getConfiguredQueueNames;
|
|
63
|
+
this.startupValidation = startupValidation;
|
|
64
|
+
|
|
65
|
+
/** Cache for queue objects to avoid multiple connections */
|
|
66
|
+
this.queueCache = new Map();
|
|
67
|
+
|
|
68
|
+
/** Gauge for queue jobs by status */
|
|
69
|
+
this.queueJobsGauge = this.createGauge({
|
|
70
|
+
name: 'app_queue_jobs_count',
|
|
71
|
+
help: 'Number of app jobs in the queue by status',
|
|
72
|
+
labelNames: this.withDefaultLabels(['queue_name', 'status'])
|
|
73
|
+
});
|
|
74
|
+
this._setCleanupHandlers();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Collect metrics for a single Bee Queue and set gauges
|
|
79
|
+
* @param {string} queueName - Name of the queue
|
|
80
|
+
* @returns {Promise<void>}
|
|
81
|
+
*/
|
|
82
|
+
collectSingleQueueMetrics = async queueName => {
|
|
83
|
+
try {
|
|
84
|
+
if (!this.queueCache.has(queueName)) {
|
|
85
|
+
this.queueCache.set(queueName, new Queue(queueName, {
|
|
86
|
+
redis: this.redisClient,
|
|
87
|
+
isWorker: false,
|
|
88
|
+
getEvents: false,
|
|
89
|
+
sendEvents: false
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
const queue = this.queueCache.get(queueName);
|
|
93
|
+
const health = await queue.checkHealth();
|
|
94
|
+
const labels = {
|
|
95
|
+
...this.getDefaultLabels(),
|
|
96
|
+
queue_name: queueName
|
|
97
|
+
};
|
|
98
|
+
this.queueJobsGauge.set({
|
|
99
|
+
...labels,
|
|
100
|
+
status: 'waiting'
|
|
101
|
+
}, health.waiting || 0);
|
|
102
|
+
this.queueJobsGauge.set({
|
|
103
|
+
...labels,
|
|
104
|
+
status: 'active'
|
|
105
|
+
}, health.active || 0);
|
|
106
|
+
this.queueJobsGauge.set({
|
|
107
|
+
...labels,
|
|
108
|
+
status: 'succeeded'
|
|
109
|
+
}, health.succeeded || 0);
|
|
110
|
+
this.queueJobsGauge.set({
|
|
111
|
+
...labels,
|
|
112
|
+
status: 'failed'
|
|
113
|
+
}, health.failed || 0);
|
|
114
|
+
this.queueJobsGauge.set({
|
|
115
|
+
...labels,
|
|
116
|
+
status: 'delayed'
|
|
117
|
+
}, health.delayed || 0);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
console.warn(`[queue-metrics] Failed to collect metrics for queue ${queueName}:`, error.message);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Collect metrics for all queues and Redis, then push to Pushgateway
|
|
125
|
+
* @returns {Promise<void>}
|
|
126
|
+
*/
|
|
127
|
+
collectQueueMetrics = async () => {
|
|
128
|
+
try {
|
|
129
|
+
const queueNames = this.getConfiguredQueueNames();
|
|
130
|
+
await this.collectRedisMetrics();
|
|
131
|
+
await Promise.allSettled(queueNames.map(queueName => this.collectSingleQueueMetrics(queueName)));
|
|
132
|
+
await this.gatewayPush();
|
|
133
|
+
if (this.metricsLogValues) {
|
|
134
|
+
const metricObjects = await this.registry.getMetricsAsJSON();
|
|
135
|
+
console.info(`[queue-metrics] Collected metrics for ${queueNames.length} queues:`, JSON.stringify(metricObjects, null, 2));
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (error.message?.includes('No queues configured') || error.message?.includes('METRICS_APP_REDIS_BQ')) {
|
|
139
|
+
console.error(`[queue-metrics] ❌ Configuration error: ${error.message}`);
|
|
140
|
+
console.error(`[queue-metrics] 💡 Example config: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"`);
|
|
141
|
+
} else {
|
|
142
|
+
console.error(`[queue-metrics] Failed to collect queue metrics: ${error.message}`);
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Start periodic collection.
|
|
150
|
+
* @param {number} [intervalSec=this.intervalSec] - Interval in seconds
|
|
151
|
+
*/
|
|
152
|
+
startPush = (intervalSec = this.intervalSec) => {
|
|
153
|
+
this._startPush(intervalSec, () => {
|
|
154
|
+
this.collectQueueMetrics().catch(err => {
|
|
155
|
+
console.error(`[queue-metrics] Failed to collect queue & Redis metrics:`, err);
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
_setCleanupHandlers = () => {
|
|
160
|
+
process.on('SIGINT', this.cleanup);
|
|
161
|
+
process.on('SIGTERM', this.cleanup);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Cleanup queues and Redis client
|
|
166
|
+
*/
|
|
167
|
+
cleanup = async () => {
|
|
168
|
+
for (const [queueName, queue] of this.queueCache) {
|
|
169
|
+
try {
|
|
170
|
+
await queue.close();
|
|
171
|
+
} catch (err) {
|
|
172
|
+
console.error(`[queue-metrics] Error closing queue ${queueName}:`, err);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
process.exit(0);
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
module.exports = {
|
|
179
|
+
QueueRedisMetricsClient
|
|
180
|
+
};
|
|
181
|
+
//# sourceMappingURL=metricsQueueRedisClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metricsQueueRedisClient.js","names":["Queue","require","RedisMetricsClient","QueueRedisMetricsClient","constructor","redisClient","metricsConfig","getConfiguredQueueNames","process","env","METRICS_APP_REDIS_BQ","Error","allQueues","split","map","q","trim","filter","Boolean","length","Set","startupValidation","queueNames","console","info","error","message","queueCache","Map","queueJobsGauge","createGauge","name","help","labelNames","withDefaultLabels","_setCleanupHandlers","collectSingleQueueMetrics","queueName","has","set","redis","isWorker","getEvents","sendEvents","queue","get","health","checkHealth","labels","getDefaultLabels","queue_name","status","waiting","active","succeeded","failed","delayed","warn","collectQueueMetrics","collectRedisMetrics","Promise","allSettled","gatewayPush","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","includes","startPush","intervalSec","_startPush","catch","err","on","cleanup","close","exit","module","exports"],"sources":["../src/metricsQueueRedisClient.js"],"sourcesContent":["const Queue = require('bee-queue')\nconst { RedisMetricsClient } = require('.')\n\n/**\n * QueueRedisMetricsClient extends MetricsClient to collect\n * Redis and Bee Queue metrics periodically and push them to Prometheus Pushgateway.\n *\n * @extends RedisMetricsClient\n */\nclass QueueRedisMetricsClient extends RedisMetricsClient {\n /**\n * @param {Object} options\n * @param {any} options.redisClient - Redis client instance (required)\n * @param {string} [options.appName] - Application name (from MetricsClient)\n * @param {string} [options.dynoId] - Dyno/instance ID (from MetricsClient)\n * @param {string} [options.processType] - Process type (from MetricsClient)\n * @param {boolean} [options.enabled] - Enable metrics collection (from MetricsClient)\n * @param {boolean} [options.logValues] - Log metrics values (from MetricsClient)\n * @param {string} [options.pushgatewayUrl] - PushGateway URL (from MetricsClient)\n * @param {string} [options.pushgatewaySecret] - PushGateway secret token (from MetricsClient)\n * @param {number} [options.intervalSec] - Interval in seconds for pushing metrics (from MetricsClient)\n * @param {boolean} [options.removeOldMetrics] - Remove old metrics by service (from MetricsClient)\n * @param {boolean} [options.scripDefaultMetrics] - Skip default metrics creation (from MetricsClient)\n * @param {function} [options.startupValidation] - Function to validate startup (from MetricsClient)\n */\n constructor({ redisClient, metricsConfig = {} } = {}) {\n const getConfiguredQueueNames = () => {\n if (!process.env.METRICS_APP_REDIS_BQ) {\n throw new Error(\n 'No queues configured for monitoring. Set METRICS_APP_REDIS_BQ with comma-separated queue names'\n )\n }\n\n const allQueues = process.env.METRICS_APP_REDIS_BQ.split(',')\n .map(q => q.trim())\n .filter(Boolean)\n\n if (allQueues.length === 0) {\n throw new Error(\n 'METRICS_APP_REDIS_BQ is empty or contains only whitespace. ' +\n 'Example: METRICS_APP_REDIS_BQ=\"adalo-compile,adalo-migrations\"'\n )\n }\n\n return [...new Set(allQueues)]\n }\n\n const startupValidation = () => {\n try {\n const queueNames = getConfiguredQueueNames()\n console.info(\n `[queue-metrics] Queue & Redis metrics collection starting for ${queueNames.length} queues`\n )\n return true\n } catch (error) {\n console.error(`[queue-metrics] ❌ Cannot start: ${error.message}`)\n console.error(\n `[queue-metrics] 💡 Example: METRICS_APP_REDIS_BQ=\"adalo-compile,adalo-migrations\"`\n )\n console.error(\n `[queue-metrics] 💡 Optional: METRICS_QUEUE_INTERVAL_SEC=\"10\"`\n )\n console.error(`[queue-metrics] Skipping queue metrics collection`)\n return false\n }\n }\n\n super({\n ...metricsConfig,\n redisClient,\n startupValidation,\n })\n\n this.getConfiguredQueueNames = getConfiguredQueueNames\n this.startupValidation = startupValidation\n\n /** Cache for queue objects to avoid multiple connections */\n this.queueCache = new Map()\n\n /** Gauge for queue jobs by status */\n this.queueJobsGauge = this.createGauge({\n name: 'app_queue_jobs_count',\n help: 'Number of app jobs in the queue by status',\n labelNames: this.withDefaultLabels(['queue_name', 'status']),\n })\n\n this._setCleanupHandlers()\n }\n\n /**\n * Collect metrics for a single Bee Queue and set gauges\n * @param {string} queueName - Name of the queue\n * @returns {Promise<void>}\n */\n collectSingleQueueMetrics = async queueName => {\n try {\n if (!this.queueCache.has(queueName)) {\n this.queueCache.set(\n queueName,\n new Queue(queueName, {\n redis: this.redisClient,\n isWorker: false,\n getEvents: false,\n sendEvents: false,\n })\n )\n }\n\n const queue = this.queueCache.get(queueName)\n const health = await queue.checkHealth()\n\n const labels = {\n ...this.getDefaultLabels(),\n queue_name: queueName,\n }\n\n this.queueJobsGauge.set(\n { ...labels, status: 'waiting' },\n health.waiting || 0\n )\n this.queueJobsGauge.set(\n { ...labels, status: 'active' },\n health.active || 0\n )\n this.queueJobsGauge.set(\n { ...labels, status: 'succeeded' },\n health.succeeded || 0\n )\n this.queueJobsGauge.set(\n { ...labels, status: 'failed' },\n health.failed || 0\n )\n this.queueJobsGauge.set(\n { ...labels, status: 'delayed' },\n health.delayed || 0\n )\n } catch (error) {\n console.warn(\n `[queue-metrics] Failed to collect metrics for queue ${queueName}:`,\n error.message\n )\n }\n }\n\n /**\n * Collect metrics for all queues and Redis, then push to Pushgateway\n * @returns {Promise<void>}\n */\n collectQueueMetrics = async () => {\n try {\n const queueNames = this.getConfiguredQueueNames()\n\n await this.collectRedisMetrics()\n await Promise.allSettled(\n queueNames.map(queueName => this.collectSingleQueueMetrics(queueName))\n )\n\n await this.gatewayPush()\n\n if (this.metricsLogValues) {\n const metricObjects = await this.registry.getMetricsAsJSON()\n console.info(\n `[queue-metrics] Collected metrics for ${queueNames.length} queues:`,\n JSON.stringify(metricObjects, null, 2)\n )\n }\n } catch (error) {\n if (\n error.message?.includes('No queues configured') ||\n error.message?.includes('METRICS_APP_REDIS_BQ')\n ) {\n console.error(\n `[queue-metrics] ❌ Configuration error: ${error.message}`\n )\n console.error(\n `[queue-metrics] 💡 Example config: METRICS_APP_REDIS_BQ=\"adalo-compile,adalo-migrations\"`\n )\n } else {\n console.error(\n `[queue-metrics] Failed to collect queue metrics: ${error.message}`\n )\n }\n throw error\n }\n }\n\n /**\n * Start periodic collection.\n * @param {number} [intervalSec=this.intervalSec] - Interval in seconds\n */\n startPush = (intervalSec = this.intervalSec) => {\n this._startPush(intervalSec, () => {\n this.collectQueueMetrics().catch(err => {\n console.error(\n `[queue-metrics] Failed to collect queue & Redis metrics:`,\n err\n )\n })\n })\n }\n\n _setCleanupHandlers = () => {\n process.on('SIGINT', this.cleanup)\n process.on('SIGTERM', this.cleanup)\n }\n\n /**\n * Cleanup queues and Redis client\n */\n cleanup = async () => {\n for (const [queueName, queue] of this.queueCache) {\n try {\n await queue.close()\n } catch (err) {\n console.error(`[queue-metrics] Error closing queue ${queueName}:`, err)\n }\n }\n process.exit(0)\n }\n}\n\nmodule.exports = { QueueRedisMetricsClient }\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,27 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* RedisMetricsClient extends MetricsClient to collect
|
|
3
|
-
* Redis
|
|
3
|
+
* Redis metrics periodically and push them to Prometheus Pushgateway.
|
|
4
4
|
*
|
|
5
5
|
* @extends MetricsClient
|
|
6
6
|
*/
|
|
7
7
|
export class RedisMetricsClient extends MetricsClient {
|
|
8
8
|
/**
|
|
9
|
-
* @param {Object} options
|
|
9
|
+
* @param {Object} options
|
|
10
10
|
* @param {any} options.redisClient - Redis client instance (required)
|
|
11
|
-
* @param {
|
|
11
|
+
* @param {string} [options.appName] - Application name (from MetricsClient)
|
|
12
|
+
* @param {string} [options.dynoId] - Dyno/instance ID (from MetricsClient)
|
|
13
|
+
* @param {string} [options.processType] - Process type (from MetricsClient)
|
|
14
|
+
* @param {boolean} [options.enabled] - Enable metrics collection (from MetricsClient)
|
|
15
|
+
* @param {boolean} [options.logValues] - Log metrics values (from MetricsClient)
|
|
16
|
+
* @param {string} [options.pushgatewayUrl] - PushGateway URL (from MetricsClient)
|
|
17
|
+
* @param {string} [options.pushgatewaySecret] - PushGateway secret token (from MetricsClient)
|
|
18
|
+
* @param {number} [options.intervalSec] - Interval in seconds for pushing metrics (from MetricsClient)
|
|
19
|
+
* @param {boolean} [options.removeOldMetrics] - Remove old metrics by service (from MetricsClient)
|
|
20
|
+
* @param {boolean} [options.scripDefaultMetrics] - Skip default metrics creation (from MetricsClient)
|
|
21
|
+
* @param {function} [options.startupValidation] - Function to validate startup (from MetricsClient)
|
|
12
22
|
*/
|
|
13
|
-
constructor({ redisClient, metricsConfig }?: {
|
|
23
|
+
constructor({ redisClient, ...metricsConfig }?: {
|
|
14
24
|
redisClient: any;
|
|
15
|
-
|
|
25
|
+
appName?: string | undefined;
|
|
26
|
+
dynoId?: string | undefined;
|
|
27
|
+
processType?: string | undefined;
|
|
28
|
+
enabled?: boolean | undefined;
|
|
29
|
+
logValues?: boolean | undefined;
|
|
30
|
+
pushgatewayUrl?: string | undefined;
|
|
31
|
+
pushgatewaySecret?: string | undefined;
|
|
32
|
+
intervalSec?: number | undefined;
|
|
33
|
+
removeOldMetrics?: boolean | undefined;
|
|
34
|
+
scripDefaultMetrics?: boolean | undefined;
|
|
35
|
+
startupValidation?: Function | undefined;
|
|
16
36
|
});
|
|
17
|
-
|
|
18
|
-
startupValidation: () => boolean;
|
|
19
|
-
/** Redis client used for queue & Redis metrics */
|
|
37
|
+
/** Redis client used for metrics */
|
|
20
38
|
redisClient: any;
|
|
21
|
-
/** Cache for queue objects to avoid multiple connections */
|
|
22
|
-
queueCache: Map<any, any>;
|
|
23
|
-
/** Gauge for queue jobs by status */
|
|
24
|
-
queueJobsGauge: import("prom-client").Gauge<string>;
|
|
25
39
|
/** Gauge for Redis client connections */
|
|
26
40
|
redisConnectionsGauge: import("prom-client").Gauge<string>;
|
|
27
41
|
/** Gauge for Redis memory usage */
|
|
@@ -34,16 +48,10 @@ export class RedisMetricsClient extends MetricsClient {
|
|
|
34
48
|
*/
|
|
35
49
|
collectRedisMetrics: () => Promise<void>;
|
|
36
50
|
/**
|
|
37
|
-
* Collect metrics for
|
|
38
|
-
* @param {string} queueName - Name of the queue
|
|
51
|
+
* Collect metrics for all Redis and push to Prometheus Pushgateway
|
|
39
52
|
* @returns {Promise<void>}
|
|
40
53
|
*/
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Collect metrics for all queues and Redis, then push to Pushgateway
|
|
44
|
-
* @returns {Promise<void>}
|
|
45
|
-
*/
|
|
46
|
-
collectQueueMetrics: () => Promise<void>;
|
|
54
|
+
pushRedisMetrics: () => Promise<void>;
|
|
47
55
|
/**
|
|
48
56
|
* Start periodic collection.
|
|
49
57
|
* @param {number} [intervalSec=this.intervalSec] - Interval in seconds
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metricsRedisClient.d.ts","sourceRoot":"","sources":["../src/metricsRedisClient.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"metricsRedisClient.d.ts","sourceRoot":"","sources":["../src/metricsRedisClient.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH;IACE;;;;;;;;;;;;;;OAcG;IACH;QAbwB,WAAW,EAAxB,GAAG;QACc,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QAChB,mBAAmB;QAClB,iBAAiB;OAwC9C;IAzBC,oCAAoC;IACpC,iBAA8B;IAE9B,yCAAyC;IACzC,2DAIE;IAEF,mCAAmC;IACnC,sDAIE;IAEF,sCAAsC;IACtC,qDAIE;IAKJ;;;OAGG;IACH,2BAFa,QAAQ,IAAI,CAAC,CAiEzB;IAED;;;OAGG;IACH,wBAFa,QAAQ,IAAI,CAAC,CAoBzB;IAED;;;OAGG;IACH,sDAMC;CAkBF"}
|
|
@@ -1,73 +1,46 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const Queue = require('bee-queue');
|
|
4
3
|
const {
|
|
5
4
|
MetricsClient
|
|
6
5
|
} = require('.');
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* RedisMetricsClient extends MetricsClient to collect
|
|
10
|
-
* Redis
|
|
9
|
+
* Redis metrics periodically and push them to Prometheus Pushgateway.
|
|
11
10
|
*
|
|
12
11
|
* @extends MetricsClient
|
|
13
12
|
*/
|
|
14
13
|
class RedisMetricsClient extends MetricsClient {
|
|
15
14
|
/**
|
|
16
|
-
* @param {Object} options
|
|
15
|
+
* @param {Object} options
|
|
17
16
|
* @param {any} options.redisClient - Redis client instance (required)
|
|
18
|
-
* @param {
|
|
17
|
+
* @param {string} [options.appName] - Application name (from MetricsClient)
|
|
18
|
+
* @param {string} [options.dynoId] - Dyno/instance ID (from MetricsClient)
|
|
19
|
+
* @param {string} [options.processType] - Process type (from MetricsClient)
|
|
20
|
+
* @param {boolean} [options.enabled] - Enable metrics collection (from MetricsClient)
|
|
21
|
+
* @param {boolean} [options.logValues] - Log metrics values (from MetricsClient)
|
|
22
|
+
* @param {string} [options.pushgatewayUrl] - PushGateway URL (from MetricsClient)
|
|
23
|
+
* @param {string} [options.pushgatewaySecret] - PushGateway secret token (from MetricsClient)
|
|
24
|
+
* @param {number} [options.intervalSec] - Interval in seconds for pushing metrics (from MetricsClient)
|
|
25
|
+
* @param {boolean} [options.removeOldMetrics] - Remove old metrics by service (from MetricsClient)
|
|
26
|
+
* @param {boolean} [options.scripDefaultMetrics] - Skip default metrics creation (from MetricsClient)
|
|
27
|
+
* @param {function} [options.startupValidation] - Function to validate startup (from MetricsClient)
|
|
19
28
|
*/
|
|
20
29
|
constructor({
|
|
21
30
|
redisClient,
|
|
22
|
-
metricsConfig
|
|
31
|
+
...metricsConfig
|
|
23
32
|
} = {}) {
|
|
24
|
-
const
|
|
25
|
-
const getConfiguredQueueNames = () => {
|
|
26
|
-
if (!process.env.METRICS_APP_REDIS_BQ) {
|
|
27
|
-
throw new Error('No queues configured for monitoring. Set METRICS_APP_REDIS_BQ with comma-separated queue names');
|
|
28
|
-
}
|
|
29
|
-
const allQueues = process.env.METRICS_APP_REDIS_BQ.split(',').map(q => q.trim()).filter(Boolean);
|
|
30
|
-
if (allQueues.length === 0) {
|
|
31
|
-
throw new Error('METRICS_APP_REDIS_BQ is empty or contains only whitespace. ' + 'Example: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"');
|
|
32
|
-
}
|
|
33
|
-
return [...new Set(allQueues)];
|
|
34
|
-
};
|
|
35
|
-
const startupValidation = () => {
|
|
36
|
-
try {
|
|
37
|
-
const queueNames = getConfiguredQueueNames();
|
|
38
|
-
console.info(`[queue-metrics] Queue & Redis metrics collection starting for ${queueNames.length} queues`);
|
|
39
|
-
return true;
|
|
40
|
-
} catch (error) {
|
|
41
|
-
console.error(`[queue-metrics] ❌ Cannot start: ${error.message}`);
|
|
42
|
-
console.error(`[queue-metrics] 💡 Example: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"`);
|
|
43
|
-
console.error(`[queue-metrics] 💡 Optional: METRICS_QUEUE_INTERVAL_SEC="10"`);
|
|
44
|
-
console.error(`[queue-metrics] Skipping queue metrics collection`);
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
};
|
|
33
|
+
const intervalSec = metricsConfig.intervalSec || parseInt(process.env.METRICS_QUEUE_INTERVAL_SEC || '', 10) || 5;
|
|
48
34
|
super({
|
|
49
35
|
...metricsConfig,
|
|
50
36
|
scripDefaultMetrics: true,
|
|
51
|
-
processType: metricsConfig.processType || '
|
|
52
|
-
intervalSec
|
|
53
|
-
startupValidation
|
|
37
|
+
processType: metricsConfig.processType || 'redis-metrics',
|
|
38
|
+
intervalSec
|
|
54
39
|
});
|
|
55
|
-
this.getConfiguredQueueNames = getConfiguredQueueNames;
|
|
56
|
-
this.startupValidation = startupValidation;
|
|
57
40
|
|
|
58
|
-
/** Redis client used for
|
|
41
|
+
/** Redis client used for metrics */
|
|
59
42
|
this.redisClient = redisClient;
|
|
60
43
|
|
|
61
|
-
/** Cache for queue objects to avoid multiple connections */
|
|
62
|
-
this.queueCache = new Map();
|
|
63
|
-
|
|
64
|
-
/** Gauge for queue jobs by status */
|
|
65
|
-
this.queueJobsGauge = this.createGauge({
|
|
66
|
-
name: 'app_queue_jobs_count',
|
|
67
|
-
help: 'Number of app jobs in the queue by status',
|
|
68
|
-
labelNames: this.withDefaultLabels(['queue_name', 'status'])
|
|
69
|
-
});
|
|
70
|
-
|
|
71
44
|
/** Gauge for Redis client connections */
|
|
72
45
|
this.redisConnectionsGauge = this.createGauge({
|
|
73
46
|
name: 'app_redis_connections_count',
|
|
@@ -138,72 +111,19 @@ class RedisMetricsClient extends MetricsClient {
|
|
|
138
111
|
};
|
|
139
112
|
|
|
140
113
|
/**
|
|
141
|
-
* Collect metrics for
|
|
142
|
-
* @param {string} queueName - Name of the queue
|
|
114
|
+
* Collect metrics for all Redis and push to Prometheus Pushgateway
|
|
143
115
|
* @returns {Promise<void>}
|
|
144
116
|
*/
|
|
145
|
-
|
|
117
|
+
pushRedisMetrics = async () => {
|
|
146
118
|
try {
|
|
147
|
-
if (!this.queueCache.has(queueName)) {
|
|
148
|
-
this.queueCache.set(queueName, new Queue(queueName, {
|
|
149
|
-
redis: this.redisClient,
|
|
150
|
-
isWorker: false,
|
|
151
|
-
getEvents: false,
|
|
152
|
-
sendEvents: false
|
|
153
|
-
}));
|
|
154
|
-
}
|
|
155
|
-
const queue = this.queueCache.get(queueName);
|
|
156
|
-
const health = await queue.checkHealth();
|
|
157
|
-
const labels = {
|
|
158
|
-
...this.getDefaultLabels(),
|
|
159
|
-
queue_name: queueName
|
|
160
|
-
};
|
|
161
|
-
this.queueJobsGauge.set({
|
|
162
|
-
...labels,
|
|
163
|
-
status: 'waiting'
|
|
164
|
-
}, health.waiting || 0);
|
|
165
|
-
this.queueJobsGauge.set({
|
|
166
|
-
...labels,
|
|
167
|
-
status: 'active'
|
|
168
|
-
}, health.active || 0);
|
|
169
|
-
this.queueJobsGauge.set({
|
|
170
|
-
...labels,
|
|
171
|
-
status: 'succeeded'
|
|
172
|
-
}, health.succeeded || 0);
|
|
173
|
-
this.queueJobsGauge.set({
|
|
174
|
-
...labels,
|
|
175
|
-
status: 'failed'
|
|
176
|
-
}, health.failed || 0);
|
|
177
|
-
this.queueJobsGauge.set({
|
|
178
|
-
...labels,
|
|
179
|
-
status: 'delayed'
|
|
180
|
-
}, health.delayed || 0);
|
|
181
|
-
} catch (error) {
|
|
182
|
-
console.warn(`[queue-metrics] Failed to collect metrics for queue ${queueName}:`, error.message);
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Collect metrics for all queues and Redis, then push to Pushgateway
|
|
188
|
-
* @returns {Promise<void>}
|
|
189
|
-
*/
|
|
190
|
-
collectQueueMetrics = async () => {
|
|
191
|
-
try {
|
|
192
|
-
const queueNames = this.getConfiguredQueueNames();
|
|
193
119
|
await this.collectRedisMetrics();
|
|
194
|
-
await Promise.allSettled(queueNames.map(queueName => this.collectSingleQueueMetrics(queueName)));
|
|
195
120
|
await this.gatewayPush();
|
|
196
121
|
if (this.metricsLogValues) {
|
|
197
122
|
const metricObjects = await this.registry.getMetricsAsJSON();
|
|
198
|
-
console.info(`[
|
|
123
|
+
console.info(`[redis-metrics] Collected metrics for Redis`, JSON.stringify(metricObjects, null, 2));
|
|
199
124
|
}
|
|
200
125
|
} catch (error) {
|
|
201
|
-
|
|
202
|
-
console.error(`[queue-metrics] ❌ Configuration error: ${error.message}`);
|
|
203
|
-
console.error(`[queue-metrics] 💡 Example config: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"`);
|
|
204
|
-
} else {
|
|
205
|
-
console.error(`[queue-metrics] Failed to collect queue metrics: ${error.message}`);
|
|
206
|
-
}
|
|
126
|
+
console.error(`[redis-metrics] Failed to collect Redis metrics: ${error.message}`);
|
|
207
127
|
throw error;
|
|
208
128
|
}
|
|
209
129
|
};
|
|
@@ -214,32 +134,27 @@ class RedisMetricsClient extends MetricsClient {
|
|
|
214
134
|
*/
|
|
215
135
|
startPush = (intervalSec = this.intervalSec) => {
|
|
216
136
|
this._startPush(intervalSec, () => {
|
|
217
|
-
this.
|
|
218
|
-
console.error(`[
|
|
137
|
+
this.pushRedisMetrics().catch(err => {
|
|
138
|
+
console.error(`[redis-metrics] Failed to push Redis metrics:`, err);
|
|
219
139
|
});
|
|
220
140
|
});
|
|
221
141
|
};
|
|
222
|
-
_setCleanupHandlers = () => {
|
|
223
|
-
process.on('SIGINT', this.cleanup);
|
|
224
|
-
process.on('SIGTERM', this.cleanup);
|
|
225
|
-
};
|
|
226
142
|
|
|
227
143
|
/**
|
|
228
|
-
* Cleanup
|
|
144
|
+
* Cleanup Redis client
|
|
229
145
|
*/
|
|
230
146
|
cleanup = async () => {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
console.error(`[queue-metrics] Error closing queue ${queueName}:`, err);
|
|
236
|
-
}
|
|
147
|
+
try {
|
|
148
|
+
this.redisClient?.quit();
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.error('[redis-metrics] Error closing Redis client:', err);
|
|
237
151
|
}
|
|
238
|
-
|
|
239
|
-
// Close Redis connection
|
|
240
|
-
this.redisClient?.quit();
|
|
241
152
|
process.exit(0);
|
|
242
153
|
};
|
|
154
|
+
_setCleanupHandlers = () => {
|
|
155
|
+
process.on('SIGINT', this.cleanup);
|
|
156
|
+
process.on('SIGTERM', this.cleanup);
|
|
157
|
+
};
|
|
243
158
|
}
|
|
244
159
|
module.exports = {
|
|
245
160
|
RedisMetricsClient
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metricsRedisClient.js","names":["Queue","require","MetricsClient","RedisMetricsClient","constructor","redisClient","metricsConfig","METRICS_QUEUE_INTERVAL_SEC","intervalSec","parseInt","process","env","getConfiguredQueueNames","METRICS_APP_REDIS_BQ","Error","allQueues","split","map","q","trim","filter","Boolean","length","Set","startupValidation","queueNames","console","info","error","message","scripDefaultMetrics","processType","queueCache","Map","queueJobsGauge","createGauge","name","help","labelNames","withDefaultLabels","redisConnectionsGauge","redisMemoryGauge","redisStatsGauge","_setCleanupHandlers","collectRedisMetrics","getRedisInfo","section","Promise","resolve","reject","err","result","clientsInfo","memoryInfo","statsInfo","all","labels","getDefaultLabels","parseRedisInfo","infoStr","Object","fromEntries","line","startsWith","parts","clients","memory","stats","connected_clients","set","connection_type","used_memory","memory_type","maxmemory","instantaneous_ops_per_sec","operation","warn","collectSingleQueueMetrics","queueName","has","redis","isWorker","getEvents","sendEvents","queue","get","health","checkHealth","queue_name","status","waiting","active","succeeded","failed","delayed","collectQueueMetrics","allSettled","gatewayPush","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","includes","startPush","_startPush","catch","on","cleanup","close","quit","exit","module","exports"],"sources":["../src/metricsRedisClient.js"],"sourcesContent":["const Queue = require('bee-queue')\nconst { MetricsClient } = require('.')\n\n/**\n * RedisMetricsClient extends MetricsClient to collect\n * Redis and Bee Queue metrics periodically and push them to Prometheus Pushgateway.\n *\n * @extends MetricsClient\n */\nclass RedisMetricsClient extends MetricsClient {\n /**\n * @param {Object} options - Metrics client options + Redis client\n * @param {any} options.redisClient - Redis client instance (required)\n * @param {Object} [options.metricsConfig] - MetricsClient configuration overrides\n */\n constructor({ redisClient, metricsConfig = {} } = {}) {\n const METRICS_QUEUE_INTERVAL_SEC =\n metricsConfig.intervalSec ||\n parseInt(process.env.METRICS_QUEUE_INTERVAL_SEC || '', 10) ||\n 5\n\n const getConfiguredQueueNames = () => {\n if (!process.env.METRICS_APP_REDIS_BQ) {\n throw new Error(\n 'No queues configured for monitoring. Set METRICS_APP_REDIS_BQ with comma-separated queue names'\n )\n }\n\n const allQueues = process.env.METRICS_APP_REDIS_BQ.split(',')\n .map(q => q.trim())\n .filter(Boolean)\n\n if (allQueues.length === 0) {\n throw new Error(\n 'METRICS_APP_REDIS_BQ is empty or contains only whitespace. ' +\n 'Example: METRICS_APP_REDIS_BQ=\"adalo-compile,adalo-migrations\"'\n )\n }\n\n return [...new Set(allQueues)]\n }\n\n const startupValidation = () => {\n try {\n const queueNames = getConfiguredQueueNames()\n console.info(\n `[queue-metrics] Queue & Redis metrics collection starting for ${queueNames.length} queues`\n )\n return true\n } catch (error) {\n console.error(`[queue-metrics] ❌ Cannot start: ${error.message}`)\n console.error(\n `[queue-metrics] 💡 Example: METRICS_APP_REDIS_BQ=\"adalo-compile,adalo-migrations\"`\n )\n console.error(\n `[queue-metrics] 💡 Optional: METRICS_QUEUE_INTERVAL_SEC=\"10\"`\n )\n console.error(`[queue-metrics] Skipping queue metrics collection`)\n return false\n }\n }\n\n super({\n ...metricsConfig,\n scripDefaultMetrics: true,\n processType: metricsConfig.processType || 'queue-metrics',\n intervalSec: METRICS_QUEUE_INTERVAL_SEC,\n startupValidation,\n })\n\n this.getConfiguredQueueNames = getConfiguredQueueNames\n this.startupValidation = startupValidation\n\n /** Redis client used for queue & Redis metrics */\n this.redisClient = redisClient\n\n /** Cache for queue objects to avoid multiple connections */\n this.queueCache = new Map()\n\n /** Gauge for queue jobs by status */\n this.queueJobsGauge = this.createGauge({\n name: 'app_queue_jobs_count',\n help: 'Number of app jobs in the queue by status',\n labelNames: this.withDefaultLabels(['queue_name', 'status']),\n })\n\n /** Gauge for Redis client connections */\n this.redisConnectionsGauge = this.createGauge({\n name: 'app_redis_connections_count',\n help: 'Number of Redis client connections',\n labelNames: this.withDefaultLabels(['connection_type']),\n })\n\n /** Gauge for Redis memory usage */\n this.redisMemoryGauge = this.createGauge({\n name: 'app_redis_memory_bytes',\n help: 'Redis memory usage in bytes',\n labelNames: this.withDefaultLabels(['memory_type']),\n })\n\n /** Gauge for Redis operation stats */\n this.redisStatsGauge = this.createGauge({\n name: 'app_redis_stats_total',\n help: 'Redis operation statistics',\n labelNames: this.withDefaultLabels(['operation']),\n })\n\n this._setCleanupHandlers()\n }\n\n /**\n * Collect basic Redis INFO metrics: clients, memory, stats\n * @returns {Promise<void>}\n */\n collectRedisMetrics = async () => {\n try {\n const getRedisInfo = section =>\n new Promise((resolve, reject) => {\n this.redisClient.info(section, (err, result) => {\n if (err) reject(err)\n else resolve(result)\n })\n })\n\n const [clientsInfo, memoryInfo, statsInfo] = await Promise.all([\n getRedisInfo('clients'),\n getRedisInfo('memory'),\n getRedisInfo('stats'),\n ])\n\n const labels = this.getDefaultLabels()\n\n const parseRedisInfo = infoStr =>\n Object.fromEntries(\n infoStr\n .split('\\r\\n')\n .filter(line => line && !line.startsWith('#'))\n .map(line => line.split(':', 2))\n .filter(parts => parts.length === 2 && parts[0] && parts[1])\n )\n\n const clients = parseRedisInfo(clientsInfo)\n const memory = parseRedisInfo(memoryInfo)\n const stats = parseRedisInfo(statsInfo)\n\n if (clients.connected_clients) {\n this.redisConnectionsGauge.set(\n { ...labels, connection_type: 'connected' },\n parseInt(clients.connected_clients, 10) || 0\n )\n }\n\n if (memory.used_memory) {\n this.redisMemoryGauge.set(\n { ...labels, memory_type: 'used' },\n parseInt(memory.used_memory, 10) || 0\n )\n }\n if (memory.maxmemory) {\n this.redisMemoryGauge.set(\n { ...labels, memory_type: 'max' },\n parseInt(memory.maxmemory, 10) || 0\n )\n }\n\n if (stats.instantaneous_ops_per_sec) {\n this.redisStatsGauge.set(\n { ...labels, operation: 'ops_per_sec' },\n parseInt(stats.instantaneous_ops_per_sec, 10) || 0\n )\n }\n } catch (error) {\n console.warn(\n `[redis-metrics] Failed to collect Redis metrics:`,\n error.message\n )\n }\n }\n\n /**\n * Collect metrics for a single Bee Queue and set gauges\n * @param {string} queueName - Name of the queue\n * @returns {Promise<void>}\n */\n collectSingleQueueMetrics = async queueName => {\n try {\n if (!this.queueCache.has(queueName)) {\n this.queueCache.set(\n queueName,\n new Queue(queueName, {\n redis: this.redisClient,\n isWorker: false,\n getEvents: false,\n sendEvents: false,\n })\n )\n }\n\n const queue = this.queueCache.get(queueName)\n const health = await queue.checkHealth()\n\n const labels = {\n ...this.getDefaultLabels(),\n queue_name: queueName,\n }\n\n this.queueJobsGauge.set(\n { ...labels, status: 'waiting' },\n health.waiting || 0\n )\n this.queueJobsGauge.set(\n { ...labels, status: 'active' },\n health.active || 0\n )\n this.queueJobsGauge.set(\n { ...labels, status: 'succeeded' },\n health.succeeded || 0\n )\n this.queueJobsGauge.set(\n { ...labels, status: 'failed' },\n health.failed || 0\n )\n this.queueJobsGauge.set(\n { ...labels, status: 'delayed' },\n health.delayed || 0\n )\n } catch (error) {\n console.warn(\n `[queue-metrics] Failed to collect metrics for queue ${queueName}:`,\n error.message\n )\n }\n }\n\n /**\n * Collect metrics for all queues and Redis, then push to Pushgateway\n * @returns {Promise<void>}\n */\n collectQueueMetrics = async () => {\n try {\n const queueNames = this.getConfiguredQueueNames()\n\n await this.collectRedisMetrics()\n await Promise.allSettled(\n queueNames.map(queueName => this.collectSingleQueueMetrics(queueName))\n )\n\n await this.gatewayPush()\n\n if (this.metricsLogValues) {\n const metricObjects = await this.registry.getMetricsAsJSON()\n console.info(\n `[queue-metrics] Collected metrics for ${queueNames.length} queues:`,\n JSON.stringify(metricObjects, null, 2)\n )\n }\n } catch (error) {\n if (\n error.message?.includes('No queues configured') ||\n error.message?.includes('METRICS_APP_REDIS_BQ')\n ) {\n console.error(\n `[queue-metrics] ❌ Configuration error: ${error.message}`\n )\n console.error(\n `[queue-metrics] 💡 Example config: METRICS_APP_REDIS_BQ=\"adalo-compile,adalo-migrations\"`\n )\n } else {\n console.error(\n `[queue-metrics] Failed to collect queue metrics: ${error.message}`\n )\n }\n throw error\n }\n }\n\n /**\n * Start periodic collection.\n * @param {number} [intervalSec=this.intervalSec] - Interval in seconds\n */\n startPush = (intervalSec = this.intervalSec) => {\n this._startPush(intervalSec, () => {\n this.collectQueueMetrics().catch(err => {\n console.error(\n `[queue-metrics] Failed to collect queue & Redis metrics:`,\n err\n )\n })\n })\n }\n\n _setCleanupHandlers = () => {\n process.on('SIGINT', this.cleanup)\n process.on('SIGTERM', this.cleanup)\n }\n\n /**\n * Cleanup queues and Redis client\n */\n cleanup = async () => {\n for (const [queueName, queue] of this.queueCache) {\n try {\n await queue.close()\n } catch (err) {\n console.error(`[queue-metrics] Error closing queue ${queueName}:`, err)\n }\n }\n\n // Close Redis connection\n this.redisClient?.quit()\n process.exit(0)\n }\n}\n\nmodule.exports = { RedisMetricsClient }\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"metricsRedisClient.js","names":["MetricsClient","require","RedisMetricsClient","constructor","redisClient","metricsConfig","intervalSec","parseInt","process","env","METRICS_QUEUE_INTERVAL_SEC","scripDefaultMetrics","processType","redisConnectionsGauge","createGauge","name","help","labelNames","withDefaultLabels","redisMemoryGauge","redisStatsGauge","_setCleanupHandlers","collectRedisMetrics","getRedisInfo","section","Promise","resolve","reject","info","err","result","clientsInfo","memoryInfo","statsInfo","all","labels","getDefaultLabels","parseRedisInfo","infoStr","Object","fromEntries","split","filter","line","startsWith","map","parts","length","clients","memory","stats","connected_clients","set","connection_type","used_memory","memory_type","maxmemory","instantaneous_ops_per_sec","operation","error","console","warn","message","pushRedisMetrics","gatewayPush","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","startPush","_startPush","catch","cleanup","quit","exit","on","module","exports"],"sources":["../src/metricsRedisClient.js"],"sourcesContent":["const { MetricsClient } = require('.')\n\n/**\n * RedisMetricsClient extends MetricsClient to collect\n * Redis metrics periodically and push them to Prometheus Pushgateway.\n *\n * @extends MetricsClient\n */\nclass RedisMetricsClient extends MetricsClient {\n /**\n * @param {Object} options\n * @param {any} options.redisClient - Redis client instance (required)\n * @param {string} [options.appName] - Application name (from MetricsClient)\n * @param {string} [options.dynoId] - Dyno/instance ID (from MetricsClient)\n * @param {string} [options.processType] - Process type (from MetricsClient)\n * @param {boolean} [options.enabled] - Enable metrics collection (from MetricsClient)\n * @param {boolean} [options.logValues] - Log metrics values (from MetricsClient)\n * @param {string} [options.pushgatewayUrl] - PushGateway URL (from MetricsClient)\n * @param {string} [options.pushgatewaySecret] - PushGateway secret token (from MetricsClient)\n * @param {number} [options.intervalSec] - Interval in seconds for pushing metrics (from MetricsClient)\n * @param {boolean} [options.removeOldMetrics] - Remove old metrics by service (from MetricsClient)\n * @param {boolean} [options.scripDefaultMetrics] - Skip default metrics creation (from MetricsClient)\n * @param {function} [options.startupValidation] - Function to validate startup (from MetricsClient)\n */\n constructor({ redisClient, ...metricsConfig } = {}) {\n const intervalSec =\n metricsConfig.intervalSec ||\n parseInt(process.env.METRICS_QUEUE_INTERVAL_SEC || '', 10) ||\n 5\n\n super({\n ...metricsConfig,\n scripDefaultMetrics: true,\n processType: metricsConfig.processType || 'redis-metrics',\n intervalSec,\n })\n\n /** Redis client used for metrics */\n this.redisClient = redisClient\n\n /** Gauge for Redis client connections */\n this.redisConnectionsGauge = this.createGauge({\n name: 'app_redis_connections_count',\n help: 'Number of Redis client connections',\n labelNames: this.withDefaultLabels(['connection_type']),\n })\n\n /** Gauge for Redis memory usage */\n this.redisMemoryGauge = this.createGauge({\n name: 'app_redis_memory_bytes',\n help: 'Redis memory usage in bytes',\n labelNames: this.withDefaultLabels(['memory_type']),\n })\n\n /** Gauge for Redis operation stats */\n this.redisStatsGauge = this.createGauge({\n name: 'app_redis_stats_total',\n help: 'Redis operation statistics',\n labelNames: this.withDefaultLabels(['operation']),\n })\n\n this._setCleanupHandlers()\n }\n\n /**\n * Collect basic Redis INFO metrics: clients, memory, stats\n * @returns {Promise<void>}\n */\n collectRedisMetrics = async () => {\n try {\n const getRedisInfo = section =>\n new Promise((resolve, reject) => {\n this.redisClient.info(section, (err, result) => {\n if (err) reject(err)\n else resolve(result)\n })\n })\n\n const [clientsInfo, memoryInfo, statsInfo] = await Promise.all([\n getRedisInfo('clients'),\n getRedisInfo('memory'),\n getRedisInfo('stats'),\n ])\n\n const labels = this.getDefaultLabels()\n\n const parseRedisInfo = infoStr =>\n Object.fromEntries(\n infoStr\n .split('\\r\\n')\n .filter(line => line && !line.startsWith('#'))\n .map(line => line.split(':', 2))\n .filter(parts => parts.length === 2 && parts[0] && parts[1])\n )\n\n const clients = parseRedisInfo(clientsInfo)\n const memory = parseRedisInfo(memoryInfo)\n const stats = parseRedisInfo(statsInfo)\n\n if (clients.connected_clients) {\n this.redisConnectionsGauge.set(\n { ...labels, connection_type: 'connected' },\n parseInt(clients.connected_clients, 10) || 0\n )\n }\n\n if (memory.used_memory) {\n this.redisMemoryGauge.set(\n { ...labels, memory_type: 'used' },\n parseInt(memory.used_memory, 10) || 0\n )\n }\n if (memory.maxmemory) {\n this.redisMemoryGauge.set(\n { ...labels, memory_type: 'max' },\n parseInt(memory.maxmemory, 10) || 0\n )\n }\n\n if (stats.instantaneous_ops_per_sec) {\n this.redisStatsGauge.set(\n { ...labels, operation: 'ops_per_sec' },\n parseInt(stats.instantaneous_ops_per_sec, 10) || 0\n )\n }\n } catch (error) {\n console.warn(\n `[redis-metrics] Failed to collect Redis metrics:`,\n error.message\n )\n }\n }\n\n /**\n * Collect metrics for all Redis and push to Prometheus Pushgateway\n * @returns {Promise<void>}\n */\n pushRedisMetrics = async () => {\n try {\n await this.collectRedisMetrics()\n await this.gatewayPush()\n\n if (this.metricsLogValues) {\n const metricObjects = await this.registry.getMetricsAsJSON()\n console.info(\n `[redis-metrics] Collected metrics for Redis`,\n JSON.stringify(metricObjects, null, 2)\n )\n }\n } catch (error) {\n console.error(\n `[redis-metrics] Failed to collect Redis metrics: ${error.message}`\n )\n throw error\n }\n }\n\n /**\n * Start periodic collection.\n * @param {number} [intervalSec=this.intervalSec] - Interval in seconds\n */\n startPush = (intervalSec = this.intervalSec) => {\n this._startPush(intervalSec, () => {\n this.pushRedisMetrics().catch(err => {\n console.error(`[redis-metrics] Failed to push Redis metrics:`, err)\n })\n })\n }\n\n /**\n * Cleanup Redis client\n */\n cleanup = async () => {\n try {\n this.redisClient?.quit()\n } catch (err) {\n console.error('[redis-metrics] Error closing Redis client:', err)\n }\n process.exit(0)\n }\n\n _setCleanupHandlers = () => {\n process.on('SIGINT', this.cleanup)\n process.on('SIGTERM', this.cleanup)\n }\n}\n\nmodule.exports = { RedisMetricsClient }\n"],"mappings":";;AAAA,MAAM;EAAEA;AAAc,CAAC,GAAGC,OAAO,CAAC,GAAG,CAAC;;AAEtC;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,kBAAkB,SAASF,aAAa,CAAC;EAC7C;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEG,WAAWA,CAAC;IAAEC,WAAW;IAAE,GAAGC;EAAc,CAAC,GAAG,CAAC,CAAC,EAAE;IAClD,MAAMC,WAAW,GACfD,aAAa,CAACC,WAAW,IACzBC,QAAQ,CAACC,OAAO,CAACC,GAAG,CAACC,0BAA0B,IAAI,EAAE,EAAE,EAAE,CAAC,IAC1D,CAAC;IAEH,KAAK,CAAC;MACJ,GAAGL,aAAa;MAChBM,mBAAmB,EAAE,IAAI;MACzBC,WAAW,EAAEP,aAAa,CAACO,WAAW,IAAI,eAAe;MACzDN;IACF,CAAC,CAAC;;IAEF;IACA,IAAI,CAACF,WAAW,GAAGA,WAAW;;IAE9B;IACA,IAAI,CAACS,qBAAqB,GAAG,IAAI,CAACC,WAAW,CAAC;MAC5CC,IAAI,EAAE,6BAA6B;MACnCC,IAAI,EAAE,oCAAoC;MAC1CC,UAAU,EAAE,IAAI,CAACC,iBAAiB,CAAC,CAAC,iBAAiB,CAAC;IACxD,CAAC,CAAC;;IAEF;IACA,IAAI,CAACC,gBAAgB,GAAG,IAAI,CAACL,WAAW,CAAC;MACvCC,IAAI,EAAE,wBAAwB;MAC9BC,IAAI,EAAE,6BAA6B;MACnCC,UAAU,EAAE,IAAI,CAACC,iBAAiB,CAAC,CAAC,aAAa,CAAC;IACpD,CAAC,CAAC;;IAEF;IACA,IAAI,CAACE,eAAe,GAAG,IAAI,CAACN,WAAW,CAAC;MACtCC,IAAI,EAAE,uBAAuB;MAC7BC,IAAI,EAAE,4BAA4B;MAClCC,UAAU,EAAE,IAAI,CAACC,iBAAiB,CAAC,CAAC,WAAW,CAAC;IAClD,CAAC,CAAC;IAEF,IAAI,CAACG,mBAAmB,CAAC,CAAC;EAC5B;;EAEA;AACF;AACA;AACA;EACEC,mBAAmB,GAAG,MAAAA,CAAA,KAAY;IAChC,IAAI;MACF,MAAMC,YAAY,GAAGC,OAAO,IAC1B,IAAIC,OAAO,CAAC,CAACC,OAAO,EAAEC,MAAM,KAAK;QAC/B,IAAI,CAACvB,WAAW,CAACwB,IAAI,CAACJ,OAAO,EAAE,CAACK,GAAG,EAAEC,MAAM,KAAK;UAC9C,IAAID,GAAG,EAAEF,MAAM,CAACE,GAAG,CAAC,MACfH,OAAO,CAACI,MAAM,CAAC;QACtB,CAAC,CAAC;MACJ,CAAC,CAAC;MAEJ,MAAM,CAACC,WAAW,EAAEC,UAAU,EAAEC,SAAS,CAAC,GAAG,MAAMR,OAAO,CAACS,GAAG,CAAC,CAC7DX,YAAY,CAAC,SAAS,CAAC,EACvBA,YAAY,CAAC,QAAQ,CAAC,EACtBA,YAAY,CAAC,OAAO,CAAC,CACtB,CAAC;MAEF,MAAMY,MAAM,GAAG,IAAI,CAACC,gBAAgB,CAAC,CAAC;MAEtC,MAAMC,cAAc,GAAGC,OAAO,IAC5BC,MAAM,CAACC,WAAW,CAChBF,OAAO,CACJG,KAAK,CAAC,MAAM,CAAC,CACbC,MAAM,CAACC,IAAI,IAAIA,IAAI,IAAI,CAACA,IAAI,CAACC,UAAU,CAAC,GAAG,CAAC,CAAC,CAC7CC,GAAG,CAACF,IAAI,IAAIA,IAAI,CAACF,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAC/BC,MAAM,CAACI,KAAK,IAAIA,KAAK,CAACC,MAAM,KAAK,CAAC,IAAID,KAAK,CAAC,CAAC,CAAC,IAAIA,KAAK,CAAC,CAAC,CAAC,CAC/D,CAAC;MAEH,MAAME,OAAO,GAAGX,cAAc,CAACN,WAAW,CAAC;MAC3C,MAAMkB,MAAM,GAAGZ,cAAc,CAACL,UAAU,CAAC;MACzC,MAAMkB,KAAK,GAAGb,cAAc,CAACJ,SAAS,CAAC;MAEvC,IAAIe,OAAO,CAACG,iBAAiB,EAAE;QAC7B,IAAI,CAACtC,qBAAqB,CAACuC,GAAG,CAC5B;UAAE,GAAGjB,MAAM;UAAEkB,eAAe,EAAE;QAAY,CAAC,EAC3C9C,QAAQ,CAACyC,OAAO,CAACG,iBAAiB,EAAE,EAAE,CAAC,IAAI,CAC7C,CAAC;MACH;MAEA,IAAIF,MAAM,CAACK,WAAW,EAAE;QACtB,IAAI,CAACnC,gBAAgB,CAACiC,GAAG,CACvB;UAAE,GAAGjB,MAAM;UAAEoB,WAAW,EAAE;QAAO,CAAC,EAClChD,QAAQ,CAAC0C,MAAM,CAACK,WAAW,EAAE,EAAE,CAAC,IAAI,CACtC,CAAC;MACH;MACA,IAAIL,MAAM,CAACO,SAAS,EAAE;QACpB,IAAI,CAACrC,gBAAgB,CAACiC,GAAG,CACvB;UAAE,GAAGjB,MAAM;UAAEoB,WAAW,EAAE;QAAM,CAAC,EACjChD,QAAQ,CAAC0C,MAAM,CAACO,SAAS,EAAE,EAAE,CAAC,IAAI,CACpC,CAAC;MACH;MAEA,IAAIN,KAAK,CAACO,yBAAyB,EAAE;QACnC,IAAI,CAACrC,eAAe,CAACgC,GAAG,CACtB;UAAE,GAAGjB,MAAM;UAAEuB,SAAS,EAAE;QAAc,CAAC,EACvCnD,QAAQ,CAAC2C,KAAK,CAACO,yBAAyB,EAAE,EAAE,CAAC,IAAI,CACnD,CAAC;MACH;IACF,CAAC,CAAC,OAAOE,KAAK,EAAE;MACdC,OAAO,CAACC,IAAI,CACV,kDAAkD,EAClDF,KAAK,CAACG,OACR,CAAC;IACH;EACF,CAAC;;EAED;AACF;AACA;AACA;EACEC,gBAAgB,GAAG,MAAAA,CAAA,KAAY;IAC7B,IAAI;MACF,MAAM,IAAI,CAACzC,mBAAmB,CAAC,CAAC;MAChC,MAAM,IAAI,CAAC0C,WAAW,CAAC,CAAC;MAExB,IAAI,IAAI,CAACC,gBAAgB,EAAE;QACzB,MAAMC,aAAa,GAAG,MAAM,IAAI,CAACC,QAAQ,CAACC,gBAAgB,CAAC,CAAC;QAC5DR,OAAO,CAAChC,IAAI,CACV,6CAA6C,EAC7CyC,IAAI,CAACC,SAAS,CAACJ,aAAa,EAAE,IAAI,EAAE,CAAC,CACvC,CAAC;MACH;IACF,CAAC,CAAC,OAAOP,KAAK,EAAE;MACdC,OAAO,CAACD,KAAK,CACX,oDAAoDA,KAAK,CAACG,OAAO,EACnE,CAAC;MACD,MAAMH,KAAK;IACb;EACF,CAAC;;EAED;AACF;AACA;AACA;EACEY,SAAS,GAAGA,CAACjE,WAAW,GAAG,IAAI,CAACA,WAAW,KAAK;IAC9C,IAAI,CAACkE,UAAU,CAAClE,WAAW,EAAE,MAAM;MACjC,IAAI,CAACyD,gBAAgB,CAAC,CAAC,CAACU,KAAK,CAAC5C,GAAG,IAAI;QACnC+B,OAAO,CAACD,KAAK,CAAC,+CAA+C,EAAE9B,GAAG,CAAC;MACrE,CAAC,CAAC;IACJ,CAAC,CAAC;EACJ,CAAC;;EAED;AACF;AACA;EACE6C,OAAO,GAAG,MAAAA,CAAA,KAAY;IACpB,IAAI;MACF,IAAI,CAACtE,WAAW,EAAEuE,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,OAAO9C,GAAG,EAAE;MACZ+B,OAAO,CAACD,KAAK,CAAC,6CAA6C,EAAE9B,GAAG,CAAC;IACnE;IACArB,OAAO,CAACoE,IAAI,CAAC,CAAC,CAAC;EACjB,CAAC;EAEDvD,mBAAmB,GAAGA,CAAA,KAAM;IAC1Bb,OAAO,CAACqE,EAAE,CAAC,QAAQ,EAAE,IAAI,CAACH,OAAO,CAAC;IAClClE,OAAO,CAACqE,EAAE,CAAC,SAAS,EAAE,IAAI,CAACH,OAAO,CAAC;EACrC,CAAC;AACH;AAEAI,MAAM,CAACC,OAAO,GAAG;EAAE7E;AAAmB,CAAC","ignoreList":[]}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
const Queue = require('bee-queue')
|
|
2
|
+
const { RedisMetricsClient } = require('.')
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* QueueRedisMetricsClient extends MetricsClient to collect
|
|
6
|
+
* Redis and Bee Queue metrics periodically and push them to Prometheus Pushgateway.
|
|
7
|
+
*
|
|
8
|
+
* @extends RedisMetricsClient
|
|
9
|
+
*/
|
|
10
|
+
class QueueRedisMetricsClient extends RedisMetricsClient {
|
|
11
|
+
/**
|
|
12
|
+
* @param {Object} options
|
|
13
|
+
* @param {any} options.redisClient - Redis client instance (required)
|
|
14
|
+
* @param {string} [options.appName] - Application name (from MetricsClient)
|
|
15
|
+
* @param {string} [options.dynoId] - Dyno/instance ID (from MetricsClient)
|
|
16
|
+
* @param {string} [options.processType] - Process type (from MetricsClient)
|
|
17
|
+
* @param {boolean} [options.enabled] - Enable metrics collection (from MetricsClient)
|
|
18
|
+
* @param {boolean} [options.logValues] - Log metrics values (from MetricsClient)
|
|
19
|
+
* @param {string} [options.pushgatewayUrl] - PushGateway URL (from MetricsClient)
|
|
20
|
+
* @param {string} [options.pushgatewaySecret] - PushGateway secret token (from MetricsClient)
|
|
21
|
+
* @param {number} [options.intervalSec] - Interval in seconds for pushing metrics (from MetricsClient)
|
|
22
|
+
* @param {boolean} [options.removeOldMetrics] - Remove old metrics by service (from MetricsClient)
|
|
23
|
+
* @param {boolean} [options.scripDefaultMetrics] - Skip default metrics creation (from MetricsClient)
|
|
24
|
+
* @param {function} [options.startupValidation] - Function to validate startup (from MetricsClient)
|
|
25
|
+
*/
|
|
26
|
+
constructor({ redisClient, metricsConfig = {} } = {}) {
|
|
27
|
+
const getConfiguredQueueNames = () => {
|
|
28
|
+
if (!process.env.METRICS_APP_REDIS_BQ) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
'No queues configured for monitoring. Set METRICS_APP_REDIS_BQ with comma-separated queue names'
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const allQueues = process.env.METRICS_APP_REDIS_BQ.split(',')
|
|
35
|
+
.map(q => q.trim())
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
|
|
38
|
+
if (allQueues.length === 0) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
'METRICS_APP_REDIS_BQ is empty or contains only whitespace. ' +
|
|
41
|
+
'Example: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"'
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return [...new Set(allQueues)]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const startupValidation = () => {
|
|
49
|
+
try {
|
|
50
|
+
const queueNames = getConfiguredQueueNames()
|
|
51
|
+
console.info(
|
|
52
|
+
`[queue-metrics] Queue & Redis metrics collection starting for ${queueNames.length} queues`
|
|
53
|
+
)
|
|
54
|
+
return true
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error(`[queue-metrics] ❌ Cannot start: ${error.message}`)
|
|
57
|
+
console.error(
|
|
58
|
+
`[queue-metrics] 💡 Example: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"`
|
|
59
|
+
)
|
|
60
|
+
console.error(
|
|
61
|
+
`[queue-metrics] 💡 Optional: METRICS_QUEUE_INTERVAL_SEC="10"`
|
|
62
|
+
)
|
|
63
|
+
console.error(`[queue-metrics] Skipping queue metrics collection`)
|
|
64
|
+
return false
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
super({
|
|
69
|
+
...metricsConfig,
|
|
70
|
+
redisClient,
|
|
71
|
+
startupValidation,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
this.getConfiguredQueueNames = getConfiguredQueueNames
|
|
75
|
+
this.startupValidation = startupValidation
|
|
76
|
+
|
|
77
|
+
/** Cache for queue objects to avoid multiple connections */
|
|
78
|
+
this.queueCache = new Map()
|
|
79
|
+
|
|
80
|
+
/** Gauge for queue jobs by status */
|
|
81
|
+
this.queueJobsGauge = this.createGauge({
|
|
82
|
+
name: 'app_queue_jobs_count',
|
|
83
|
+
help: 'Number of app jobs in the queue by status',
|
|
84
|
+
labelNames: this.withDefaultLabels(['queue_name', 'status']),
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
this._setCleanupHandlers()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Collect metrics for a single Bee Queue and set gauges
|
|
92
|
+
* @param {string} queueName - Name of the queue
|
|
93
|
+
* @returns {Promise<void>}
|
|
94
|
+
*/
|
|
95
|
+
collectSingleQueueMetrics = async queueName => {
|
|
96
|
+
try {
|
|
97
|
+
if (!this.queueCache.has(queueName)) {
|
|
98
|
+
this.queueCache.set(
|
|
99
|
+
queueName,
|
|
100
|
+
new Queue(queueName, {
|
|
101
|
+
redis: this.redisClient,
|
|
102
|
+
isWorker: false,
|
|
103
|
+
getEvents: false,
|
|
104
|
+
sendEvents: false,
|
|
105
|
+
})
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const queue = this.queueCache.get(queueName)
|
|
110
|
+
const health = await queue.checkHealth()
|
|
111
|
+
|
|
112
|
+
const labels = {
|
|
113
|
+
...this.getDefaultLabels(),
|
|
114
|
+
queue_name: queueName,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.queueJobsGauge.set(
|
|
118
|
+
{ ...labels, status: 'waiting' },
|
|
119
|
+
health.waiting || 0
|
|
120
|
+
)
|
|
121
|
+
this.queueJobsGauge.set(
|
|
122
|
+
{ ...labels, status: 'active' },
|
|
123
|
+
health.active || 0
|
|
124
|
+
)
|
|
125
|
+
this.queueJobsGauge.set(
|
|
126
|
+
{ ...labels, status: 'succeeded' },
|
|
127
|
+
health.succeeded || 0
|
|
128
|
+
)
|
|
129
|
+
this.queueJobsGauge.set(
|
|
130
|
+
{ ...labels, status: 'failed' },
|
|
131
|
+
health.failed || 0
|
|
132
|
+
)
|
|
133
|
+
this.queueJobsGauge.set(
|
|
134
|
+
{ ...labels, status: 'delayed' },
|
|
135
|
+
health.delayed || 0
|
|
136
|
+
)
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.warn(
|
|
139
|
+
`[queue-metrics] Failed to collect metrics for queue ${queueName}:`,
|
|
140
|
+
error.message
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Collect metrics for all queues and Redis, then push to Pushgateway
|
|
147
|
+
* @returns {Promise<void>}
|
|
148
|
+
*/
|
|
149
|
+
collectQueueMetrics = async () => {
|
|
150
|
+
try {
|
|
151
|
+
const queueNames = this.getConfiguredQueueNames()
|
|
152
|
+
|
|
153
|
+
await this.collectRedisMetrics()
|
|
154
|
+
await Promise.allSettled(
|
|
155
|
+
queueNames.map(queueName => this.collectSingleQueueMetrics(queueName))
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
await this.gatewayPush()
|
|
159
|
+
|
|
160
|
+
if (this.metricsLogValues) {
|
|
161
|
+
const metricObjects = await this.registry.getMetricsAsJSON()
|
|
162
|
+
console.info(
|
|
163
|
+
`[queue-metrics] Collected metrics for ${queueNames.length} queues:`,
|
|
164
|
+
JSON.stringify(metricObjects, null, 2)
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
} catch (error) {
|
|
168
|
+
if (
|
|
169
|
+
error.message?.includes('No queues configured') ||
|
|
170
|
+
error.message?.includes('METRICS_APP_REDIS_BQ')
|
|
171
|
+
) {
|
|
172
|
+
console.error(
|
|
173
|
+
`[queue-metrics] ❌ Configuration error: ${error.message}`
|
|
174
|
+
)
|
|
175
|
+
console.error(
|
|
176
|
+
`[queue-metrics] 💡 Example config: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"`
|
|
177
|
+
)
|
|
178
|
+
} else {
|
|
179
|
+
console.error(
|
|
180
|
+
`[queue-metrics] Failed to collect queue metrics: ${error.message}`
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
throw error
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Start periodic collection.
|
|
189
|
+
* @param {number} [intervalSec=this.intervalSec] - Interval in seconds
|
|
190
|
+
*/
|
|
191
|
+
startPush = (intervalSec = this.intervalSec) => {
|
|
192
|
+
this._startPush(intervalSec, () => {
|
|
193
|
+
this.collectQueueMetrics().catch(err => {
|
|
194
|
+
console.error(
|
|
195
|
+
`[queue-metrics] Failed to collect queue & Redis metrics:`,
|
|
196
|
+
err
|
|
197
|
+
)
|
|
198
|
+
})
|
|
199
|
+
})
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
_setCleanupHandlers = () => {
|
|
203
|
+
process.on('SIGINT', this.cleanup)
|
|
204
|
+
process.on('SIGTERM', this.cleanup)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Cleanup queues and Redis client
|
|
209
|
+
*/
|
|
210
|
+
cleanup = async () => {
|
|
211
|
+
for (const [queueName, queue] of this.queueCache) {
|
|
212
|
+
try {
|
|
213
|
+
await queue.close()
|
|
214
|
+
} catch (err) {
|
|
215
|
+
console.error(`[queue-metrics] Error closing queue ${queueName}:`, err)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
process.exit(0)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
module.exports = { QueueRedisMetricsClient }
|
|
@@ -1,89 +1,43 @@
|
|
|
1
|
-
const Queue = require('bee-queue')
|
|
2
1
|
const { MetricsClient } = require('.')
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* RedisMetricsClient extends MetricsClient to collect
|
|
6
|
-
* Redis
|
|
5
|
+
* Redis metrics periodically and push them to Prometheus Pushgateway.
|
|
7
6
|
*
|
|
8
7
|
* @extends MetricsClient
|
|
9
8
|
*/
|
|
10
9
|
class RedisMetricsClient extends MetricsClient {
|
|
11
10
|
/**
|
|
12
|
-
* @param {Object} options
|
|
11
|
+
* @param {Object} options
|
|
13
12
|
* @param {any} options.redisClient - Redis client instance (required)
|
|
14
|
-
* @param {
|
|
13
|
+
* @param {string} [options.appName] - Application name (from MetricsClient)
|
|
14
|
+
* @param {string} [options.dynoId] - Dyno/instance ID (from MetricsClient)
|
|
15
|
+
* @param {string} [options.processType] - Process type (from MetricsClient)
|
|
16
|
+
* @param {boolean} [options.enabled] - Enable metrics collection (from MetricsClient)
|
|
17
|
+
* @param {boolean} [options.logValues] - Log metrics values (from MetricsClient)
|
|
18
|
+
* @param {string} [options.pushgatewayUrl] - PushGateway URL (from MetricsClient)
|
|
19
|
+
* @param {string} [options.pushgatewaySecret] - PushGateway secret token (from MetricsClient)
|
|
20
|
+
* @param {number} [options.intervalSec] - Interval in seconds for pushing metrics (from MetricsClient)
|
|
21
|
+
* @param {boolean} [options.removeOldMetrics] - Remove old metrics by service (from MetricsClient)
|
|
22
|
+
* @param {boolean} [options.scripDefaultMetrics] - Skip default metrics creation (from MetricsClient)
|
|
23
|
+
* @param {function} [options.startupValidation] - Function to validate startup (from MetricsClient)
|
|
15
24
|
*/
|
|
16
|
-
constructor({ redisClient, metricsConfig
|
|
17
|
-
const
|
|
25
|
+
constructor({ redisClient, ...metricsConfig } = {}) {
|
|
26
|
+
const intervalSec =
|
|
18
27
|
metricsConfig.intervalSec ||
|
|
19
28
|
parseInt(process.env.METRICS_QUEUE_INTERVAL_SEC || '', 10) ||
|
|
20
29
|
5
|
|
21
30
|
|
|
22
|
-
const getConfiguredQueueNames = () => {
|
|
23
|
-
if (!process.env.METRICS_APP_REDIS_BQ) {
|
|
24
|
-
throw new Error(
|
|
25
|
-
'No queues configured for monitoring. Set METRICS_APP_REDIS_BQ with comma-separated queue names'
|
|
26
|
-
)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const allQueues = process.env.METRICS_APP_REDIS_BQ.split(',')
|
|
30
|
-
.map(q => q.trim())
|
|
31
|
-
.filter(Boolean)
|
|
32
|
-
|
|
33
|
-
if (allQueues.length === 0) {
|
|
34
|
-
throw new Error(
|
|
35
|
-
'METRICS_APP_REDIS_BQ is empty or contains only whitespace. ' +
|
|
36
|
-
'Example: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"'
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return [...new Set(allQueues)]
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const startupValidation = () => {
|
|
44
|
-
try {
|
|
45
|
-
const queueNames = getConfiguredQueueNames()
|
|
46
|
-
console.info(
|
|
47
|
-
`[queue-metrics] Queue & Redis metrics collection starting for ${queueNames.length} queues`
|
|
48
|
-
)
|
|
49
|
-
return true
|
|
50
|
-
} catch (error) {
|
|
51
|
-
console.error(`[queue-metrics] ❌ Cannot start: ${error.message}`)
|
|
52
|
-
console.error(
|
|
53
|
-
`[queue-metrics] 💡 Example: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"`
|
|
54
|
-
)
|
|
55
|
-
console.error(
|
|
56
|
-
`[queue-metrics] 💡 Optional: METRICS_QUEUE_INTERVAL_SEC="10"`
|
|
57
|
-
)
|
|
58
|
-
console.error(`[queue-metrics] Skipping queue metrics collection`)
|
|
59
|
-
return false
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
31
|
super({
|
|
64
32
|
...metricsConfig,
|
|
65
33
|
scripDefaultMetrics: true,
|
|
66
|
-
processType: metricsConfig.processType || '
|
|
67
|
-
intervalSec
|
|
68
|
-
startupValidation,
|
|
34
|
+
processType: metricsConfig.processType || 'redis-metrics',
|
|
35
|
+
intervalSec,
|
|
69
36
|
})
|
|
70
37
|
|
|
71
|
-
|
|
72
|
-
this.startupValidation = startupValidation
|
|
73
|
-
|
|
74
|
-
/** Redis client used for queue & Redis metrics */
|
|
38
|
+
/** Redis client used for metrics */
|
|
75
39
|
this.redisClient = redisClient
|
|
76
40
|
|
|
77
|
-
/** Cache for queue objects to avoid multiple connections */
|
|
78
|
-
this.queueCache = new Map()
|
|
79
|
-
|
|
80
|
-
/** Gauge for queue jobs by status */
|
|
81
|
-
this.queueJobsGauge = this.createGauge({
|
|
82
|
-
name: 'app_queue_jobs_count',
|
|
83
|
-
help: 'Number of app jobs in the queue by status',
|
|
84
|
-
labelNames: this.withDefaultLabels(['queue_name', 'status']),
|
|
85
|
-
})
|
|
86
|
-
|
|
87
41
|
/** Gauge for Redis client connections */
|
|
88
42
|
this.redisConnectionsGauge = this.createGauge({
|
|
89
43
|
name: 'app_redis_connections_count',
|
|
@@ -178,98 +132,25 @@ class RedisMetricsClient extends MetricsClient {
|
|
|
178
132
|
}
|
|
179
133
|
|
|
180
134
|
/**
|
|
181
|
-
* Collect metrics for
|
|
182
|
-
* @param {string} queueName - Name of the queue
|
|
135
|
+
* Collect metrics for all Redis and push to Prometheus Pushgateway
|
|
183
136
|
* @returns {Promise<void>}
|
|
184
137
|
*/
|
|
185
|
-
|
|
138
|
+
pushRedisMetrics = async () => {
|
|
186
139
|
try {
|
|
187
|
-
if (!this.queueCache.has(queueName)) {
|
|
188
|
-
this.queueCache.set(
|
|
189
|
-
queueName,
|
|
190
|
-
new Queue(queueName, {
|
|
191
|
-
redis: this.redisClient,
|
|
192
|
-
isWorker: false,
|
|
193
|
-
getEvents: false,
|
|
194
|
-
sendEvents: false,
|
|
195
|
-
})
|
|
196
|
-
)
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const queue = this.queueCache.get(queueName)
|
|
200
|
-
const health = await queue.checkHealth()
|
|
201
|
-
|
|
202
|
-
const labels = {
|
|
203
|
-
...this.getDefaultLabels(),
|
|
204
|
-
queue_name: queueName,
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
this.queueJobsGauge.set(
|
|
208
|
-
{ ...labels, status: 'waiting' },
|
|
209
|
-
health.waiting || 0
|
|
210
|
-
)
|
|
211
|
-
this.queueJobsGauge.set(
|
|
212
|
-
{ ...labels, status: 'active' },
|
|
213
|
-
health.active || 0
|
|
214
|
-
)
|
|
215
|
-
this.queueJobsGauge.set(
|
|
216
|
-
{ ...labels, status: 'succeeded' },
|
|
217
|
-
health.succeeded || 0
|
|
218
|
-
)
|
|
219
|
-
this.queueJobsGauge.set(
|
|
220
|
-
{ ...labels, status: 'failed' },
|
|
221
|
-
health.failed || 0
|
|
222
|
-
)
|
|
223
|
-
this.queueJobsGauge.set(
|
|
224
|
-
{ ...labels, status: 'delayed' },
|
|
225
|
-
health.delayed || 0
|
|
226
|
-
)
|
|
227
|
-
} catch (error) {
|
|
228
|
-
console.warn(
|
|
229
|
-
`[queue-metrics] Failed to collect metrics for queue ${queueName}:`,
|
|
230
|
-
error.message
|
|
231
|
-
)
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Collect metrics for all queues and Redis, then push to Pushgateway
|
|
237
|
-
* @returns {Promise<void>}
|
|
238
|
-
*/
|
|
239
|
-
collectQueueMetrics = async () => {
|
|
240
|
-
try {
|
|
241
|
-
const queueNames = this.getConfiguredQueueNames()
|
|
242
|
-
|
|
243
140
|
await this.collectRedisMetrics()
|
|
244
|
-
await Promise.allSettled(
|
|
245
|
-
queueNames.map(queueName => this.collectSingleQueueMetrics(queueName))
|
|
246
|
-
)
|
|
247
|
-
|
|
248
141
|
await this.gatewayPush()
|
|
249
142
|
|
|
250
143
|
if (this.metricsLogValues) {
|
|
251
144
|
const metricObjects = await this.registry.getMetricsAsJSON()
|
|
252
145
|
console.info(
|
|
253
|
-
`[
|
|
146
|
+
`[redis-metrics] Collected metrics for Redis`,
|
|
254
147
|
JSON.stringify(metricObjects, null, 2)
|
|
255
148
|
)
|
|
256
149
|
}
|
|
257
150
|
} catch (error) {
|
|
258
|
-
|
|
259
|
-
error.message
|
|
260
|
-
|
|
261
|
-
) {
|
|
262
|
-
console.error(
|
|
263
|
-
`[queue-metrics] ❌ Configuration error: ${error.message}`
|
|
264
|
-
)
|
|
265
|
-
console.error(
|
|
266
|
-
`[queue-metrics] 💡 Example config: METRICS_APP_REDIS_BQ="adalo-compile,adalo-migrations"`
|
|
267
|
-
)
|
|
268
|
-
} else {
|
|
269
|
-
console.error(
|
|
270
|
-
`[queue-metrics] Failed to collect queue metrics: ${error.message}`
|
|
271
|
-
)
|
|
272
|
-
}
|
|
151
|
+
console.error(
|
|
152
|
+
`[redis-metrics] Failed to collect Redis metrics: ${error.message}`
|
|
153
|
+
)
|
|
273
154
|
throw error
|
|
274
155
|
}
|
|
275
156
|
}
|
|
@@ -280,36 +161,28 @@ class RedisMetricsClient extends MetricsClient {
|
|
|
280
161
|
*/
|
|
281
162
|
startPush = (intervalSec = this.intervalSec) => {
|
|
282
163
|
this._startPush(intervalSec, () => {
|
|
283
|
-
this.
|
|
284
|
-
console.error(
|
|
285
|
-
`[queue-metrics] Failed to collect queue & Redis metrics:`,
|
|
286
|
-
err
|
|
287
|
-
)
|
|
164
|
+
this.pushRedisMetrics().catch(err => {
|
|
165
|
+
console.error(`[redis-metrics] Failed to push Redis metrics:`, err)
|
|
288
166
|
})
|
|
289
167
|
})
|
|
290
168
|
}
|
|
291
169
|
|
|
292
|
-
_setCleanupHandlers = () => {
|
|
293
|
-
process.on('SIGINT', this.cleanup)
|
|
294
|
-
process.on('SIGTERM', this.cleanup)
|
|
295
|
-
}
|
|
296
|
-
|
|
297
170
|
/**
|
|
298
|
-
* Cleanup
|
|
171
|
+
* Cleanup Redis client
|
|
299
172
|
*/
|
|
300
173
|
cleanup = async () => {
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
console.error(`[queue-metrics] Error closing queue ${queueName}:`, err)
|
|
306
|
-
}
|
|
174
|
+
try {
|
|
175
|
+
this.redisClient?.quit()
|
|
176
|
+
} catch (err) {
|
|
177
|
+
console.error('[redis-metrics] Error closing Redis client:', err)
|
|
307
178
|
}
|
|
308
|
-
|
|
309
|
-
// Close Redis connection
|
|
310
|
-
this.redisClient?.quit()
|
|
311
179
|
process.exit(0)
|
|
312
180
|
}
|
|
181
|
+
|
|
182
|
+
_setCleanupHandlers = () => {
|
|
183
|
+
process.on('SIGINT', this.cleanup)
|
|
184
|
+
process.on('SIGTERM', this.cleanup)
|
|
185
|
+
}
|
|
313
186
|
}
|
|
314
187
|
|
|
315
188
|
module.exports = { RedisMetricsClient }
|