@adalo/metrics 0.1.56 → 0.1.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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 and Bee Queue metrics periodically and push them to Prometheus Pushgateway.
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 - Metrics client options + Redis client
11
+ * @param {Object} options
13
12
  * @param {any} options.redisClient - Redis client instance (required)
14
- * @param {Object} [options.metricsConfig] - MetricsClient configuration overrides
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 METRICS_QUEUE_INTERVAL_SEC =
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 || 'queue-metrics',
67
- intervalSec: METRICS_QUEUE_INTERVAL_SEC,
68
- startupValidation,
34
+ processType: metricsConfig.processType || 'redis-metrics',
35
+ intervalSec,
69
36
  })
70
37
 
71
- this.getConfiguredQueueNames = getConfiguredQueueNames
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 a single Bee Queue and set gauges
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
- collectSingleQueueMetrics = async queueName => {
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
- `[queue-metrics] Collected metrics for ${queueNames.length} queues:`,
146
+ `[redis-metrics] Collected metrics for Redis`,
254
147
  JSON.stringify(metricObjects, null, 2)
255
148
  )
256
149
  }
257
150
  } catch (error) {
258
- if (
259
- error.message?.includes('No queues configured') ||
260
- error.message?.includes('METRICS_APP_REDIS_BQ')
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.collectQueueMetrics().catch(err => {
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 queues and Redis client
171
+ * Cleanup Redis client
299
172
  */
300
173
  cleanup = async () => {
301
- for (const [queueName, queue] of this.queueCache) {
302
- try {
303
- await queue.close()
304
- } catch (err) {
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 }
package/src/redisUtils.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * @param {string} [defaultAppName='undefined-app'] - Fallback app name if METRICS_APP_NAME is not set.
5
5
  * @returns {(client: import('redis').RedisClient, name: string) => void}
6
6
  */
7
- const createSetClientName = (defaultAppName = 'undefined-app') => {
7
+ const createSetClientNameForRedisV3 = (defaultAppName = 'undefined-app') => {
8
8
  return (client, name) => {
9
9
  const appName = process.env.METRICS_APP_NAME || defaultAppName
10
10
  const dyno = process.env.BUILD_DYNO_PROCESS_TYPE || 'undefined-dyno'
@@ -27,4 +27,60 @@ const createSetClientName = (defaultAppName = 'undefined-app') => {
27
27
  }
28
28
  }
29
29
 
30
- module.exports = { createSetClientName }
30
+ /**
31
+ * Creates a function to set Redis client name for ioredis
32
+ * @param {string} defaultAppName
33
+ * @returns {(client: Redis, name: string) => void}
34
+ */
35
+ const createSetClientNameForIoredis = (defaultAppName = 'undefined-app') => {
36
+ return (client, name) => {
37
+ const appName = process.env.METRICS_APP_NAME || defaultAppName
38
+ const dyno = process.env.BUILD_DYNO_PROCESS_TYPE || 'undefined-dyno'
39
+
40
+ if (process.env.NODE_ENV !== 'test') {
41
+ client.once('connect', () => {
42
+ client
43
+ .call('CLIENT', ['SETNAME', `${appName}:${dyno}:${name}`])
44
+ .then(() => {
45
+ console.log(`Connected to Redis for pid:${process.pid} (${name})`)
46
+ })
47
+ .catch(err => {
48
+ console.error(`Failed to set client name for ${name}:`, err)
49
+ })
50
+ })
51
+ }
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Creates a function to set Redis client name for node-redis v4
57
+ * @param {string} defaultAppName
58
+ * @returns {(client: ReturnType<createClient>, name: string) => void}
59
+ */
60
+ const createSetClientNameForRedisV4 = (defaultAppName = 'undefined-app') => {
61
+ return (client, name) => {
62
+ const appName = process.env.METRICS_APP_NAME || defaultAppName
63
+ const dyno = process.env.BUILD_DYNO_PROCESS_TYPE || 'undefined-dyno'
64
+
65
+ if (process.env.NODE_ENV !== 'test') {
66
+ client.on('ready', async () => {
67
+ try {
68
+ await client.sendCommand([
69
+ 'CLIENT',
70
+ 'SETNAME',
71
+ `${appName}:${dyno}:${name}`,
72
+ ])
73
+ console.log(`Connected to Redis for pid:${process.pid} (${name})`)
74
+ } catch (err) {
75
+ console.error(`Failed to set client name for ${name}:`, err)
76
+ }
77
+ })
78
+ }
79
+ }
80
+ }
81
+
82
+ module.exports = {
83
+ createSetClientNameForRedisV3,
84
+ createSetClientNameForRedisV4,
85
+ createSetClientNameForIoredis,
86
+ }