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

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,8 +1,9 @@
1
1
  /**
2
2
  * Drain worker: reads HTTP aggregates from Redis (written by {@link HttpMetricsRedisRecorder}),
3
3
  * applies them to `app_requests_*` counters, then pushes the registry to the VM-agent (same as {@link BaseMetricsClient}).
4
- * Always passes `blockNodeDefaultMetrics: true` to the base client (HTTP-only registry; no default Node heap/event-loop metrics).
5
- * Redis key segment must match writers (`appName` + `processType` / {@link HttpMetricsRedisStore}).
4
+ * **Minimal usage:** `{ redisClient }` only. Redis keys use segment **`web`** unless you pass **`redisProcessTypeForKeys`**.
5
+ * `processType` / `appName` / `dynoId` follow {@link BaseMetricsClient} defaults (e.g. `BUILD_DYNO_PROCESS_TYPE`) and do **not** select Redis hash names.
6
+ * Always passes `blockNodeDefaultMetrics: true` (HTTP-focused registry).
6
7
  *
7
8
  * @extends BaseMetricsClient
8
9
  */
@@ -21,7 +22,7 @@ export class HttpMetricsRedisCollector extends BaseMetricsClient {
21
22
  * @param {boolean} [config.removeOldMetrics] Clear old series on shutdown where supported
22
23
  * @param {function} [config.startupValidation] Run before first push
23
24
  * @param {boolean} [config.disablePushgateway] Skip POST to VM-agent
24
- * @param {string} [config.redisProcessTypeForKeys] Segment in Redis keys for HTTP hashes (default `web`; env `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE` overrides)
25
+ * @param {string} [config.redisProcessTypeForKeys] Segment in Redis keys for HTTP hashes (default **`web`**). Optional; only if writers use a non-`web` segment.
25
26
  * @param {number} [config.ttlSec] Passed to {@link HttpMetricsRedisStore} (should match writers)
26
27
  */
27
28
  constructor(config?: {
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisCollector.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisCollector.js"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH;IACE;;;;;;;;;;;;;;;;OAgBG;IACH;qBAfW,OAAO,OAAO,EAAE,WAAW;;;;;;;;;;;;;;mBAoErC;IAhCC,8BAKE;CAgDL"}
1
+ {"version":3,"file":"httpMetricsRedisCollector.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisCollector.js"],"names":[],"mappings":"AAGA;;;;;;;;GAQG;AACH;IACE;;;;;;;;;;;;;;;;OAgBG;IACH;qBAfW,OAAO,OAAO,EAAE,WAAW;;;;;;;;;;;;;;mBAiErC;IAhCC,8BAKE;CAgDL"}
@@ -10,8 +10,9 @@ const {
10
10
  /**
11
11
  * Drain worker: reads HTTP aggregates from Redis (written by {@link HttpMetricsRedisRecorder}),
12
12
  * applies them to `app_requests_*` counters, then pushes the registry to the VM-agent (same as {@link BaseMetricsClient}).
13
- * Always passes `blockNodeDefaultMetrics: true` to the base client (HTTP-only registry; no default Node heap/event-loop metrics).
14
- * Redis key segment must match writers (`appName` + `processType` / {@link HttpMetricsRedisStore}).
13
+ * **Minimal usage:** `{ redisClient }` only. Redis keys use segment **`web`** unless you pass **`redisProcessTypeForKeys`**.
14
+ * `processType` / `appName` / `dynoId` follow {@link BaseMetricsClient} defaults (e.g. `BUILD_DYNO_PROCESS_TYPE`) and do **not** select Redis hash names.
15
+ * Always passes `blockNodeDefaultMetrics: true` (HTTP-focused registry).
15
16
  *
16
17
  * @extends BaseMetricsClient
17
18
  */
@@ -30,7 +31,7 @@ class HttpMetricsRedisCollector extends BaseMetricsClient {
30
31
  * @param {boolean} [config.removeOldMetrics] Clear old series on shutdown where supported
31
32
  * @param {function} [config.startupValidation] Run before first push
32
33
  * @param {boolean} [config.disablePushgateway] Skip POST to VM-agent
33
- * @param {string} [config.redisProcessTypeForKeys] Segment in Redis keys for HTTP hashes (default `web`; env `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE` overrides)
34
+ * @param {string} [config.redisProcessTypeForKeys] Segment in Redis keys for HTTP hashes (default **`web`**). Optional; only if writers use a non-`web` segment.
34
35
  * @param {number} [config.ttlSec] Passed to {@link HttpMetricsRedisStore} (should match writers)
35
36
  */
36
37
  constructor(config = {}) {
@@ -44,7 +45,7 @@ class HttpMetricsRedisCollector extends BaseMetricsClient {
44
45
  ...config,
45
46
  blockNodeDefaultMetrics: true
46
47
  });
47
- const keyProcessType = config.redisProcessTypeForKeys || process.env.METRICS_HTTP_REDIS_KEY_PROCESS_TYPE || 'web';
48
+ const keyProcessType = config.redisProcessTypeForKeys || 'web';
48
49
  this.defaultLabelsWithoutDynoId = {
49
50
  app: this.appName,
50
51
  process_type: keyProcessType
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisCollector.js","names":["BaseMetricsClient","require","HttpMetricsRedisStore","HttpMetricsRedisCollector","constructor","config","redisClient","Error","blockNodeDefaultMetrics","keyProcessType","redisProcessTypeForKeys","process","env","METRICS_HTTP_REDIS_KEY_PROCESS_TYPE","defaultLabelsWithoutDynoId","app","appName","process_type","_store","processType","ttlSec","createCounter","name","help","labelNames","withDefaultLabelsWithoutDynoId","useLabelsWithoutDynoId","pushMetrics","countersFunctions","app_requests_total","app_requests_total_duration","flushToCounters","labels","value","_pushMetrics","module","exports"],"sources":["../../src/metrics/httpMetricsRedisCollector.js"],"sourcesContent":["const { BaseMetricsClient } = require('./baseMetricsClient')\nconst { HttpMetricsRedisStore } = require('./httpMetricsRedisStore')\n\n/**\n * Drain worker: reads HTTP aggregates from Redis (written by {@link HttpMetricsRedisRecorder}),\n * applies them to `app_requests_*` counters, then pushes the registry to the VM-agent (same as {@link BaseMetricsClient}).\n * Always passes `blockNodeDefaultMetrics: true` to the base client (HTTP-only registry; no default Node heap/event-loop metrics).\n * Redis key segment must match writers (`appName` + `processType` / {@link HttpMetricsRedisStore}).\n *\n * @extends BaseMetricsClient\n */\nclass HttpMetricsRedisCollector extends BaseMetricsClient {\n /**\n * @param {Object} [config]\n * @param {import('redis').RedisClient} config.redisClient **Required.** Injected client (same pattern as {@link RedisMetricsClient}).\n * @param {string} [config.appName] Application name (defaults per {@link BaseMetricsClient})\n * @param {string} [config.dynoId] Dyno/instance ID\n * @param {string} [config.processType] Label `process_type` on push (default from env / base)\n * @param {boolean} [config.enabled] Enable collection and push\n * @param {boolean} [config.logValues] Log metric JSON to console\n * @param {string} [config.pushgatewayUrl] VM-agent import URL\n * @param {string} [config.pushgatewaySecret] Basic auth secret (Base64)\n * @param {number} [config.intervalSec] Push interval (seconds)\n * @param {boolean} [config.removeOldMetrics] Clear old series on shutdown where supported\n * @param {function} [config.startupValidation] Run before first push\n * @param {boolean} [config.disablePushgateway] Skip POST to VM-agent\n * @param {string} [config.redisProcessTypeForKeys] Segment in Redis keys for HTTP hashes (default `web`; env `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE` overrides)\n * @param {number} [config.ttlSec] Passed to {@link HttpMetricsRedisStore} (should match writers)\n */\n constructor(config = {}) {\n const { redisClient } = config\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisCollector: redisClient is required')\n }\n\n super({\n ...config,\n blockNodeDefaultMetrics: true,\n })\n\n const keyProcessType =\n config.redisProcessTypeForKeys ||\n process.env.METRICS_HTTP_REDIS_KEY_PROCESS_TYPE ||\n 'web'\n\n this.defaultLabelsWithoutDynoId = {\n app: this.appName,\n process_type: keyProcessType,\n }\n\n this._store = new HttpMetricsRedisStore({\n redisClient,\n appName: this.appName,\n processType: keyProcessType,\n ttlSec: config.ttlSec,\n })\n\n this.createCounter({\n name: 'app_requests_total',\n help: 'Total number of HTTP requests',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n\n this.createCounter({\n name: 'app_requests_total_duration',\n help: 'Total duration of HTTP requests in milliseconds',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n }\n\n /**\n * Drains Redis into counters, then runs gauge updates and VM-agent push ({@link BaseMetricsClient#_pushMetrics}).\n * @returns {Promise<void>}\n */\n pushMetrics = async () => {\n if (\n this._store &&\n this.countersFunctions?.app_requests_total &&\n this.countersFunctions?.app_requests_total_duration\n ) {\n await this._store.flushToCounters(\n (labels, value) =>\n this.countersFunctions.app_requests_total(labels, value),\n (labels, value) =>\n this.countersFunctions.app_requests_total_duration(labels, value)\n )\n }\n return this._pushMetrics()\n }\n}\n\nmodule.exports = { HttpMetricsRedisCollector }\n"],"mappings":";;AAAA,MAAM;EAAEA;AAAkB,CAAC,GAAGC,OAAO,CAAC,qBAAqB,CAAC;AAC5D,MAAM;EAAEC;AAAsB,CAAC,GAAGD,OAAO,CAAC,yBAAyB,CAAC;;AAEpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAME,yBAAyB,SAASH,iBAAiB,CAAC;EACxD;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEI,WAAWA,CAACC,MAAM,GAAG,CAAC,CAAC,EAAE;IACvB,MAAM;MAAEC;IAAY,CAAC,GAAGD,MAAM;IAC9B,IAAIC,WAAW,IAAI,IAAI,EAAE;MACvB,MAAM,IAAIC,KAAK,CAAC,oDAAoD,CAAC;IACvE;IAEA,KAAK,CAAC;MACJ,GAAGF,MAAM;MACTG,uBAAuB,EAAE;IAC3B,CAAC,CAAC;IAEF,MAAMC,cAAc,GAClBJ,MAAM,CAACK,uBAAuB,IAC9BC,OAAO,CAACC,GAAG,CAACC,mCAAmC,IAC/C,KAAK;IAEP,IAAI,CAACC,0BAA0B,GAAG;MAChCC,GAAG,EAAE,IAAI,CAACC,OAAO;MACjBC,YAAY,EAAER;IAChB,CAAC;IAED,IAAI,CAACS,MAAM,GAAG,IAAIhB,qBAAqB,CAAC;MACtCI,WAAW;MACXU,OAAO,EAAE,IAAI,CAACA,OAAO;MACrBG,WAAW,EAAEV,cAAc;MAC3BW,MAAM,EAAEf,MAAM,CAACe;IACjB,CAAC,CAAC;IAEF,IAAI,CAACC,aAAa,CAAC;MACjBC,IAAI,EAAE,oBAAoB;MAC1BC,IAAI,EAAE,+BAA+B;MACrCC,UAAU,EAAE,IAAI,CAACC,8BAA8B,CAAC,CAC9C,QAAQ,EACR,OAAO,EACP,OAAO,EACP,YAAY,EACZ,aAAa,CACd,CAAC;MACFC,sBAAsB,EAAE;IAC1B,CAAC,CAAC;IAEF,IAAI,CAACL,aAAa,CAAC;MACjBC,IAAI,EAAE,6BAA6B;MACnCC,IAAI,EAAE,iDAAiD;MACvDC,UAAU,EAAE,IAAI,CAACC,8BAA8B,CAAC,CAC9C,QAAQ,EACR,OAAO,EACP,OAAO,EACP,YAAY,EACZ,aAAa,CACd,CAAC;MACFC,sBAAsB,EAAE;IAC1B,CAAC,CAAC;EACJ;;EAEA;AACF;AACA;AACA;EACEC,WAAW,GAAG,MAAAA,CAAA,KAAY;IACxB,IACE,IAAI,CAACT,MAAM,IACX,IAAI,CAACU,iBAAiB,EAAEC,kBAAkB,IAC1C,IAAI,CAACD,iBAAiB,EAAEE,2BAA2B,EACnD;MACA,MAAM,IAAI,CAACZ,MAAM,CAACa,eAAe,CAC/B,CAACC,MAAM,EAAEC,KAAK,KACZ,IAAI,CAACL,iBAAiB,CAACC,kBAAkB,CAACG,MAAM,EAAEC,KAAK,CAAC,EAC1D,CAACD,MAAM,EAAEC,KAAK,KACZ,IAAI,CAACL,iBAAiB,CAACE,2BAA2B,CAACE,MAAM,EAAEC,KAAK,CACpE,CAAC;IACH;IACA,OAAO,IAAI,CAACC,YAAY,CAAC,CAAC;EAC5B,CAAC;AACH;AAEAC,MAAM,CAACC,OAAO,GAAG;EAAEjC;AAA0B,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"httpMetricsRedisCollector.js","names":["BaseMetricsClient","require","HttpMetricsRedisStore","HttpMetricsRedisCollector","constructor","config","redisClient","Error","blockNodeDefaultMetrics","keyProcessType","redisProcessTypeForKeys","defaultLabelsWithoutDynoId","app","appName","process_type","_store","processType","ttlSec","createCounter","name","help","labelNames","withDefaultLabelsWithoutDynoId","useLabelsWithoutDynoId","pushMetrics","countersFunctions","app_requests_total","app_requests_total_duration","flushToCounters","labels","value","_pushMetrics","module","exports"],"sources":["../../src/metrics/httpMetricsRedisCollector.js"],"sourcesContent":["const { BaseMetricsClient } = require('./baseMetricsClient')\nconst { HttpMetricsRedisStore } = require('./httpMetricsRedisStore')\n\n/**\n * Drain worker: reads HTTP aggregates from Redis (written by {@link HttpMetricsRedisRecorder}),\n * applies them to `app_requests_*` counters, then pushes the registry to the VM-agent (same as {@link BaseMetricsClient}).\n * **Minimal usage:** `{ redisClient }` only. Redis keys use segment **`web`** unless you pass **`redisProcessTypeForKeys`**.\n * `processType` / `appName` / `dynoId` follow {@link BaseMetricsClient} defaults (e.g. `BUILD_DYNO_PROCESS_TYPE`) and do **not** select Redis hash names.\n * Always passes `blockNodeDefaultMetrics: true` (HTTP-focused registry).\n *\n * @extends BaseMetricsClient\n */\nclass HttpMetricsRedisCollector extends BaseMetricsClient {\n /**\n * @param {Object} [config]\n * @param {import('redis').RedisClient} config.redisClient **Required.** Injected client (same pattern as {@link RedisMetricsClient}).\n * @param {string} [config.appName] Application name (defaults per {@link BaseMetricsClient})\n * @param {string} [config.dynoId] Dyno/instance ID\n * @param {string} [config.processType] Label `process_type` on push (default from env / base)\n * @param {boolean} [config.enabled] Enable collection and push\n * @param {boolean} [config.logValues] Log metric JSON to console\n * @param {string} [config.pushgatewayUrl] VM-agent import URL\n * @param {string} [config.pushgatewaySecret] Basic auth secret (Base64)\n * @param {number} [config.intervalSec] Push interval (seconds)\n * @param {boolean} [config.removeOldMetrics] Clear old series on shutdown where supported\n * @param {function} [config.startupValidation] Run before first push\n * @param {boolean} [config.disablePushgateway] Skip POST to VM-agent\n * @param {string} [config.redisProcessTypeForKeys] Segment in Redis keys for HTTP hashes (default **`web`**). Optional; only if writers use a non-`web` segment.\n * @param {number} [config.ttlSec] Passed to {@link HttpMetricsRedisStore} (should match writers)\n */\n constructor(config = {}) {\n const { redisClient } = config\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisCollector: redisClient is required')\n }\n\n super({\n ...config,\n blockNodeDefaultMetrics: true,\n })\n\n const keyProcessType = config.redisProcessTypeForKeys || 'web'\n\n this.defaultLabelsWithoutDynoId = {\n app: this.appName,\n process_type: keyProcessType,\n }\n\n this._store = new HttpMetricsRedisStore({\n redisClient,\n appName: this.appName,\n processType: keyProcessType,\n ttlSec: config.ttlSec,\n })\n\n this.createCounter({\n name: 'app_requests_total',\n help: 'Total number of HTTP requests',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n\n this.createCounter({\n name: 'app_requests_total_duration',\n help: 'Total duration of HTTP requests in milliseconds',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n }\n\n /**\n * Drains Redis into counters, then runs gauge updates and VM-agent push ({@link BaseMetricsClient#_pushMetrics}).\n * @returns {Promise<void>}\n */\n pushMetrics = async () => {\n if (\n this._store &&\n this.countersFunctions?.app_requests_total &&\n this.countersFunctions?.app_requests_total_duration\n ) {\n await this._store.flushToCounters(\n (labels, value) =>\n this.countersFunctions.app_requests_total(labels, value),\n (labels, value) =>\n this.countersFunctions.app_requests_total_duration(labels, value)\n )\n }\n return this._pushMetrics()\n }\n}\n\nmodule.exports = { HttpMetricsRedisCollector }\n"],"mappings":";;AAAA,MAAM;EAAEA;AAAkB,CAAC,GAAGC,OAAO,CAAC,qBAAqB,CAAC;AAC5D,MAAM;EAAEC;AAAsB,CAAC,GAAGD,OAAO,CAAC,yBAAyB,CAAC;;AAEpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,MAAME,yBAAyB,SAASH,iBAAiB,CAAC;EACxD;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEI,WAAWA,CAACC,MAAM,GAAG,CAAC,CAAC,EAAE;IACvB,MAAM;MAAEC;IAAY,CAAC,GAAGD,MAAM;IAC9B,IAAIC,WAAW,IAAI,IAAI,EAAE;MACvB,MAAM,IAAIC,KAAK,CAAC,oDAAoD,CAAC;IACvE;IAEA,KAAK,CAAC;MACJ,GAAGF,MAAM;MACTG,uBAAuB,EAAE;IAC3B,CAAC,CAAC;IAEF,MAAMC,cAAc,GAAGJ,MAAM,CAACK,uBAAuB,IAAI,KAAK;IAE9D,IAAI,CAACC,0BAA0B,GAAG;MAChCC,GAAG,EAAE,IAAI,CAACC,OAAO;MACjBC,YAAY,EAAEL;IAChB,CAAC;IAED,IAAI,CAACM,MAAM,GAAG,IAAIb,qBAAqB,CAAC;MACtCI,WAAW;MACXO,OAAO,EAAE,IAAI,CAACA,OAAO;MACrBG,WAAW,EAAEP,cAAc;MAC3BQ,MAAM,EAAEZ,MAAM,CAACY;IACjB,CAAC,CAAC;IAEF,IAAI,CAACC,aAAa,CAAC;MACjBC,IAAI,EAAE,oBAAoB;MAC1BC,IAAI,EAAE,+BAA+B;MACrCC,UAAU,EAAE,IAAI,CAACC,8BAA8B,CAAC,CAC9C,QAAQ,EACR,OAAO,EACP,OAAO,EACP,YAAY,EACZ,aAAa,CACd,CAAC;MACFC,sBAAsB,EAAE;IAC1B,CAAC,CAAC;IAEF,IAAI,CAACL,aAAa,CAAC;MACjBC,IAAI,EAAE,6BAA6B;MACnCC,IAAI,EAAE,iDAAiD;MACvDC,UAAU,EAAE,IAAI,CAACC,8BAA8B,CAAC,CAC9C,QAAQ,EACR,OAAO,EACP,OAAO,EACP,YAAY,EACZ,aAAa,CACd,CAAC;MACFC,sBAAsB,EAAE;IAC1B,CAAC,CAAC;EACJ;;EAEA;AACF;AACA;AACA;EACEC,WAAW,GAAG,MAAAA,CAAA,KAAY;IACxB,IACE,IAAI,CAACT,MAAM,IACX,IAAI,CAACU,iBAAiB,EAAEC,kBAAkB,IAC1C,IAAI,CAACD,iBAAiB,EAAEE,2BAA2B,EACnD;MACA,MAAM,IAAI,CAACZ,MAAM,CAACa,eAAe,CAC/B,CAACC,MAAM,EAAEC,KAAK,KACZ,IAAI,CAACL,iBAAiB,CAACC,kBAAkB,CAACG,MAAM,EAAEC,KAAK,CAAC,EAC1D,CAACD,MAAM,EAAEC,KAAK,KACZ,IAAI,CAACL,iBAAiB,CAACE,2BAA2B,CAACE,MAAM,EAAEC,KAAK,CACpE,CAAC;IACH;IACA,OAAO,IAAI,CAACC,YAAY,CAAC,CAAC;EAC5B,CAAC;AACH;AAEAC,MAAM,CAACC,OAAO,GAAG;EAAE9B;AAA0B,CAAC","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisRecorder.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisRecorder.js"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH;IACE;;;;;;OAMG;IACH;QAL6C,WAAW,EAA7C,OAAO,OAAO,EAAE,WAAW;QACd,OAAO,EAApB,MAAM;QACO,WAAW,EAAxB,MAAM;QACQ,MAAM;OAc9B;IARC,oBAA8B;IAC9B,gBAAsB;IACtB,8BAKE;IAGJ;;;;;;;;OAQG;IACH;QAP0B,MAAM,EAArB,MAAM;QACS,KAAK,EAApB,MAAM;QACS,WAAW,EAA1B,MAAM;QACU,KAAK;QACL,UAAU;QACX,QAAQ,EAAvB,MAAM;aAWhB;IAED;;;;;;;OAOG;IACH,kCAJW,OAAO,MAAM,EAAE,eAAe,OAC9B,OAAO,MAAM,EAAE,cAAc,0BAkCvC;CACF"}
1
+ {"version":3,"file":"httpMetricsRedisRecorder.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisRecorder.js"],"names":[],"mappings":"AASA;;;;;GAKG;AACH;IACE;;;;;;OAMG;IACH;QAL6C,WAAW,EAA7C,OAAO,OAAO,EAAE,WAAW;QACd,OAAO,EAApB,MAAM;QACO,WAAW,EAAxB,MAAM;QACQ,MAAM;OAc9B;IARC,oBAA8B;IAC9B,gBAAsB;IACtB,8BAKE;IAGJ;;;;;;;;OAQG;IACH;QAP0B,MAAM,EAArB,MAAM;QACS,KAAK,EAApB,MAAM;QACS,WAAW,EAA1B,MAAM;QACU,KAAK;QACL,UAAU;QACX,QAAQ,EAAvB,MAAM;aAgBhB;IAED;;;;;;;OAOG;IACH,kCAJW,OAAO,MAAM,EAAE,eAAe,OAC9B,OAAO,MAAM,EAAE,cAAc,0BAkCvC;CACF"}
@@ -3,6 +3,11 @@
3
3
  const {
4
4
  HttpMetricsRedisStore
5
5
  } = require('./httpMetricsRedisStore');
6
+ const LOG = '[http-metrics-redis]';
7
+ function trunc(s, max = 120) {
8
+ const t = String(s);
9
+ return t.length > max ? `${t.slice(0, max - 3)}...` : t;
10
+ }
6
11
 
7
12
  /**
8
13
  * Records HTTP request aggregates only to Redis (no in-process Prometheus counters on this path).
@@ -54,6 +59,7 @@ class HttpMetricsRedisRecorder {
54
59
  databaseId = '',
55
60
  duration
56
61
  }) {
62
+ console.warn(`${LOG} 1_track pid=${process.pid} app=${this.appName} segment=${this.processType} ` + `method=${method} route=${trunc(route)} status=${status_code} durationMs=${duration} ` + `appId=${trunc(appId || '—', 40)} databaseId=${trunc(databaseId || '—', 40)}`);
57
63
  this._store.record(method, route, status_code, appId, databaseId, duration);
58
64
  }
59
65
 
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisRecorder.js","names":["HttpMetricsRedisStore","require","HttpMetricsRedisRecorder","constructor","redisClient","appName","processType","ttlSec","Error","_store","trackHttpRequest","method","route","status_code","appId","databaseId","duration","record","trackHttpRequestMiddleware","req","res","next","start","Date","now","on","path","params","body","query","datasourceId","statusCode","module","exports"],"sources":["../../src/metrics/httpMetricsRedisRecorder.js"],"sourcesContent":["const { HttpMetricsRedisStore } = require('./httpMetricsRedisStore')\n\n/**\n * Records HTTP request aggregates only to Redis (no in-process Prometheus counters on this path).\n * Pair with {@link HttpMetricsRedisCollector} on a drain process to flush into counters and push to the VM-agent.\n *\n * @see HttpMetricsRedisStore\n */\nclass HttpMetricsRedisRecorder {\n /**\n * @param {Object} opts\n * @param {import('redis').RedisClient} opts.redisClient **Required.** Injected client (same pattern as {@link RedisMetricsClient}).\n * @param {string} opts.appName Application name (must match collector; typically `BUILD_APP_NAME`).\n * @param {string} opts.processType Logical process segment in Redis keys (e.g. `web`; must match collector’s key segment).\n * @param {number} [opts.ttlSec] Redis hash key TTL in seconds (sliding; default 120).\n */\n constructor({ redisClient, appName, processType, ttlSec }) {\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisRecorder: redisClient is required')\n }\n this.processType = processType\n this.appName = appName\n this._store = new HttpMetricsRedisStore({\n redisClient,\n appName,\n processType,\n ttlSec,\n })\n }\n\n /**\n * @param {Object} params\n * @param {string} params.method\n * @param {string} params.route\n * @param {number} params.status_code\n * @param {string} [params.appId]\n * @param {string} [params.databaseId]\n * @param {number} params.duration\n */\n trackHttpRequest({\n method,\n route,\n status_code,\n appId = '',\n databaseId = '',\n duration,\n }) {\n this._store.record(method, route, status_code, appId, databaseId, duration)\n }\n\n /**\n * Express middleware: appends a `finish` listener and writes one aggregate row per request to Redis.\n * Does not check `METRICS_ENABLED`; the app should only mount this when HTTP Redis metrics should run.\n *\n * @param {import('http').IncomingMessage} req\n * @param {import('http').ServerResponse} res\n * @param {function} next\n */\n trackHttpRequestMiddleware = (req, res, next) => {\n if (req.method === 'OPTIONS') {\n next()\n return\n }\n\n const start = Date.now()\n res.on('finish', () => {\n const route = req.route?.path || req.path || 'unknown'\n const appId =\n req.params?.appId || req.body?.appId || req.query?.appId || ''\n const databaseId =\n req.params?.databaseId ||\n req.body?.databaseId ||\n req.query?.databaseId ||\n req.params?.datasourceId ||\n req.body?.datasourceId ||\n req.query?.datasourceId ||\n ''\n\n this.trackHttpRequest({\n method: req.method,\n route,\n status_code: res.statusCode,\n appId,\n databaseId,\n duration: Date.now() - start,\n })\n })\n\n next()\n }\n}\n\nmodule.exports = { HttpMetricsRedisRecorder }\n"],"mappings":";;AAAA,MAAM;EAAEA;AAAsB,CAAC,GAAGC,OAAO,CAAC,yBAAyB,CAAC;;AAEpE;AACA;AACA;AACA;AACA;AACA;AACA,MAAMC,wBAAwB,CAAC;EAC7B;AACF;AACA;AACA;AACA;AACA;AACA;EACEC,WAAWA,CAAC;IAAEC,WAAW;IAAEC,OAAO;IAAEC,WAAW;IAAEC;EAAO,CAAC,EAAE;IACzD,IAAIH,WAAW,IAAI,IAAI,EAAE;MACvB,MAAM,IAAII,KAAK,CAAC,mDAAmD,CAAC;IACtE;IACA,IAAI,CAACF,WAAW,GAAGA,WAAW;IAC9B,IAAI,CAACD,OAAO,GAAGA,OAAO;IACtB,IAAI,CAACI,MAAM,GAAG,IAAIT,qBAAqB,CAAC;MACtCI,WAAW;MACXC,OAAO;MACPC,WAAW;MACXC;IACF,CAAC,CAAC;EACJ;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEG,gBAAgBA,CAAC;IACfC,MAAM;IACNC,KAAK;IACLC,WAAW;IACXC,KAAK,GAAG,EAAE;IACVC,UAAU,GAAG,EAAE;IACfC;EACF,CAAC,EAAE;IACD,IAAI,CAACP,MAAM,CAACQ,MAAM,CAACN,MAAM,EAAEC,KAAK,EAAEC,WAAW,EAAEC,KAAK,EAAEC,UAAU,EAAEC,QAAQ,CAAC;EAC7E;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEE,0BAA0B,GAAGA,CAACC,GAAG,EAAEC,GAAG,EAAEC,IAAI,KAAK;IAC/C,IAAIF,GAAG,CAACR,MAAM,KAAK,SAAS,EAAE;MAC5BU,IAAI,CAAC,CAAC;MACN;IACF;IAEA,MAAMC,KAAK,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC;IACxBJ,GAAG,CAACK,EAAE,CAAC,QAAQ,EAAE,MAAM;MACrB,MAAMb,KAAK,GAAGO,GAAG,CAACP,KAAK,EAAEc,IAAI,IAAIP,GAAG,CAACO,IAAI,IAAI,SAAS;MACtD,MAAMZ,KAAK,GACTK,GAAG,CAACQ,MAAM,EAAEb,KAAK,IAAIK,GAAG,CAACS,IAAI,EAAEd,KAAK,IAAIK,GAAG,CAACU,KAAK,EAAEf,KAAK,IAAI,EAAE;MAChE,MAAMC,UAAU,GACdI,GAAG,CAACQ,MAAM,EAAEZ,UAAU,IACtBI,GAAG,CAACS,IAAI,EAAEb,UAAU,IACpBI,GAAG,CAACU,KAAK,EAAEd,UAAU,IACrBI,GAAG,CAACQ,MAAM,EAAEG,YAAY,IACxBX,GAAG,CAACS,IAAI,EAAEE,YAAY,IACtBX,GAAG,CAACU,KAAK,EAAEC,YAAY,IACvB,EAAE;MAEJ,IAAI,CAACpB,gBAAgB,CAAC;QACpBC,MAAM,EAAEQ,GAAG,CAACR,MAAM;QAClBC,KAAK;QACLC,WAAW,EAAEO,GAAG,CAACW,UAAU;QAC3BjB,KAAK;QACLC,UAAU;QACVC,QAAQ,EAAEO,IAAI,CAACC,GAAG,CAAC,CAAC,GAAGF;MACzB,CAAC,CAAC;IACJ,CAAC,CAAC;IAEFD,IAAI,CAAC,CAAC;EACR,CAAC;AACH;AAEAW,MAAM,CAACC,OAAO,GAAG;EAAE/B;AAAyB,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"httpMetricsRedisRecorder.js","names":["HttpMetricsRedisStore","require","LOG","trunc","s","max","t","String","length","slice","HttpMetricsRedisRecorder","constructor","redisClient","appName","processType","ttlSec","Error","_store","trackHttpRequest","method","route","status_code","appId","databaseId","duration","console","warn","process","pid","record","trackHttpRequestMiddleware","req","res","next","start","Date","now","on","path","params","body","query","datasourceId","statusCode","module","exports"],"sources":["../../src/metrics/httpMetricsRedisRecorder.js"],"sourcesContent":["const { HttpMetricsRedisStore } = require('./httpMetricsRedisStore')\n\nconst LOG = '[http-metrics-redis]'\n\nfunction trunc(s, max = 120) {\n const t = String(s)\n return t.length > max ? `${t.slice(0, max - 3)}...` : t\n}\n\n/**\n * Records HTTP request aggregates only to Redis (no in-process Prometheus counters on this path).\n * Pair with {@link HttpMetricsRedisCollector} on a drain process to flush into counters and push to the VM-agent.\n *\n * @see HttpMetricsRedisStore\n */\nclass HttpMetricsRedisRecorder {\n /**\n * @param {Object} opts\n * @param {import('redis').RedisClient} opts.redisClient **Required.** Injected client (same pattern as {@link RedisMetricsClient}).\n * @param {string} opts.appName Application name (must match collector; typically `BUILD_APP_NAME`).\n * @param {string} opts.processType Logical process segment in Redis keys (e.g. `web`; must match collector’s key segment).\n * @param {number} [opts.ttlSec] Redis hash key TTL in seconds (sliding; default 120).\n */\n constructor({ redisClient, appName, processType, ttlSec }) {\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisRecorder: redisClient is required')\n }\n this.processType = processType\n this.appName = appName\n this._store = new HttpMetricsRedisStore({\n redisClient,\n appName,\n processType,\n ttlSec,\n })\n }\n\n /**\n * @param {Object} params\n * @param {string} params.method\n * @param {string} params.route\n * @param {number} params.status_code\n * @param {string} [params.appId]\n * @param {string} [params.databaseId]\n * @param {number} params.duration\n */\n trackHttpRequest({\n method,\n route,\n status_code,\n appId = '',\n databaseId = '',\n duration,\n }) {\n console.warn(\n `${LOG} 1_track pid=${process.pid} app=${this.appName} segment=${this.processType} ` +\n `method=${method} route=${trunc(route)} status=${status_code} durationMs=${duration} ` +\n `appId=${trunc(appId || '—', 40)} databaseId=${trunc(databaseId || '—', 40)}`\n )\n this._store.record(method, route, status_code, appId, databaseId, duration)\n }\n\n /**\n * Express middleware: appends a `finish` listener and writes one aggregate row per request to Redis.\n * Does not check `METRICS_ENABLED`; the app should only mount this when HTTP Redis metrics should run.\n *\n * @param {import('http').IncomingMessage} req\n * @param {import('http').ServerResponse} res\n * @param {function} next\n */\n trackHttpRequestMiddleware = (req, res, next) => {\n if (req.method === 'OPTIONS') {\n next()\n return\n }\n\n const start = Date.now()\n res.on('finish', () => {\n const route = req.route?.path || req.path || 'unknown'\n const appId =\n req.params?.appId || req.body?.appId || req.query?.appId || ''\n const databaseId =\n req.params?.databaseId ||\n req.body?.databaseId ||\n req.query?.databaseId ||\n req.params?.datasourceId ||\n req.body?.datasourceId ||\n req.query?.datasourceId ||\n ''\n\n this.trackHttpRequest({\n method: req.method,\n route,\n status_code: res.statusCode,\n appId,\n databaseId,\n duration: Date.now() - start,\n })\n })\n\n next()\n }\n}\n\nmodule.exports = { HttpMetricsRedisRecorder }\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisStore.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisStore.js"],"names":[],"mappings":"AAwDA;;;;;;GAMG;AACH;IACE;;;;;;OAMG;IACH;QAL6C,WAAW,EAA7C,OAAO,OAAO,EAAE,WAAW;QACd,OAAO,EAApB,MAAM;QACO,WAAW,EAAxB,MAAM;QACQ,MAAM;OAiB9B;IAXC,qCAA0B;IAC1B,eAGwC;IAIxC,iBAAiD;IACjD,eAA6C;IAC7C,gBAAqD;IAGvD;;;OAGG;IACH,sBAEC;IAED;;;;;;;OAOG;IACH,eAPW,MAAM,SACN,MAAM,cACN,MAAM,SACN,MAAM,cACN,MAAM,cACN,MAAM,QAqBhB;IAED;;;;OAIG;IACH,qCAJoB,MAAM,SAAS,MAAM,KAAK,IAAI,0BAC9B,MAAM,SAAS,MAAM,KAAK,IAAI,GACrC,QAAQ,OAAO,CAAC,CA6E5B;CACF;AA7KD;;;;;;;GAOG;AACH,sCAPW,MAAM,SACN,MAAM,cACN,MAAM,SACN,MAAM,cACN,MAAM,GACJ,MAAM,CAIlB;AA3CD;;;GAGG;AACH,wBAFU,MAAM,CAEQ;AAiBxB;;GAEG;AACH,wCAFa,OAAO,CASnB;AAzBD;;;GAGG;AACH,iDAFU,MAAM,CAE8B"}
1
+ {"version":3,"file":"httpMetricsRedisStore.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisStore.js"],"names":[],"mappings":"AA0DA;;;;;;GAMG;AACH;IACE;;;;;;OAMG;IACH;QAL6C,WAAW,EAA7C,OAAO,OAAO,EAAE,WAAW;QACd,OAAO,EAApB,MAAM;QACO,WAAW,EAAxB,MAAM;QACQ,MAAM;OAiB9B;IAXC,qCAA0B;IAC1B,eAGwC;IAIxC,iBAAiD;IACjD,eAA6C;IAC7C,gBAAqD;IAGvD;;;OAGG;IACH,sBAEC;IAED;;;;;;;OAOG;IACH,eAPW,MAAM,SACN,MAAM,cACN,MAAM,SACN,MAAM,cACN,MAAM,cACN,MAAM,QAyBhB;IAED;;;;OAIG;IACH,qCAJoB,MAAM,SAAS,MAAM,KAAK,IAAI,0BAC9B,MAAM,SAAS,MAAM,KAAK,IAAI,GACrC,QAAQ,OAAO,CAAC,CAgG5B;CACF;AApMD;;;;;;;GAOG;AACH,sCAPW,MAAM,SACN,MAAM,cACN,MAAM,SACN,MAAM,cACN,MAAM,GACJ,MAAM,CAIlB;AA7CD;;;GAGG;AACH,wBAFU,MAAM,CAEQ;AAmBxB;;GAEG;AACH,wCAFa,OAAO,CASnB;AA3BD;;;GAGG;AACH,iDAFU,MAAM,CAE8B"}
@@ -11,6 +11,7 @@ const FIELD_SEP = '\x1e';
11
11
  * @type {number}
12
12
  */
13
13
  const DEFAULT_HTTP_METRICS_REDIS_TTL_SEC = 120;
14
+ const LOG = '[http-metrics-redis]';
14
15
  const DRAIN_LUA = `
15
16
  local function drain(key)
16
17
  local v = redis.call('HGETALL', key)
@@ -110,7 +111,9 @@ class HttpMetricsRedisStore {
110
111
  client.multi().hincrby(this.countKey, field, 1).hincrby(this.durKey, field, dur).expire(this.countKey, this.ttlSec).expire(this.durKey, this.ttlSec).exec(err => {
111
112
  if (err) {
112
113
  console.error('[HttpMetricsRedisStore] record failed:', err.message);
114
+ return;
113
115
  }
116
+ console.warn(`${LOG} 2_redis_write pid=${process.pid} countKey=${this.countKey} durKey=${this.durKey} ok`);
114
117
  });
115
118
  } catch (e) {
116
119
  console.error('[HttpMetricsRedisStore] record:', e.message);
@@ -133,6 +136,7 @@ class HttpMetricsRedisStore {
133
136
  return new Promise(resolve => {
134
137
  client.set(this.lockKey, '1', 'EX', 25, 'NX', (setErr, ok) => {
135
138
  if (setErr || ok !== 'OK') {
139
+ console.warn(`${LOG} 3_redis_drain_skip pid=${process.pid} countKey=${this.countKey} reason=${setErr ? `error:${setErr.message}` : 'lock_not_acquired'}`);
136
140
  resolve(false);
137
141
  return;
138
142
  }
@@ -147,12 +151,15 @@ class HttpMetricsRedisStore {
147
151
  }
148
152
  try {
149
153
  if (!raw || !Array.isArray(raw) || raw.length < 2) {
154
+ console.warn(`${LOG} 3_redis_drain pid=${process.pid} countKey=${this.countKey} hash_fields=0 sum_requests=0 (empty_after_lua)`);
150
155
  finish();
151
156
  return;
152
157
  }
153
158
  const counts = hgetallPairsToObject(raw[0]);
154
159
  const durs = hgetallPairsToObject(raw[1]);
155
160
  const fieldKeys = Object.keys(counts);
161
+ let sumRequests = 0;
162
+ const samples = [];
156
163
  for (const field of fieldKeys) {
157
164
  const count = parseInt(counts[field], 10);
158
165
  if (!count || count < 1) {
@@ -164,6 +171,10 @@ class HttpMetricsRedisStore {
164
171
  continue;
165
172
  }
166
173
  const [m, route, statusStr, aid, did] = parts;
174
+ sumRequests += count;
175
+ if (samples.length < 3) {
176
+ samples.push(`${m} ${route} ${statusStr} x${count}`);
177
+ }
167
178
  const labels = {
168
179
  method: m,
169
180
  route,
@@ -176,6 +187,7 @@ class HttpMetricsRedisStore {
176
187
  applyDuration(labels, dur);
177
188
  }
178
189
  }
190
+ console.warn(`${LOG} 3_redis_drain pid=${process.pid} countKey=${this.countKey} hash_fields=${fieldKeys.length} sum_requests=${sumRequests} sample=${samples.join(' | ') || '—'}`);
179
191
  } catch (e) {
180
192
  console.error('[HttpMetricsRedisStore] flush apply failed:', e.message);
181
193
  }
@@ -1 +1 @@
1
- {"version":3,"file":"httpMetricsRedisStore.js","names":["FIELD_SEP","DEFAULT_HTTP_METRICS_REDIS_TTL_SEC","DRAIN_LUA","isRedisPeerInstalled","require","resolve","buildFieldKey","method","route","statusCode","appId","databaseId","String","join","hgetallPairsToObject","pairs","o","length","i","HttpMetricsRedisStore","constructor","redisClient","appName","processType","ttlSec","Error","_client","keySeg","encodeURIComponent","countKey","durKey","lockKey","_ensureClient","record","durationMs","client","field","dur","Math","max","round","Number","multi","hincrby","expire","exec","err","console","error","message","e","flushToCounters","applyCount","applyDuration","Promise","set","setErr","ok","eval","evalErr","raw","finish","del","Array","isArray","counts","durs","fieldKeys","Object","keys","count","parseInt","parts","split","m","statusStr","aid","did","labels","status_code","module","exports"],"sources":["../../src/metrics/httpMetricsRedisStore.js"],"sourcesContent":["/**\n * Record separator for hash fields (avoids collisions when route contains \"|\").\n * @type {string}\n */\nconst FIELD_SEP = '\\x1e'\n\n/**\n * Default Redis key TTL in seconds (sliding: refreshed on each `record` write).\n * @type {number}\n */\nconst DEFAULT_HTTP_METRICS_REDIS_TTL_SEC = 120\n\nconst DRAIN_LUA = `\nlocal function drain(key)\n local v = redis.call('HGETALL', key)\n redis.call('DEL', key)\n return v\nend\nreturn {drain(KEYS[1]), drain(KEYS[2])}\n`\n\n/**\n * @returns {boolean} Whether the `redis` npm package is resolvable (optional peer).\n */\nfunction isRedisPeerInstalled() {\n try {\n require.resolve('redis')\n return true\n } catch {\n return false\n }\n}\n\n/**\n * @param {string} method\n * @param {string} route\n * @param {number} statusCode\n * @param {string} appId\n * @param {string} databaseId\n * @returns {string}\n */\nfunction buildFieldKey(method, route, statusCode, appId, databaseId) {\n return [method, route, String(statusCode), appId, databaseId].join(FIELD_SEP)\n}\n\nfunction hgetallPairsToObject(pairs) {\n const o = {}\n if (!pairs || !pairs.length) {\n return o\n }\n for (let i = 0; i < pairs.length; i += 2) {\n o[pairs[i]] = pairs[i + 1]\n }\n return o\n}\n\n/**\n * Redis HTTP aggregate store. Uses an **injected** client (same pattern as {@link RedisMetricsClient}).\n * Expects `redis` v3-style API: `multi().hincrby().expire().exec(cb)`, `eval`, `set`, `del`.\n *\n * **Stored metrics:** two hashes per app/segment — `:count` (HINCRBY per route group) and `:dur`\n * (sum of duration ms per same field). Hash field = `method\\\\x1eroute\\\\x1estatus\\\\x1eappId\\\\x1edatabaseId`.\n */\nclass HttpMetricsRedisStore {\n /**\n * @param {Object} opts\n * @param {import('redis').RedisClient} opts.redisClient\n * @param {string} opts.appName BUILD_APP_NAME (key segment)\n * @param {string} opts.processType logical process for key (e.g. web)\n * @param {number} [opts.ttlSec] Expire keys after this many seconds (default 120)\n */\n constructor({ redisClient, appName, processType, ttlSec }) {\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisStore: redisClient is required')\n }\n this._client = redisClient\n this.ttlSec =\n typeof ttlSec === 'number' && ttlSec > 0\n ? ttlSec\n : DEFAULT_HTTP_METRICS_REDIS_TTL_SEC\n const keySeg = `${encodeURIComponent(appName)}:${encodeURIComponent(\n processType\n )}`\n this.countKey = `metrics:http:v2:${keySeg}:count`\n this.durKey = `metrics:http:v2:${keySeg}:dur`\n this.lockKey = `metrics:http:v2:${keySeg}:drain_lock`\n }\n\n /**\n * @returns {import('redis').RedisClient}\n * @private\n */\n _ensureClient() {\n return this._client\n }\n\n /**\n * @param {string} method\n * @param {string} route\n * @param {number} statusCode\n * @param {string} appId\n * @param {string} databaseId\n * @param {number} durationMs\n */\n record(method, route, statusCode, appId, databaseId, durationMs) {\n try {\n const client = this._ensureClient()\n const field = buildFieldKey(method, route, statusCode, appId, databaseId)\n const dur = Math.max(0, Math.round(Number(durationMs) || 0))\n client\n .multi()\n .hincrby(this.countKey, field, 1)\n .hincrby(this.durKey, field, dur)\n .expire(this.countKey, this.ttlSec)\n .expire(this.durKey, this.ttlSec)\n .exec(err => {\n if (err) {\n console.error('[HttpMetricsRedisStore] record failed:', err.message)\n }\n })\n } catch (e) {\n console.error('[HttpMetricsRedisStore] record:', e.message)\n }\n }\n\n /**\n * @param {(labels: Object, value: number) => void} applyCount\n * @param {(labels: Object, value: number) => void} applyDuration\n * @returns {Promise<boolean>}\n */\n flushToCounters(applyCount, applyDuration) {\n let client\n try {\n client = this._ensureClient()\n } catch (e) {\n console.error('[HttpMetricsRedisStore] flush:', e.message)\n return Promise.resolve(false)\n }\n return new Promise(resolve => {\n client.set(this.lockKey, '1', 'EX', 25, 'NX', (setErr, ok) => {\n if (setErr || ok !== 'OK') {\n resolve(false)\n return\n }\n client.eval(\n DRAIN_LUA,\n 2,\n this.countKey,\n this.durKey,\n (evalErr, raw) => {\n const finish = () => {\n client.del(this.lockKey, () => resolve(true))\n }\n\n if (evalErr) {\n console.error(\n '[HttpMetricsRedisStore] drain failed:',\n evalErr.message\n )\n finish()\n return\n }\n\n try {\n if (!raw || !Array.isArray(raw) || raw.length < 2) {\n finish()\n return\n }\n const counts = hgetallPairsToObject(raw[0])\n const durs = hgetallPairsToObject(raw[1])\n const fieldKeys = Object.keys(counts)\n for (const field of fieldKeys) {\n const count = parseInt(counts[field], 10)\n if (!count || count < 1) {\n continue\n }\n const dur = parseInt(durs[field] || '0', 10) || 0\n const parts = field.split(FIELD_SEP)\n if (parts.length !== 5) {\n continue\n }\n const [m, route, statusStr, aid, did] = parts\n const labels = {\n method: m,\n route,\n status_code: statusStr,\n appId: aid,\n databaseId: did,\n }\n applyCount(labels, count)\n if (dur > 0) {\n applyDuration(labels, dur)\n }\n }\n } catch (e) {\n console.error(\n '[HttpMetricsRedisStore] flush apply failed:',\n e.message\n )\n }\n finish()\n }\n )\n })\n })\n }\n}\n\nmodule.exports = {\n HttpMetricsRedisStore,\n buildFieldKey,\n FIELD_SEP,\n isRedisPeerInstalled,\n DEFAULT_HTTP_METRICS_REDIS_TTL_SEC,\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"httpMetricsRedisStore.js","names":["FIELD_SEP","DEFAULT_HTTP_METRICS_REDIS_TTL_SEC","LOG","DRAIN_LUA","isRedisPeerInstalled","require","resolve","buildFieldKey","method","route","statusCode","appId","databaseId","String","join","hgetallPairsToObject","pairs","o","length","i","HttpMetricsRedisStore","constructor","redisClient","appName","processType","ttlSec","Error","_client","keySeg","encodeURIComponent","countKey","durKey","lockKey","_ensureClient","record","durationMs","client","field","dur","Math","max","round","Number","multi","hincrby","expire","exec","err","console","error","message","warn","process","pid","e","flushToCounters","applyCount","applyDuration","Promise","set","setErr","ok","eval","evalErr","raw","finish","del","Array","isArray","counts","durs","fieldKeys","Object","keys","sumRequests","samples","count","parseInt","parts","split","m","statusStr","aid","did","push","labels","status_code","module","exports"],"sources":["../../src/metrics/httpMetricsRedisStore.js"],"sourcesContent":["/**\n * Record separator for hash fields (avoids collisions when route contains \"|\").\n * @type {string}\n */\nconst FIELD_SEP = '\\x1e'\n\n/**\n * Default Redis key TTL in seconds (sliding: refreshed on each `record` write).\n * @type {number}\n */\nconst DEFAULT_HTTP_METRICS_REDIS_TTL_SEC = 120\n\nconst LOG = '[http-metrics-redis]'\n\nconst DRAIN_LUA = `\nlocal function drain(key)\n local v = redis.call('HGETALL', key)\n redis.call('DEL', key)\n return v\nend\nreturn {drain(KEYS[1]), drain(KEYS[2])}\n`\n\n/**\n * @returns {boolean} Whether the `redis` npm package is resolvable (optional peer).\n */\nfunction isRedisPeerInstalled() {\n try {\n require.resolve('redis')\n return true\n } catch {\n return false\n }\n}\n\n/**\n * @param {string} method\n * @param {string} route\n * @param {number} statusCode\n * @param {string} appId\n * @param {string} databaseId\n * @returns {string}\n */\nfunction buildFieldKey(method, route, statusCode, appId, databaseId) {\n return [method, route, String(statusCode), appId, databaseId].join(FIELD_SEP)\n}\n\nfunction hgetallPairsToObject(pairs) {\n const o = {}\n if (!pairs || !pairs.length) {\n return o\n }\n for (let i = 0; i < pairs.length; i += 2) {\n o[pairs[i]] = pairs[i + 1]\n }\n return o\n}\n\n/**\n * Redis HTTP aggregate store. Uses an **injected** client (same pattern as {@link RedisMetricsClient}).\n * Expects `redis` v3-style API: `multi().hincrby().expire().exec(cb)`, `eval`, `set`, `del`.\n *\n * **Stored metrics:** two hashes per app/segment — `:count` (HINCRBY per route group) and `:dur`\n * (sum of duration ms per same field). Hash field = `method\\\\x1eroute\\\\x1estatus\\\\x1eappId\\\\x1edatabaseId`.\n */\nclass HttpMetricsRedisStore {\n /**\n * @param {Object} opts\n * @param {import('redis').RedisClient} opts.redisClient\n * @param {string} opts.appName BUILD_APP_NAME (key segment)\n * @param {string} opts.processType logical process for key (e.g. web)\n * @param {number} [opts.ttlSec] Expire keys after this many seconds (default 120)\n */\n constructor({ redisClient, appName, processType, ttlSec }) {\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisStore: redisClient is required')\n }\n this._client = redisClient\n this.ttlSec =\n typeof ttlSec === 'number' && ttlSec > 0\n ? ttlSec\n : DEFAULT_HTTP_METRICS_REDIS_TTL_SEC\n const keySeg = `${encodeURIComponent(appName)}:${encodeURIComponent(\n processType\n )}`\n this.countKey = `metrics:http:v2:${keySeg}:count`\n this.durKey = `metrics:http:v2:${keySeg}:dur`\n this.lockKey = `metrics:http:v2:${keySeg}:drain_lock`\n }\n\n /**\n * @returns {import('redis').RedisClient}\n * @private\n */\n _ensureClient() {\n return this._client\n }\n\n /**\n * @param {string} method\n * @param {string} route\n * @param {number} statusCode\n * @param {string} appId\n * @param {string} databaseId\n * @param {number} durationMs\n */\n record(method, route, statusCode, appId, databaseId, durationMs) {\n try {\n const client = this._ensureClient()\n const field = buildFieldKey(method, route, statusCode, appId, databaseId)\n const dur = Math.max(0, Math.round(Number(durationMs) || 0))\n client\n .multi()\n .hincrby(this.countKey, field, 1)\n .hincrby(this.durKey, field, dur)\n .expire(this.countKey, this.ttlSec)\n .expire(this.durKey, this.ttlSec)\n .exec(err => {\n if (err) {\n console.error('[HttpMetricsRedisStore] record failed:', err.message)\n return\n }\n console.warn(\n `${LOG} 2_redis_write pid=${process.pid} countKey=${this.countKey} durKey=${this.durKey} ok`\n )\n })\n } catch (e) {\n console.error('[HttpMetricsRedisStore] record:', e.message)\n }\n }\n\n /**\n * @param {(labels: Object, value: number) => void} applyCount\n * @param {(labels: Object, value: number) => void} applyDuration\n * @returns {Promise<boolean>}\n */\n flushToCounters(applyCount, applyDuration) {\n let client\n try {\n client = this._ensureClient()\n } catch (e) {\n console.error('[HttpMetricsRedisStore] flush:', e.message)\n return Promise.resolve(false)\n }\n return new Promise(resolve => {\n client.set(this.lockKey, '1', 'EX', 25, 'NX', (setErr, ok) => {\n if (setErr || ok !== 'OK') {\n console.warn(\n `${LOG} 3_redis_drain_skip pid=${process.pid} countKey=${this.countKey} reason=${\n setErr ? `error:${setErr.message}` : 'lock_not_acquired'\n }`\n )\n resolve(false)\n return\n }\n client.eval(\n DRAIN_LUA,\n 2,\n this.countKey,\n this.durKey,\n (evalErr, raw) => {\n const finish = () => {\n client.del(this.lockKey, () => resolve(true))\n }\n\n if (evalErr) {\n console.error(\n '[HttpMetricsRedisStore] drain failed:',\n evalErr.message\n )\n finish()\n return\n }\n\n try {\n if (!raw || !Array.isArray(raw) || raw.length < 2) {\n console.warn(\n `${LOG} 3_redis_drain pid=${process.pid} countKey=${this.countKey} hash_fields=0 sum_requests=0 (empty_after_lua)`\n )\n finish()\n return\n }\n const counts = hgetallPairsToObject(raw[0])\n const durs = hgetallPairsToObject(raw[1])\n const fieldKeys = Object.keys(counts)\n let sumRequests = 0\n const samples = []\n for (const field of fieldKeys) {\n const count = parseInt(counts[field], 10)\n if (!count || count < 1) {\n continue\n }\n const dur = parseInt(durs[field] || '0', 10) || 0\n const parts = field.split(FIELD_SEP)\n if (parts.length !== 5) {\n continue\n }\n const [m, route, statusStr, aid, did] = parts\n sumRequests += count\n if (samples.length < 3) {\n samples.push(`${m} ${route} ${statusStr} x${count}`)\n }\n const labels = {\n method: m,\n route,\n status_code: statusStr,\n appId: aid,\n databaseId: did,\n }\n applyCount(labels, count)\n if (dur > 0) {\n applyDuration(labels, dur)\n }\n }\n console.warn(\n `${LOG} 3_redis_drain pid=${process.pid} countKey=${this.countKey} hash_fields=${\n fieldKeys.length\n } sum_requests=${sumRequests} sample=${samples.join(' | ') || '—'}`\n )\n } catch (e) {\n console.error(\n '[HttpMetricsRedisStore] flush apply failed:',\n e.message\n )\n }\n finish()\n }\n )\n })\n })\n }\n}\n\nmodule.exports = {\n HttpMetricsRedisStore,\n buildFieldKey,\n FIELD_SEP,\n isRedisPeerInstalled,\n DEFAULT_HTTP_METRICS_REDIS_TTL_SEC,\n}\n"],"mappings":"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","ignoreList":[]}
@@ -35,9 +35,8 @@ 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 **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}.
38
+ * — it does not run HTTP request metrics. HTTP Redis **key segment** for API traffic is fixed **`web`**
39
+ * (`HttpMetricsRedisCollector` / `HttpMetricsRedisRecorder`), not `BUILD_DYNO_PROCESS_TYPE`.
41
40
  *
42
41
  * @module metrics/metricsProcessTypeUtils
43
42
  */
@@ -51,8 +50,6 @@ export const METRICS_PROCESS_TYPE_REDIS: "redis-metrics";
51
50
  export const METRICS_PROCESS_TYPE_WEB: "web";
52
51
  /** Build/compile workers and similar (e.g. backend `worker:`) — no HTTP server metrics here. */
53
52
  export const METRICS_PROCESS_TYPE_WORKER: "worker";
54
- /** HTTP Redis **drain + push** process (e.g. backend `http-metrics:` / `http-metrics-collector.ts`). */
55
- export const METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR: "http-metrics";
56
53
  /**
57
54
  * Parent {@link RedisMetricsClient} allows either redis-only or queue stack (`QueueRedisMetricsClient`).
58
55
  * @type {readonly string[]}
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"metricsProcessTypeUtils.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsProcessTypeUtils.js"],"names":[],"mappings":"AAoCA;;;;;;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;AAxFD;;;;;;;;;;GAUG;AAEH,6DAA6D;AAC7D,+DAAwD;AAExD,gEAAgE;AAChE,yDAAkD;AAElD,yEAAyE;AACzE,yDAAkD;AAElD,kGAAkG;AAClG,6CAAsC;AAEtC,gGAAgG;AAChG,mDAA4C;AAE5C;;;GAGG;AACH,yDAFU,SAAS,MAAM,EAAE,CAKzB"}
@@ -6,9 +6,8 @@
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 **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}.
9
+ * — it does not run HTTP request metrics. HTTP Redis **key segment** for API traffic is fixed **`web`**
10
+ * (`HttpMetricsRedisCollector` / `HttpMetricsRedisRecorder`), not `BUILD_DYNO_PROCESS_TYPE`.
12
11
  *
13
12
  * @module metrics/metricsProcessTypeUtils
14
13
  */
@@ -28,9 +27,6 @@ const METRICS_PROCESS_TYPE_WEB = 'web';
28
27
  /** Build/compile workers and similar (e.g. backend `worker:`) — no HTTP server metrics here. */
29
28
  const METRICS_PROCESS_TYPE_WORKER = 'worker';
30
29
 
31
- /** HTTP Redis **drain + push** process (e.g. backend `http-metrics:` / `http-metrics-collector.ts`). */
32
- const METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR = 'http-metrics';
33
-
34
30
  /**
35
31
  * Parent {@link RedisMetricsClient} allows either redis-only or queue stack (`QueueRedisMetricsClient`).
36
32
  * @type {readonly string[]}
@@ -85,7 +81,6 @@ module.exports = {
85
81
  METRICS_PROCESS_TYPE_REDIS,
86
82
  METRICS_PROCESS_TYPE_WEB,
87
83
  METRICS_PROCESS_TYPE_WORKER,
88
- METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR,
89
84
  REDIS_METRICS_CLIENT_ALLOWED_PROCESS_TYPES
90
85
  };
91
86
  //# sourceMappingURL=metricsProcessTypeUtils.js.map
@@ -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 **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":[]}
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","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** for API traffic is fixed **`web`**\n * (`HttpMetricsRedisCollector` / `HttpMetricsRedisRecorder`), not `BUILD_DYNO_PROCESS_TYPE`.\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/**\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 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;AACA;AACA;AACA,MAAMC,0CAA0C,GAAGC,MAAM,CAACC,MAAM,CAAC,CAC/DL,0BAA0B,EAC1BD,0BAA0B,CAC3B,CAAC;;AAEF;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASO,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;EACvBnB,6BAA6B;EAC7BC,0BAA0B;EAC1BC,0BAA0B;EAC1BC,wBAAwB;EACxBC,2BAA2B;EAC3BC;AACF,CAAC","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adalo/metrics",
3
- "version": "0.0.0-staging.22",
3
+ "version": "0.0.0-staging.23",
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 @@ const { HttpMetricsRedisStore } = require('./httpMetricsRedisStore')
4
4
  /**
5
5
  * Drain worker: reads HTTP aggregates from Redis (written by {@link HttpMetricsRedisRecorder}),
6
6
  * applies them to `app_requests_*` counters, then pushes the registry to the VM-agent (same as {@link BaseMetricsClient}).
7
- * Always passes `blockNodeDefaultMetrics: true` to the base client (HTTP-only registry; no default Node heap/event-loop metrics).
8
- * Redis key segment must match writers (`appName` + `processType` / {@link HttpMetricsRedisStore}).
7
+ * **Minimal usage:** `{ redisClient }` only. Redis keys use segment **`web`** unless you pass **`redisProcessTypeForKeys`**.
8
+ * `processType` / `appName` / `dynoId` follow {@link BaseMetricsClient} defaults (e.g. `BUILD_DYNO_PROCESS_TYPE`) and do **not** select Redis hash names.
9
+ * Always passes `blockNodeDefaultMetrics: true` (HTTP-focused registry).
9
10
  *
10
11
  * @extends BaseMetricsClient
11
12
  */
@@ -24,7 +25,7 @@ class HttpMetricsRedisCollector extends BaseMetricsClient {
24
25
  * @param {boolean} [config.removeOldMetrics] Clear old series on shutdown where supported
25
26
  * @param {function} [config.startupValidation] Run before first push
26
27
  * @param {boolean} [config.disablePushgateway] Skip POST to VM-agent
27
- * @param {string} [config.redisProcessTypeForKeys] Segment in Redis keys for HTTP hashes (default `web`; env `METRICS_HTTP_REDIS_KEY_PROCESS_TYPE` overrides)
28
+ * @param {string} [config.redisProcessTypeForKeys] Segment in Redis keys for HTTP hashes (default **`web`**). Optional; only if writers use a non-`web` segment.
28
29
  * @param {number} [config.ttlSec] Passed to {@link HttpMetricsRedisStore} (should match writers)
29
30
  */
30
31
  constructor(config = {}) {
@@ -38,10 +39,7 @@ class HttpMetricsRedisCollector extends BaseMetricsClient {
38
39
  blockNodeDefaultMetrics: true,
39
40
  })
40
41
 
41
- const keyProcessType =
42
- config.redisProcessTypeForKeys ||
43
- process.env.METRICS_HTTP_REDIS_KEY_PROCESS_TYPE ||
44
- 'web'
42
+ const keyProcessType = config.redisProcessTypeForKeys || 'web'
45
43
 
46
44
  this.defaultLabelsWithoutDynoId = {
47
45
  app: this.appName,
@@ -1,5 +1,12 @@
1
1
  const { HttpMetricsRedisStore } = require('./httpMetricsRedisStore')
2
2
 
3
+ const LOG = '[http-metrics-redis]'
4
+
5
+ function trunc(s, max = 120) {
6
+ const t = String(s)
7
+ return t.length > max ? `${t.slice(0, max - 3)}...` : t
8
+ }
9
+
3
10
  /**
4
11
  * Records HTTP request aggregates only to Redis (no in-process Prometheus counters on this path).
5
12
  * Pair with {@link HttpMetricsRedisCollector} on a drain process to flush into counters and push to the VM-agent.
@@ -45,6 +52,11 @@ class HttpMetricsRedisRecorder {
45
52
  databaseId = '',
46
53
  duration,
47
54
  }) {
55
+ console.warn(
56
+ `${LOG} 1_track pid=${process.pid} app=${this.appName} segment=${this.processType} ` +
57
+ `method=${method} route=${trunc(route)} status=${status_code} durationMs=${duration} ` +
58
+ `appId=${trunc(appId || '—', 40)} databaseId=${trunc(databaseId || '—', 40)}`
59
+ )
48
60
  this._store.record(method, route, status_code, appId, databaseId, duration)
49
61
  }
50
62
 
@@ -10,6 +10,8 @@ const FIELD_SEP = '\x1e'
10
10
  */
11
11
  const DEFAULT_HTTP_METRICS_REDIS_TTL_SEC = 120
12
12
 
13
+ const LOG = '[http-metrics-redis]'
14
+
13
15
  const DRAIN_LUA = `
14
16
  local function drain(key)
15
17
  local v = redis.call('HGETALL', key)
@@ -116,7 +118,11 @@ class HttpMetricsRedisStore {
116
118
  .exec(err => {
117
119
  if (err) {
118
120
  console.error('[HttpMetricsRedisStore] record failed:', err.message)
121
+ return
119
122
  }
123
+ console.warn(
124
+ `${LOG} 2_redis_write pid=${process.pid} countKey=${this.countKey} durKey=${this.durKey} ok`
125
+ )
120
126
  })
121
127
  } catch (e) {
122
128
  console.error('[HttpMetricsRedisStore] record:', e.message)
@@ -139,6 +145,11 @@ class HttpMetricsRedisStore {
139
145
  return new Promise(resolve => {
140
146
  client.set(this.lockKey, '1', 'EX', 25, 'NX', (setErr, ok) => {
141
147
  if (setErr || ok !== 'OK') {
148
+ console.warn(
149
+ `${LOG} 3_redis_drain_skip pid=${process.pid} countKey=${this.countKey} reason=${
150
+ setErr ? `error:${setErr.message}` : 'lock_not_acquired'
151
+ }`
152
+ )
142
153
  resolve(false)
143
154
  return
144
155
  }
@@ -163,12 +174,17 @@ class HttpMetricsRedisStore {
163
174
 
164
175
  try {
165
176
  if (!raw || !Array.isArray(raw) || raw.length < 2) {
177
+ console.warn(
178
+ `${LOG} 3_redis_drain pid=${process.pid} countKey=${this.countKey} hash_fields=0 sum_requests=0 (empty_after_lua)`
179
+ )
166
180
  finish()
167
181
  return
168
182
  }
169
183
  const counts = hgetallPairsToObject(raw[0])
170
184
  const durs = hgetallPairsToObject(raw[1])
171
185
  const fieldKeys = Object.keys(counts)
186
+ let sumRequests = 0
187
+ const samples = []
172
188
  for (const field of fieldKeys) {
173
189
  const count = parseInt(counts[field], 10)
174
190
  if (!count || count < 1) {
@@ -180,6 +196,10 @@ class HttpMetricsRedisStore {
180
196
  continue
181
197
  }
182
198
  const [m, route, statusStr, aid, did] = parts
199
+ sumRequests += count
200
+ if (samples.length < 3) {
201
+ samples.push(`${m} ${route} ${statusStr} x${count}`)
202
+ }
183
203
  const labels = {
184
204
  method: m,
185
205
  route,
@@ -192,6 +212,11 @@ class HttpMetricsRedisStore {
192
212
  applyDuration(labels, dur)
193
213
  }
194
214
  }
215
+ console.warn(
216
+ `${LOG} 3_redis_drain pid=${process.pid} countKey=${this.countKey} hash_fields=${
217
+ fieldKeys.length
218
+ } sum_requests=${sumRequests} sample=${samples.join(' | ') || '—'}`
219
+ )
195
220
  } catch (e) {
196
221
  console.error(
197
222
  '[HttpMetricsRedisStore] flush apply failed:',
@@ -4,9 +4,8 @@
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 **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}.
7
+ * — it does not run HTTP request metrics. HTTP Redis **key segment** for API traffic is fixed **`web`**
8
+ * (`HttpMetricsRedisCollector` / `HttpMetricsRedisRecorder`), not `BUILD_DYNO_PROCESS_TYPE`.
10
9
  *
11
10
  * @module metrics/metricsProcessTypeUtils
12
11
  */
@@ -26,9 +25,6 @@ const METRICS_PROCESS_TYPE_WEB = 'web'
26
25
  /** Build/compile workers and similar (e.g. backend `worker:`) — no HTTP server metrics here. */
27
26
  const METRICS_PROCESS_TYPE_WORKER = 'worker'
28
27
 
29
- /** HTTP Redis **drain + push** process (e.g. backend `http-metrics:` / `http-metrics-collector.ts`). */
30
- const METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR = 'http-metrics'
31
-
32
28
  /**
33
29
  * Parent {@link RedisMetricsClient} allows either redis-only or queue stack (`QueueRedisMetricsClient`).
34
30
  * @type {readonly string[]}
@@ -101,6 +97,5 @@ module.exports = {
101
97
  METRICS_PROCESS_TYPE_REDIS,
102
98
  METRICS_PROCESS_TYPE_WEB,
103
99
  METRICS_PROCESS_TYPE_WORKER,
104
- METRICS_PROCESS_TYPE_HTTP_METRICS_COLLECTOR,
105
100
  REDIS_METRICS_CLIENT_ALLOWED_PROCESS_TYPES,
106
101
  }