@adalo/metrics 0.0.0-staging.21 → 0.0.0-staging.22

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.
@@ -0,0 +1,74 @@
1
+ const { HttpMetricsRedisCollector } = require('../src/metrics/httpMetricsRedisCollector')
2
+ const { buildFieldKey } = require('../src/metrics/httpMetricsRedisStore')
3
+
4
+ function createRedisV3Mock() {
5
+ const multiChain = {
6
+ hincrby: jest.fn().mockReturnThis(),
7
+ expire: jest.fn().mockReturnThis(),
8
+ exec: jest.fn(cb => {
9
+ if (cb) {
10
+ setImmediate(() => cb(null))
11
+ }
12
+ }),
13
+ }
14
+ return {
15
+ multi: jest.fn(() => multiChain),
16
+ set: jest.fn(),
17
+ eval: jest.fn(),
18
+ del: jest.fn(),
19
+ _multiChain: multiChain,
20
+ }
21
+ }
22
+
23
+ describe('HttpMetricsRedisCollector', () => {
24
+ const originalEnv = process.env
25
+
26
+ beforeEach(() => {
27
+ process.env = { ...originalEnv, METRICS_DISABLE_PUSHGATEWAY: 'true' }
28
+ })
29
+
30
+ afterEach(() => {
31
+ process.env = originalEnv
32
+ })
33
+
34
+ it('throws without redisClient', () => {
35
+ expect(() => new HttpMetricsRedisCollector({ appName: 'a' })).toThrow('redisClient is required')
36
+ })
37
+
38
+ it('pushMetrics drains Redis then completes without network push', async () => {
39
+ const redis = createRedisV3Mock()
40
+ redis.set.mockImplementation((key, val, mode, ttl, nx, cb) => {
41
+ cb(null, 'OK')
42
+ })
43
+ const field = buildFieldKey('GET', '/health', 200, '', '')
44
+ redis.eval.mockImplementation((lua, numKeys, k1, k2, cb) => {
45
+ cb(null, [
46
+ [field, '1'],
47
+ [field, '10'],
48
+ ])
49
+ })
50
+ redis.del.mockImplementation((key, cb) => {
51
+ if (cb) {
52
+ cb()
53
+ }
54
+ })
55
+
56
+ const collector = new HttpMetricsRedisCollector({
57
+ redisClient: redis,
58
+ appName: 'test-app',
59
+ dynoId: 'dyno-1',
60
+ processType: 'http-metrics',
61
+ redisProcessTypeForKeys: 'web',
62
+ disablePushgateway: true,
63
+ })
64
+
65
+ await collector.pushMetrics()
66
+
67
+ expect(redis.eval).toHaveBeenCalled()
68
+ const metrics = await collector._registry.getMetricsAsJSON()
69
+ const total = metrics.find(m => m.name === 'app_requests_total')
70
+ expect(total).toBeDefined()
71
+ const totalDur = metrics.find(m => m.name === 'app_requests_total_duration')
72
+ expect(totalDur).toBeDefined()
73
+ })
74
+ })
@@ -0,0 +1,56 @@
1
+ const { HttpMetricsRedisRecorder } = require('../src/metrics/httpMetricsRedisRecorder')
2
+ const { buildFieldKey } = require('../src/metrics/httpMetricsRedisStore')
3
+
4
+ const flushMicrotasks = () => new Promise(resolve => setImmediate(resolve))
5
+
6
+ function createRedisV3Mock() {
7
+ const multiChain = {
8
+ hincrby: jest.fn().mockReturnThis(),
9
+ expire: jest.fn().mockReturnThis(),
10
+ exec: jest.fn(cb => {
11
+ if (cb) {
12
+ setImmediate(() => cb(null))
13
+ }
14
+ }),
15
+ }
16
+ return {
17
+ multi: jest.fn(() => multiChain),
18
+ set: jest.fn(),
19
+ eval: jest.fn(),
20
+ del: jest.fn(),
21
+ _multiChain: multiChain,
22
+ }
23
+ }
24
+
25
+ describe('HttpMetricsRedisRecorder', () => {
26
+ it('throws without redisClient', () => {
27
+ expect(() => new HttpMetricsRedisRecorder({ appName: 'a', processType: 'web' })).toThrow(
28
+ 'redisClient is required'
29
+ )
30
+ })
31
+
32
+ it('trackHttpRequest forwards to Redis multi/hincrby', async () => {
33
+ const redis = createRedisV3Mock()
34
+ const rec = new HttpMetricsRedisRecorder({
35
+ redisClient: redis,
36
+ appName: 'app',
37
+ processType: 'web',
38
+ })
39
+ const countKey = rec._store.countKey
40
+ const durKey = rec._store.durKey
41
+ const field = buildFieldKey('GET', '/p', 404, 'x', 'y')
42
+
43
+ rec.trackHttpRequest({
44
+ method: 'GET',
45
+ route: '/p',
46
+ status_code: 404,
47
+ appId: 'x',
48
+ databaseId: 'y',
49
+ duration: 5,
50
+ })
51
+ await flushMicrotasks()
52
+
53
+ expect(redis._multiChain.hincrby).toHaveBeenCalledWith(countKey, field, 1)
54
+ expect(redis._multiChain.hincrby).toHaveBeenCalledWith(durKey, field, 5)
55
+ })
56
+ })
@@ -0,0 +1,182 @@
1
+ const {
2
+ HttpMetricsRedisStore,
3
+ buildFieldKey,
4
+ FIELD_SEP,
5
+ } = require('../src/metrics/httpMetricsRedisStore')
6
+
7
+ const flushMicrotasks = () => new Promise(resolve => setImmediate(resolve))
8
+
9
+ /**
10
+ * redis@3-style mock: multi().hincrby().expire().exec(cb)
11
+ */
12
+ function createRedisV3Mock() {
13
+ const multiChain = {
14
+ hincrby: jest.fn().mockReturnThis(),
15
+ expire: jest.fn().mockReturnThis(),
16
+ exec: jest.fn(cb => {
17
+ if (cb) {
18
+ setImmediate(() => cb(null))
19
+ }
20
+ }),
21
+ }
22
+ return {
23
+ multi: jest.fn(() => multiChain),
24
+ set: jest.fn(),
25
+ eval: jest.fn(),
26
+ del: jest.fn(),
27
+ _multiChain: multiChain,
28
+ }
29
+ }
30
+
31
+ describe('HttpMetricsRedisStore', () => {
32
+ describe('buildFieldKey', () => {
33
+ it('joins parts with FIELD_SEP', () => {
34
+ expect(buildFieldKey('GET', '/api', 200, 'app1', 'db1')).toBe(
35
+ ['GET', '/api', '200', 'app1', 'db1'].join(FIELD_SEP)
36
+ )
37
+ })
38
+ })
39
+
40
+ describe('constructor', () => {
41
+ it('throws without redisClient', () => {
42
+ expect(() => new HttpMetricsRedisStore({ appName: 'a', processType: 'web' })).toThrow(
43
+ 'redisClient is required'
44
+ )
45
+ })
46
+
47
+ it('builds encoded v2 keys', () => {
48
+ const redis = createRedisV3Mock()
49
+ const store = new HttpMetricsRedisStore({
50
+ redisClient: redis,
51
+ appName: 'my app',
52
+ processType: 'web',
53
+ })
54
+ const seg = `${encodeURIComponent('my app')}:${encodeURIComponent('web')}`
55
+ expect(store.countKey).toBe(`metrics:http:v2:${seg}:count`)
56
+ expect(store.durKey).toBe(`metrics:http:v2:${seg}:dur`)
57
+ expect(store.lockKey).toBe(`metrics:http:v2:${seg}:drain_lock`)
58
+ })
59
+ })
60
+
61
+ describe('record', () => {
62
+ it('increments count and duration hashes with TTL', async () => {
63
+ const redis = createRedisV3Mock()
64
+ const store = new HttpMetricsRedisStore({
65
+ redisClient: redis,
66
+ appName: 'app',
67
+ processType: 'web',
68
+ ttlSec: 90,
69
+ })
70
+ const field = buildFieldKey('GET', '/x', 200, '', '')
71
+
72
+ store.record('GET', '/x', 200, '', '', 12)
73
+ await flushMicrotasks()
74
+
75
+ expect(redis.multi).toHaveBeenCalled()
76
+ expect(redis._multiChain.hincrby).toHaveBeenCalledWith(store.countKey, field, 1)
77
+ expect(redis._multiChain.hincrby).toHaveBeenCalledWith(store.durKey, field, 12)
78
+ expect(redis._multiChain.expire).toHaveBeenCalledWith(store.countKey, 90)
79
+ expect(redis._multiChain.expire).toHaveBeenCalledWith(store.durKey, 90)
80
+ expect(redis._multiChain.exec).toHaveBeenCalled()
81
+ })
82
+ })
83
+
84
+ describe('flushToCounters', () => {
85
+ it('returns false when lock is not acquired', async () => {
86
+ const redis = createRedisV3Mock()
87
+ redis.set.mockImplementation((key, val, mode, ttl, nx, cb) => {
88
+ cb(null, null)
89
+ })
90
+ const store = new HttpMetricsRedisStore({
91
+ redisClient: redis,
92
+ appName: 'app',
93
+ processType: 'web',
94
+ })
95
+ const ok = await store.flushToCounters(jest.fn(), jest.fn())
96
+ expect(ok).toBe(false)
97
+ expect(redis.eval).not.toHaveBeenCalled()
98
+ })
99
+
100
+ it('drains hashes and applies count and duration', async () => {
101
+ const redis = createRedisV3Mock()
102
+ redis.set.mockImplementation((key, val, mode, ttl, nx, cb) => {
103
+ cb(null, 'OK')
104
+ })
105
+ const field = buildFieldKey('POST', '/r', 201, 'a1', 'd1')
106
+ redis.eval.mockImplementation((lua, numKeys, k1, k2, cb) => {
107
+ cb(null, [
108
+ [field, '2'],
109
+ [field, '50'],
110
+ ])
111
+ })
112
+ redis.del.mockImplementation((key, cb) => {
113
+ if (cb) {
114
+ cb()
115
+ }
116
+ })
117
+
118
+ const store = new HttpMetricsRedisStore({
119
+ redisClient: redis,
120
+ appName: 'app',
121
+ processType: 'web',
122
+ })
123
+ const applyCount = jest.fn()
124
+ const applyDuration = jest.fn()
125
+
126
+ const ok = await store.flushToCounters(applyCount, applyDuration)
127
+
128
+ expect(ok).toBe(true)
129
+ expect(redis.eval).toHaveBeenCalledWith(
130
+ expect.stringContaining('HGETALL'),
131
+ 2,
132
+ store.countKey,
133
+ store.durKey,
134
+ expect.any(Function)
135
+ )
136
+ expect(applyCount).toHaveBeenCalledWith(
137
+ {
138
+ method: 'POST',
139
+ route: '/r',
140
+ status_code: '201',
141
+ appId: 'a1',
142
+ databaseId: 'd1',
143
+ },
144
+ 2
145
+ )
146
+ expect(applyDuration).toHaveBeenCalledWith(
147
+ {
148
+ method: 'POST',
149
+ route: '/r',
150
+ status_code: '201',
151
+ appId: 'a1',
152
+ databaseId: 'd1',
153
+ },
154
+ 50
155
+ )
156
+ })
157
+
158
+ it('resolves true with no applies when eval returns short array', async () => {
159
+ const redis = createRedisV3Mock()
160
+ redis.set.mockImplementation((key, val, mode, ttl, nx, cb) => {
161
+ cb(null, 'OK')
162
+ })
163
+ redis.eval.mockImplementation((lua, numKeys, k1, k2, cb) => {
164
+ cb(null, [])
165
+ })
166
+ redis.del.mockImplementation((key, cb) => {
167
+ if (cb) {
168
+ cb()
169
+ }
170
+ })
171
+ const store = new HttpMetricsRedisStore({
172
+ redisClient: redis,
173
+ appName: 'app',
174
+ processType: 'web',
175
+ })
176
+ const applyCount = jest.fn()
177
+ const ok = await store.flushToCounters(applyCount, jest.fn())
178
+ expect(ok).toBe(true)
179
+ expect(applyCount).not.toHaveBeenCalled()
180
+ })
181
+ })
182
+ })
@@ -35,8 +35,9 @@ export function exitUnlessProcessTypeIn(metricsConfig: {
35
35
  *
36
36
  * **Canonical names** align with typical Procfile processes (e.g. backend: `web`, `worker`,
37
37
  * `queue-metrics`, `database-metrics`, `http-metrics`). The compile **worker** dyno serves queues/builds
38
- * — it does not run HTTP request metrics; HTTP Redis **writers** use `web` (or `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE`);
39
- * the HTTP **collector** dyno uses {@link METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR}.
38
+ * — it does not run HTTP request metrics. HTTP Redis **key segment** is **`METRICS_HTTP_REDIS_KEY_PROCESS_TYPE`**
39
+ * (default **`web`**), not `BUILD_DYNO_PROCESS_TYPE` (workers/collector differ). The HTTP **collector** dyno
40
+ * identity for logs is often {@link METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR}.
40
41
  *
41
42
  * @module metrics/metricsProcessTypeUtils
42
43
  */
@@ -1 +1 @@
1
- {"version":3,"file":"metricsProcessTypeUtils.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsProcessTypeUtils.js"],"names":[],"mappings":"AAuCA;;;;;;GAMG;AACH,yDAJW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,sBACxB,MAAM,GACJ,MAAM,CAQlB;AAED;;;;;;GAMG;AACH,uDAJW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,uBACxB,MAAM,GACJ,IAAI,CAUhB;AAED;;;;;;;GAOG;AACH,uDALW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,uBACxB,SAAS,MAAM,EAAE,0BACjB,MAAM,GACJ,IAAI,CAchB;AA3FD;;;;;;;;;;GAUG;AAEH,6DAA6D;AAC7D,+DAAwD;AAExD,gEAAgE;AAChE,yDAAkD;AAElD,yEAAyE;AACzE,yDAAkD;AAElD,kGAAkG;AAClG,6CAAsC;AAEtC,gGAAgG;AAChG,mDAA4C;AAE5C,wGAAwG;AACxG,yEAAkE;AAElE;;;GAGG;AACH,yDAFU,SAAS,MAAM,EAAE,CAKzB"}
1
+ {"version":3,"file":"metricsProcessTypeUtils.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsProcessTypeUtils.js"],"names":[],"mappings":"AAwCA;;;;;;GAMG;AACH,yDAJW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,sBACxB,MAAM,GACJ,MAAM,CAQlB;AAED;;;;;;GAMG;AACH,uDAJW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,uBACxB,MAAM,GACJ,IAAI,CAUhB;AAED;;;;;;;GAOG;AACH,uDALW;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,uBACxB,SAAS,MAAM,EAAE,0BACjB,MAAM,GACJ,IAAI,CAchB;AA5FD;;;;;;;;;;;GAWG;AAEH,6DAA6D;AAC7D,+DAAwD;AAExD,gEAAgE;AAChE,yDAAkD;AAElD,yEAAyE;AACzE,yDAAkD;AAElD,kGAAkG;AAClG,6CAAsC;AAEtC,gGAAgG;AAChG,mDAA4C;AAE5C,wGAAwG;AACxG,yEAAkE;AAElE;;;GAGG;AACH,yDAFU,SAAS,MAAM,EAAE,CAKzB"}
@@ -6,8 +6,9 @@
6
6
  *
7
7
  * **Canonical names** align with typical Procfile processes (e.g. backend: `web`, `worker`,
8
8
  * `queue-metrics`, `database-metrics`, `http-metrics`). The compile **worker** dyno serves queues/builds
9
- * — it does not run HTTP request metrics; HTTP Redis **writers** use `web` (or `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE`);
10
- * the HTTP **collector** dyno uses {@link METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR}.
9
+ * — it does not run HTTP request metrics. HTTP Redis **key segment** is **`METRICS_HTTP_REDIS_KEY_PROCESS_TYPE`**
10
+ * (default **`web`**), not `BUILD_DYNO_PROCESS_TYPE` (workers/collector differ). The HTTP **collector** dyno
11
+ * identity for logs is often {@link METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR}.
11
12
  *
12
13
  * @module metrics/metricsProcessTypeUtils
13
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"metricsProcessTypeUtils.js","names":["METRICS_PROCESS_TYPE_DATABASE","METRICS_PROCESS_TYPE_QUEUE","METRICS_PROCESS_TYPE_REDIS","METRICS_PROCESS_TYPE_WEB","METRICS_PROCESS_TYPE_WORKER","METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR","REDIS_METRICS_CLIENT_ALLOWED_PROCESS_TYPES","Object","freeze","resolveMetricsProcessType","metricsConfig","defaultProcessType","processType","process","env","BUILD_DYNO_PROCESS_TYPE","exitUnlessProcessTypeIs","expectedProcessType","resolved","exit","exitUnlessProcessTypeIn","allowedProcessTypes","defaultWhenUnspecified","includes","module","exports"],"sources":["../../src/metrics/metricsProcessTypeUtils.js"],"sourcesContent":["/**\n * Helpers for resolving `processType` and silently exiting when a specialized metrics client\n * is constructed on the wrong dyno / process (no log output).\n *\n * **Canonical names** align with typical Procfile processes (e.g. backend: `web`, `worker`,\n * `queue-metrics`, `database-metrics`, `http-metrics`). The compile **worker** dyno serves queues/builds\n * — it does not run HTTP request metrics; HTTP Redis **writers** use `web` (or `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE`);\n * the HTTP **collector** dyno uses {@link METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR}.\n *\n * @module metrics/metricsProcessTypeUtils\n */\n\n/** DB-only metrics dyno (`database-metrics` in Procfile). */\nconst METRICS_PROCESS_TYPE_DATABASE = 'database-metrics'\n\n/** Queue + Redis metrics dyno (`queue-metrics` in Procfile). */\nconst METRICS_PROCESS_TYPE_QUEUE = 'queue-metrics'\n\n/** Redis-only metrics dyno (no Bee Queue; optional separate process). */\nconst METRICS_PROCESS_TYPE_REDIS = 'redis-metrics'\n\n/** Web servers — HTTP traffic, HTTP Redis **writers** typically use this in Redis key segment. */\nconst METRICS_PROCESS_TYPE_WEB = 'web'\n\n/** Build/compile workers and similar (e.g. backend `worker:`) — no HTTP server metrics here. */\nconst METRICS_PROCESS_TYPE_WORKER = 'worker'\n\n/** HTTP Redis **drain + push** process (e.g. backend `http-metrics:` / `http-metrics-collector.ts`). */\nconst METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR = 'http-metrics'\n\n/**\n * Parent {@link RedisMetricsClient} allows either redis-only or queue stack (`QueueRedisMetricsClient`).\n * @type {readonly string[]}\n */\nconst REDIS_METRICS_CLIENT_ALLOWED_PROCESS_TYPES = Object.freeze([\n METRICS_PROCESS_TYPE_REDIS,\n METRICS_PROCESS_TYPE_QUEUE,\n])\n\n/**\n * Resolve logical process type the same way specialized metrics clients do.\n *\n * @param {{ processType?: string }} metricsConfig - Remainder of constructor options (e.g. after destructuring `redisClient`, `databaseUrl`, …)\n * @param {string} defaultProcessType - Fallback when `metricsConfig.processType` and `BUILD_DYNO_PROCESS_TYPE` are unset\n * @returns {string}\n */\nfunction resolveMetricsProcessType(metricsConfig, defaultProcessType) {\n return (\n metricsConfig.processType ||\n process.env.BUILD_DYNO_PROCESS_TYPE ||\n defaultProcessType\n )\n}\n\n/**\n * Exit with no logs if the resolved process type is not exactly `expectedProcessType`.\n *\n * @param {{ processType?: string }} metricsConfig\n * @param {string} expectedProcessType - Single allowed value (use a module constant, e.g. {@link METRICS_PROCESS_TYPE_DATABASE})\n * @returns {void}\n */\nfunction exitUnlessProcessTypeIs(metricsConfig, expectedProcessType) {\n const resolved = resolveMetricsProcessType(\n metricsConfig,\n expectedProcessType\n )\n if (resolved !== expectedProcessType) {\n process.exit(0)\n }\n}\n\n/**\n * Exit with no logs if the resolved process type is not in `allowedProcessTypes`.\n *\n * @param {{ processType?: string }} metricsConfig\n * @param {readonly string[]} allowedProcessTypes\n * @param {string} defaultWhenUnspecified - Used only to resolve when config/env omit `processType` (e.g. {@link METRICS_PROCESS_TYPE_QUEUE} for {@link RedisMetricsClient})\n * @returns {void}\n */\nfunction exitUnlessProcessTypeIn(\n metricsConfig,\n allowedProcessTypes,\n defaultWhenUnspecified\n) {\n const resolved = resolveMetricsProcessType(\n metricsConfig,\n defaultWhenUnspecified\n )\n if (!allowedProcessTypes.includes(resolved)) {\n process.exit(0)\n }\n}\n\nmodule.exports = {\n resolveMetricsProcessType,\n exitUnlessProcessTypeIs,\n exitUnlessProcessTypeIn,\n METRICS_PROCESS_TYPE_DATABASE,\n METRICS_PROCESS_TYPE_QUEUE,\n METRICS_PROCESS_TYPE_REDIS,\n METRICS_PROCESS_TYPE_WEB,\n METRICS_PROCESS_TYPE_WORKER,\n METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR,\n REDIS_METRICS_CLIENT_ALLOWED_PROCESS_TYPES,\n}\n"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,MAAMA,6BAA6B,GAAG,kBAAkB;;AAExD;AACA,MAAMC,0BAA0B,GAAG,eAAe;;AAElD;AACA,MAAMC,0BAA0B,GAAG,eAAe;;AAElD;AACA,MAAMC,wBAAwB,GAAG,KAAK;;AAEtC;AACA,MAAMC,2BAA2B,GAAG,QAAQ;;AAE5C;AACA,MAAMC,2CAA2C,GAAG,cAAc;;AAElE;AACA;AACA;AACA;AACA,MAAMC,0CAA0C,GAAGC,MAAM,CAACC,MAAM,CAAC,CAC/DN,0BAA0B,EAC1BD,0BAA0B,CAC3B,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASQ,yBAAyBA,CAACC,aAAa,EAAEC,kBAAkB,EAAE;EACpE,OACED,aAAa,CAACE,WAAW,IACzBC,OAAO,CAACC,GAAG,CAACC,uBAAuB,IACnCJ,kBAAkB;AAEtB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASK,uBAAuBA,CAACN,aAAa,EAAEO,mBAAmB,EAAE;EACnE,MAAMC,QAAQ,GAAGT,yBAAyB,CACxCC,aAAa,EACbO,mBACF,CAAC;EACD,IAAIC,QAAQ,KAAKD,mBAAmB,EAAE;IACpCJ,OAAO,CAACM,IAAI,CAAC,CAAC,CAAC;EACjB;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,uBAAuBA,CAC9BV,aAAa,EACbW,mBAAmB,EACnBC,sBAAsB,EACtB;EACA,MAAMJ,QAAQ,GAAGT,yBAAyB,CACxCC,aAAa,EACbY,sBACF,CAAC;EACD,IAAI,CAACD,mBAAmB,CAACE,QAAQ,CAACL,QAAQ,CAAC,EAAE;IAC3CL,OAAO,CAACM,IAAI,CAAC,CAAC,CAAC;EACjB;AACF;AAEAK,MAAM,CAACC,OAAO,GAAG;EACfhB,yBAAyB;EACzBO,uBAAuB;EACvBI,uBAAuB;EACvBpB,6BAA6B;EAC7BC,0BAA0B;EAC1BC,0BAA0B;EAC1BC,wBAAwB;EACxBC,2BAA2B;EAC3BC,2CAA2C;EAC3CC;AACF,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"metricsProcessTypeUtils.js","names":["METRICS_PROCESS_TYPE_DATABASE","METRICS_PROCESS_TYPE_QUEUE","METRICS_PROCESS_TYPE_REDIS","METRICS_PROCESS_TYPE_WEB","METRICS_PROCESS_TYPE_WORKER","METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR","REDIS_METRICS_CLIENT_ALLOWED_PROCESS_TYPES","Object","freeze","resolveMetricsProcessType","metricsConfig","defaultProcessType","processType","process","env","BUILD_DYNO_PROCESS_TYPE","exitUnlessProcessTypeIs","expectedProcessType","resolved","exit","exitUnlessProcessTypeIn","allowedProcessTypes","defaultWhenUnspecified","includes","module","exports"],"sources":["../../src/metrics/metricsProcessTypeUtils.js"],"sourcesContent":["/**\n * Helpers for resolving `processType` and silently exiting when a specialized metrics client\n * is constructed on the wrong dyno / process (no log output).\n *\n * **Canonical names** align with typical Procfile processes (e.g. backend: `web`, `worker`,\n * `queue-metrics`, `database-metrics`, `http-metrics`). The compile **worker** dyno serves queues/builds\n * — it does not run HTTP request metrics. HTTP Redis **key segment** is **`METRICS_HTTP_REDIS_KEY_PROCESS_TYPE`**\n * (default **`web`**), not `BUILD_DYNO_PROCESS_TYPE` (workers/collector differ). The HTTP **collector** dyno\n * identity for logs is often {@link METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR}.\n *\n * @module metrics/metricsProcessTypeUtils\n */\n\n/** DB-only metrics dyno (`database-metrics` in Procfile). */\nconst METRICS_PROCESS_TYPE_DATABASE = 'database-metrics'\n\n/** Queue + Redis metrics dyno (`queue-metrics` in Procfile). */\nconst METRICS_PROCESS_TYPE_QUEUE = 'queue-metrics'\n\n/** Redis-only metrics dyno (no Bee Queue; optional separate process). */\nconst METRICS_PROCESS_TYPE_REDIS = 'redis-metrics'\n\n/** Web servers — HTTP traffic, HTTP Redis **writers** typically use this in Redis key segment. */\nconst METRICS_PROCESS_TYPE_WEB = 'web'\n\n/** Build/compile workers and similar (e.g. backend `worker:`) — no HTTP server metrics here. */\nconst METRICS_PROCESS_TYPE_WORKER = 'worker'\n\n/** HTTP Redis **drain + push** process (e.g. backend `http-metrics:` / `http-metrics-collector.ts`). */\nconst METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR = 'http-metrics'\n\n/**\n * Parent {@link RedisMetricsClient} allows either redis-only or queue stack (`QueueRedisMetricsClient`).\n * @type {readonly string[]}\n */\nconst REDIS_METRICS_CLIENT_ALLOWED_PROCESS_TYPES = Object.freeze([\n METRICS_PROCESS_TYPE_REDIS,\n METRICS_PROCESS_TYPE_QUEUE,\n])\n\n/**\n * Resolve logical process type the same way specialized metrics clients do.\n *\n * @param {{ processType?: string }} metricsConfig - Remainder of constructor options (e.g. after destructuring `redisClient`, `databaseUrl`, …)\n * @param {string} defaultProcessType - Fallback when `metricsConfig.processType` and `BUILD_DYNO_PROCESS_TYPE` are unset\n * @returns {string}\n */\nfunction resolveMetricsProcessType(metricsConfig, defaultProcessType) {\n return (\n metricsConfig.processType ||\n process.env.BUILD_DYNO_PROCESS_TYPE ||\n defaultProcessType\n )\n}\n\n/**\n * Exit with no logs if the resolved process type is not exactly `expectedProcessType`.\n *\n * @param {{ processType?: string }} metricsConfig\n * @param {string} expectedProcessType - Single allowed value (use a module constant, e.g. {@link METRICS_PROCESS_TYPE_DATABASE})\n * @returns {void}\n */\nfunction exitUnlessProcessTypeIs(metricsConfig, expectedProcessType) {\n const resolved = resolveMetricsProcessType(\n metricsConfig,\n expectedProcessType\n )\n if (resolved !== expectedProcessType) {\n process.exit(0)\n }\n}\n\n/**\n * Exit with no logs if the resolved process type is not in `allowedProcessTypes`.\n *\n * @param {{ processType?: string }} metricsConfig\n * @param {readonly string[]} allowedProcessTypes\n * @param {string} defaultWhenUnspecified - Used only to resolve when config/env omit `processType` (e.g. {@link METRICS_PROCESS_TYPE_QUEUE} for {@link RedisMetricsClient})\n * @returns {void}\n */\nfunction exitUnlessProcessTypeIn(\n metricsConfig,\n allowedProcessTypes,\n defaultWhenUnspecified\n) {\n const resolved = resolveMetricsProcessType(\n metricsConfig,\n defaultWhenUnspecified\n )\n if (!allowedProcessTypes.includes(resolved)) {\n process.exit(0)\n }\n}\n\nmodule.exports = {\n resolveMetricsProcessType,\n exitUnlessProcessTypeIs,\n exitUnlessProcessTypeIn,\n METRICS_PROCESS_TYPE_DATABASE,\n METRICS_PROCESS_TYPE_QUEUE,\n METRICS_PROCESS_TYPE_REDIS,\n METRICS_PROCESS_TYPE_WEB,\n METRICS_PROCESS_TYPE_WORKER,\n METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR,\n REDIS_METRICS_CLIENT_ALLOWED_PROCESS_TYPES,\n}\n"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,MAAMA,6BAA6B,GAAG,kBAAkB;;AAExD;AACA,MAAMC,0BAA0B,GAAG,eAAe;;AAElD;AACA,MAAMC,0BAA0B,GAAG,eAAe;;AAElD;AACA,MAAMC,wBAAwB,GAAG,KAAK;;AAEtC;AACA,MAAMC,2BAA2B,GAAG,QAAQ;;AAE5C;AACA,MAAMC,2CAA2C,GAAG,cAAc;;AAElE;AACA;AACA;AACA;AACA,MAAMC,0CAA0C,GAAGC,MAAM,CAACC,MAAM,CAAC,CAC/DN,0BAA0B,EAC1BD,0BAA0B,CAC3B,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASQ,yBAAyBA,CAACC,aAAa,EAAEC,kBAAkB,EAAE;EACpE,OACED,aAAa,CAACE,WAAW,IACzBC,OAAO,CAACC,GAAG,CAACC,uBAAuB,IACnCJ,kBAAkB;AAEtB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASK,uBAAuBA,CAACN,aAAa,EAAEO,mBAAmB,EAAE;EACnE,MAAMC,QAAQ,GAAGT,yBAAyB,CACxCC,aAAa,EACbO,mBACF,CAAC;EACD,IAAIC,QAAQ,KAAKD,mBAAmB,EAAE;IACpCJ,OAAO,CAACM,IAAI,CAAC,CAAC,CAAC;EACjB;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,uBAAuBA,CAC9BV,aAAa,EACbW,mBAAmB,EACnBC,sBAAsB,EACtB;EACA,MAAMJ,QAAQ,GAAGT,yBAAyB,CACxCC,aAAa,EACbY,sBACF,CAAC;EACD,IAAI,CAACD,mBAAmB,CAACE,QAAQ,CAACL,QAAQ,CAAC,EAAE;IAC3CL,OAAO,CAACM,IAAI,CAAC,CAAC,CAAC;EACjB;AACF;AAEAK,MAAM,CAACC,OAAO,GAAG;EACfhB,yBAAyB;EACzBO,uBAAuB;EACvBI,uBAAuB;EACvBpB,6BAA6B;EAC7BC,0BAA0B;EAC1BC,0BAA0B;EAC1BC,wBAAwB;EACxBC,2BAA2B;EAC3BC,2CAA2C;EAC3CC;AACF,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adalo/metrics",
3
- "version": "0.0.0-staging.21",
3
+ "version": "0.0.0-staging.22",
4
4
  "description": "Reusable metrics utilities for Node.js and Laravel apps",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -4,8 +4,9 @@
4
4
  *
5
5
  * **Canonical names** align with typical Procfile processes (e.g. backend: `web`, `worker`,
6
6
  * `queue-metrics`, `database-metrics`, `http-metrics`). The compile **worker** dyno serves queues/builds
7
- * — it does not run HTTP request metrics; HTTP Redis **writers** use `web` (or `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE`);
8
- * the HTTP **collector** dyno uses {@link METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR}.
7
+ * — it does not run HTTP request metrics. HTTP Redis **key segment** is **`METRICS_HTTP_REDIS_KEY_PROCESS_TYPE`**
8
+ * (default **`web`**), not `BUILD_DYNO_PROCESS_TYPE` (workers/collector differ). The HTTP **collector** dyno
9
+ * identity for logs is often {@link METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR}.
9
10
  *
10
11
  * @module metrics/metricsProcessTypeUtils
11
12
  */