@adalo/metrics 0.1.17 → 0.1.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/metricsClient.d.ts +86 -14
- package/lib/metricsClient.d.ts.map +1 -1
- package/lib/metricsClient.js +168 -37
- package/lib/metricsClient.js.map +1 -1
- package/package.json +1 -1
- package/src/metricsClient.js +172 -69
package/lib/metricsClient.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export class MetricsClient {
|
|
|
15
15
|
* @param {string} [config.pushgatewayPassword] PushGateway password
|
|
16
16
|
* @param {number} [config.intervalSec] Interval in seconds for pushing metrics
|
|
17
17
|
* @param {boolean} [config.removeAllJobs] Enable to clear all jobs at startup
|
|
18
|
+
* @param {boolean} [config.scripDefaultMetrics] Enable to scip default metrics creation
|
|
19
|
+
* @param {function} [config.startupValidation] Add to validate on start push.
|
|
18
20
|
*/
|
|
19
21
|
constructor(config?: {
|
|
20
22
|
appName?: string | undefined;
|
|
@@ -27,6 +29,8 @@ export class MetricsClient {
|
|
|
27
29
|
pushgatewayPassword?: string | undefined;
|
|
28
30
|
intervalSec?: number | undefined;
|
|
29
31
|
removeAllJobs?: boolean | undefined;
|
|
32
|
+
scripDefaultMetrics?: boolean | undefined;
|
|
33
|
+
startupValidation?: Function | undefined;
|
|
30
34
|
});
|
|
31
35
|
appName: string;
|
|
32
36
|
dynoId: string;
|
|
@@ -37,25 +41,27 @@ export class MetricsClient {
|
|
|
37
41
|
pushgatewayUser: string;
|
|
38
42
|
pushgatewayPassword: string;
|
|
39
43
|
intervalSec: number;
|
|
44
|
+
startupValidation: Function | undefined;
|
|
40
45
|
prefixLogs: string;
|
|
41
|
-
registry:
|
|
46
|
+
get registry(): any;
|
|
42
47
|
defaultLabels: {
|
|
43
48
|
app: string;
|
|
44
49
|
dyno_id: string;
|
|
45
50
|
process_type: string;
|
|
46
51
|
};
|
|
47
52
|
authToken: string;
|
|
48
|
-
gateway: client.Pushgateway<
|
|
53
|
+
gateway: client.Pushgateway<client.RegistryContentType>;
|
|
49
54
|
gauges: {};
|
|
50
55
|
counters: {};
|
|
51
56
|
/** @type {Object<string, function(): number | Promise<number>>} */
|
|
52
57
|
gaugeUpdaters: {
|
|
53
58
|
[x: string]: () => number | Promise<number>;
|
|
54
59
|
};
|
|
60
|
+
customsMetricsTracking: {};
|
|
55
61
|
_lastUsageMicros: number;
|
|
56
62
|
_lastCheckTime: number;
|
|
57
63
|
/**
|
|
58
|
-
* Register default
|
|
64
|
+
* Register all built-in default Gauges and Counters.
|
|
59
65
|
* @private
|
|
60
66
|
*/
|
|
61
67
|
private _initDefaultMetrics;
|
|
@@ -67,16 +73,25 @@ export class MetricsClient {
|
|
|
67
73
|
* @param {string[]} [labelNames]
|
|
68
74
|
* @returns {client.Gauge}
|
|
69
75
|
*/
|
|
70
|
-
createGauge: (name
|
|
76
|
+
createGauge: ({ name, help, updateFn, labelNames, }: string) => client.Gauge;
|
|
71
77
|
/**
|
|
72
|
-
* Create a
|
|
73
|
-
*
|
|
74
|
-
* @param {
|
|
75
|
-
* @param {string}
|
|
76
|
-
* @param {string
|
|
77
|
-
* @
|
|
78
|
+
* Create a Prometheus Counter metric.
|
|
79
|
+
*
|
|
80
|
+
* @param {object} params
|
|
81
|
+
* @param {string} params.name Metric name
|
|
82
|
+
* @param {string} params.help Metric description
|
|
83
|
+
* @param {string[]} [params.labelNames]
|
|
84
|
+
* Optional list of label names. Defaults to this.defaultLabels keys.
|
|
85
|
+
*
|
|
86
|
+
* @returns {function(data?: object, value?: number): void}
|
|
87
|
+
* A trigger function to increment the counter:
|
|
88
|
+
* triggerFn(labels?, incrementValue?)
|
|
78
89
|
*/
|
|
79
|
-
createCounter(name
|
|
90
|
+
createCounter({ name, help, labelNames }: {
|
|
91
|
+
name: string;
|
|
92
|
+
help: string;
|
|
93
|
+
labelNames?: string[] | undefined;
|
|
94
|
+
}): (arg0: data | null, arg1: object, arg2: value | null, arg3: number) => void;
|
|
80
95
|
/**
|
|
81
96
|
* Get CPU usage percent (cgroup-aware)
|
|
82
97
|
* @returns {number}
|
|
@@ -112,13 +127,70 @@ export class MetricsClient {
|
|
|
112
127
|
*/
|
|
113
128
|
pushMetrics: () => Promise<void>;
|
|
114
129
|
/**
|
|
115
|
-
* Start
|
|
130
|
+
* Start periodic metrics collection + push.
|
|
131
|
+
*
|
|
116
132
|
* @param {number} [interval] Interval in seconds
|
|
133
|
+
* @param {function(): void|Promise<void>} [customPushMetics]
|
|
134
|
+
* Optional custom push function. If provided, Prometheus push is skipped.
|
|
117
135
|
*/
|
|
118
|
-
startPush: (interval?: number | undefined) => void;
|
|
136
|
+
startPush: (interval?: number | undefined, customPushMetics?: (() => void | Promise<void>) | undefined) => void;
|
|
119
137
|
cleanup: () => Promise<never>;
|
|
120
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Remove old/stale dyno/instance metrics from PushGateway.
|
|
140
|
+
*
|
|
141
|
+
* Compares existing PushGateway metrics for this job and deletes any instances
|
|
142
|
+
* that do not match the current dynoId.
|
|
143
|
+
*
|
|
144
|
+
* @param {boolean} removeAllJobs If true, performs cleanup; otherwise does nothing
|
|
145
|
+
* @returns {Promise<void>}
|
|
146
|
+
* @private
|
|
147
|
+
*/
|
|
148
|
+
private _clearOldWorkers;
|
|
149
|
+
/**
|
|
150
|
+
* Delete metrics for this job/instance from PushGateway.
|
|
151
|
+
*
|
|
152
|
+
* @param {Object} [params]
|
|
153
|
+
* @param {string} [params.jobName] Job name (defaults to appName)
|
|
154
|
+
* @param {Object} [params.groupings] Grouping labels
|
|
155
|
+
* @param {string} [params.groupings.process_type] Process type label
|
|
156
|
+
* @param {string} [params.groupings.instance] Instance/dyno ID
|
|
157
|
+
* @returns {Promise<void>}
|
|
158
|
+
*/
|
|
159
|
+
gatewayDelete: (params?: {
|
|
160
|
+
jobName?: string | undefined;
|
|
161
|
+
groupings?: {
|
|
162
|
+
process_type?: string | undefined;
|
|
163
|
+
instance?: string | undefined;
|
|
164
|
+
} | undefined;
|
|
165
|
+
} | undefined) => Promise<void>;
|
|
166
|
+
/**
|
|
167
|
+
* Push metrics to PushGateway.
|
|
168
|
+
*
|
|
169
|
+
* @param {object} [params]
|
|
170
|
+
* @param {string} [params.jobName]
|
|
171
|
+
* @param {object} [params.groupings]
|
|
172
|
+
* @returns {Promise<void>}
|
|
173
|
+
*/
|
|
174
|
+
gatewayPush: (params?: {
|
|
175
|
+
jobName?: string | undefined;
|
|
176
|
+
groupings?: object | undefined;
|
|
177
|
+
} | undefined) => Promise<void>;
|
|
178
|
+
/**
|
|
179
|
+
* Merge the default metric labels (`app`, `dyno_id`, `process_type`)
|
|
180
|
+
* with custom label names.
|
|
181
|
+
*
|
|
182
|
+
* @param {string[]} labels Additional label names
|
|
183
|
+
* @returns {string[]} Combined label names
|
|
184
|
+
*/
|
|
185
|
+
withDefaultLabels: (labels?: string[]) => string[];
|
|
186
|
+
getDefaultLabels: (labels?: any[]) => {
|
|
187
|
+
app: string;
|
|
188
|
+
dyno_id: string;
|
|
189
|
+
process_type: string;
|
|
190
|
+
};
|
|
121
191
|
_setCleanupHandlers: () => void;
|
|
192
|
+
get metricsEnabled(): boolean;
|
|
193
|
+
get metricsLogValues(): boolean;
|
|
122
194
|
}
|
|
123
195
|
import client = require("prom-client");
|
|
124
196
|
//# sourceMappingURL=metricsClient.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metricsClient.d.ts","sourceRoot":"","sources":["../src/metricsClient.js"],"names":[],"mappings":"AAKA;;;GAGG;AACH;IACE
|
|
1
|
+
{"version":3,"file":"metricsClient.d.ts","sourceRoot":"","sources":["../src/metricsClient.js"],"names":[],"mappings":"AAKA;;;GAGG;AACH;IACE;;;;;;;;;;;;;;OAcG;IACH;QAb2B,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,eAAe;QACf,mBAAmB;QACnB,WAAW;QACV,aAAa;QACb,mBAAmB;QAClB,iBAAiB;OAgE7C;IA7DC,gBACiE;IACjE,eAAqE;IACrE,oBAG6B;IAC7B,iBAAuE;IACvE,mBAC+D;IAC/D,uBACoE;IACpE,wBACsE;IACtE,4BAGI;IACJ,oBAGI;IACJ,wCAAiD;IAEjD,mBAAyF;IAsf3F,oBAEC;IAnfC;;;;MAIC;IAED,kBAEoB;IACpB,wDAOC;IAED,WAAgB;IAChB,aAAkB;IAElB,mEAAmE;IACnE;YADkB,MAAM,SAAc,MAAM,GAAG,QAAQ,MAAM,CAAC;MACvC;IACvB,2BAAgC;IAChC,yBAAyB;IACzB,uBAAgC;IASlC;;;OAGG;IACH,4BAkDC;IAED;;;;;;;OAOG;IACH,qDANW,MAAM,KAIJ,OAAO,KAAK,CAuBxB;IAED;;;;;;;;;;;;OAYG;IACH;QAT0B,IAAI,EAAnB,MAAM;QACS,IAAI,EAAnB,MAAM;QACY,UAAU;kCAGV,MAAM,4BAAU,MAAM,KAAG,IAAI,CAqBzD;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;;;OAGG;IACH,oEA0BC;IAED;;OAEG;IACH,iCAmCC;IAED;;;;;;OAMG;IACH,qEAHuB,IAAI,GAAC,QAAQ,IAAI,CAAC,uBA0BxC;IAED,8BAKC;IAED;;;;;;;;;OASG;IACH,yBAuDC;IAED;;;;;;;;;OASG;IACH;;;;;;sBAFa,QAAQ,IAAI,CAAC,CAUzB;IAED;;;;;;;OAOG;IACH;;;sBAFa,QAAQ,IAAI,CAAC,CAUzB;IAED;;;;;;OAMG;IACH,6BAHW,MAAM,EAAE,KACN,MAAM,EAAE,CAIpB;IAED;;;;MAEC;IAED,gCAGC;IAID,8BAEC;IAED,gCAEC;CAKF"}
|
package/lib/metricsClient.js
CHANGED
|
@@ -22,6 +22,8 @@ class MetricsClient {
|
|
|
22
22
|
* @param {string} [config.pushgatewayPassword] PushGateway password
|
|
23
23
|
* @param {number} [config.intervalSec] Interval in seconds for pushing metrics
|
|
24
24
|
* @param {boolean} [config.removeAllJobs] Enable to clear all jobs at startup
|
|
25
|
+
* @param {boolean} [config.scripDefaultMetrics] Enable to scip default metrics creation
|
|
26
|
+
* @param {function} [config.startupValidation] Add to validate on start push.
|
|
25
27
|
*/
|
|
26
28
|
constructor(config = {}) {
|
|
27
29
|
this.appName = config.appName || process.env.METRICS_APP_NAME || 'unknown-app';
|
|
@@ -33,6 +35,7 @@ class MetricsClient {
|
|
|
33
35
|
this.pushgatewayUser = config.pushgatewayUser || process.env.METRICS_PUSHGATEWAY_USER || '';
|
|
34
36
|
this.pushgatewayPassword = config.pushgatewayPassword || process.env.METRICS_PUSHGATEWAY_PASSWORD || '';
|
|
35
37
|
this.intervalSec = config.intervalSec || parseInt(process.env.METRICS_INTERVAL_SEC || '', 10) || 15;
|
|
38
|
+
this.startupValidation = config.startupValidation;
|
|
36
39
|
this.prefixLogs = `[${this.processType}] [${this.appName}] [${this.dynoId}] [Monitoring]`;
|
|
37
40
|
this.registry = new client.Registry();
|
|
38
41
|
client.collectDefaultMetrics({
|
|
@@ -57,26 +60,57 @@ class MetricsClient {
|
|
|
57
60
|
|
|
58
61
|
/** @type {Object<string, function(): number | Promise<number>>} */
|
|
59
62
|
this.gaugeUpdaters = {};
|
|
63
|
+
this.customsMetricsTracking = {};
|
|
60
64
|
this._lastUsageMicros = 0;
|
|
61
65
|
this._lastCheckTime = Date.now();
|
|
62
66
|
this._clearOldWorkers(config.removeAllJobs).then(_ => {
|
|
63
|
-
|
|
67
|
+
if (config.scripDefaultMetrics) {
|
|
68
|
+
this._initDefaultMetrics();
|
|
69
|
+
}
|
|
64
70
|
this._setCleanupHandlers();
|
|
65
71
|
});
|
|
66
72
|
}
|
|
67
73
|
|
|
68
74
|
/**
|
|
69
|
-
* Register default
|
|
75
|
+
* Register all built-in default Gauges and Counters.
|
|
70
76
|
* @private
|
|
71
77
|
*/
|
|
72
78
|
_initDefaultMetrics = () => {
|
|
73
|
-
this.createGauge(
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
this.createGauge(
|
|
79
|
-
|
|
79
|
+
this.createGauge({
|
|
80
|
+
name: 'app_process_cpu_usage_percent',
|
|
81
|
+
help: 'Current CPU usage of the Node.js process in percent',
|
|
82
|
+
updateFn: this.getCpuUsagePercent
|
|
83
|
+
});
|
|
84
|
+
this.createGauge({
|
|
85
|
+
name: 'app_available_cpu_count',
|
|
86
|
+
help: 'How many CPU cores are available to this process',
|
|
87
|
+
updateFn: this.getAvailableCPUs
|
|
88
|
+
});
|
|
89
|
+
this.createGauge({
|
|
90
|
+
name: 'app_container_memory_usage_bytes',
|
|
91
|
+
help: 'Current container RAM usage from cgroup',
|
|
92
|
+
updateFn: this.getContainerMemoryUsage
|
|
93
|
+
});
|
|
94
|
+
this.createGauge({
|
|
95
|
+
name: 'app_event_loop_lag_ms',
|
|
96
|
+
help: 'Estimated event loop lag in milliseconds',
|
|
97
|
+
updateFn: this.measureLag
|
|
98
|
+
});
|
|
99
|
+
this.createGauge({
|
|
100
|
+
name: 'app_container_memory_limit_bytes',
|
|
101
|
+
help: 'Max RAM available to container from cgroup (memory.max)',
|
|
102
|
+
updateFn: this.getContainerMemoryLimit
|
|
103
|
+
});
|
|
104
|
+
this.createGauge({
|
|
105
|
+
name: 'app_uptime_seconds',
|
|
106
|
+
help: 'How long the process has been running',
|
|
107
|
+
updateFn: process.uptime
|
|
108
|
+
});
|
|
109
|
+
this.createCounter({
|
|
110
|
+
name: 'app_http_requests_total',
|
|
111
|
+
help: 'Total number of HTTP requests handled by this process',
|
|
112
|
+
labelNames: this.withDefaultLabels(['method', 'route', 'appId', 'databaseId', 'duration', 'requestSize', 'status_code'])
|
|
113
|
+
});
|
|
80
114
|
};
|
|
81
115
|
|
|
82
116
|
/**
|
|
@@ -87,7 +121,12 @@ class MetricsClient {
|
|
|
87
121
|
* @param {string[]} [labelNames]
|
|
88
122
|
* @returns {client.Gauge}
|
|
89
123
|
*/
|
|
90
|
-
createGauge = (
|
|
124
|
+
createGauge = ({
|
|
125
|
+
name,
|
|
126
|
+
help,
|
|
127
|
+
updateFn,
|
|
128
|
+
labelNames = Object.keys(this.defaultLabels)
|
|
129
|
+
}) => {
|
|
91
130
|
if (this.gauges[name]) return this.gauges[name];
|
|
92
131
|
const g = new client.Gauge({
|
|
93
132
|
name,
|
|
@@ -96,19 +135,30 @@ class MetricsClient {
|
|
|
96
135
|
registers: [this.registry]
|
|
97
136
|
});
|
|
98
137
|
this.gauges[name] = g;
|
|
99
|
-
if (typeof updateFn === 'function')
|
|
138
|
+
if (updateFn && typeof updateFn === 'function') {
|
|
139
|
+
this.gaugeUpdaters[name] = updateFn;
|
|
140
|
+
}
|
|
100
141
|
return g;
|
|
101
142
|
};
|
|
102
143
|
|
|
103
144
|
/**
|
|
104
|
-
* Create a
|
|
105
|
-
*
|
|
106
|
-
* @param {
|
|
107
|
-
* @param {string}
|
|
108
|
-
* @param {string
|
|
109
|
-
* @
|
|
145
|
+
* Create a Prometheus Counter metric.
|
|
146
|
+
*
|
|
147
|
+
* @param {object} params
|
|
148
|
+
* @param {string} params.name Metric name
|
|
149
|
+
* @param {string} params.help Metric description
|
|
150
|
+
* @param {string[]} [params.labelNames]
|
|
151
|
+
* Optional list of label names. Defaults to this.defaultLabels keys.
|
|
152
|
+
*
|
|
153
|
+
* @returns {function(data?: object, value?: number): void}
|
|
154
|
+
* A trigger function to increment the counter:
|
|
155
|
+
* triggerFn(labels?, incrementValue?)
|
|
110
156
|
*/
|
|
111
|
-
createCounter(
|
|
157
|
+
createCounter({
|
|
158
|
+
name,
|
|
159
|
+
help,
|
|
160
|
+
labelNames = Object.keys(this.defaultLabels)
|
|
161
|
+
}) {
|
|
112
162
|
if (this.counters[name]) return this.counters[name].triggerFn;
|
|
113
163
|
const c = new client.Counter({
|
|
114
164
|
name,
|
|
@@ -244,6 +294,9 @@ class MetricsClient {
|
|
|
244
294
|
try {
|
|
245
295
|
for (const [name, updateFn] of Object.entries(this.gaugeUpdaters)) {
|
|
246
296
|
try {
|
|
297
|
+
if (!updateFn) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
247
300
|
const result = updateFn();
|
|
248
301
|
const val = result instanceof Promise ? await result : result;
|
|
249
302
|
if (val !== undefined) this.gauges[name].set(this.defaultLabels, val);
|
|
@@ -251,7 +304,7 @@ class MetricsClient {
|
|
|
251
304
|
console.error(`${this.prefixLogs} Failed to update gauge ${name}:`, err);
|
|
252
305
|
}
|
|
253
306
|
}
|
|
254
|
-
await this.
|
|
307
|
+
await this.gatewayPush({
|
|
255
308
|
jobName: this.appName,
|
|
256
309
|
groupings: {
|
|
257
310
|
process_type: this.processType,
|
|
@@ -269,32 +322,48 @@ class MetricsClient {
|
|
|
269
322
|
};
|
|
270
323
|
|
|
271
324
|
/**
|
|
272
|
-
* Start
|
|
325
|
+
* Start periodic metrics collection + push.
|
|
326
|
+
*
|
|
273
327
|
* @param {number} [interval] Interval in seconds
|
|
328
|
+
* @param {function(): void|Promise<void>} [customPushMetics]
|
|
329
|
+
* Optional custom push function. If provided, Prometheus push is skipped.
|
|
274
330
|
*/
|
|
275
|
-
startPush = (interval = this.intervalSec) => {
|
|
331
|
+
startPush = (interval = this.intervalSec, customPushMetics = () => {}) => {
|
|
276
332
|
if (!this.enabled) {
|
|
277
333
|
console.warn(`${this.prefixLogs} Metrics disabled`);
|
|
334
|
+
return;
|
|
278
335
|
}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
336
|
+
if (this.startupValidation && !this.startupValidation()) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (customPushMetics) {
|
|
340
|
+
setInterval(customPushMetics, interval * 1000);
|
|
341
|
+
} else {
|
|
342
|
+
setInterval(() => {
|
|
343
|
+
this.pushMetrics().catch(err => {
|
|
344
|
+
console.error(`${this.prefixLogs} Failed to push metrics:`, err);
|
|
345
|
+
});
|
|
346
|
+
}, interval * 1000);
|
|
347
|
+
}
|
|
348
|
+
console.warn(`${this.prefixLogs} Metrics collection started. (interval: ${this.intervalSec}s)`);
|
|
285
349
|
};
|
|
286
350
|
cleanup = async () => {
|
|
287
351
|
if (this.enabled) {
|
|
288
|
-
await this.
|
|
289
|
-
jobName: this.appName,
|
|
290
|
-
groupings: {
|
|
291
|
-
process_type: this.processType,
|
|
292
|
-
instance: this.dynoId
|
|
293
|
-
}
|
|
294
|
-
});
|
|
352
|
+
await this.gatewayDelete();
|
|
295
353
|
}
|
|
296
354
|
process.exit(0);
|
|
297
355
|
};
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Remove old/stale dyno/instance metrics from PushGateway.
|
|
359
|
+
*
|
|
360
|
+
* Compares existing PushGateway metrics for this job and deletes any instances
|
|
361
|
+
* that do not match the current dynoId.
|
|
362
|
+
*
|
|
363
|
+
* @param {boolean} removeAllJobs If true, performs cleanup; otherwise does nothing
|
|
364
|
+
* @returns {Promise<void>}
|
|
365
|
+
* @private
|
|
366
|
+
*/
|
|
298
367
|
_clearOldWorkers = async removeAllJobs => {
|
|
299
368
|
if (!removeAllJobs) return;
|
|
300
369
|
try {
|
|
@@ -323,10 +392,8 @@ class MetricsClient {
|
|
|
323
392
|
return;
|
|
324
393
|
}
|
|
325
394
|
for (const instance of instances) {
|
|
326
|
-
await this.
|
|
327
|
-
jobName: this.appName,
|
|
395
|
+
await this.gatewayDelete({
|
|
328
396
|
groupings: {
|
|
329
|
-
process_type: this.processType,
|
|
330
397
|
instance
|
|
331
398
|
}
|
|
332
399
|
});
|
|
@@ -337,10 +404,74 @@ class MetricsClient {
|
|
|
337
404
|
console.error(`${this.prefixLogs} Error deleting old metrics:`, err);
|
|
338
405
|
}
|
|
339
406
|
};
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Delete metrics for this job/instance from PushGateway.
|
|
410
|
+
*
|
|
411
|
+
* @param {Object} [params]
|
|
412
|
+
* @param {string} [params.jobName] Job name (defaults to appName)
|
|
413
|
+
* @param {Object} [params.groupings] Grouping labels
|
|
414
|
+
* @param {string} [params.groupings.process_type] Process type label
|
|
415
|
+
* @param {string} [params.groupings.instance] Instance/dyno ID
|
|
416
|
+
* @returns {Promise<void>}
|
|
417
|
+
*/
|
|
418
|
+
gatewayDelete = async (params = {}) => {
|
|
419
|
+
return this.gateway.delete({
|
|
420
|
+
jobName: params.jobName || this.appName,
|
|
421
|
+
groupings: {
|
|
422
|
+
process_type: params.groupings?.process_type || this.processType,
|
|
423
|
+
instance: params.groupings?.instance || this.dynoId
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Push metrics to PushGateway.
|
|
430
|
+
*
|
|
431
|
+
* @param {object} [params]
|
|
432
|
+
* @param {string} [params.jobName]
|
|
433
|
+
* @param {object} [params.groupings]
|
|
434
|
+
* @returns {Promise<void>}
|
|
435
|
+
*/
|
|
436
|
+
gatewayPush = async (params = {}) => {
|
|
437
|
+
return this.gateway.push({
|
|
438
|
+
jobName: params.jobName || this.appName,
|
|
439
|
+
groupings: {
|
|
440
|
+
process_type: params.groupings?.process_type || this.processType,
|
|
441
|
+
instance: params.groupings?.instance || this.dynoId
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Merge the default metric labels (`app`, `dyno_id`, `process_type`)
|
|
448
|
+
* with custom label names.
|
|
449
|
+
*
|
|
450
|
+
* @param {string[]} labels Additional label names
|
|
451
|
+
* @returns {string[]} Combined label names
|
|
452
|
+
*/
|
|
453
|
+
withDefaultLabels = (labels = []) => {
|
|
454
|
+
return [...Object.keys(this.defaultLabels), labels];
|
|
455
|
+
};
|
|
456
|
+
getDefaultLabels = (labels = []) => {
|
|
457
|
+
return this.defaultLabels;
|
|
458
|
+
};
|
|
340
459
|
_setCleanupHandlers = () => {
|
|
341
460
|
process.on('SIGINT', this.cleanup);
|
|
342
461
|
process.on('SIGTERM', this.cleanup);
|
|
343
462
|
};
|
|
463
|
+
|
|
464
|
+
// GETTERS
|
|
465
|
+
|
|
466
|
+
get metricsEnabled() {
|
|
467
|
+
return this.enabled;
|
|
468
|
+
}
|
|
469
|
+
get metricsLogValues() {
|
|
470
|
+
return this.logValues;
|
|
471
|
+
}
|
|
472
|
+
get registry() {
|
|
473
|
+
return this.registry;
|
|
474
|
+
}
|
|
344
475
|
}
|
|
345
476
|
module.exports = {
|
|
346
477
|
MetricsClient
|
package/lib/metricsClient.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metricsClient.js","names":["client","require","fs","os","https","MetricsClient","constructor","config","appName","process","env","METRICS_APP_NAME","dynoId","HOSTNAME","processType","BUILD_DYNO_PROCESS_TYPE","enabled","METRICS_ENABLED","logValues","METRICS_LOG_VALUES","pushgatewayUrl","METRICS_PUSHGATEWAY_URL","pushgatewayUser","METRICS_PUSHGATEWAY_USER","pushgatewayPassword","METRICS_PUSHGATEWAY_PASSWORD","intervalSec","parseInt","METRICS_INTERVAL_SEC","prefixLogs","registry","Registry","collectDefaultMetrics","register","defaultLabels","app","dyno_id","process_type","authToken","Buffer","from","toString","gateway","Pushgateway","headers","Authorization","agent","Agent","keepAlive","gauges","counters","gaugeUpdaters","_lastUsageMicros","_lastCheckTime","Date","now","_clearOldWorkers","removeAllJobs","then","_","_initDefaultMetrics","_setCleanupHandlers","createGauge","getCpuUsagePercent","getAvailableCPUs","getContainerMemoryUsage","measureLag","getContainerMemoryLimit","uptime","createCounter","Object","keys","name","help","updateFn","labelNames","g","Gauge","registers","triggerFn","c","Counter","data","value","inc","stat","readFileSync","match","currentUsage","deltaUsage","deltaTime","cpuMaxPath","existsSync","quotaStr","periodStr","trim","split","cpus","length","memoryUsage","rss","path","val","parsed","totalmem","Promise","resolve","start","setImmediate","countHttpRequestMiddleware","req","res","next","on","route","appId","params","body","query","databaseId","app_http_requests_total","method","status_code","statusCode","duration","requestSize","pushMetrics","entries","result","undefined","set","err","console","error","push","jobName","groupings","instance","values","forEach","counter","reset","metrics","getMetricsAsJSON","log","JSON","stringify","startPush","interval","warn","setInterval","catch","cleanup","delete","exit","url","fetch","Accept","ok","status","text","regex","RegExp","instances","Set","exec","add","size","module","exports"],"sources":["../src/metricsClient.js"],"sourcesContent":["const client = require('prom-client')\nconst fs = require('fs')\nconst os = require('os')\nconst https = require('https')\n\n/**\n * MetricsClient handles Prometheus metrics collection and push.\n * Supports gauges, counters, default metrics, and custom metrics.\n */\nclass MetricsClient {\n /**\n * @param {Object} config\n * @param {string} [config.appName] Name of the application\n * @param {string} [config.dynoId] Dyno/instance ID\n * @param {string} [config.processType] Process type (web, worker, etc.)\n * @param {boolean} [config.enabled] Enable metrics collection\n * @param {boolean} [config.logValues] Log metrics values to console\n * @param {string} [config.pushgatewayUrl] PushGateway URL\n * @param {string} [config.pushgatewayUser] PushGateway username\n * @param {string} [config.pushgatewayPassword] PushGateway password\n * @param {number} [config.intervalSec] Interval in seconds for pushing metrics\n * @param {boolean} [config.removeAllJobs] Enable to clear all jobs at startup\n */\n constructor(config = {}) {\n this.appName =\n config.appName || process.env.METRICS_APP_NAME || 'unknown-app'\n this.dynoId = config.dynoId || process.env.HOSTNAME || 'unknown-dyno'\n this.processType =\n config.processType ||\n process.env.BUILD_DYNO_PROCESS_TYPE ||\n 'undefined_build_dyno_type'\n this.enabled = config.enabled ?? process.env.METRICS_ENABLED === 'true'\n this.logValues =\n config.logValues ?? process.env.METRICS_LOG_VALUES === 'true'\n this.pushgatewayUrl =\n config.pushgatewayUrl || process.env.METRICS_PUSHGATEWAY_URL || ''\n this.pushgatewayUser =\n config.pushgatewayUser || process.env.METRICS_PUSHGATEWAY_USER || ''\n this.pushgatewayPassword =\n config.pushgatewayPassword ||\n process.env.METRICS_PUSHGATEWAY_PASSWORD ||\n ''\n this.intervalSec =\n config.intervalSec ||\n parseInt(process.env.METRICS_INTERVAL_SEC || '', 10) ||\n 15\n\n this.prefixLogs = `[${this.processType}] [${this.appName}] [${this.dynoId}] [Monitoring]`\n\n this.registry = new client.Registry()\n client.collectDefaultMetrics({ register: this.registry })\n\n this.defaultLabels = {\n app: this.appName,\n dyno_id: this.dynoId,\n process_type: this.processType,\n }\n\n this.authToken = Buffer.from(\n `${this.pushgatewayUser}:${this.pushgatewayPassword}`\n ).toString('base64')\n this.gateway = new client.Pushgateway(\n this.pushgatewayUrl,\n {\n headers: { Authorization: `Basic ${this.authToken}` },\n agent: new https.Agent({ keepAlive: true }),\n },\n this.registry\n )\n\n this.gauges = {}\n this.counters = {}\n\n /** @type {Object<string, function(): number | Promise<number>>} */\n this.gaugeUpdaters = {}\n this._lastUsageMicros = 0\n this._lastCheckTime = Date.now()\n this._clearOldWorkers(config.removeAllJobs).then(_ => {\n this._initDefaultMetrics()\n this._setCleanupHandlers()\n })\n }\n\n /**\n * Register default gauges and counters.\n * @private\n */\n _initDefaultMetrics = () => {\n this.createGauge(\n 'app_process_cpu_usage_percent',\n 'Current CPU usage of the Node.js process in percent',\n this.getCpuUsagePercent\n )\n this.createGauge(\n 'app_available_cpu_count',\n 'How many CPU cores are available to this process',\n this.getAvailableCPUs\n )\n this.createGauge(\n 'app_container_memory_usage_bytes',\n 'Current container RAM usage from cgroup',\n this.getContainerMemoryUsage\n )\n this.createGauge(\n 'app_event_loop_lag_ms',\n 'Estimated event loop lag in milliseconds',\n this.measureLag\n )\n this.createGauge(\n 'app_container_memory_limit_bytes',\n 'Max RAM available to container from cgroup (memory.max)',\n this.getContainerMemoryLimit\n )\n this.createGauge(\n 'app_uptime_seconds',\n 'How long the process has been running',\n process.uptime\n )\n\n this.createCounter(\n 'app_http_requests_total',\n 'Total number of HTTP requests handled by this process',\n [\n ...Object.keys(this.defaultLabels),\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'duration',\n 'requestSize',\n 'status_code',\n ]\n )\n }\n\n /**\n * Create a gauge metric.\n * @param {string} name\n * @param {string} help\n * @param {function(): number|Promise<number>} [updateFn] Optional function returning value\n * @param {string[]} [labelNames]\n * @returns {client.Gauge}\n */\n createGauge = (\n name,\n help,\n updateFn,\n labelNames = Object.keys(this.defaultLabels)\n ) => {\n if (this.gauges[name]) return this.gauges[name]\n\n const g = new client.Gauge({\n name,\n help,\n labelNames,\n registers: [this.registry],\n })\n this.gauges[name] = g\n\n if (typeof updateFn === 'function') this.gaugeUpdaters[name] = updateFn\n\n return g\n }\n\n /**\n * Create a counter metric.\n * Returns a trigger function to increment the counter manually.\n * @param {string} name\n * @param {string} help\n * @param {string[]} [labelNames]\n * @returns {function(data?: object, value?: number): void} triggerFn\n */\n createCounter(name, help, labelNames = Object.keys(this.defaultLabels)) {\n if (this.counters[name]) return this.counters[name].triggerFn\n\n const c = new client.Counter({\n name,\n help,\n labelNames,\n registers: [this.registry],\n })\n this.counters[name] = c\n\n const triggerFn = (data = {}, value = 1) => {\n c.inc({ ...this.defaultLabels, ...data }, value)\n }\n\n this.counters[name].triggerFn = triggerFn\n return triggerFn\n }\n\n /**\n * Get CPU usage percent (cgroup-aware)\n * @returns {number}\n */\n getCpuUsagePercent = () => {\n try {\n const stat = fs.readFileSync('/sys/fs/cgroup/cpu.stat', 'utf-8')\n const match = stat.match(/usage_usec (\\d+)/)\n if (!match) return 0\n\n const now = Date.now()\n const currentUsage = parseInt(match[1], 10)\n\n if (this._lastUsageMicros === 0) {\n this._lastUsageMicros = currentUsage\n this._lastCheckTime = now\n return 0\n }\n\n const deltaUsage = currentUsage - this._lastUsageMicros\n const deltaTime = now - this._lastCheckTime\n\n this._lastUsageMicros = currentUsage\n this._lastCheckTime = now\n\n return (deltaUsage / (deltaTime * 1000)) * 100\n } catch {\n return 0\n }\n }\n\n /**\n * Get available CPU cores.\n * @returns {number}\n */\n getAvailableCPUs() {\n try {\n const cpuMaxPath = '/sys/fs/cgroup/cpu.max'\n if (fs.existsSync(cpuMaxPath)) {\n const [quotaStr, periodStr] = fs\n .readFileSync(cpuMaxPath, 'utf8')\n .trim()\n .split(' ')\n if (quotaStr === 'max') return os.cpus().length\n return parseInt(quotaStr, 10) / parseInt(periodStr, 10)\n }\n return os.cpus().length\n } catch {\n return 1\n }\n }\n\n /**\n * Get container memory usage in bytes.\n * @returns {number}\n */\n getContainerMemoryUsage() {\n try {\n return parseInt(\n fs.readFileSync('/sys/fs/cgroup/memory.current', 'utf-8').trim(),\n 10\n )\n } catch {\n return process.memoryUsage().rss\n }\n }\n\n /**\n * Get container memory limit in bytes.\n * @returns {number}\n */\n getContainerMemoryLimit() {\n try {\n const path = '/sys/fs/cgroup/memory.max'\n if (fs.existsSync(path)) {\n const val = fs.readFileSync(path, 'utf-8').trim()\n if (val !== 'max') {\n const parsed = parseInt(val, 10)\n if (parsed && parsed < os.totalmem()) return parsed\n }\n }\n return os.totalmem()\n } catch {\n return os.totalmem()\n }\n }\n\n /**\n * Measure event loop lag in ms.\n * @returns {Promise<number>}\n */\n measureLag() {\n return new Promise(resolve => {\n const start = Date.now()\n setImmediate(() => resolve(Date.now() - start))\n })\n }\n\n /**\n * Express middleware to count HTTP requests.\n * Increments the `app_http_requests_total` counter.\n */\n countHttpRequestMiddleware = (req, res, next) => {\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 ''\n\n this.counters?.app_http_requests_total?.triggerFn({\n method: req.method,\n route,\n status_code: res.statusCode,\n appId,\n databaseId,\n duration: Date.now() - start,\n requestSize: req.headers['content-length']\n ? parseInt(req.headers['content-length'], 10)\n : 0,\n })\n })\n\n next()\n }\n\n /**\n * Push all gauges and counters to PushGateway and optionally log.\n */\n pushMetrics = async () => {\n try {\n for (const [name, updateFn] of Object.entries(this.gaugeUpdaters)) {\n try {\n const result = updateFn()\n const val = result instanceof Promise ? await result : result\n if (val !== undefined) this.gauges[name].set(this.defaultLabels, val)\n } catch (err) {\n console.error(\n `${this.prefixLogs} Failed to update gauge ${name}:`,\n err\n )\n }\n }\n\n await this.gateway.push({\n jobName: this.appName,\n groupings: { process_type: this.processType, instance: this.dynoId },\n })\n\n Object.values(this.counters).forEach(counter => counter.reset())\n\n if (this.logValues) {\n const metrics = await this.registry.getMetricsAsJSON()\n console.log(\n `${this.prefixLogs} Metrics:\\n`,\n JSON.stringify(metrics, null, 2)\n )\n }\n } catch (err) {\n console.error(`${this.prefixLogs} Failed to push metrics:`, err)\n }\n }\n\n /**\n * Start automatic periodic push of metrics.\n * @param {number} [interval] Interval in seconds\n */\n startPush = (interval = this.intervalSec) => {\n if (!this.enabled) {\n console.warn(`${this.prefixLogs} Metrics disabled`)\n }\n\n setInterval(() => {\n this.pushMetrics().catch(err => {\n console.error(`${this.prefixLogs} Failed to push metrics:`, err)\n })\n }, interval * 1000)\n\n console.warn(`${this.prefixLogs} Metrics collection started.`)\n }\n\n cleanup = async () => {\n if (this.enabled) {\n await this.gateway.delete({\n jobName: this.appName,\n groupings: {\n process_type: this.processType,\n instance: this.dynoId,\n },\n })\n }\n process.exit(0)\n }\n\n _clearOldWorkers = async removeAllJobs => {\n if (!removeAllJobs) return\n\n try {\n const url = `${this.pushgatewayUrl}/metrics`\n const res = await fetch(url, {\n headers: {\n Authorization: `Basic ${this.authToken}`,\n Accept: 'text/plain',\n },\n })\n\n if (!res.ok) {\n console.error(\n `${this.prefixLogs} Failed to fetch metrics: ${res.status}`\n )\n return\n }\n\n const text = await res.text()\n\n const regex = new RegExp(\n `(?:instance=\"([^\"]+)\".*job=\"${this.appName}\"|job=\"${this.appName}\".*instance=\"([^\"]+)\")`,\n 'g'\n )\n const instances = new Set()\n let match\n // eslint-disable-next-line no-cond-assign\n while ((match = regex.exec(text)) !== null) {\n const instance = match[1] || match[2]\n if (instance && instance !== this.dynoId) instances.add(instance)\n }\n\n if (instances.size === 0) {\n console.log(`${this.prefixLogs} No old dynos to delete.`)\n return\n }\n\n for (const instance of instances) {\n await this.gateway.delete({\n jobName: this.appName,\n groupings: {\n process_type: this.processType,\n instance,\n },\n })\n console.log(\n `${this.prefixLogs} Deleted metrics for old dyno: ${instance}`\n )\n }\n\n console.log(\n `${this.prefixLogs} Cleared all old instances for job ${this.appName}`\n )\n } catch (err) {\n console.error(`${this.prefixLogs} Error deleting old metrics:`, err)\n }\n }\n\n _setCleanupHandlers = () => {\n process.on('SIGINT', this.cleanup)\n process.on('SIGTERM', this.cleanup)\n }\n}\n\nmodule.exports = { MetricsClient }\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"metricsClient.js","names":["client","require","fs","os","https","MetricsClient","constructor","config","appName","process","env","METRICS_APP_NAME","dynoId","HOSTNAME","processType","BUILD_DYNO_PROCESS_TYPE","enabled","METRICS_ENABLED","logValues","METRICS_LOG_VALUES","pushgatewayUrl","METRICS_PUSHGATEWAY_URL","pushgatewayUser","METRICS_PUSHGATEWAY_USER","pushgatewayPassword","METRICS_PUSHGATEWAY_PASSWORD","intervalSec","parseInt","METRICS_INTERVAL_SEC","startupValidation","prefixLogs","registry","Registry","collectDefaultMetrics","register","defaultLabels","app","dyno_id","process_type","authToken","Buffer","from","toString","gateway","Pushgateway","headers","Authorization","agent","Agent","keepAlive","gauges","counters","gaugeUpdaters","customsMetricsTracking","_lastUsageMicros","_lastCheckTime","Date","now","_clearOldWorkers","removeAllJobs","then","_","scripDefaultMetrics","_initDefaultMetrics","_setCleanupHandlers","createGauge","name","help","updateFn","getCpuUsagePercent","getAvailableCPUs","getContainerMemoryUsage","measureLag","getContainerMemoryLimit","uptime","createCounter","labelNames","withDefaultLabels","Object","keys","g","Gauge","registers","triggerFn","c","Counter","data","value","inc","stat","readFileSync","match","currentUsage","deltaUsage","deltaTime","cpuMaxPath","existsSync","quotaStr","periodStr","trim","split","cpus","length","memoryUsage","rss","path","val","parsed","totalmem","Promise","resolve","start","setImmediate","countHttpRequestMiddleware","req","res","next","on","route","appId","params","body","query","databaseId","app_http_requests_total","method","status_code","statusCode","duration","requestSize","pushMetrics","entries","result","undefined","set","err","console","error","gatewayPush","jobName","groupings","instance","values","forEach","counter","reset","metrics","getMetricsAsJSON","log","JSON","stringify","startPush","interval","customPushMetics","warn","setInterval","catch","cleanup","gatewayDelete","exit","url","fetch","Accept","ok","status","text","regex","RegExp","instances","Set","exec","add","size","delete","push","labels","getDefaultLabels","metricsEnabled","metricsLogValues","module","exports"],"sources":["../src/metricsClient.js"],"sourcesContent":["const client = require('prom-client')\nconst fs = require('fs')\nconst os = require('os')\nconst https = require('https')\n\n/**\n * MetricsClient handles Prometheus metrics collection and push.\n * Supports gauges, counters, default metrics, and custom metrics.\n */\nclass MetricsClient {\n /**\n * @param {Object} config\n * @param {string} [config.appName] Name of the application\n * @param {string} [config.dynoId] Dyno/instance ID\n * @param {string} [config.processType] Process type (web, worker, etc.)\n * @param {boolean} [config.enabled] Enable metrics collection\n * @param {boolean} [config.logValues] Log metrics values to console\n * @param {string} [config.pushgatewayUrl] PushGateway URL\n * @param {string} [config.pushgatewayUser] PushGateway username\n * @param {string} [config.pushgatewayPassword] PushGateway password\n * @param {number} [config.intervalSec] Interval in seconds for pushing metrics\n * @param {boolean} [config.removeAllJobs] Enable to clear all jobs at startup\n * @param {boolean} [config.scripDefaultMetrics] Enable to scip default metrics creation\n * @param {function} [config.startupValidation] Add to validate on start push.\n */\n constructor(config = {}) {\n this.appName =\n config.appName || process.env.METRICS_APP_NAME || 'unknown-app'\n this.dynoId = config.dynoId || process.env.HOSTNAME || 'unknown-dyno'\n this.processType =\n config.processType ||\n process.env.BUILD_DYNO_PROCESS_TYPE ||\n 'undefined_build_dyno_type'\n this.enabled = config.enabled ?? process.env.METRICS_ENABLED === 'true'\n this.logValues =\n config.logValues ?? process.env.METRICS_LOG_VALUES === 'true'\n this.pushgatewayUrl =\n config.pushgatewayUrl || process.env.METRICS_PUSHGATEWAY_URL || ''\n this.pushgatewayUser =\n config.pushgatewayUser || process.env.METRICS_PUSHGATEWAY_USER || ''\n this.pushgatewayPassword =\n config.pushgatewayPassword ||\n process.env.METRICS_PUSHGATEWAY_PASSWORD ||\n ''\n this.intervalSec =\n config.intervalSec ||\n parseInt(process.env.METRICS_INTERVAL_SEC || '', 10) ||\n 15\n this.startupValidation = config.startupValidation\n\n this.prefixLogs = `[${this.processType}] [${this.appName}] [${this.dynoId}] [Monitoring]`\n\n this.registry = new client.Registry()\n client.collectDefaultMetrics({ register: this.registry })\n\n this.defaultLabels = {\n app: this.appName,\n dyno_id: this.dynoId,\n process_type: this.processType,\n }\n\n this.authToken = Buffer.from(\n `${this.pushgatewayUser}:${this.pushgatewayPassword}`\n ).toString('base64')\n this.gateway = new client.Pushgateway(\n this.pushgatewayUrl,\n {\n headers: { Authorization: `Basic ${this.authToken}` },\n agent: new https.Agent({ keepAlive: true }),\n },\n this.registry\n )\n\n this.gauges = {}\n this.counters = {}\n\n /** @type {Object<string, function(): number | Promise<number>>} */\n this.gaugeUpdaters = {}\n this.customsMetricsTracking = {}\n this._lastUsageMicros = 0\n this._lastCheckTime = Date.now()\n this._clearOldWorkers(config.removeAllJobs).then(_ => {\n if (config.scripDefaultMetrics) {\n this._initDefaultMetrics()\n }\n this._setCleanupHandlers()\n })\n }\n\n /**\n * Register all built-in default Gauges and Counters.\n * @private\n */\n _initDefaultMetrics = () => {\n this.createGauge({\n name: 'app_process_cpu_usage_percent',\n help: 'Current CPU usage of the Node.js process in percent',\n updateFn: this.getCpuUsagePercent,\n })\n\n this.createGauge({\n name: 'app_available_cpu_count',\n help: 'How many CPU cores are available to this process',\n updateFn: this.getAvailableCPUs,\n })\n\n this.createGauge({\n name: 'app_container_memory_usage_bytes',\n help: 'Current container RAM usage from cgroup',\n updateFn: this.getContainerMemoryUsage,\n })\n\n this.createGauge({\n name: 'app_event_loop_lag_ms',\n help: 'Estimated event loop lag in milliseconds',\n updateFn: this.measureLag,\n })\n\n this.createGauge({\n name: 'app_container_memory_limit_bytes',\n help: 'Max RAM available to container from cgroup (memory.max)',\n updateFn: this.getContainerMemoryLimit,\n })\n\n this.createGauge({\n name: 'app_uptime_seconds',\n help: 'How long the process has been running',\n updateFn: process.uptime,\n })\n\n this.createCounter({\n name: 'app_http_requests_total',\n help: 'Total number of HTTP requests handled by this process',\n labelNames: this.withDefaultLabels([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'duration',\n 'requestSize',\n 'status_code',\n ]),\n })\n }\n\n /**\n * Create a gauge metric.\n * @param {string} name\n * @param {string} help\n * @param {function(): number|Promise<number>} [updateFn] Optional function returning value\n * @param {string[]} [labelNames]\n * @returns {client.Gauge}\n */\n createGauge = ({\n name,\n help,\n updateFn,\n labelNames = Object.keys(this.defaultLabels),\n }) => {\n if (this.gauges[name]) return this.gauges[name]\n\n const g = new client.Gauge({\n name,\n help,\n labelNames,\n registers: [this.registry],\n })\n this.gauges[name] = g\n\n if (updateFn && typeof updateFn === 'function') {\n this.gaugeUpdaters[name] = updateFn\n }\n\n return g\n }\n\n /**\n * Create a Prometheus Counter metric.\n *\n * @param {object} params\n * @param {string} params.name Metric name\n * @param {string} params.help Metric description\n * @param {string[]} [params.labelNames]\n * Optional list of label names. Defaults to this.defaultLabels keys.\n *\n * @returns {function(data?: object, value?: number): void}\n * A trigger function to increment the counter:\n * triggerFn(labels?, incrementValue?)\n */\n createCounter({ name, help, labelNames = Object.keys(this.defaultLabels) }) {\n if (this.counters[name]) return this.counters[name].triggerFn\n\n const c = new client.Counter({\n name,\n help,\n labelNames,\n registers: [this.registry],\n })\n this.counters[name] = c\n\n const triggerFn = (data = {}, value = 1) => {\n c.inc({ ...this.defaultLabels, ...data }, value)\n }\n\n this.counters[name].triggerFn = triggerFn\n return triggerFn\n }\n\n /**\n * Get CPU usage percent (cgroup-aware)\n * @returns {number}\n */\n getCpuUsagePercent = () => {\n try {\n const stat = fs.readFileSync('/sys/fs/cgroup/cpu.stat', 'utf-8')\n const match = stat.match(/usage_usec (\\d+)/)\n if (!match) return 0\n\n const now = Date.now()\n const currentUsage = parseInt(match[1], 10)\n\n if (this._lastUsageMicros === 0) {\n this._lastUsageMicros = currentUsage\n this._lastCheckTime = now\n return 0\n }\n\n const deltaUsage = currentUsage - this._lastUsageMicros\n const deltaTime = now - this._lastCheckTime\n\n this._lastUsageMicros = currentUsage\n this._lastCheckTime = now\n\n return (deltaUsage / (deltaTime * 1000)) * 100\n } catch {\n return 0\n }\n }\n\n /**\n * Get available CPU cores.\n * @returns {number}\n */\n getAvailableCPUs() {\n try {\n const cpuMaxPath = '/sys/fs/cgroup/cpu.max'\n if (fs.existsSync(cpuMaxPath)) {\n const [quotaStr, periodStr] = fs\n .readFileSync(cpuMaxPath, 'utf8')\n .trim()\n .split(' ')\n if (quotaStr === 'max') return os.cpus().length\n return parseInt(quotaStr, 10) / parseInt(periodStr, 10)\n }\n return os.cpus().length\n } catch {\n return 1\n }\n }\n\n /**\n * Get container memory usage in bytes.\n * @returns {number}\n */\n getContainerMemoryUsage() {\n try {\n return parseInt(\n fs.readFileSync('/sys/fs/cgroup/memory.current', 'utf-8').trim(),\n 10\n )\n } catch {\n return process.memoryUsage().rss\n }\n }\n\n /**\n * Get container memory limit in bytes.\n * @returns {number}\n */\n getContainerMemoryLimit() {\n try {\n const path = '/sys/fs/cgroup/memory.max'\n if (fs.existsSync(path)) {\n const val = fs.readFileSync(path, 'utf-8').trim()\n if (val !== 'max') {\n const parsed = parseInt(val, 10)\n if (parsed && parsed < os.totalmem()) return parsed\n }\n }\n return os.totalmem()\n } catch {\n return os.totalmem()\n }\n }\n\n /**\n * Measure event loop lag in ms.\n * @returns {Promise<number>}\n */\n measureLag() {\n return new Promise(resolve => {\n const start = Date.now()\n setImmediate(() => resolve(Date.now() - start))\n })\n }\n\n /**\n * Express middleware to count HTTP requests.\n * Increments the `app_http_requests_total` counter.\n */\n countHttpRequestMiddleware = (req, res, next) => {\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 ''\n\n this.counters?.app_http_requests_total?.triggerFn({\n method: req.method,\n route,\n status_code: res.statusCode,\n appId,\n databaseId,\n duration: Date.now() - start,\n requestSize: req.headers['content-length']\n ? parseInt(req.headers['content-length'], 10)\n : 0,\n })\n })\n\n next()\n }\n\n /**\n * Push all gauges and counters to PushGateway and optionally log.\n */\n pushMetrics = async () => {\n try {\n for (const [name, updateFn] of Object.entries(this.gaugeUpdaters)) {\n try {\n if (!updateFn) {\n return\n }\n const result = updateFn()\n const val = result instanceof Promise ? await result : result\n if (val !== undefined) this.gauges[name].set(this.defaultLabels, val)\n } catch (err) {\n console.error(\n `${this.prefixLogs} Failed to update gauge ${name}:`,\n err\n )\n }\n }\n\n await this.gatewayPush({\n jobName: this.appName,\n groupings: { process_type: this.processType, instance: this.dynoId },\n })\n\n Object.values(this.counters).forEach(counter => counter.reset())\n\n if (this.logValues) {\n const metrics = await this.registry.getMetricsAsJSON()\n console.log(\n `${this.prefixLogs} Metrics:\\n`,\n JSON.stringify(metrics, null, 2)\n )\n }\n } catch (err) {\n console.error(`${this.prefixLogs} Failed to push metrics:`, err)\n }\n }\n\n /**\n * Start periodic metrics collection + push.\n *\n * @param {number} [interval] Interval in seconds\n * @param {function(): void|Promise<void>} [customPushMetics]\n * Optional custom push function. If provided, Prometheus push is skipped.\n */\n startPush = (interval = this.intervalSec, customPushMetics = () => {}) => {\n if (!this.enabled) {\n console.warn(`${this.prefixLogs} Metrics disabled`)\n return\n }\n\n if (this.startupValidation && !this.startupValidation()) {\n return\n }\n\n if (customPushMetics) {\n setInterval(customPushMetics, interval * 1000)\n } else {\n setInterval(() => {\n this.pushMetrics().catch(err => {\n console.error(`${this.prefixLogs} Failed to push metrics:`, err)\n })\n }, interval * 1000)\n }\n\n console.warn(\n `${this.prefixLogs} Metrics collection started. (interval: ${this.intervalSec}s)`\n )\n }\n\n cleanup = async () => {\n if (this.enabled) {\n await this.gatewayDelete()\n }\n process.exit(0)\n }\n\n /**\n * Remove old/stale dyno/instance metrics from PushGateway.\n *\n * Compares existing PushGateway metrics for this job and deletes any instances\n * that do not match the current dynoId.\n *\n * @param {boolean} removeAllJobs If true, performs cleanup; otherwise does nothing\n * @returns {Promise<void>}\n * @private\n */\n _clearOldWorkers = async removeAllJobs => {\n if (!removeAllJobs) return\n\n try {\n const url = `${this.pushgatewayUrl}/metrics`\n const res = await fetch(url, {\n headers: {\n Authorization: `Basic ${this.authToken}`,\n Accept: 'text/plain',\n },\n })\n\n if (!res.ok) {\n console.error(\n `${this.prefixLogs} Failed to fetch metrics: ${res.status}`\n )\n return\n }\n\n const text = await res.text()\n\n const regex = new RegExp(\n `(?:instance=\"([^\"]+)\".*job=\"${this.appName}\"|job=\"${this.appName}\".*instance=\"([^\"]+)\")`,\n 'g'\n )\n const instances = new Set()\n let match\n // eslint-disable-next-line no-cond-assign\n while ((match = regex.exec(text)) !== null) {\n const instance = match[1] || match[2]\n if (instance && instance !== this.dynoId) instances.add(instance)\n }\n\n if (instances.size === 0) {\n console.log(`${this.prefixLogs} No old dynos to delete.`)\n return\n }\n\n for (const instance of instances) {\n await this.gatewayDelete({\n groupings: {\n instance,\n },\n })\n console.log(\n `${this.prefixLogs} Deleted metrics for old dyno: ${instance}`\n )\n }\n\n console.log(\n `${this.prefixLogs} Cleared all old instances for job ${this.appName}`\n )\n } catch (err) {\n console.error(`${this.prefixLogs} Error deleting old metrics:`, err)\n }\n }\n\n /**\n * Delete metrics for this job/instance from PushGateway.\n *\n * @param {Object} [params]\n * @param {string} [params.jobName] Job name (defaults to appName)\n * @param {Object} [params.groupings] Grouping labels\n * @param {string} [params.groupings.process_type] Process type label\n * @param {string} [params.groupings.instance] Instance/dyno ID\n * @returns {Promise<void>}\n */\n gatewayDelete = async (params = {}) => {\n return this.gateway.delete({\n jobName: params.jobName || this.appName,\n groupings: {\n process_type: params.groupings?.process_type || this.processType,\n instance: params.groupings?.instance || this.dynoId,\n },\n })\n }\n\n /**\n * Push metrics to PushGateway.\n *\n * @param {object} [params]\n * @param {string} [params.jobName]\n * @param {object} [params.groupings]\n * @returns {Promise<void>}\n */\n gatewayPush = async (params = {}) => {\n return this.gateway.push({\n jobName: params.jobName || this.appName,\n groupings: {\n process_type: params.groupings?.process_type || this.processType,\n instance: params.groupings?.instance || this.dynoId,\n },\n })\n }\n\n /**\n * Merge the default metric labels (`app`, `dyno_id`, `process_type`)\n * with custom label names.\n *\n * @param {string[]} labels Additional label names\n * @returns {string[]} Combined label names\n */\n withDefaultLabels = (labels = []) => {\n return [...Object.keys(this.defaultLabels), labels]\n }\n\n getDefaultLabels = (labels = []) => {\n return this.defaultLabels\n }\n\n _setCleanupHandlers = () => {\n process.on('SIGINT', this.cleanup)\n process.on('SIGTERM', this.cleanup)\n }\n\n // GETTERS\n\n get metricsEnabled() {\n return this.enabled\n }\n\n get metricsLogValues() {\n return this.logValues\n }\n\n get registry() {\n return this.registry\n }\n}\n\nmodule.exports = { MetricsClient }\n"],"mappings":"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","ignoreList":[]}
|
package/package.json
CHANGED
package/src/metricsClient.js
CHANGED
|
@@ -20,6 +20,8 @@ class MetricsClient {
|
|
|
20
20
|
* @param {string} [config.pushgatewayPassword] PushGateway password
|
|
21
21
|
* @param {number} [config.intervalSec] Interval in seconds for pushing metrics
|
|
22
22
|
* @param {boolean} [config.removeAllJobs] Enable to clear all jobs at startup
|
|
23
|
+
* @param {boolean} [config.scripDefaultMetrics] Enable to scip default metrics creation
|
|
24
|
+
* @param {function} [config.startupValidation] Add to validate on start push.
|
|
23
25
|
*/
|
|
24
26
|
constructor(config = {}) {
|
|
25
27
|
this.appName =
|
|
@@ -44,6 +46,7 @@ class MetricsClient {
|
|
|
44
46
|
config.intervalSec ||
|
|
45
47
|
parseInt(process.env.METRICS_INTERVAL_SEC || '', 10) ||
|
|
46
48
|
15
|
|
49
|
+
this.startupValidation = config.startupValidation
|
|
47
50
|
|
|
48
51
|
this.prefixLogs = `[${this.processType}] [${this.appName}] [${this.dynoId}] [Monitoring]`
|
|
49
52
|
|
|
@@ -73,55 +76,62 @@ class MetricsClient {
|
|
|
73
76
|
|
|
74
77
|
/** @type {Object<string, function(): number | Promise<number>>} */
|
|
75
78
|
this.gaugeUpdaters = {}
|
|
79
|
+
this.customsMetricsTracking = {}
|
|
76
80
|
this._lastUsageMicros = 0
|
|
77
81
|
this._lastCheckTime = Date.now()
|
|
78
82
|
this._clearOldWorkers(config.removeAllJobs).then(_ => {
|
|
79
|
-
|
|
83
|
+
if (config.scripDefaultMetrics) {
|
|
84
|
+
this._initDefaultMetrics()
|
|
85
|
+
}
|
|
80
86
|
this._setCleanupHandlers()
|
|
81
87
|
})
|
|
82
88
|
}
|
|
83
89
|
|
|
84
90
|
/**
|
|
85
|
-
* Register default
|
|
91
|
+
* Register all built-in default Gauges and Counters.
|
|
86
92
|
* @private
|
|
87
93
|
*/
|
|
88
94
|
_initDefaultMetrics = () => {
|
|
89
|
-
this.createGauge(
|
|
90
|
-
'app_process_cpu_usage_percent',
|
|
91
|
-
'Current CPU usage of the Node.js process in percent',
|
|
92
|
-
this.getCpuUsagePercent
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
this
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
'
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
)
|
|
95
|
+
this.createGauge({
|
|
96
|
+
name: 'app_process_cpu_usage_percent',
|
|
97
|
+
help: 'Current CPU usage of the Node.js process in percent',
|
|
98
|
+
updateFn: this.getCpuUsagePercent,
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
this.createGauge({
|
|
102
|
+
name: 'app_available_cpu_count',
|
|
103
|
+
help: 'How many CPU cores are available to this process',
|
|
104
|
+
updateFn: this.getAvailableCPUs,
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
this.createGauge({
|
|
108
|
+
name: 'app_container_memory_usage_bytes',
|
|
109
|
+
help: 'Current container RAM usage from cgroup',
|
|
110
|
+
updateFn: this.getContainerMemoryUsage,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
this.createGauge({
|
|
114
|
+
name: 'app_event_loop_lag_ms',
|
|
115
|
+
help: 'Estimated event loop lag in milliseconds',
|
|
116
|
+
updateFn: this.measureLag,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
this.createGauge({
|
|
120
|
+
name: 'app_container_memory_limit_bytes',
|
|
121
|
+
help: 'Max RAM available to container from cgroup (memory.max)',
|
|
122
|
+
updateFn: this.getContainerMemoryLimit,
|
|
123
|
+
})
|
|
119
124
|
|
|
120
|
-
this.
|
|
121
|
-
'
|
|
122
|
-
'
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
this.createGauge({
|
|
126
|
+
name: 'app_uptime_seconds',
|
|
127
|
+
help: 'How long the process has been running',
|
|
128
|
+
updateFn: process.uptime,
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
this.createCounter({
|
|
132
|
+
name: 'app_http_requests_total',
|
|
133
|
+
help: 'Total number of HTTP requests handled by this process',
|
|
134
|
+
labelNames: this.withDefaultLabels([
|
|
125
135
|
'method',
|
|
126
136
|
'route',
|
|
127
137
|
'appId',
|
|
@@ -129,8 +139,8 @@ class MetricsClient {
|
|
|
129
139
|
'duration',
|
|
130
140
|
'requestSize',
|
|
131
141
|
'status_code',
|
|
132
|
-
]
|
|
133
|
-
)
|
|
142
|
+
]),
|
|
143
|
+
})
|
|
134
144
|
}
|
|
135
145
|
|
|
136
146
|
/**
|
|
@@ -141,12 +151,12 @@ class MetricsClient {
|
|
|
141
151
|
* @param {string[]} [labelNames]
|
|
142
152
|
* @returns {client.Gauge}
|
|
143
153
|
*/
|
|
144
|
-
createGauge = (
|
|
154
|
+
createGauge = ({
|
|
145
155
|
name,
|
|
146
156
|
help,
|
|
147
157
|
updateFn,
|
|
148
|
-
labelNames = Object.keys(this.defaultLabels)
|
|
149
|
-
) => {
|
|
158
|
+
labelNames = Object.keys(this.defaultLabels),
|
|
159
|
+
}) => {
|
|
150
160
|
if (this.gauges[name]) return this.gauges[name]
|
|
151
161
|
|
|
152
162
|
const g = new client.Gauge({
|
|
@@ -157,20 +167,27 @@ class MetricsClient {
|
|
|
157
167
|
})
|
|
158
168
|
this.gauges[name] = g
|
|
159
169
|
|
|
160
|
-
if (typeof updateFn === 'function')
|
|
170
|
+
if (updateFn && typeof updateFn === 'function') {
|
|
171
|
+
this.gaugeUpdaters[name] = updateFn
|
|
172
|
+
}
|
|
161
173
|
|
|
162
174
|
return g
|
|
163
175
|
}
|
|
164
176
|
|
|
165
177
|
/**
|
|
166
|
-
* Create a
|
|
167
|
-
*
|
|
168
|
-
* @param {
|
|
169
|
-
* @param {string}
|
|
170
|
-
* @param {string
|
|
171
|
-
* @
|
|
178
|
+
* Create a Prometheus Counter metric.
|
|
179
|
+
*
|
|
180
|
+
* @param {object} params
|
|
181
|
+
* @param {string} params.name Metric name
|
|
182
|
+
* @param {string} params.help Metric description
|
|
183
|
+
* @param {string[]} [params.labelNames]
|
|
184
|
+
* Optional list of label names. Defaults to this.defaultLabels keys.
|
|
185
|
+
*
|
|
186
|
+
* @returns {function(data?: object, value?: number): void}
|
|
187
|
+
* A trigger function to increment the counter:
|
|
188
|
+
* triggerFn(labels?, incrementValue?)
|
|
172
189
|
*/
|
|
173
|
-
createCounter(name, help, labelNames = Object.keys(this.defaultLabels)) {
|
|
190
|
+
createCounter({ name, help, labelNames = Object.keys(this.defaultLabels) }) {
|
|
174
191
|
if (this.counters[name]) return this.counters[name].triggerFn
|
|
175
192
|
|
|
176
193
|
const c = new client.Counter({
|
|
@@ -326,6 +343,9 @@ class MetricsClient {
|
|
|
326
343
|
try {
|
|
327
344
|
for (const [name, updateFn] of Object.entries(this.gaugeUpdaters)) {
|
|
328
345
|
try {
|
|
346
|
+
if (!updateFn) {
|
|
347
|
+
return
|
|
348
|
+
}
|
|
329
349
|
const result = updateFn()
|
|
330
350
|
const val = result instanceof Promise ? await result : result
|
|
331
351
|
if (val !== undefined) this.gauges[name].set(this.defaultLabels, val)
|
|
@@ -337,7 +357,7 @@ class MetricsClient {
|
|
|
337
357
|
}
|
|
338
358
|
}
|
|
339
359
|
|
|
340
|
-
await this.
|
|
360
|
+
await this.gatewayPush({
|
|
341
361
|
jobName: this.appName,
|
|
342
362
|
groupings: { process_type: this.processType, instance: this.dynoId },
|
|
343
363
|
})
|
|
@@ -357,36 +377,54 @@ class MetricsClient {
|
|
|
357
377
|
}
|
|
358
378
|
|
|
359
379
|
/**
|
|
360
|
-
* Start
|
|
380
|
+
* Start periodic metrics collection + push.
|
|
381
|
+
*
|
|
361
382
|
* @param {number} [interval] Interval in seconds
|
|
383
|
+
* @param {function(): void|Promise<void>} [customPushMetics]
|
|
384
|
+
* Optional custom push function. If provided, Prometheus push is skipped.
|
|
362
385
|
*/
|
|
363
|
-
startPush = (interval = this.intervalSec) => {
|
|
386
|
+
startPush = (interval = this.intervalSec, customPushMetics = () => {}) => {
|
|
364
387
|
if (!this.enabled) {
|
|
365
388
|
console.warn(`${this.prefixLogs} Metrics disabled`)
|
|
389
|
+
return
|
|
366
390
|
}
|
|
367
391
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
})
|
|
372
|
-
}, interval * 1000)
|
|
392
|
+
if (this.startupValidation && !this.startupValidation()) {
|
|
393
|
+
return
|
|
394
|
+
}
|
|
373
395
|
|
|
374
|
-
|
|
396
|
+
if (customPushMetics) {
|
|
397
|
+
setInterval(customPushMetics, interval * 1000)
|
|
398
|
+
} else {
|
|
399
|
+
setInterval(() => {
|
|
400
|
+
this.pushMetrics().catch(err => {
|
|
401
|
+
console.error(`${this.prefixLogs} Failed to push metrics:`, err)
|
|
402
|
+
})
|
|
403
|
+
}, interval * 1000)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
console.warn(
|
|
407
|
+
`${this.prefixLogs} Metrics collection started. (interval: ${this.intervalSec}s)`
|
|
408
|
+
)
|
|
375
409
|
}
|
|
376
410
|
|
|
377
411
|
cleanup = async () => {
|
|
378
412
|
if (this.enabled) {
|
|
379
|
-
await this.
|
|
380
|
-
jobName: this.appName,
|
|
381
|
-
groupings: {
|
|
382
|
-
process_type: this.processType,
|
|
383
|
-
instance: this.dynoId,
|
|
384
|
-
},
|
|
385
|
-
})
|
|
413
|
+
await this.gatewayDelete()
|
|
386
414
|
}
|
|
387
415
|
process.exit(0)
|
|
388
416
|
}
|
|
389
417
|
|
|
418
|
+
/**
|
|
419
|
+
* Remove old/stale dyno/instance metrics from PushGateway.
|
|
420
|
+
*
|
|
421
|
+
* Compares existing PushGateway metrics for this job and deletes any instances
|
|
422
|
+
* that do not match the current dynoId.
|
|
423
|
+
*
|
|
424
|
+
* @param {boolean} removeAllJobs If true, performs cleanup; otherwise does nothing
|
|
425
|
+
* @returns {Promise<void>}
|
|
426
|
+
* @private
|
|
427
|
+
*/
|
|
390
428
|
_clearOldWorkers = async removeAllJobs => {
|
|
391
429
|
if (!removeAllJobs) return
|
|
392
430
|
|
|
@@ -426,10 +464,8 @@ class MetricsClient {
|
|
|
426
464
|
}
|
|
427
465
|
|
|
428
466
|
for (const instance of instances) {
|
|
429
|
-
await this.
|
|
430
|
-
jobName: this.appName,
|
|
467
|
+
await this.gatewayDelete({
|
|
431
468
|
groupings: {
|
|
432
|
-
process_type: this.processType,
|
|
433
469
|
instance,
|
|
434
470
|
},
|
|
435
471
|
})
|
|
@@ -446,10 +482,77 @@ class MetricsClient {
|
|
|
446
482
|
}
|
|
447
483
|
}
|
|
448
484
|
|
|
485
|
+
/**
|
|
486
|
+
* Delete metrics for this job/instance from PushGateway.
|
|
487
|
+
*
|
|
488
|
+
* @param {Object} [params]
|
|
489
|
+
* @param {string} [params.jobName] Job name (defaults to appName)
|
|
490
|
+
* @param {Object} [params.groupings] Grouping labels
|
|
491
|
+
* @param {string} [params.groupings.process_type] Process type label
|
|
492
|
+
* @param {string} [params.groupings.instance] Instance/dyno ID
|
|
493
|
+
* @returns {Promise<void>}
|
|
494
|
+
*/
|
|
495
|
+
gatewayDelete = async (params = {}) => {
|
|
496
|
+
return this.gateway.delete({
|
|
497
|
+
jobName: params.jobName || this.appName,
|
|
498
|
+
groupings: {
|
|
499
|
+
process_type: params.groupings?.process_type || this.processType,
|
|
500
|
+
instance: params.groupings?.instance || this.dynoId,
|
|
501
|
+
},
|
|
502
|
+
})
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Push metrics to PushGateway.
|
|
507
|
+
*
|
|
508
|
+
* @param {object} [params]
|
|
509
|
+
* @param {string} [params.jobName]
|
|
510
|
+
* @param {object} [params.groupings]
|
|
511
|
+
* @returns {Promise<void>}
|
|
512
|
+
*/
|
|
513
|
+
gatewayPush = async (params = {}) => {
|
|
514
|
+
return this.gateway.push({
|
|
515
|
+
jobName: params.jobName || this.appName,
|
|
516
|
+
groupings: {
|
|
517
|
+
process_type: params.groupings?.process_type || this.processType,
|
|
518
|
+
instance: params.groupings?.instance || this.dynoId,
|
|
519
|
+
},
|
|
520
|
+
})
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Merge the default metric labels (`app`, `dyno_id`, `process_type`)
|
|
525
|
+
* with custom label names.
|
|
526
|
+
*
|
|
527
|
+
* @param {string[]} labels Additional label names
|
|
528
|
+
* @returns {string[]} Combined label names
|
|
529
|
+
*/
|
|
530
|
+
withDefaultLabels = (labels = []) => {
|
|
531
|
+
return [...Object.keys(this.defaultLabels), labels]
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
getDefaultLabels = (labels = []) => {
|
|
535
|
+
return this.defaultLabels
|
|
536
|
+
}
|
|
537
|
+
|
|
449
538
|
_setCleanupHandlers = () => {
|
|
450
539
|
process.on('SIGINT', this.cleanup)
|
|
451
540
|
process.on('SIGTERM', this.cleanup)
|
|
452
541
|
}
|
|
542
|
+
|
|
543
|
+
// GETTERS
|
|
544
|
+
|
|
545
|
+
get metricsEnabled() {
|
|
546
|
+
return this.enabled
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
get metricsLogValues() {
|
|
550
|
+
return this.logValues
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
get registry() {
|
|
554
|
+
return this.registry
|
|
555
|
+
}
|
|
453
556
|
}
|
|
454
557
|
|
|
455
558
|
module.exports = { MetricsClient }
|