@adalo/metrics 0.1.151 → 0.1.152
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/metrics/metricsQueueRedisClient.d.ts +28 -8
- package/lib/metrics/metricsQueueRedisClient.d.ts.map +1 -1
- package/lib/metrics/metricsQueueRedisClient.js +95 -33
- package/lib/metrics/metricsQueueRedisClient.js.map +1 -1
- package/package.json +1 -1
- package/src/metrics/metricsQueueRedisClient.js +126 -27
|
@@ -12,17 +12,37 @@ export class QueueRedisMetricsClient extends RedisMetricsClient {
|
|
|
12
12
|
/** Gauge for queue jobs by status */
|
|
13
13
|
queueJobsGauge: import("prom-client").Gauge<string>;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Execute a Redis command in a client-type safe way.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* For other Redis clients (v3/v4), returns the client instance directly.
|
|
20
|
-
*
|
|
21
|
-
* @returns {object|any} Redis config object for BeeQueue or Redis client instance
|
|
17
|
+
* @param {string[]} args Command args array, e.g. ['LLEN', 'key']
|
|
18
|
+
* @returns {Promise<any>}
|
|
22
19
|
*/
|
|
23
|
-
|
|
20
|
+
_send: (args: string[]) => Promise<any>;
|
|
21
|
+
_toNumber: (v: any) => number;
|
|
22
|
+
_getQueueType: (queueName: any) => Promise<"bull" | "bee">;
|
|
23
|
+
_getBullHealth: (queueName: any) => Promise<{
|
|
24
|
+
waiting: number;
|
|
25
|
+
active: number;
|
|
26
|
+
succeeded: number;
|
|
27
|
+
failed: number;
|
|
28
|
+
delayed: number;
|
|
29
|
+
}>;
|
|
30
|
+
_getBeeQueueHealth: (queueName: any) => Promise<{
|
|
31
|
+
waiting: number;
|
|
32
|
+
active: number;
|
|
33
|
+
succeeded: number;
|
|
34
|
+
failed: number;
|
|
35
|
+
delayed: number;
|
|
36
|
+
}>;
|
|
24
37
|
/**
|
|
25
|
-
* Collect metrics for a single
|
|
38
|
+
* Collect metrics for a single queue and set gauges.
|
|
39
|
+
*
|
|
40
|
+
* Supports:
|
|
41
|
+
* - Bull (default): keys like `bull:<queue>:wait`, `...:active`, `...:completed`, `...:failed`, `...:delayed`
|
|
42
|
+
* - Bee-Queue: keys like `bq:<queue>:waiting`, `...:active`, `...:succeeded`, `...:failed`, `...:delayed`
|
|
43
|
+
*
|
|
44
|
+
* Auto-detects queue type unless `METRICS_QUEUE_TYPE` is set to `bull` or `bee`.
|
|
45
|
+
*
|
|
26
46
|
* @param {string} queueName - Name of the queue
|
|
27
47
|
* @returns {Promise<void>}
|
|
28
48
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metricsQueueRedisClient.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsQueueRedisClient.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"metricsQueueRedisClient.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsQueueRedisClient.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH;IAgEI,wCAAsD;IACtD,iCAA0C;IAE1C,4DAA4D;IAa5D,0BAA2B;IAE3B,qCAAqC;IACrC,oDAIE;IAKJ;;;;;OAKG;IACH,cAHW,MAAM,EAAE,KACN,QAAQ,GAAG,CAAC,CA2BxB;IAED,8BAGC;IAED,2DAyBC;IAED;;;;;;OAmBC;IAED;;;;;;OAoBC;IAED;;;;;;;;;;;OAWG;IACH,uCAHW,MAAM,KACJ,QAAQ,IAAI,CAAC,CAyCzB;IAED;;;OAGG;IACH,2BAFa,QAAQ,IAAI,CAAC,CAuCzB;CAoCF"}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
const Queue = require('bee-queue');
|
|
4
3
|
const {
|
|
5
4
|
RedisMetricsClient
|
|
6
5
|
} = require('./metricsRedisClient');
|
|
7
6
|
const {
|
|
8
|
-
IOREDIS
|
|
7
|
+
IOREDIS,
|
|
8
|
+
REDIS_V3,
|
|
9
|
+
REDIS_V4
|
|
9
10
|
} = require('../redisUtils');
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -66,6 +67,18 @@ class QueueRedisMetricsClient extends RedisMetricsClient {
|
|
|
66
67
|
this.startupValidation = startupValidation;
|
|
67
68
|
|
|
68
69
|
/** Cache for queue objects to avoid multiple connections */
|
|
70
|
+
// NOTE:
|
|
71
|
+
// Historically we used `bee-queue`'s `Queue#checkHealth()` here.
|
|
72
|
+
// But bee-queue depends on `redis@3`, which is not compatible with Node 22
|
|
73
|
+
// in some environments and can crash with errors like:
|
|
74
|
+
// - "this._ready.then is not a function"
|
|
75
|
+
// - "TypeError: this.stream.once is not a function"
|
|
76
|
+
//
|
|
77
|
+
// To avoid pulling in node-redis v3 at runtime, we read queue counters
|
|
78
|
+
// directly from Redis using the *provided* redis client (ioredis or node-redis).
|
|
79
|
+
//
|
|
80
|
+
// Keep this map reserved for possible future caching (e.g. key schema detection),
|
|
81
|
+
// but we no longer store Queue instances.
|
|
69
82
|
this.queueCache = new Map();
|
|
70
83
|
|
|
71
84
|
/** Gauge for queue jobs by status */
|
|
@@ -78,49 +91,98 @@ class QueueRedisMetricsClient extends RedisMetricsClient {
|
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
/**
|
|
81
|
-
*
|
|
94
|
+
* Execute a Redis command in a client-type safe way.
|
|
82
95
|
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* For other Redis clients (v3/v4), returns the client instance directly.
|
|
86
|
-
*
|
|
87
|
-
* @returns {object|any} Redis config object for BeeQueue or Redis client instance
|
|
96
|
+
* @param {string[]} args Command args array, e.g. ['LLEN', 'key']
|
|
97
|
+
* @returns {Promise<any>}
|
|
88
98
|
*/
|
|
89
|
-
|
|
99
|
+
_send = args => {
|
|
100
|
+
if (!this.redisClient) return Promise.reject(new Error('Redis client not provided'));
|
|
101
|
+
|
|
102
|
+
// node-redis v3 (callback API)
|
|
103
|
+
if (this.redisClientType === REDIS_V3) {
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
this.redisClient.send_command(args[0], args.slice(1), (err, result) => {
|
|
106
|
+
if (err) reject(err);else resolve(result);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// node-redis v4 (Promise API)
|
|
112
|
+
if (this.redisClientType === REDIS_V4) {
|
|
113
|
+
return this.redisClient.sendCommand(args);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ioredis (Promise API)
|
|
90
117
|
if (this.redisClientType === IOREDIS) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
port,
|
|
94
|
-
db,
|
|
95
|
-
password
|
|
96
|
-
} = this.redisClient.options;
|
|
97
|
-
return {
|
|
98
|
-
host,
|
|
99
|
-
port,
|
|
100
|
-
db,
|
|
101
|
-
password
|
|
102
|
-
};
|
|
118
|
+
// ioredis supports `.call(command, ...args)`
|
|
119
|
+
return this.redisClient.call(args[0], ...args.slice(1));
|
|
103
120
|
}
|
|
104
|
-
return
|
|
121
|
+
return Promise.reject(new Error('Unsupported Redis client type'));
|
|
122
|
+
};
|
|
123
|
+
_toNumber = v => {
|
|
124
|
+
const n = typeof v === 'number' ? v : parseInt(String(v || '0'), 10);
|
|
125
|
+
return Number.isFinite(n) ? n : 0;
|
|
126
|
+
};
|
|
127
|
+
_getQueueType = async queueName => {
|
|
128
|
+
const forced = (process.env.METRICS_QUEUE_TYPE || '').trim().toLowerCase();
|
|
129
|
+
if (forced === 'bull' || forced === 'bee') return forced;
|
|
130
|
+
const bullPrefix = (process.env.BULL_QUEUE_PREFIX || 'bull').trim() || 'bull';
|
|
131
|
+
const beePrefix = (process.env.BEE_QUEUE_PREFIX || process.env.METRICS_BEE_QUEUE_PREFIX || 'bq').trim() || 'bq';
|
|
132
|
+
|
|
133
|
+
// Detect by checking the canonical "waiting" key names.
|
|
134
|
+
const bullWaitKey = `${bullPrefix}:${queueName}:wait`;
|
|
135
|
+
const beeWaitKey = `${beePrefix}:${queueName}:waiting`;
|
|
136
|
+
try {
|
|
137
|
+
const [bullExists, beeExists] = await Promise.all([this._send(['EXISTS', bullWaitKey]), this._send(['EXISTS', beeWaitKey])]);
|
|
138
|
+
if (this._toNumber(bullExists) > 0) return 'bull';
|
|
139
|
+
if (this._toNumber(beeExists) > 0) return 'bee';
|
|
140
|
+
} catch {
|
|
141
|
+
// If EXISTS is blocked/unavailable, fall back to trying bull first.
|
|
142
|
+
}
|
|
143
|
+
return 'bull';
|
|
144
|
+
};
|
|
145
|
+
_getBullHealth = async queueName => {
|
|
146
|
+
const prefix = (process.env.BULL_QUEUE_PREFIX || 'bull').trim() || 'bull';
|
|
147
|
+
const base = `${prefix}:${queueName}:`;
|
|
148
|
+
const [waiting, active, succeeded, failed, delayed] = await Promise.all([this._send(['LLEN', `${base}wait`]), this._send(['LLEN', `${base}active`]), this._send(['ZCARD', `${base}completed`]), this._send(['ZCARD', `${base}failed`]), this._send(['ZCARD', `${base}delayed`])]);
|
|
149
|
+
return {
|
|
150
|
+
waiting: this._toNumber(waiting),
|
|
151
|
+
active: this._toNumber(active),
|
|
152
|
+
succeeded: this._toNumber(succeeded),
|
|
153
|
+
failed: this._toNumber(failed),
|
|
154
|
+
delayed: this._toNumber(delayed)
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
_getBeeQueueHealth = async queueName => {
|
|
158
|
+
const prefix = (process.env.BEE_QUEUE_PREFIX || process.env.METRICS_BEE_QUEUE_PREFIX || 'bq').trim() || 'bq';
|
|
159
|
+
const base = `${prefix}:${queueName}:`;
|
|
160
|
+
const [waiting, active, succeeded, failed, delayed] = await Promise.all([this._send(['LLEN', `${base}waiting`]), this._send(['LLEN', `${base}active`]), this._send(['SCARD', `${base}succeeded`]), this._send(['SCARD', `${base}failed`]), this._send(['ZCARD', `${base}delayed`])]);
|
|
161
|
+
return {
|
|
162
|
+
waiting: this._toNumber(waiting),
|
|
163
|
+
active: this._toNumber(active),
|
|
164
|
+
succeeded: this._toNumber(succeeded),
|
|
165
|
+
failed: this._toNumber(failed),
|
|
166
|
+
delayed: this._toNumber(delayed)
|
|
167
|
+
};
|
|
105
168
|
};
|
|
106
169
|
|
|
107
170
|
/**
|
|
108
|
-
* Collect metrics for a single
|
|
171
|
+
* Collect metrics for a single queue and set gauges.
|
|
172
|
+
*
|
|
173
|
+
* Supports:
|
|
174
|
+
* - Bull (default): keys like `bull:<queue>:wait`, `...:active`, `...:completed`, `...:failed`, `...:delayed`
|
|
175
|
+
* - Bee-Queue: keys like `bq:<queue>:waiting`, `...:active`, `...:succeeded`, `...:failed`, `...:delayed`
|
|
176
|
+
*
|
|
177
|
+
* Auto-detects queue type unless `METRICS_QUEUE_TYPE` is set to `bull` or `bee`.
|
|
178
|
+
*
|
|
109
179
|
* @param {string} queueName - Name of the queue
|
|
110
180
|
* @returns {Promise<void>}
|
|
111
181
|
*/
|
|
112
182
|
collectSingleQueueMetrics = async queueName => {
|
|
113
183
|
try {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
redis: this.getConfigForDifferentRedis(),
|
|
117
|
-
isWorker: false,
|
|
118
|
-
getEvents: false,
|
|
119
|
-
sendEvents: false
|
|
120
|
-
}));
|
|
121
|
-
}
|
|
122
|
-
const queue = this.queueCache.get(queueName);
|
|
123
|
-
const health = await queue.checkHealth();
|
|
184
|
+
const queueType = await this._getQueueType(queueName);
|
|
185
|
+
const health = queueType === 'bee' ? await this._getBeeQueueHealth(queueName) : await this._getBullHealth(queueName);
|
|
124
186
|
const labels = {
|
|
125
187
|
...this.getDefaultLabels(),
|
|
126
188
|
queue_name: queueName
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metricsQueueRedisClient.js","names":["Queue","require","RedisMetricsClient","IOREDIS","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","getConfigForDifferentRedis","redisClientType","host","port","db","password","options","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","clearAllCounters","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","includes","startPush","intervalSec","_startPush","catch","err","on","cleanup","close","exit","module","exports"],"sources":["../../src/metrics/metricsQueueRedisClient.js"],"sourcesContent":["const Queue = require('bee-queue')\nconst { RedisMetricsClient } = require('./metricsRedisClient')\nconst { IOREDIS } = require('../redisUtils')\n\n/**\n * QueueRedisMetricsClient extends RedisMetricsClient 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 BaseMetricsClient)\n * @param {string} [options.dynoId] - Dyno/instance ID (from BaseMetricsClient)\n * @param {string} [options.processType] - Process type (from BaseMetricsClient)\n * @param {boolean} [options.enabled] - Enable metrics collection (from BaseMetricsClient)\n * @param {boolean} [options.logValues] - Log metrics values (from BaseMetricsClient)\n * @param {string} [options.pushgatewayUrl] - PushGateway URL (from BaseMetricsClient)\n * @param {string} [options.pushgatewaySecret] - PushGateway secret token (from BaseMetricsClient)\n * @param {number} [options.intervalSec] - Interval in seconds for pushing metrics (from BaseMetricsClient)\n * @param {boolean} [options.removeOldMetrics] - Remove old metrics by service (from BaseMetricsClient)\n * @param {function} [options.startupValidation] - Function to validate startup (from BaseMetricsClient)\n * @param {boolean} [options.disablePushgateway] - Disable pushing to Pushgateway (use HTTP scraping instead)\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 * Get the correct Redis configuration for a queue depending on the client type\n *\n * For ioredis, returns { host, port, db, password } because BeeQueue\n * cannot accept the full client instance.\n * For other Redis clients (v3/v4), returns the client instance directly.\n *\n * @returns {object|any} Redis config object for BeeQueue or Redis client instance\n */\n getConfigForDifferentRedis = () => {\n if (this.redisClientType === IOREDIS) {\n const { host, port, db, password } = this.redisClient.options\n return { host, port, db, password }\n }\n\n return this.redisClient\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.getConfigForDifferentRedis(),\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 this.clearAllCounters()\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 exit process.\n * @returns {Promise<void>}\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
|
+
{"version":3,"file":"metricsQueueRedisClient.js","names":["RedisMetricsClient","require","IOREDIS","REDIS_V3","REDIS_V4","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","_send","args","Promise","reject","redisClientType","resolve","send_command","slice","err","result","sendCommand","call","_toNumber","v","n","parseInt","String","Number","isFinite","_getQueueType","queueName","forced","METRICS_QUEUE_TYPE","toLowerCase","bullPrefix","BULL_QUEUE_PREFIX","beePrefix","BEE_QUEUE_PREFIX","METRICS_BEE_QUEUE_PREFIX","bullWaitKey","beeWaitKey","bullExists","beeExists","all","_getBullHealth","prefix","base","waiting","active","succeeded","failed","delayed","_getBeeQueueHealth","collectSingleQueueMetrics","queueType","health","labels","getDefaultLabels","queue_name","set","status","warn","collectQueueMetrics","collectRedisMetrics","allSettled","gatewayPush","clearAllCounters","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","includes","startPush","intervalSec","_startPush","catch","on","cleanup","queue","close","exit","module","exports"],"sources":["../../src/metrics/metricsQueueRedisClient.js"],"sourcesContent":["const { RedisMetricsClient } = require('./metricsRedisClient')\nconst { IOREDIS, REDIS_V3, REDIS_V4 } = require('../redisUtils')\n\n/**\n * QueueRedisMetricsClient extends RedisMetricsClient 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 BaseMetricsClient)\n * @param {string} [options.dynoId] - Dyno/instance ID (from BaseMetricsClient)\n * @param {string} [options.processType] - Process type (from BaseMetricsClient)\n * @param {boolean} [options.enabled] - Enable metrics collection (from BaseMetricsClient)\n * @param {boolean} [options.logValues] - Log metrics values (from BaseMetricsClient)\n * @param {string} [options.pushgatewayUrl] - PushGateway URL (from BaseMetricsClient)\n * @param {string} [options.pushgatewaySecret] - PushGateway secret token (from BaseMetricsClient)\n * @param {number} [options.intervalSec] - Interval in seconds for pushing metrics (from BaseMetricsClient)\n * @param {boolean} [options.removeOldMetrics] - Remove old metrics by service (from BaseMetricsClient)\n * @param {function} [options.startupValidation] - Function to validate startup (from BaseMetricsClient)\n * @param {boolean} [options.disablePushgateway] - Disable pushing to Pushgateway (use HTTP scraping instead)\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 // NOTE:\n // Historically we used `bee-queue`'s `Queue#checkHealth()` here.\n // But bee-queue depends on `redis@3`, which is not compatible with Node 22\n // in some environments and can crash with errors like:\n // - \"this._ready.then is not a function\"\n // - \"TypeError: this.stream.once is not a function\"\n //\n // To avoid pulling in node-redis v3 at runtime, we read queue counters\n // directly from Redis using the *provided* redis client (ioredis or node-redis).\n //\n // Keep this map reserved for possible future caching (e.g. key schema detection),\n // but we no longer store Queue instances.\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 * Execute a Redis command in a client-type safe way.\n *\n * @param {string[]} args Command args array, e.g. ['LLEN', 'key']\n * @returns {Promise<any>}\n */\n _send = args => {\n if (!this.redisClient) return Promise.reject(new Error('Redis client not provided'))\n\n // node-redis v3 (callback API)\n if (this.redisClientType === REDIS_V3) {\n return new Promise((resolve, reject) => {\n this.redisClient.send_command(args[0], args.slice(1), (err, result) => {\n if (err) reject(err)\n else resolve(result)\n })\n })\n }\n\n // node-redis v4 (Promise API)\n if (this.redisClientType === REDIS_V4) {\n return this.redisClient.sendCommand(args)\n }\n\n // ioredis (Promise API)\n if (this.redisClientType === IOREDIS) {\n // ioredis supports `.call(command, ...args)`\n return this.redisClient.call(args[0], ...args.slice(1))\n }\n\n return Promise.reject(new Error('Unsupported Redis client type'))\n }\n\n _toNumber = v => {\n const n = typeof v === 'number' ? v : parseInt(String(v || '0'), 10)\n return Number.isFinite(n) ? n : 0\n }\n\n _getQueueType = async queueName => {\n const forced = (process.env.METRICS_QUEUE_TYPE || '').trim().toLowerCase()\n if (forced === 'bull' || forced === 'bee') return forced\n\n const bullPrefix = (process.env.BULL_QUEUE_PREFIX || 'bull').trim() || 'bull'\n const beePrefix =\n (process.env.BEE_QUEUE_PREFIX || process.env.METRICS_BEE_QUEUE_PREFIX || 'bq').trim() ||\n 'bq'\n\n // Detect by checking the canonical \"waiting\" key names.\n const bullWaitKey = `${bullPrefix}:${queueName}:wait`\n const beeWaitKey = `${beePrefix}:${queueName}:waiting`\n\n try {\n const [bullExists, beeExists] = await Promise.all([\n this._send(['EXISTS', bullWaitKey]),\n this._send(['EXISTS', beeWaitKey]),\n ])\n if (this._toNumber(bullExists) > 0) return 'bull'\n if (this._toNumber(beeExists) > 0) return 'bee'\n } catch {\n // If EXISTS is blocked/unavailable, fall back to trying bull first.\n }\n\n return 'bull'\n }\n\n _getBullHealth = async queueName => {\n const prefix = (process.env.BULL_QUEUE_PREFIX || 'bull').trim() || 'bull'\n const base = `${prefix}:${queueName}:`\n\n const [waiting, active, succeeded, failed, delayed] = await Promise.all([\n this._send(['LLEN', `${base}wait`]),\n this._send(['LLEN', `${base}active`]),\n this._send(['ZCARD', `${base}completed`]),\n this._send(['ZCARD', `${base}failed`]),\n this._send(['ZCARD', `${base}delayed`]),\n ])\n\n return {\n waiting: this._toNumber(waiting),\n active: this._toNumber(active),\n succeeded: this._toNumber(succeeded),\n failed: this._toNumber(failed),\n delayed: this._toNumber(delayed),\n }\n }\n\n _getBeeQueueHealth = async queueName => {\n const prefix =\n (process.env.BEE_QUEUE_PREFIX || process.env.METRICS_BEE_QUEUE_PREFIX || 'bq').trim() || 'bq'\n const base = `${prefix}:${queueName}:`\n\n const [waiting, active, succeeded, failed, delayed] = await Promise.all([\n this._send(['LLEN', `${base}waiting`]),\n this._send(['LLEN', `${base}active`]),\n this._send(['SCARD', `${base}succeeded`]),\n this._send(['SCARD', `${base}failed`]),\n this._send(['ZCARD', `${base}delayed`]),\n ])\n\n return {\n waiting: this._toNumber(waiting),\n active: this._toNumber(active),\n succeeded: this._toNumber(succeeded),\n failed: this._toNumber(failed),\n delayed: this._toNumber(delayed),\n }\n }\n\n /**\n * Collect metrics for a single queue and set gauges.\n *\n * Supports:\n * - Bull (default): keys like `bull:<queue>:wait`, `...:active`, `...:completed`, `...:failed`, `...:delayed`\n * - Bee-Queue: keys like `bq:<queue>:waiting`, `...:active`, `...:succeeded`, `...:failed`, `...:delayed`\n *\n * Auto-detects queue type unless `METRICS_QUEUE_TYPE` is set to `bull` or `bee`.\n *\n * @param {string} queueName - Name of the queue\n * @returns {Promise<void>}\n */\n collectSingleQueueMetrics = async queueName => {\n try {\n const queueType = await this._getQueueType(queueName)\n const health =\n queueType === 'bee'\n ? await this._getBeeQueueHealth(queueName)\n : await this._getBullHealth(queueName)\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 this.clearAllCounters()\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 exit process.\n * @returns {Promise<void>}\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":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
const Queue = require('bee-queue')
|
|
2
1
|
const { RedisMetricsClient } = require('./metricsRedisClient')
|
|
3
|
-
const { IOREDIS } = require('../redisUtils')
|
|
2
|
+
const { IOREDIS, REDIS_V3, REDIS_V4 } = require('../redisUtils')
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* QueueRedisMetricsClient extends RedisMetricsClient to collect
|
|
@@ -76,6 +75,18 @@ class QueueRedisMetricsClient extends RedisMetricsClient {
|
|
|
76
75
|
this.startupValidation = startupValidation
|
|
77
76
|
|
|
78
77
|
/** Cache for queue objects to avoid multiple connections */
|
|
78
|
+
// NOTE:
|
|
79
|
+
// Historically we used `bee-queue`'s `Queue#checkHealth()` here.
|
|
80
|
+
// But bee-queue depends on `redis@3`, which is not compatible with Node 22
|
|
81
|
+
// in some environments and can crash with errors like:
|
|
82
|
+
// - "this._ready.then is not a function"
|
|
83
|
+
// - "TypeError: this.stream.once is not a function"
|
|
84
|
+
//
|
|
85
|
+
// To avoid pulling in node-redis v3 at runtime, we read queue counters
|
|
86
|
+
// directly from Redis using the *provided* redis client (ioredis or node-redis).
|
|
87
|
+
//
|
|
88
|
+
// Keep this map reserved for possible future caching (e.g. key schema detection),
|
|
89
|
+
// but we no longer store Queue instances.
|
|
79
90
|
this.queueCache = new Map()
|
|
80
91
|
|
|
81
92
|
/** Gauge for queue jobs by status */
|
|
@@ -89,44 +100,132 @@ class QueueRedisMetricsClient extends RedisMetricsClient {
|
|
|
89
100
|
}
|
|
90
101
|
|
|
91
102
|
/**
|
|
92
|
-
*
|
|
103
|
+
* Execute a Redis command in a client-type safe way.
|
|
93
104
|
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* For other Redis clients (v3/v4), returns the client instance directly.
|
|
97
|
-
*
|
|
98
|
-
* @returns {object|any} Redis config object for BeeQueue or Redis client instance
|
|
105
|
+
* @param {string[]} args Command args array, e.g. ['LLEN', 'key']
|
|
106
|
+
* @returns {Promise<any>}
|
|
99
107
|
*/
|
|
100
|
-
|
|
108
|
+
_send = args => {
|
|
109
|
+
if (!this.redisClient) return Promise.reject(new Error('Redis client not provided'))
|
|
110
|
+
|
|
111
|
+
// node-redis v3 (callback API)
|
|
112
|
+
if (this.redisClientType === REDIS_V3) {
|
|
113
|
+
return new Promise((resolve, reject) => {
|
|
114
|
+
this.redisClient.send_command(args[0], args.slice(1), (err, result) => {
|
|
115
|
+
if (err) reject(err)
|
|
116
|
+
else resolve(result)
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// node-redis v4 (Promise API)
|
|
122
|
+
if (this.redisClientType === REDIS_V4) {
|
|
123
|
+
return this.redisClient.sendCommand(args)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ioredis (Promise API)
|
|
101
127
|
if (this.redisClientType === IOREDIS) {
|
|
102
|
-
|
|
103
|
-
return
|
|
128
|
+
// ioredis supports `.call(command, ...args)`
|
|
129
|
+
return this.redisClient.call(args[0], ...args.slice(1))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return Promise.reject(new Error('Unsupported Redis client type'))
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_toNumber = v => {
|
|
136
|
+
const n = typeof v === 'number' ? v : parseInt(String(v || '0'), 10)
|
|
137
|
+
return Number.isFinite(n) ? n : 0
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_getQueueType = async queueName => {
|
|
141
|
+
const forced = (process.env.METRICS_QUEUE_TYPE || '').trim().toLowerCase()
|
|
142
|
+
if (forced === 'bull' || forced === 'bee') return forced
|
|
143
|
+
|
|
144
|
+
const bullPrefix = (process.env.BULL_QUEUE_PREFIX || 'bull').trim() || 'bull'
|
|
145
|
+
const beePrefix =
|
|
146
|
+
(process.env.BEE_QUEUE_PREFIX || process.env.METRICS_BEE_QUEUE_PREFIX || 'bq').trim() ||
|
|
147
|
+
'bq'
|
|
148
|
+
|
|
149
|
+
// Detect by checking the canonical "waiting" key names.
|
|
150
|
+
const bullWaitKey = `${bullPrefix}:${queueName}:wait`
|
|
151
|
+
const beeWaitKey = `${beePrefix}:${queueName}:waiting`
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
const [bullExists, beeExists] = await Promise.all([
|
|
155
|
+
this._send(['EXISTS', bullWaitKey]),
|
|
156
|
+
this._send(['EXISTS', beeWaitKey]),
|
|
157
|
+
])
|
|
158
|
+
if (this._toNumber(bullExists) > 0) return 'bull'
|
|
159
|
+
if (this._toNumber(beeExists) > 0) return 'bee'
|
|
160
|
+
} catch {
|
|
161
|
+
// If EXISTS is blocked/unavailable, fall back to trying bull first.
|
|
104
162
|
}
|
|
105
163
|
|
|
106
|
-
return
|
|
164
|
+
return 'bull'
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
_getBullHealth = async queueName => {
|
|
168
|
+
const prefix = (process.env.BULL_QUEUE_PREFIX || 'bull').trim() || 'bull'
|
|
169
|
+
const base = `${prefix}:${queueName}:`
|
|
170
|
+
|
|
171
|
+
const [waiting, active, succeeded, failed, delayed] = await Promise.all([
|
|
172
|
+
this._send(['LLEN', `${base}wait`]),
|
|
173
|
+
this._send(['LLEN', `${base}active`]),
|
|
174
|
+
this._send(['ZCARD', `${base}completed`]),
|
|
175
|
+
this._send(['ZCARD', `${base}failed`]),
|
|
176
|
+
this._send(['ZCARD', `${base}delayed`]),
|
|
177
|
+
])
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
waiting: this._toNumber(waiting),
|
|
181
|
+
active: this._toNumber(active),
|
|
182
|
+
succeeded: this._toNumber(succeeded),
|
|
183
|
+
failed: this._toNumber(failed),
|
|
184
|
+
delayed: this._toNumber(delayed),
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
_getBeeQueueHealth = async queueName => {
|
|
189
|
+
const prefix =
|
|
190
|
+
(process.env.BEE_QUEUE_PREFIX || process.env.METRICS_BEE_QUEUE_PREFIX || 'bq').trim() || 'bq'
|
|
191
|
+
const base = `${prefix}:${queueName}:`
|
|
192
|
+
|
|
193
|
+
const [waiting, active, succeeded, failed, delayed] = await Promise.all([
|
|
194
|
+
this._send(['LLEN', `${base}waiting`]),
|
|
195
|
+
this._send(['LLEN', `${base}active`]),
|
|
196
|
+
this._send(['SCARD', `${base}succeeded`]),
|
|
197
|
+
this._send(['SCARD', `${base}failed`]),
|
|
198
|
+
this._send(['ZCARD', `${base}delayed`]),
|
|
199
|
+
])
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
waiting: this._toNumber(waiting),
|
|
203
|
+
active: this._toNumber(active),
|
|
204
|
+
succeeded: this._toNumber(succeeded),
|
|
205
|
+
failed: this._toNumber(failed),
|
|
206
|
+
delayed: this._toNumber(delayed),
|
|
207
|
+
}
|
|
107
208
|
}
|
|
108
209
|
|
|
109
210
|
/**
|
|
110
|
-
* Collect metrics for a single
|
|
211
|
+
* Collect metrics for a single queue and set gauges.
|
|
212
|
+
*
|
|
213
|
+
* Supports:
|
|
214
|
+
* - Bull (default): keys like `bull:<queue>:wait`, `...:active`, `...:completed`, `...:failed`, `...:delayed`
|
|
215
|
+
* - Bee-Queue: keys like `bq:<queue>:waiting`, `...:active`, `...:succeeded`, `...:failed`, `...:delayed`
|
|
216
|
+
*
|
|
217
|
+
* Auto-detects queue type unless `METRICS_QUEUE_TYPE` is set to `bull` or `bee`.
|
|
218
|
+
*
|
|
111
219
|
* @param {string} queueName - Name of the queue
|
|
112
220
|
* @returns {Promise<void>}
|
|
113
221
|
*/
|
|
114
222
|
collectSingleQueueMetrics = async queueName => {
|
|
115
223
|
try {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
isWorker: false,
|
|
122
|
-
getEvents: false,
|
|
123
|
-
sendEvents: false,
|
|
124
|
-
})
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const queue = this.queueCache.get(queueName)
|
|
129
|
-
const health = await queue.checkHealth()
|
|
224
|
+
const queueType = await this._getQueueType(queueName)
|
|
225
|
+
const health =
|
|
226
|
+
queueType === 'bee'
|
|
227
|
+
? await this._getBeeQueueHealth(queueName)
|
|
228
|
+
: await this._getBullHealth(queueName)
|
|
130
229
|
|
|
131
230
|
const labels = {
|
|
132
231
|
...this.getDefaultLabels(),
|