@adalo/metrics 0.1.173 → 0.1.174

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.
Files changed (53) hide show
  1. package/README.md +7 -0
  2. package/__tests__/httpMetricsRedisCollector.test.js +203 -0
  3. package/__tests__/httpMetricsRedisRecorder.test.js +60 -0
  4. package/__tests__/httpMetricsRedisStore.test.js +431 -0
  5. package/docs/http-metrics-redis.md +19 -0
  6. package/lib/index.d.ts +4 -0
  7. package/lib/index.d.ts.map +1 -1
  8. package/lib/index.js +44 -0
  9. package/lib/index.js.map +1 -1
  10. package/lib/metrics/baseMetricsClient.d.ts +2 -0
  11. package/lib/metrics/baseMetricsClient.d.ts.map +1 -1
  12. package/lib/metrics/baseMetricsClient.js +6 -3
  13. package/lib/metrics/baseMetricsClient.js.map +1 -1
  14. package/lib/metrics/httpMetricsRedisCollector.d.ts +50 -0
  15. package/lib/metrics/httpMetricsRedisCollector.d.ts.map +1 -0
  16. package/lib/metrics/httpMetricsRedisCollector.js +117 -0
  17. package/lib/metrics/httpMetricsRedisCollector.js.map +1 -0
  18. package/lib/metrics/httpMetricsRedisRecorder.d.ts +48 -0
  19. package/lib/metrics/httpMetricsRedisRecorder.d.ts.map +1 -0
  20. package/lib/metrics/httpMetricsRedisRecorder.js +86 -0
  21. package/lib/metrics/httpMetricsRedisRecorder.js.map +1 -0
  22. package/lib/metrics/httpMetricsRedisStore.d.ts +88 -0
  23. package/lib/metrics/httpMetricsRedisStore.d.ts.map +1 -0
  24. package/lib/metrics/httpMetricsRedisStore.js +223 -0
  25. package/lib/metrics/httpMetricsRedisStore.js.map +1 -0
  26. package/lib/metrics/metricsClient.d.ts +34 -27
  27. package/lib/metrics/metricsClient.d.ts.map +1 -1
  28. package/lib/metrics/metricsClient.js +35 -37
  29. package/lib/metrics/metricsClient.js.map +1 -1
  30. package/lib/metrics/metricsDatabaseClient.d.ts.map +1 -1
  31. package/lib/metrics/metricsDatabaseClient.js +6 -1
  32. package/lib/metrics/metricsDatabaseClient.js.map +1 -1
  33. package/lib/metrics/metricsProcessTypeUtils.d.ts +58 -0
  34. package/lib/metrics/metricsProcessTypeUtils.d.ts.map +1 -0
  35. package/lib/metrics/metricsProcessTypeUtils.js +86 -0
  36. package/lib/metrics/metricsProcessTypeUtils.js.map +1 -0
  37. package/lib/metrics/metricsQueueRedisClient.d.ts.map +1 -1
  38. package/lib/metrics/metricsQueueRedisClient.js +5 -0
  39. package/lib/metrics/metricsQueueRedisClient.js.map +1 -1
  40. package/lib/metrics/metricsRedisClient.d.ts.map +1 -1
  41. package/lib/metrics/metricsRedisClient.js +7 -1
  42. package/lib/metrics/metricsRedisClient.js.map +1 -1
  43. package/package.json +5 -5
  44. package/src/index.ts +4 -0
  45. package/src/metrics/baseMetricsClient.js +4 -1
  46. package/src/metrics/httpMetricsRedisCollector.js +131 -0
  47. package/src/metrics/httpMetricsRedisRecorder.js +74 -0
  48. package/src/metrics/httpMetricsRedisStore.js +208 -0
  49. package/src/metrics/metricsClient.js +34 -53
  50. package/src/metrics/metricsDatabaseClient.js +7 -1
  51. package/src/metrics/metricsProcessTypeUtils.js +98 -0
  52. package/src/metrics/metricsQueueRedisClient.js +6 -0
  53. package/src/metrics/metricsRedisClient.js +12 -1
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+
3
+ const {
4
+ HttpMetricsRedisStore
5
+ } = require('./httpMetricsRedisStore');
6
+
7
+ /**
8
+ * Records HTTP request aggregates only to Redis (no in-process Prometheus counters on this path).
9
+ * Pair with {@link HttpMetricsRedisCollector} on a drain process to flush into counters and push to the VM-agent.
10
+ *
11
+ * @see HttpMetricsRedisStore
12
+ */
13
+ class HttpMetricsRedisRecorder {
14
+ /**
15
+ * @param {Object} opts
16
+ * @param {import('redis').RedisClient} opts.redisClient **Required.** Injected client (same pattern as {@link RedisMetricsClient}).
17
+ * @param {string} [opts.appName] Optional override; default same as {@link BaseMetricsClient}: `BUILD_APP_NAME` or `unknown-app`.
18
+ * @param {string} [opts.processType] Redis key segment (default **`web`**, same as {@link HttpMetricsRedisCollector}).
19
+ * @param {number} [opts.ttlSec] Redis hash key TTL in seconds (sliding; default 120).
20
+ */
21
+ constructor({
22
+ redisClient,
23
+ appName,
24
+ processType = 'web',
25
+ ttlSec
26
+ } = {}) {
27
+ if (redisClient == null) {
28
+ throw new Error('HttpMetricsRedisRecorder: redisClient is required');
29
+ }
30
+ const resolvedAppName = appName || process.env.BUILD_APP_NAME || 'unknown-app';
31
+ this.processType = processType;
32
+ this.appName = resolvedAppName;
33
+ this._store = new HttpMetricsRedisStore({
34
+ redisClient,
35
+ appName: resolvedAppName,
36
+ processType,
37
+ ttlSec
38
+ });
39
+ }
40
+
41
+ /**
42
+ * @param {Object} params
43
+ * @param {string} params.method
44
+ * @param {string} params.route
45
+ * @param {number} params.status_code
46
+ * @param {number} params.duration
47
+ */
48
+ trackHttpRequest({
49
+ method,
50
+ route,
51
+ status_code,
52
+ duration
53
+ }) {
54
+ this._store.record(method, route, status_code, duration);
55
+ }
56
+
57
+ /**
58
+ * Express middleware: appends a `finish` listener and writes one aggregate row per request to Redis.
59
+ * Does not check `METRICS_ENABLED`; the app should only mount this when HTTP Redis metrics should run.
60
+ *
61
+ * @param {import('http').IncomingMessage} req
62
+ * @param {import('http').ServerResponse} res
63
+ * @param {function} next
64
+ */
65
+ trackHttpRequestMiddleware = (req, res, next) => {
66
+ if (req.method === 'OPTIONS') {
67
+ next();
68
+ return;
69
+ }
70
+ const start = Date.now();
71
+ res.on('finish', () => {
72
+ const route = req.route?.path || req.path || 'unknown';
73
+ this.trackHttpRequest({
74
+ method: req.method,
75
+ route,
76
+ status_code: res.statusCode,
77
+ duration: Date.now() - start
78
+ });
79
+ });
80
+ next();
81
+ };
82
+ }
83
+ module.exports = {
84
+ HttpMetricsRedisRecorder
85
+ };
86
+ //# sourceMappingURL=httpMetricsRedisRecorder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpMetricsRedisRecorder.js","names":["HttpMetricsRedisStore","require","HttpMetricsRedisRecorder","constructor","redisClient","appName","processType","ttlSec","Error","resolvedAppName","process","env","BUILD_APP_NAME","_store","trackHttpRequest","method","route","status_code","duration","record","trackHttpRequestMiddleware","req","res","next","start","Date","now","on","path","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] Optional override; default same as {@link BaseMetricsClient}: `BUILD_APP_NAME` or `unknown-app`.\n * @param {string} [opts.processType] Redis key segment (default **`web`**, same as {@link HttpMetricsRedisCollector}).\n * @param {number} [opts.ttlSec] Redis hash key TTL in seconds (sliding; default 120).\n */\n constructor({ redisClient, appName, processType = 'web', ttlSec } = {}) {\n if (redisClient == null) {\n throw new Error('HttpMetricsRedisRecorder: redisClient is required')\n }\n const resolvedAppName =\n appName || process.env.BUILD_APP_NAME || 'unknown-app'\n this.processType = processType\n this.appName = resolvedAppName\n this._store = new HttpMetricsRedisStore({\n redisClient,\n appName: resolvedAppName,\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 {number} params.duration\n */\n trackHttpRequest({ method, route, status_code, duration }) {\n this._store.record(method, route, status_code, 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\n this.trackHttpRequest({\n method: req.method,\n route,\n status_code: res.statusCode,\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,GAAG,KAAK;IAAEC;EAAO,CAAC,GAAG,CAAC,CAAC,EAAE;IACtE,IAAIH,WAAW,IAAI,IAAI,EAAE;MACvB,MAAM,IAAII,KAAK,CAAC,mDAAmD,CAAC;IACtE;IACA,MAAMC,eAAe,GACnBJ,OAAO,IAAIK,OAAO,CAACC,GAAG,CAACC,cAAc,IAAI,aAAa;IACxD,IAAI,CAACN,WAAW,GAAGA,WAAW;IAC9B,IAAI,CAACD,OAAO,GAAGI,eAAe;IAC9B,IAAI,CAACI,MAAM,GAAG,IAAIb,qBAAqB,CAAC;MACtCI,WAAW;MACXC,OAAO,EAAEI,eAAe;MACxBH,WAAW;MACXC;IACF,CAAC,CAAC;EACJ;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;EACEO,gBAAgBA,CAAC;IAAEC,MAAM;IAAEC,KAAK;IAAEC,WAAW;IAAEC;EAAS,CAAC,EAAE;IACzD,IAAI,CAACL,MAAM,CAACM,MAAM,CAACJ,MAAM,EAAEC,KAAK,EAAEC,WAAW,EAAEC,QAAQ,CAAC;EAC1D;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEE,0BAA0B,GAAGA,CAACC,GAAG,EAAEC,GAAG,EAAEC,IAAI,KAAK;IAC/C,IAAIF,GAAG,CAACN,MAAM,KAAK,SAAS,EAAE;MAC5BQ,IAAI,CAAC,CAAC;MACN;IACF;IAEA,MAAMC,KAAK,GAAGC,IAAI,CAACC,GAAG,CAAC,CAAC;IACxBJ,GAAG,CAACK,EAAE,CAAC,QAAQ,EAAE,MAAM;MACrB,MAAMX,KAAK,GAAGK,GAAG,CAACL,KAAK,EAAEY,IAAI,IAAIP,GAAG,CAACO,IAAI,IAAI,SAAS;MAEtD,IAAI,CAACd,gBAAgB,CAAC;QACpBC,MAAM,EAAEM,GAAG,CAACN,MAAM;QAClBC,KAAK;QACLC,WAAW,EAAEK,GAAG,CAACO,UAAU;QAC3BX,QAAQ,EAAEO,IAAI,CAACC,GAAG,CAAC,CAAC,GAAGF;MACzB,CAAC,CAAC;IACJ,CAAC,CAAC;IAEFD,IAAI,CAAC,CAAC;EACR,CAAC;AACH;AAEAO,MAAM,CAACC,OAAO,GAAG;EAAE7B;AAAyB,CAAC","ignoreList":[]}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Redis HTTP aggregate store. Uses an **injected** client (same pattern as {@link RedisMetricsClient}).
3
+ * Expects `redis` v3-style API: `multi().hincrby().expire().exec(cb)`, `eval`.
4
+ *
5
+ * **Structure:** `countKey` / `durKey` are each **one Redis hash**. Each **field name** encodes
6
+ * `(method, route, status_code)`. Values: `:count` is HINCRBY 1 per request; `:dur` is summed ms.
7
+ */
8
+ export class HttpMetricsRedisStore {
9
+ /**
10
+ * @param {Object} opts
11
+ * @param {import('redis').RedisClient} opts.redisClient
12
+ * @param {string} opts.appName BUILD_APP_NAME (key segment)
13
+ * @param {string} opts.processType logical process for key (e.g. web)
14
+ * @param {number} [opts.ttlSec] Expire keys after this many seconds (default 120)
15
+ */
16
+ constructor({ redisClient, appName, processType, ttlSec }: {
17
+ redisClient: import('redis').RedisClient;
18
+ appName: string;
19
+ processType: string;
20
+ ttlSec?: number | undefined;
21
+ });
22
+ _client: import("redis").RedisClient;
23
+ ttlSec: number;
24
+ countKey: string;
25
+ durKey: string;
26
+ /**
27
+ * @returns {import('redis').RedisClient}
28
+ * @private
29
+ */
30
+ private _ensureClient;
31
+ /**
32
+ * @param {string} method
33
+ * @param {string} route
34
+ * @param {number} statusCode
35
+ * @param {number} durationMs
36
+ */
37
+ record(method: string, route: string, statusCode: number, durationMs: number): void;
38
+ /**
39
+ * Atomically drain Redis hashes (same Lua as `flushToCounters`) and return parsed rows.
40
+ *
41
+ * @returns {Promise<{ ok: boolean, rows: { labels: Object, count: number, dur: number }[] }>}
42
+ */
43
+ drainRows(): Promise<{
44
+ ok: boolean;
45
+ rows: {
46
+ labels: Object;
47
+ count: number;
48
+ dur: number;
49
+ }[];
50
+ }>;
51
+ /**
52
+ * @param {(labels: Object, value: number) => void} applyCount
53
+ * @param {(labels: Object, value: number) => void} applyDuration
54
+ * @returns {Promise<boolean>}
55
+ */
56
+ flushToCounters(applyCount: (labels: Object, value: number) => void, applyDuration: (labels: Object, value: number) => void): Promise<boolean>;
57
+ }
58
+ /**
59
+ * @param {string} method
60
+ * @param {string} route
61
+ * @param {number} statusCode
62
+ * @returns {string}
63
+ */
64
+ export function buildFieldKey(method: string, route: string, statusCode: number): string;
65
+ /**
66
+ * Record separator for hash fields (avoids collisions when route contains "|").
67
+ * @type {string}
68
+ */
69
+ export const FIELD_SEP: string;
70
+ /**
71
+ * Default Redis key TTL in seconds (sliding: refreshed on each `record` write).
72
+ * @type {number}
73
+ */
74
+ export const DEFAULT_HTTP_METRICS_REDIS_TTL_SEC: number;
75
+ /**
76
+ * @param {unknown} raw redis eval result [countPairs, durPairs]
77
+ * @returns {{ labels: { method: string, route: string, status_code: string }, count: number, dur: number }[]}
78
+ */
79
+ export function rowsFromDrainRaw(raw: unknown): {
80
+ labels: {
81
+ method: string;
82
+ route: string;
83
+ status_code: string;
84
+ };
85
+ count: number;
86
+ dur: number;
87
+ }[];
88
+ //# sourceMappingURL=httpMetricsRedisStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpMetricsRedisStore.d.ts","sourceRoot":"","sources":["../../src/metrics/httpMetricsRedisStore.js"],"names":[],"mappings":"AA6EA;;;;;;GAMG;AACH;IACE;;;;;;OAMG;IACH;QAL6C,WAAW,EAA7C,OAAO,OAAO,EAAE,WAAW;QACd,OAAO,EAApB,MAAM;QACO,WAAW,EAAxB,MAAM;QACQ,MAAM;OAgB9B;IAVC,qCAA0B;IAC1B,eAGwC;IAIxC,iBAAiD;IACjD,eAA6C;IAG/C;;;OAGG;IACH,sBAEC;IAED;;;;;OAKG;IACH,eALW,MAAM,SACN,MAAM,cACN,MAAM,cACN,MAAM,QAqBhB;IAED;;;;OAIG;IACH,aAFa,QAAQ;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,CAAC,CAgC5F;IAED;;;;OAIG;IACH,qCAJoB,MAAM,SAAS,MAAM,KAAK,IAAI,0BAC9B,MAAM,SAAS,MAAM,KAAK,IAAI,GACrC,QAAQ,OAAO,CAAC,CAe5B;CACF;AAlLD;;;;;GAKG;AACH,sCALW,MAAM,SACN,MAAM,cACN,MAAM,GACJ,MAAM,CAIlB;AA7BD;;;GAGG;AACH,wBAFU,MAAM,CAEQ;AAExB;;;GAGG;AACH,iDAFU,MAAM,CAE8B;AAgC9C;;;GAGG;AACH,sCAHW,OAAO,GACL;IAAE,MAAM,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EAAE,CA+B5G"}
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Record separator for hash fields (avoids collisions when route contains "|").
5
+ * @type {string}
6
+ */
7
+ const FIELD_SEP = '\x1e';
8
+
9
+ /**
10
+ * Default Redis key TTL in seconds (sliding: refreshed on each `record` write).
11
+ * @type {number}
12
+ */
13
+ const DEFAULT_HTTP_METRICS_REDIS_TTL_SEC = 120;
14
+ const DRAIN_LUA = `
15
+ local function drain(key)
16
+ local v = redis.call('HGETALL', key)
17
+ redis.call('DEL', key)
18
+ return v
19
+ end
20
+ return {drain(KEYS[1]), drain(KEYS[2])}
21
+ `;
22
+
23
+ /**
24
+ * @param {string} method
25
+ * @param {string} route
26
+ * @param {number} statusCode
27
+ * @returns {string}
28
+ */
29
+ function buildFieldKey(method, route, statusCode) {
30
+ return [method, route, String(statusCode)].join(FIELD_SEP);
31
+ }
32
+ function hgetallPairsToObject(pairs) {
33
+ const o = {};
34
+ if (!pairs || !pairs.length) {
35
+ return o;
36
+ }
37
+ for (let i = 0; i < pairs.length; i += 2) {
38
+ o[pairs[i]] = pairs[i + 1];
39
+ }
40
+ return o;
41
+ }
42
+
43
+ /**
44
+ * @param {unknown} raw redis eval result [countPairs, durPairs]
45
+ * @returns {{ labels: { method: string, route: string, status_code: string }, count: number, dur: number }[]}
46
+ */
47
+ function rowsFromDrainRaw(raw) {
48
+ const rows = [];
49
+ if (!raw || !Array.isArray(raw) || raw.length < 2) {
50
+ return rows;
51
+ }
52
+ const counts = hgetallPairsToObject(raw[0]);
53
+ const durs = hgetallPairsToObject(raw[1]);
54
+ const fieldKeys = Object.keys(counts);
55
+ for (const field of fieldKeys) {
56
+ const count = parseInt(counts[field], 10);
57
+ if (!count || count < 1) {
58
+ continue;
59
+ }
60
+ const dur = parseInt(durs[field] || '0', 10) || 0;
61
+ const parts = field.split(FIELD_SEP);
62
+ let labels = null;
63
+ if (parts.length === 3) {
64
+ const [m, route, statusStr] = parts;
65
+ labels = {
66
+ method: m,
67
+ route,
68
+ status_code: statusStr
69
+ };
70
+ } else if (parts.length === 5 || parts.length === 6) {
71
+ const [m, route, statusStr] = parts;
72
+ labels = {
73
+ method: m,
74
+ route,
75
+ status_code: statusStr
76
+ };
77
+ }
78
+ if (!labels) {
79
+ continue;
80
+ }
81
+ rows.push({
82
+ labels,
83
+ count,
84
+ dur
85
+ });
86
+ }
87
+ return rows;
88
+ }
89
+
90
+ /**
91
+ * Redis HTTP aggregate store. Uses an **injected** client (same pattern as {@link RedisMetricsClient}).
92
+ * Expects `redis` v3-style API: `multi().hincrby().expire().exec(cb)`, `eval`.
93
+ *
94
+ * **Structure:** `countKey` / `durKey` are each **one Redis hash**. Each **field name** encodes
95
+ * `(method, route, status_code)`. Values: `:count` is HINCRBY 1 per request; `:dur` is summed ms.
96
+ */
97
+ class HttpMetricsRedisStore {
98
+ /**
99
+ * @param {Object} opts
100
+ * @param {import('redis').RedisClient} opts.redisClient
101
+ * @param {string} opts.appName BUILD_APP_NAME (key segment)
102
+ * @param {string} opts.processType logical process for key (e.g. web)
103
+ * @param {number} [opts.ttlSec] Expire keys after this many seconds (default 120)
104
+ */
105
+ constructor({
106
+ redisClient,
107
+ appName,
108
+ processType,
109
+ ttlSec
110
+ }) {
111
+ if (redisClient == null) {
112
+ throw new Error('HttpMetricsRedisStore: redisClient is required');
113
+ }
114
+ this._client = redisClient;
115
+ this.ttlSec = typeof ttlSec === 'number' && ttlSec > 0 ? ttlSec : DEFAULT_HTTP_METRICS_REDIS_TTL_SEC;
116
+ const keySeg = `${encodeURIComponent(appName)}:${encodeURIComponent(processType)}`;
117
+ this.countKey = `metrics:http:v2:${keySeg}:count`;
118
+ this.durKey = `metrics:http:v2:${keySeg}:dur`;
119
+ }
120
+
121
+ /**
122
+ * @returns {import('redis').RedisClient}
123
+ * @private
124
+ */
125
+ _ensureClient() {
126
+ return this._client;
127
+ }
128
+
129
+ /**
130
+ * @param {string} method
131
+ * @param {string} route
132
+ * @param {number} statusCode
133
+ * @param {number} durationMs
134
+ */
135
+ record(method, route, statusCode, durationMs) {
136
+ try {
137
+ const client = this._ensureClient();
138
+ const field = buildFieldKey(method, route, statusCode);
139
+ const dur = Math.max(0, Math.round(Number(durationMs) || 0));
140
+ client.multi().hincrby(this.countKey, field, 1).hincrby(this.durKey, field, dur).expire(this.countKey, this.ttlSec).expire(this.durKey, this.ttlSec).exec(err => {
141
+ if (err) {
142
+ console.error('[HttpMetricsRedisStore] record failed:', err.message);
143
+ }
144
+ });
145
+ } catch (e) {
146
+ console.error('[HttpMetricsRedisStore] record:', e.message);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Atomically drain Redis hashes (same Lua as `flushToCounters`) and return parsed rows.
152
+ *
153
+ * @returns {Promise<{ ok: boolean, rows: { labels: Object, count: number, dur: number }[] }>}
154
+ */
155
+ drainRows() {
156
+ let client;
157
+ try {
158
+ client = this._ensureClient();
159
+ } catch (e) {
160
+ console.error('[HttpMetricsRedisStore] drainRows:', e.message);
161
+ return Promise.resolve({
162
+ ok: false,
163
+ rows: []
164
+ });
165
+ }
166
+ return new Promise(resolve => {
167
+ client.eval(DRAIN_LUA, 2, this.countKey, this.durKey, (evalErr, raw) => {
168
+ if (evalErr) {
169
+ console.error('[HttpMetricsRedisStore] drain failed:', evalErr.message);
170
+ resolve({
171
+ ok: false,
172
+ rows: []
173
+ });
174
+ return;
175
+ }
176
+ try {
177
+ const rows = rowsFromDrainRaw(raw);
178
+ resolve({
179
+ ok: true,
180
+ rows
181
+ });
182
+ } catch (e) {
183
+ console.error('[HttpMetricsRedisStore] drainRows parse failed:', e.message);
184
+ resolve({
185
+ ok: false,
186
+ rows: []
187
+ });
188
+ }
189
+ });
190
+ });
191
+ }
192
+
193
+ /**
194
+ * @param {(labels: Object, value: number) => void} applyCount
195
+ * @param {(labels: Object, value: number) => void} applyDuration
196
+ * @returns {Promise<boolean>}
197
+ */
198
+ flushToCounters(applyCount, applyDuration) {
199
+ return this.drainRows().then(({
200
+ ok,
201
+ rows
202
+ }) => {
203
+ if (!ok) {
204
+ return false;
205
+ }
206
+ for (const row of rows) {
207
+ applyCount(row.labels, row.count);
208
+ if (row.dur > 0) {
209
+ applyDuration(row.labels, row.dur);
210
+ }
211
+ }
212
+ return true;
213
+ });
214
+ }
215
+ }
216
+ module.exports = {
217
+ HttpMetricsRedisStore,
218
+ buildFieldKey,
219
+ FIELD_SEP,
220
+ DEFAULT_HTTP_METRICS_REDIS_TTL_SEC,
221
+ rowsFromDrainRaw
222
+ };
223
+ //# sourceMappingURL=httpMetricsRedisStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpMetricsRedisStore.js","names":["FIELD_SEP","DEFAULT_HTTP_METRICS_REDIS_TTL_SEC","DRAIN_LUA","buildFieldKey","method","route","statusCode","String","join","hgetallPairsToObject","pairs","o","length","i","rowsFromDrainRaw","raw","rows","Array","isArray","counts","durs","fieldKeys","Object","keys","field","count","parseInt","dur","parts","split","labels","m","statusStr","status_code","push","HttpMetricsRedisStore","constructor","redisClient","appName","processType","ttlSec","Error","_client","keySeg","encodeURIComponent","countKey","durKey","_ensureClient","record","durationMs","client","Math","max","round","Number","multi","hincrby","expire","exec","err","console","error","message","e","drainRows","Promise","resolve","ok","eval","evalErr","flushToCounters","applyCount","applyDuration","then","row","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 * @param {string} method\n * @param {string} route\n * @param {number} statusCode\n * @returns {string}\n */\nfunction buildFieldKey(method, route, statusCode) {\n return [method, route, String(statusCode)].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 * @param {unknown} raw redis eval result [countPairs, durPairs]\n * @returns {{ labels: { method: string, route: string, status_code: string }, count: number, dur: number }[]}\n */\nfunction rowsFromDrainRaw(raw) {\n const rows = []\n if (!raw || !Array.isArray(raw) || raw.length < 2) {\n return rows\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 let labels = null\n if (parts.length === 3) {\n const [m, route, statusStr] = parts\n labels = { method: m, route, status_code: statusStr }\n } else if (parts.length === 5 || parts.length === 6) {\n const [m, route, statusStr] = parts\n labels = { method: m, route, status_code: statusStr }\n }\n if (!labels) {\n continue\n }\n rows.push({ labels, count, dur })\n }\n return rows\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`.\n *\n * **Structure:** `countKey` / `durKey` are each **one Redis hash**. Each **field name** encodes\n * `(method, route, status_code)`. Values: `:count` is HINCRBY 1 per request; `:dur` is summed ms.\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 }\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 {number} durationMs\n */\n record(method, route, statusCode, durationMs) {\n try {\n const client = this._ensureClient()\n const field = buildFieldKey(method, route, statusCode)\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 * Atomically drain Redis hashes (same Lua as `flushToCounters`) and return parsed rows.\n *\n * @returns {Promise<{ ok: boolean, rows: { labels: Object, count: number, dur: number }[] }>}\n */\n drainRows() {\n let client\n try {\n client = this._ensureClient()\n } catch (e) {\n console.error('[HttpMetricsRedisStore] drainRows:', e.message)\n return Promise.resolve({ ok: false, rows: [] })\n }\n return new Promise(resolve => {\n client.eval(DRAIN_LUA, 2, this.countKey, this.durKey, (evalErr, raw) => {\n if (evalErr) {\n console.error(\n '[HttpMetricsRedisStore] drain failed:',\n evalErr.message\n )\n resolve({ ok: false, rows: [] })\n return\n }\n try {\n const rows = rowsFromDrainRaw(raw)\n resolve({ ok: true, rows })\n } catch (e) {\n console.error(\n '[HttpMetricsRedisStore] drainRows parse failed:',\n e.message\n )\n resolve({ ok: false, rows: [] })\n }\n })\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 return this.drainRows().then(({ ok, rows }) => {\n if (!ok) {\n return false\n }\n for (const row of rows) {\n applyCount(row.labels, row.count)\n if (row.dur > 0) {\n applyDuration(row.labels, row.dur)\n }\n }\n return true\n })\n }\n}\n\nmodule.exports = {\n HttpMetricsRedisStore,\n buildFieldKey,\n FIELD_SEP,\n DEFAULT_HTTP_METRICS_REDIS_TTL_SEC,\n rowsFromDrainRaw,\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,23 +1,29 @@
1
1
  /**
2
2
  * MetricsClient handles Prometheus metrics collection and push.
3
- * Supports gauges, counters, default metrics, and custom metrics.
4
- * Extends BaseMetricsClient for common functionality.
3
+ * Supports gauges, default process metrics, optional HTTP counters, and custom metrics.
4
+ *
5
+ * **HTTP metrics:** In-process counters only (`app_requests_*`), gated by `httpMetricsEnabled` /
6
+ * `METRICS_HTTP_ENABLED`. For Redis-backed HTTP aggregation (multi-web / cluster), use
7
+ * {@link HttpMetricsRedisRecorder} and {@link HttpMetricsRedisCollector} — not this class.
8
+ *
9
+ * @extends BaseMetricsClient
5
10
  */
6
11
  export class MetricsClient extends BaseMetricsClient {
7
12
  /**
8
- * @param {Object} config
13
+ * @param {Object} [config]
9
14
  * @param {string} [config.appName] Name of the application
10
15
  * @param {string} [config.dynoId] Dyno/instance ID
11
16
  * @param {string} [config.processType] Process type (web, worker, etc.)
12
17
  * @param {boolean} [config.enabled] Enable metrics collection
13
- * @param {boolean} [config.httpMetricsEnabled=false] Enable HTTP request metrics (app_requests_total, app_requests_total_duration)
18
+ * @param {boolean} [config.httpMetricsEnabled] Enable HTTP request metrics (`app_requests_total`, `app_requests_total_duration`); defaults from `METRICS_HTTP_ENABLED === 'true'`
14
19
  * @param {boolean} [config.logValues] Log metrics values to console
15
- * @param {string} [config.pushgatewayUrl] PushGateway URL
16
- * @param {string} [config.pushgatewaySecret] PushGateway secret token
20
+ * @param {string} [config.pushgatewayUrl] Push URL (VM-agent import endpoint, e.g. .../api/v1/import/prometheus). /metrics is for GET (scrape), not POST (push).
21
+ * @param {string} [config.pushgatewaySecret] Basic auth secret (Base64 of `user:password`)
17
22
  * @param {number} [config.intervalSec] Interval in seconds for pushing metrics
18
23
  * @param {boolean} [config.removeOldMetrics] Enable to clear metrics by service name
19
- * @param {function} [config.startupValidation] Add to validate on start push.
20
- * @param {boolean} [config.disablePushgateway] Disable pushing to Pushgateway (use HTTP scraping instead)
24
+ * @param {function} [config.startupValidation] Add to validate on start push
25
+ * @param {boolean} [config.disablePushgateway] Disable pushing to VM-agent (use HTTP scraping instead)
26
+ * @param {boolean} [config.blockNodeDefaultMetrics] When true, skip prom-client default process metrics (rare; see {@link BaseMetricsClient})
21
27
  */
22
28
  constructor(config?: {
23
29
  appName?: string | undefined;
@@ -32,7 +38,8 @@ export class MetricsClient extends BaseMetricsClient {
32
38
  removeOldMetrics?: boolean | undefined;
33
39
  startupValidation?: Function | undefined;
34
40
  disablePushgateway?: boolean | undefined;
35
- });
41
+ blockNodeDefaultMetrics?: boolean | undefined;
42
+ } | undefined);
36
43
  httpMetricsEnabled: boolean;
37
44
  _lastUsageMicros: number;
38
45
  _lastCheckTime: number;
@@ -47,49 +54,49 @@ export class MetricsClient extends BaseMetricsClient {
47
54
  */
48
55
  getCpuUsagePercent: () => number;
49
56
  /**
50
- * Get available CPU cores.
57
+ * Available CPU cores (cgroup quota or `os.cpus().length`).
51
58
  * @returns {number}
52
59
  */
53
60
  getAvailableCPUs(): number;
54
61
  /**
55
- * Get container memory usage in bytes.
62
+ * Container memory usage in bytes (`memory.current` or RSS fallback).
56
63
  * @returns {number}
57
64
  */
58
65
  getContainerMemoryUsage(): number;
59
66
  /**
60
- * Get container memory limit in bytes.
67
+ * Container memory limit in bytes (`memory.max` or host total).
61
68
  * @returns {number}
62
69
  */
63
70
  getContainerMemoryLimit(): number;
64
71
  /**
65
- * Measure event loop lag in ms.
72
+ * Event loop lag sample in milliseconds.
66
73
  * @returns {Promise<number>}
67
74
  */
68
75
  measureLag(): Promise<number>;
69
76
  /**
70
- * Increment the HTTP requests counter with detailed request information.
77
+ * Increment HTTP request counters (in-process). No-op if `httpMetricsEnabled` is false.
71
78
  *
72
- * @param {Object} params - The parameters for the request counter.
73
- * @param {string} params.method - HTTP method (GET, POST, etc.).
74
- * @param {string} params.route - The full requested URL or route.
75
- * @param {number} params.status_code - HTTP response status code.
76
- * @param {string} [params.appId=''] - Optional application identifier.
77
- * @param {string} [params.databaseId=''] - Optional database identifier.
78
- * @param {number} params.duration - Request duration in milliseconds.
79
+ * @param {Object} params
80
+ * @param {string} params.method HTTP method
81
+ * @param {string} params.route Route or path pattern
82
+ * @param {number} params.status_code HTTP status code
83
+ * @param {number} params.duration Duration in milliseconds
79
84
  */
80
- trackHttpRequest({ method, route, status_code, appId, databaseId, duration, }: {
85
+ trackHttpRequest({ method, route, status_code, duration }: {
81
86
  method: string;
82
87
  route: string;
83
88
  status_code: number;
84
- appId?: string | undefined;
85
- databaseId?: string | undefined;
86
89
  duration: number;
87
90
  }): void;
88
91
  /**
89
- * Express middleware to track HTTP requests.
90
- * Track the `app_requests_total` and `app_requests_total_duration` metric.
92
+ * Express middleware: records `app_requests_*` on response finish.
93
+ * Skips when disabled, HTTP metrics off, or `OPTIONS`.
94
+ *
95
+ * @param {import('http').IncomingMessage} req
96
+ * @param {import('http').ServerResponse} res
97
+ * @param {function} next
91
98
  */
92
- trackHttpRequestMiddleware: (req: any, res: any, next: any) => void;
99
+ trackHttpRequestMiddleware: (req: import('http').IncomingMessage, res: import('http').ServerResponse, next: Function) => void;
93
100
  }
94
101
  import { BaseMetricsClient } from "./baseMetricsClient";
95
102
  //# sourceMappingURL=metricsClient.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"metricsClient.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsClient.js"],"names":[],"mappings":"AAIA;;;;GAIG;AACH;IACE;;;;;;;;;;;;;;OAcG;IACH;QAb2B,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,kBAAkB;QAClB,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QACf,iBAAiB;QAClB,kBAAkB;OAc7C;IATC,4BAGO;IAEP,yBAAyB;IACzB,uBAAgC;IAKlC;;;OAGG;IACH,4BAgEC;IAED;;;OAGG;IACH,0BAFa,MAAM,CA2BlB;IAED;;;OAGG;IACH,oBAFa,MAAM,CAiBlB;IAED;;;OAGG;IACH,2BAFa,MAAM,CAWlB;IAED;;;OAGG;IACH,2BAFa,MAAM,CAgBlB;IAED;;;OAGG;IACH,cAFa,QAAQ,MAAM,CAAC,CAO3B;IAED;;;;;;;;;;OAUG;IACH;QAP0B,MAAM,EAArB,MAAM;QACS,KAAK,EAApB,MAAM;QACS,WAAW,EAA1B,MAAM;QACU,KAAK;QACL,UAAU;QACX,QAAQ,EAAvB,MAAM;aA6BhB;IAED;;;OAGG;IACH,oEA+BC;CACF"}
1
+ {"version":3,"file":"metricsClient.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsClient.js"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AACH;IACE;;;;;;;;;;;;;;;OAeG;IACH;;;;;;;;;;;;;;mBAYC;IATC,4BAGiD;IAEjD,yBAAyB;IACzB,uBAAgC;IAKlC;;;OAGG;IACH,4BA4DC;IAED;;;OAGG;IACH,0BAFa,MAAM,CA2BlB;IAED;;;OAGG;IACH,oBAFa,MAAM,CAiBlB;IAED;;;OAGG;IACH,2BAFa,MAAM,CAWlB;IAED;;;OAGG;IACH,2BAFa,MAAM,CAgBlB;IAED;;;OAGG;IACH,cAFa,QAAQ,MAAM,CAAC,CAO3B;IAED;;;;;;;;OAQG;IACH;QAL0B,MAAM,EAArB,MAAM;QACS,KAAK,EAApB,MAAM;QACS,WAAW,EAA1B,MAAM;QACS,QAAQ,EAAvB,MAAM;aAkBhB;IAED;;;;;;;OAOG;IACH,kCAJW,OAAO,MAAM,EAAE,eAAe,OAC9B,OAAO,MAAM,EAAE,cAAc,0BAsBvC;CACF"}