@adalo/metrics 0.0.0-staging.1 → 0.0.0-staging.10

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/README.md CHANGED
@@ -117,4 +117,4 @@ Environment variables:
117
117
  secret env was created as
118
118
  ```js
119
119
  Buffer.from(`${username}:${password}`).toString('base64')
120
- ```
120
+ ```
@@ -1 +1 @@
1
- {"version":3,"file":"baseMetricsClient.d.ts","sourceRoot":"","sources":["../../src/metrics/baseMetricsClient.js"],"names":[],"mappings":"AAKA;;;;GAIG;AACH;IACE;;;;;;;;;;;;;OAaG;IACH;QAZ2B,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QACf,iBAAiB;QAClB,kBAAkB;OAoD7C;IAjDC,gBAA4E;IAC5E,eAAqE;IACrE,oBAG6B;IAC7B,iBAAuE;IACvE,mBAC+D;IAC/D,uBACoE;IACpE,kBAC0E;IAC1E,oBAGI;IACJ,wCAAiD;IACjD,4BAEoD;IACpD,0BAEmD;IAEnD,mBAAyF;IAEzF,uEAAsC;IAGtC;;;;MAIC;IAGD,aAAmB;IACnB,WAAgB;IAChB,aAAkB;IAClB,sBAA2B;IAE3B,mEAAmE;IACnE;YADkB,MAAM,SAAc,MAAM,GAAG,QAAQ,MAAM,CAAC;MACvC;IAKvB,sCAAwC;IAG1C;;;;;;;;OAQG;IACH;QAN2B,IAAI,EAApB,MAAM;QACU,IAAI,EAApB,MAAM;QACuC,QAAQ,UAAzC,MAAM,GAAC,QAAQ,MAAM,CAAC;QACf,UAAU;UAC3B,OAAO,aAAa,EAAE,KAAK,CAuBvC;IAED;;;;;;;;;;;OAWG;IACH;QAR0B,IAAI,EAAnB,MAAM;QACS,IAAI,EAAnB,MAAM;QACY,UAAU;kBAEhB,MAAM,mBAAmB,MAAM,KAAK,IAAI,CAuB9D;IAED;;OAEG;IACH,6BAKC;IAED;;OAEG;IACH,kCAiCC;IAED,sEAmDC;IA/CK,+CAA0D;IAiDhE,iCAEC;IAED;;;;;;;;;OASG;IACH,iFAEC;IAED;;;OAGG;IACH,eAFa,QAAQ,IAAI,CAAC,CAOzB;IAED;;;;;;;;;OASG;IACH,yBAEC;IAED;;;OAGG;IACH,qBAFa,QAAQ,IAAI,CAAC,CAezB;IAED;;;OAGG;IACH,8BAkDC;IAED;;;;;OAKG;IACH,8CAFa,QAAQ,IAAI,CAAC,CAgBzB;IAED;;;OAGG;IACH,uBAoDC;IAED;;;;;;OAMG;IACH,6BAHW,MAAM,EAAE,KACN,MAAM,EAAE,CAIpB;IAED;;;;MAEC;IAED,gCAGC;IAID,8BAEC;IAED,gCAEC;IAED,4EAEC;IAED,sCAEC;IAED,2DAWC;CACF"}
1
+ {"version":3,"file":"baseMetricsClient.d.ts","sourceRoot":"","sources":["../../src/metrics/baseMetricsClient.js"],"names":[],"mappings":"AAKA;;;;GAIG;AACH;IACE;;;;;;;;;;;;;OAaG;IACH;QAZ2B,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QACf,iBAAiB;QAClB,kBAAkB;OAoD7C;IAjDC,gBAA4E;IAC5E,eAAqE;IACrE,oBAG6B;IAC7B,iBAAuE;IACvE,mBAC+D;IAC/D,uBACoE;IACpE,kBAC0E;IAC1E,oBAGI;IACJ,wCAAiD;IACjD,4BAEoD;IACpD,0BAEmD;IAEnD,mBAAyF;IAEzF,uEAAsC;IAGtC;;;;MAIC;IAGD,aAAmB;IACnB,WAAgB;IAChB,aAAkB;IAClB,sBAA2B;IAE3B,mEAAmE;IACnE;YADkB,MAAM,SAAc,MAAM,GAAG,QAAQ,MAAM,CAAC;MACvC;IAKvB,sCAAwC;IAG1C;;;;;;;;OAQG;IACH;QAN2B,IAAI,EAApB,MAAM;QACU,IAAI,EAApB,MAAM;QACuC,QAAQ,UAAzC,MAAM,GAAC,QAAQ,MAAM,CAAC;QACf,UAAU;UAC3B,OAAO,aAAa,EAAE,KAAK,CAuBvC;IAED;;;;;;;;;;;OAWG;IACH;QAR0B,IAAI,EAAnB,MAAM;QACS,IAAI,EAAnB,MAAM;QACY,UAAU;kBAEhB,MAAM,mBAAmB,MAAM,KAAK,IAAI,CAuB9D;IAED;;OAEG;IACH,6BAKC;IAED;;OAEG;IACH,kCAiCC;IAED,sEAoEC;IAhEK,+CAA0D;IAkEhE,iCAEC;IAED;;;;;;;;;OASG;IACH,iFAEC;IAED;;;OAGG;IACH,eAFa,QAAQ,IAAI,CAAC,CAOzB;IAED;;;;;;;;;OASG;IACH,yBAEC;IAED;;;OAGG;IACH,qBAFa,QAAQ,IAAI,CAAC,CAezB;IAED;;;OAGG;IACH,8BAkDC;IAED;;;;;OAKG;IACH,8CAFa,QAAQ,IAAI,CAAC,CAgBzB;IAED;;;OAGG;IACH,uBAoDC;IAED;;;;;;OAMG;IACH,6BAHW,MAAM,EAAE,KACN,MAAM,EAAE,CAIpB;IAED;;;;MAEC;IAED,gCAGC;IAID,8BAEC;IAED,gCAEC;IAED,4EAEC;IAED,sCAEC;IAED,2DAWC;CACF"}
@@ -181,38 +181,50 @@ class BaseMetricsClient {
181
181
  clearInterval(this._idleInterval);
182
182
  this._idleInterval = null;
183
183
  }
184
- if (this.startupValidation && !this.startupValidation()) {
185
- return;
186
- }
187
- const runPush = () => {
184
+ const doStart = () => {
185
+ const runPush = () => {
186
+ if (customPushMetics && typeof customPushMetics === 'function') {
187
+ return Promise.resolve(customPushMetics());
188
+ }
189
+ return this.pushMetrics();
190
+ };
188
191
  if (customPushMetics && typeof customPushMetics === 'function') {
189
- return Promise.resolve(customPushMetics());
192
+ setInterval(() => customPushMetics(), interval * 1000);
193
+ } else {
194
+ setInterval(() => {
195
+ runPush().catch(err => {
196
+ console.error(`${this.prefixLogs} Failed to push metrics:`, err);
197
+ });
198
+ }, interval * 1000);
199
+ }
200
+
201
+ // First push immediately so metrics appear without waiting for the first interval
202
+ runPush().catch(err => {
203
+ console.error(`${this.prefixLogs} Failed to push metrics (initial):`, err);
204
+ });
205
+ let pushOrigin = 'none';
206
+ try {
207
+ if (this.pushgatewayUrl && this.pushgatewayUrl.trim()) {
208
+ pushOrigin = new URL(this.pushgatewayUrl.trim()).origin;
209
+ }
210
+ } catch {
211
+ pushOrigin = 'invalid URL';
190
212
  }
191
- return this.pushMetrics();
213
+ console.warn(`${this.prefixLogs} Metrics collection started. (interval: ${this.intervalSec}s, push: ${pushOrigin})`);
192
214
  };
193
- if (customPushMetics && typeof customPushMetics === 'function') {
194
- setInterval(() => customPushMetics(), interval * 1000);
195
- } else {
196
- setInterval(() => {
197
- runPush().catch(err => {
198
- console.error(`${this.prefixLogs} Failed to push metrics:`, err);
215
+ if (this.startupValidation) {
216
+ const result = this.startupValidation();
217
+ if (result != null && typeof result.then === 'function') {
218
+ result.then(ok => {
219
+ if (ok !== false) doStart();
220
+ }).catch(err => {
221
+ console.error(`${this.prefixLogs} Startup validation failed:`, err);
199
222
  });
200
- }, interval * 1000);
201
- }
202
-
203
- // First push immediately so metrics appear without waiting for the first interval
204
- runPush().catch(err => {
205
- console.error(`${this.prefixLogs} Failed to push metrics (initial):`, err);
206
- });
207
- let pushOrigin = 'none';
208
- try {
209
- if (this.pushgatewayUrl && this.pushgatewayUrl.trim()) {
210
- pushOrigin = new URL(this.pushgatewayUrl.trim()).origin;
223
+ return;
211
224
  }
212
- } catch {
213
- pushOrigin = 'invalid URL';
225
+ if (result === false) return;
214
226
  }
215
- console.warn(`${this.prefixLogs} Metrics collection started. (interval: ${this.intervalSec}s, push: ${pushOrigin})`);
227
+ doStart();
216
228
  };
217
229
  pushMetrics = async () => {
218
230
  return this._pushMetrics();
@@ -1 +1 @@
1
- {"version":3,"file":"baseMetricsClient.js","names":["client","require","https","http","URL","BaseMetricsClient","constructor","config","appName","process","env","BUILD_APP_NAME","dynoId","HOSTNAME","processType","BUILD_DYNO_PROCESS_TYPE","enabled","METRICS_ENABLED","logValues","METRICS_LOG_VALUES","pushgatewayUrl","METRICS_PUSHGATEWAY_URL","authToken","pushgatewaySecret","METRICS_PUSHGATEWAY_SECRET","intervalSec","parseInt","METRICS_INTERVAL_SEC","startupValidation","disablePushgateway","METRICS_DISABLE_PUSHGATEWAY","removeOldMetrics","METRICS_REMOVE_OLD_METRICS","prefixLogs","_registry","Registry","collectDefaultMetrics","register","defaultLabels","app","dyno_id","process_type","gateway","gauges","counters","countersFunctions","gaugeUpdaters","_clearOldWorkers","_setCleanupHandlers","keepProcessAliveWhenDisabled","createGauge","name","help","updateFn","labelNames","Object","keys","g","Gauge","registers","createCounter","c","Counter","data","value","inc","clearAllCounters","metricsLogValues","console","log","values","forEach","counter","reset","_pushMetrics","entries","result","val","Promise","undefined","set","err","error","gatewayPush","metrics","getMetricsAsJSON","JSON","stringify","_startPush","interval","customPushMetics","warn","_idleInterval","setInterval","clearInterval","runPush","resolve","pushMetrics","catch","pushOrigin","trim","origin","startPush","cleanup","gatewayDelete","exit","_deleteFromVMByLabels","message","esc","s","String","replace","selector","u","reject","Error","path","encodeURIComponent","req","protocol","request","hostname","port","method","headers","Authorization","agent","Agent","keepAlive","res","statusCode","on","chunk","end","params","_pushToVMAgent","pushUrl","pathname","search","contentType","then","setHeader","Buffer","byteLength","withDefaultLabels","labels","getDefaultLabels","metricsEnabled","registry","getMetricsAsString","metricsMiddleware","status","module","exports"],"sources":["../../src/metrics/baseMetricsClient.js"],"sourcesContent":["const client = require('prom-client')\nconst https = require('https')\nconst http = require('http')\nconst { URL } = require('url')\n\n/**\n * BaseMetricsClient provides common functionality for all metrics clients.\n * Handles registry setup, push to remote (VM-agent), default labels, and common operations.\n * Always pushes registry to the configured URL (POST Prometheus text format + Basic auth). No Pushgateway.\n */\nclass BaseMetricsClient {\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] Push URL (VM-agent import endpoint, e.g. .../api/v1/import/prometheus). /metrics is for GET (scrape), not POST (push).\n * @param {string} [config.pushgatewaySecret] Basic auth secret (Base64 of user:password)\n * @param {number} [config.intervalSec] Interval in seconds for pushing metrics\n * @param {boolean} [config.removeOldMetrics] Enable to clear metrics by service name\n * @param {function} [config.startupValidation] Add to validate on start push.\n * @param {boolean} [config.disablePushgateway] Disable pushing to Pushgateway (use HTTP scraping instead)\n */\n constructor(config = {}) {\n this.appName = config.appName || process.env.BUILD_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.authToken =\n config.pushgatewaySecret || process.env.METRICS_PUSHGATEWAY_SECRET || ''\n this.intervalSec =\n config.intervalSec ||\n parseInt(process.env.METRICS_INTERVAL_SEC || '', 10) ||\n 15\n this.startupValidation = config.startupValidation\n this.disablePushgateway =\n config.disablePushgateway ??\n process.env.METRICS_DISABLE_PUSHGATEWAY === 'true'\n this.removeOldMetrics =\n config.removeOldMetrics ??\n process.env.METRICS_REMOVE_OLD_METRICS === 'true'\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 // Always push to configured URL (VM-agent). No Pushgateway.\n this.gateway = null\n this.gauges = {}\n this.counters = {}\n this.countersFunctions = {}\n\n /** @type {Object<string, function(): number | Promise<number>>} */\n this.gaugeUpdaters = {}\n\n this._clearOldWorkers(config.removeOldMetrics)\n this._setCleanupHandlers()\n\n this.keepProcessAliveWhenDisabled = true\n }\n\n /**\n * Create a gauge metric.\n * @param {Object} options - Gauge configuration\n * @param {string} options.name - Name of the gauge\n * @param {string} options.help - Help text describing the gauge\n * @param {function(): number|Promise<number>} [options.updateFn] - Optional function returning the gauge value\n * @param {string[]} [options.labelNames] - Optional custom label names\n * @returns {import('prom-client').Gauge} The created Prometheus 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 - Counter configuration\n * @param {string} params.name - Metric name\n * @param {string} params.help - Metric description\n * @param {string[]} [params.labelNames] - Optional list of label names. Defaults to this.defaultLabels keys.\n *\n * @returns {(labels?: Object, incrementValue?: number) => void}\n * A function to increment the counter.\n * Usage: (labels?, incrementValue?)\n */\n createCounter({ name, help, labelNames = Object.keys(this.defaultLabels) }) {\n if (this.counters[name]) return this.countersFunctions[name]\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 this.countersFunctions = {\n ...this.countersFunctions,\n [name]: (data = {}, value = 1) => {\n c.inc({ ...this.defaultLabels, ...data }, value)\n },\n }\n\n return this.countersFunctions[name]\n }\n\n /**\n * Clear all collected counters\n */\n clearAllCounters = () => {\n if (this.metricsLogValues) {\n console.log('Counters to clear: ', Object.keys(this.counters))\n }\n Object.values(this.counters).forEach(counter => counter.reset())\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 continue\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 if (!this.disablePushgateway) {\n await this.gatewayPush()\n }\n // this.clearAllCounters() //TODO: or uncommit or delete (based on grafana expectation)\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 _startPush = (interval = this.intervalSec, customPushMetics = undefined) => {\n if (!this.enabled) {\n console.warn(`${this.prefixLogs} Metrics disabled`)\n if (this.keepProcessAliveWhenDisabled && !this._idleInterval) {\n this._idleInterval = setInterval(() => {}, 60 * 60 * 1000)\n }\n return\n }\n\n if (this._idleInterval) {\n clearInterval(this._idleInterval)\n this._idleInterval = null\n }\n\n if (this.startupValidation && !this.startupValidation()) {\n return\n }\n\n const runPush = () => {\n if (customPushMetics && typeof customPushMetics === 'function') {\n return Promise.resolve(customPushMetics())\n }\n return this.pushMetrics()\n }\n\n if (customPushMetics && typeof customPushMetics === 'function') {\n setInterval(() => customPushMetics(), interval * 1000)\n } else {\n setInterval(() => {\n runPush().catch(err => {\n console.error(`${this.prefixLogs} Failed to push metrics:`, err)\n })\n }, interval * 1000)\n }\n\n // First push immediately so metrics appear without waiting for the first interval\n runPush().catch(err => {\n console.error(`${this.prefixLogs} Failed to push metrics (initial):`, err)\n })\n\n let pushOrigin = 'none'\n try {\n if (this.pushgatewayUrl && this.pushgatewayUrl.trim()) {\n pushOrigin = new URL(this.pushgatewayUrl.trim()).origin\n }\n } catch {\n pushOrigin = 'invalid URL'\n }\n console.warn(\n `${this.prefixLogs} Metrics collection started. (interval: ${this.intervalSec}s, push: ${pushOrigin})`\n )\n }\n\n pushMetrics = async () => {\n return this._pushMetrics()\n }\n\n /**\n * Start periodic metrics collection and push.\n *\n * This method wraps the internal `_startPush` method.\n * If a `customPushMetrics` function is provided, it will be executed\n * at the given interval instead of the default `pushMetrics` behavior.\n *\n * @param {number} [interval=this.intervalSec] - Interval in seconds between pushes.\n * @param {() => void | Promise<void>} [customPushMetrics] - Optional custom push function. If provided, Prometheus push is skipped.\n */\n startPush = (interval, customPushMetics = undefined) => {\n this._startPush(interval, customPushMetics)\n }\n\n /**\n * Cleanup metrics and exit process.\n * @returns {Promise<void>}\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} removeOldMetrics If true, performs cleanup; otherwise does nothing\n * @returns {Promise<void>}\n * @private\n */\n _clearOldWorkers = async removeOldMetrics => {\n // No Pushgateway; VM-agent does not support per-instance delete. Skip.\n }\n\n /**\n * On shutdown: optionally delete this instance's metrics from VictoriaMetrics (by app, dyno_id, process_type).\n * @returns {Promise<void>}\n */\n gatewayDelete = async () => {\n if (\n this.removeOldMetrics &&\n this.pushgatewayUrl &&\n this.pushgatewayUrl.trim()\n ) {\n await this._deleteFromVMByLabels().catch(err => {\n console.warn(\n `${this.prefixLogs} Deletion from VM on shutdown failed:`,\n err.message\n )\n })\n }\n }\n\n /**\n * Call VictoriaMetrics delete_series API to remove all series matching this instance's labels (app, dyno_id, process_type).\n * @private\n */\n _deleteFromVMByLabels = () => {\n const esc = s => String(s).replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')\n const selector = `{app=\"${esc(this.appName)}\",dyno_id=\"${esc(\n this.dynoId\n )}\",process_type=\"${esc(this.processType)}\"}`\n let origin\n try {\n const u = new URL((this.pushgatewayUrl || '').trim())\n origin = u.origin\n } catch {\n return Promise.reject(new Error('Invalid push URL'))\n }\n const path = `/api/v1/admin/tsdb/delete_series?match[]=${encodeURIComponent(\n selector\n )}`\n return new Promise((resolve, reject) => {\n const u = new URL(origin)\n const req = (u.protocol === 'https:' ? https : http).request(\n {\n hostname: u.hostname,\n port: u.port || (u.protocol === 'https:' ? 443 : 80),\n path,\n method: 'POST',\n headers: {\n 'Content-Length': '0',\n Authorization: this.authToken\n ? `Basic ${this.authToken}`\n : undefined,\n },\n agent:\n u.protocol === 'https:'\n ? new https.Agent({ keepAlive: false })\n : undefined,\n },\n res => {\n if (res.statusCode >= 200 && res.statusCode < 300) resolve()\n else {\n let data = ''\n res.on('data', chunk => {\n data += chunk\n })\n res.on('end', () =>\n reject(new Error(`Delete failed: ${res.statusCode} ${data}`))\n )\n }\n }\n )\n req.on('error', reject)\n req.end()\n })\n }\n\n /**\n * Push registry to configured URL (VM-agent). POST Prometheus text format + Basic auth.\n *\n * @param {object} [params] Unused; kept for API compatibility.\n * @returns {Promise<void>}\n */\n gatewayPush = async (params = {}) => {\n if (this.disablePushgateway) {\n console.warn(\n `${this.prefixLogs} Metrics push skipped: METRICS_DISABLE_PUSHGATEWAY is set`\n )\n return Promise.resolve()\n }\n if (!this.pushgatewayUrl || !this.pushgatewayUrl.trim()) {\n console.warn(\n `${this.prefixLogs} Metrics push skipped: METRICS_PUSHGATEWAY_URL is not set`\n )\n return Promise.resolve()\n }\n return this._pushToVMAgent()\n }\n\n /**\n * POST registry (Prometheus text format) to VM-agent. VM-agent accepts push at /api/v1/import/prometheus; /metrics is GET (scrape) only.\n * @private\n */\n _pushToVMAgent = () => {\n let pushUrl = (this.pushgatewayUrl || '').trim()\n try {\n const u = new URL(pushUrl)\n if (!u.pathname || u.pathname === '/' || u.pathname === '/metrics') {\n pushUrl = `${u.origin}/api/v1/import/prometheus${u.search}`\n }\n } catch {\n // leave pushUrl as-is\n }\n return new Promise((resolve, reject) => {\n const u = new URL(pushUrl)\n const req = (u.protocol === 'https:' ? https : http).request(\n {\n hostname: u.hostname,\n port: u.port || (u.protocol === 'https:' ? 443 : 80),\n path: u.pathname + u.search,\n method: 'POST',\n headers: {\n 'Content-Type': client.register.contentType,\n Authorization: this.authToken\n ? `Basic ${this.authToken}`\n : undefined,\n },\n agent:\n u.protocol === 'https:'\n ? new https.Agent({ keepAlive: true })\n : undefined,\n },\n res => {\n if (res.statusCode >= 200 && res.statusCode < 300) {\n resolve()\n } else {\n let data = ''\n res.on('data', chunk => {\n data += chunk\n })\n res.on('end', () =>\n reject(new Error(`Push failed: ${res.statusCode} ${data}`))\n )\n }\n }\n )\n req.on('error', reject)\n this._registry\n .metrics()\n .then(metrics => {\n req.setHeader('Content-Length', Buffer.byteLength(metrics, 'utf8'))\n req.end(metrics, 'utf8')\n })\n .catch(reject)\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 async getMetricsAsString() {\n return this._registry.metrics()\n }\n\n metricsMiddleware() {\n return async (req, res) => {\n try {\n const metrics = await this.getMetricsAsString()\n res.set('Content-Type', client.register.contentType)\n res.end(metrics)\n } catch (err) {\n console.error(`${this.prefixLogs} Failed to get metrics:`, err)\n res.status(500).end('Failed to collect metrics')\n }\n }\n }\n}\n\nmodule.exports = { BaseMetricsClient }\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"baseMetricsClient.js","names":["client","require","https","http","URL","BaseMetricsClient","constructor","config","appName","process","env","BUILD_APP_NAME","dynoId","HOSTNAME","processType","BUILD_DYNO_PROCESS_TYPE","enabled","METRICS_ENABLED","logValues","METRICS_LOG_VALUES","pushgatewayUrl","METRICS_PUSHGATEWAY_URL","authToken","pushgatewaySecret","METRICS_PUSHGATEWAY_SECRET","intervalSec","parseInt","METRICS_INTERVAL_SEC","startupValidation","disablePushgateway","METRICS_DISABLE_PUSHGATEWAY","removeOldMetrics","METRICS_REMOVE_OLD_METRICS","prefixLogs","_registry","Registry","collectDefaultMetrics","register","defaultLabels","app","dyno_id","process_type","gateway","gauges","counters","countersFunctions","gaugeUpdaters","_clearOldWorkers","_setCleanupHandlers","keepProcessAliveWhenDisabled","createGauge","name","help","updateFn","labelNames","Object","keys","g","Gauge","registers","createCounter","c","Counter","data","value","inc","clearAllCounters","metricsLogValues","console","log","values","forEach","counter","reset","_pushMetrics","entries","result","val","Promise","undefined","set","err","error","gatewayPush","metrics","getMetricsAsJSON","JSON","stringify","_startPush","interval","customPushMetics","warn","_idleInterval","setInterval","clearInterval","doStart","runPush","resolve","pushMetrics","catch","pushOrigin","trim","origin","then","ok","startPush","cleanup","gatewayDelete","exit","_deleteFromVMByLabels","message","esc","s","String","replace","selector","u","reject","Error","path","encodeURIComponent","req","protocol","request","hostname","port","method","headers","Authorization","agent","Agent","keepAlive","res","statusCode","on","chunk","end","params","_pushToVMAgent","pushUrl","pathname","search","contentType","setHeader","Buffer","byteLength","withDefaultLabels","labels","getDefaultLabels","metricsEnabled","registry","getMetricsAsString","metricsMiddleware","status","module","exports"],"sources":["../../src/metrics/baseMetricsClient.js"],"sourcesContent":["const client = require('prom-client')\nconst https = require('https')\nconst http = require('http')\nconst { URL } = require('url')\n\n/**\n * BaseMetricsClient provides common functionality for all metrics clients.\n * Handles registry setup, push to remote (VM-agent), default labels, and common operations.\n * Always pushes registry to the configured URL (POST Prometheus text format + Basic auth). No Pushgateway.\n */\nclass BaseMetricsClient {\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] Push URL (VM-agent import endpoint, e.g. .../api/v1/import/prometheus). /metrics is for GET (scrape), not POST (push).\n * @param {string} [config.pushgatewaySecret] Basic auth secret (Base64 of user:password)\n * @param {number} [config.intervalSec] Interval in seconds for pushing metrics\n * @param {boolean} [config.removeOldMetrics] Enable to clear metrics by service name\n * @param {function} [config.startupValidation] Add to validate on start push.\n * @param {boolean} [config.disablePushgateway] Disable pushing to Pushgateway (use HTTP scraping instead)\n */\n constructor(config = {}) {\n this.appName = config.appName || process.env.BUILD_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.authToken =\n config.pushgatewaySecret || process.env.METRICS_PUSHGATEWAY_SECRET || ''\n this.intervalSec =\n config.intervalSec ||\n parseInt(process.env.METRICS_INTERVAL_SEC || '', 10) ||\n 15\n this.startupValidation = config.startupValidation\n this.disablePushgateway =\n config.disablePushgateway ??\n process.env.METRICS_DISABLE_PUSHGATEWAY === 'true'\n this.removeOldMetrics =\n config.removeOldMetrics ??\n process.env.METRICS_REMOVE_OLD_METRICS === 'true'\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 // Always push to configured URL (VM-agent). No Pushgateway.\n this.gateway = null\n this.gauges = {}\n this.counters = {}\n this.countersFunctions = {}\n\n /** @type {Object<string, function(): number | Promise<number>>} */\n this.gaugeUpdaters = {}\n\n this._clearOldWorkers(config.removeOldMetrics)\n this._setCleanupHandlers()\n\n this.keepProcessAliveWhenDisabled = true\n }\n\n /**\n * Create a gauge metric.\n * @param {Object} options - Gauge configuration\n * @param {string} options.name - Name of the gauge\n * @param {string} options.help - Help text describing the gauge\n * @param {function(): number|Promise<number>} [options.updateFn] - Optional function returning the gauge value\n * @param {string[]} [options.labelNames] - Optional custom label names\n * @returns {import('prom-client').Gauge} The created Prometheus 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 - Counter configuration\n * @param {string} params.name - Metric name\n * @param {string} params.help - Metric description\n * @param {string[]} [params.labelNames] - Optional list of label names. Defaults to this.defaultLabels keys.\n *\n * @returns {(labels?: Object, incrementValue?: number) => void}\n * A function to increment the counter.\n * Usage: (labels?, incrementValue?)\n */\n createCounter({ name, help, labelNames = Object.keys(this.defaultLabels) }) {\n if (this.counters[name]) return this.countersFunctions[name]\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 this.countersFunctions = {\n ...this.countersFunctions,\n [name]: (data = {}, value = 1) => {\n c.inc({ ...this.defaultLabels, ...data }, value)\n },\n }\n\n return this.countersFunctions[name]\n }\n\n /**\n * Clear all collected counters\n */\n clearAllCounters = () => {\n if (this.metricsLogValues) {\n console.log('Counters to clear: ', Object.keys(this.counters))\n }\n Object.values(this.counters).forEach(counter => counter.reset())\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 continue\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 if (!this.disablePushgateway) {\n await this.gatewayPush()\n }\n // this.clearAllCounters() //TODO: or uncommit or delete (based on grafana expectation)\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 _startPush = (interval = this.intervalSec, customPushMetics = undefined) => {\n if (!this.enabled) {\n console.warn(`${this.prefixLogs} Metrics disabled`)\n if (this.keepProcessAliveWhenDisabled && !this._idleInterval) {\n this._idleInterval = setInterval(() => {}, 60 * 60 * 1000)\n }\n return\n }\n\n if (this._idleInterval) {\n clearInterval(this._idleInterval)\n this._idleInterval = null\n }\n\n const doStart = () => {\n const runPush = () => {\n if (customPushMetics && typeof customPushMetics === 'function') {\n return Promise.resolve(customPushMetics())\n }\n return this.pushMetrics()\n }\n\n if (customPushMetics && typeof customPushMetics === 'function') {\n setInterval(() => customPushMetics(), interval * 1000)\n } else {\n setInterval(() => {\n runPush().catch(err => {\n console.error(`${this.prefixLogs} Failed to push metrics:`, err)\n })\n }, interval * 1000)\n }\n\n // First push immediately so metrics appear without waiting for the first interval\n runPush().catch(err => {\n console.error(`${this.prefixLogs} Failed to push metrics (initial):`, err)\n })\n\n let pushOrigin = 'none'\n try {\n if (this.pushgatewayUrl && this.pushgatewayUrl.trim()) {\n pushOrigin = new URL(this.pushgatewayUrl.trim()).origin\n }\n } catch {\n pushOrigin = 'invalid URL'\n }\n console.warn(\n `${this.prefixLogs} Metrics collection started. (interval: ${this.intervalSec}s, push: ${pushOrigin})`\n )\n }\n\n if (this.startupValidation) {\n const result = this.startupValidation()\n if (result != null && typeof result.then === 'function') {\n result\n .then(ok => {\n if (ok !== false) doStart()\n })\n .catch(err => {\n console.error(\n `${this.prefixLogs} Startup validation failed:`,\n err\n )\n })\n return\n }\n if (result === false) return\n }\n doStart()\n }\n\n pushMetrics = async () => {\n return this._pushMetrics()\n }\n\n /**\n * Start periodic metrics collection and push.\n *\n * This method wraps the internal `_startPush` method.\n * If a `customPushMetrics` function is provided, it will be executed\n * at the given interval instead of the default `pushMetrics` behavior.\n *\n * @param {number} [interval=this.intervalSec] - Interval in seconds between pushes.\n * @param {() => void | Promise<void>} [customPushMetrics] - Optional custom push function. If provided, Prometheus push is skipped.\n */\n startPush = (interval, customPushMetics = undefined) => {\n this._startPush(interval, customPushMetics)\n }\n\n /**\n * Cleanup metrics and exit process.\n * @returns {Promise<void>}\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} removeOldMetrics If true, performs cleanup; otherwise does nothing\n * @returns {Promise<void>}\n * @private\n */\n _clearOldWorkers = async removeOldMetrics => {\n // No Pushgateway; VM-agent does not support per-instance delete. Skip.\n }\n\n /**\n * On shutdown: optionally delete this instance's metrics from VictoriaMetrics (by app, dyno_id, process_type).\n * @returns {Promise<void>}\n */\n gatewayDelete = async () => {\n if (\n this.removeOldMetrics &&\n this.pushgatewayUrl &&\n this.pushgatewayUrl.trim()\n ) {\n await this._deleteFromVMByLabels().catch(err => {\n console.warn(\n `${this.prefixLogs} Deletion from VM on shutdown failed:`,\n err.message\n )\n })\n }\n }\n\n /**\n * Call VictoriaMetrics delete_series API to remove all series matching this instance's labels (app, dyno_id, process_type).\n * @private\n */\n _deleteFromVMByLabels = () => {\n const esc = s => String(s).replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')\n const selector = `{app=\"${esc(this.appName)}\",dyno_id=\"${esc(\n this.dynoId\n )}\",process_type=\"${esc(this.processType)}\"}`\n let origin\n try {\n const u = new URL((this.pushgatewayUrl || '').trim())\n origin = u.origin\n } catch {\n return Promise.reject(new Error('Invalid push URL'))\n }\n const path = `/api/v1/admin/tsdb/delete_series?match[]=${encodeURIComponent(\n selector\n )}`\n return new Promise((resolve, reject) => {\n const u = new URL(origin)\n const req = (u.protocol === 'https:' ? https : http).request(\n {\n hostname: u.hostname,\n port: u.port || (u.protocol === 'https:' ? 443 : 80),\n path,\n method: 'POST',\n headers: {\n 'Content-Length': '0',\n Authorization: this.authToken\n ? `Basic ${this.authToken}`\n : undefined,\n },\n agent:\n u.protocol === 'https:'\n ? new https.Agent({ keepAlive: false })\n : undefined,\n },\n res => {\n if (res.statusCode >= 200 && res.statusCode < 300) resolve()\n else {\n let data = ''\n res.on('data', chunk => {\n data += chunk\n })\n res.on('end', () =>\n reject(new Error(`Delete failed: ${res.statusCode} ${data}`))\n )\n }\n }\n )\n req.on('error', reject)\n req.end()\n })\n }\n\n /**\n * Push registry to configured URL (VM-agent). POST Prometheus text format + Basic auth.\n *\n * @param {object} [params] Unused; kept for API compatibility.\n * @returns {Promise<void>}\n */\n gatewayPush = async (params = {}) => {\n if (this.disablePushgateway) {\n console.warn(\n `${this.prefixLogs} Metrics push skipped: METRICS_DISABLE_PUSHGATEWAY is set`\n )\n return Promise.resolve()\n }\n if (!this.pushgatewayUrl || !this.pushgatewayUrl.trim()) {\n console.warn(\n `${this.prefixLogs} Metrics push skipped: METRICS_PUSHGATEWAY_URL is not set`\n )\n return Promise.resolve()\n }\n return this._pushToVMAgent()\n }\n\n /**\n * POST registry (Prometheus text format) to VM-agent. VM-agent accepts push at /api/v1/import/prometheus; /metrics is GET (scrape) only.\n * @private\n */\n _pushToVMAgent = () => {\n let pushUrl = (this.pushgatewayUrl || '').trim()\n try {\n const u = new URL(pushUrl)\n if (!u.pathname || u.pathname === '/' || u.pathname === '/metrics') {\n pushUrl = `${u.origin}/api/v1/import/prometheus${u.search}`\n }\n } catch {\n // leave pushUrl as-is\n }\n return new Promise((resolve, reject) => {\n const u = new URL(pushUrl)\n const req = (u.protocol === 'https:' ? https : http).request(\n {\n hostname: u.hostname,\n port: u.port || (u.protocol === 'https:' ? 443 : 80),\n path: u.pathname + u.search,\n method: 'POST',\n headers: {\n 'Content-Type': client.register.contentType,\n Authorization: this.authToken\n ? `Basic ${this.authToken}`\n : undefined,\n },\n agent:\n u.protocol === 'https:'\n ? new https.Agent({ keepAlive: true })\n : undefined,\n },\n res => {\n if (res.statusCode >= 200 && res.statusCode < 300) {\n resolve()\n } else {\n let data = ''\n res.on('data', chunk => {\n data += chunk\n })\n res.on('end', () =>\n reject(new Error(`Push failed: ${res.statusCode} ${data}`))\n )\n }\n }\n )\n req.on('error', reject)\n this._registry\n .metrics()\n .then(metrics => {\n req.setHeader('Content-Length', Buffer.byteLength(metrics, 'utf8'))\n req.end(metrics, 'utf8')\n })\n .catch(reject)\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 async getMetricsAsString() {\n return this._registry.metrics()\n }\n\n metricsMiddleware() {\n return async (req, res) => {\n try {\n const metrics = await this.getMetricsAsString()\n res.set('Content-Type', client.register.contentType)\n res.end(metrics)\n } catch (err) {\n console.error(`${this.prefixLogs} Failed to get metrics:`, err)\n res.status(500).end('Failed to collect metrics')\n }\n }\n }\n}\n\nmodule.exports = { BaseMetricsClient }\n"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"metricsDatabaseClient.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsDatabaseClient.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH;IACE;;;;;;;;;;;;;;;;OAgBG;IACH;QAf2B,WAAW,EAA3B,MAAM;QACU,YAAY,EAA5B,MAAM;QAC2B,wBAAwB;;;QACxC,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QACf,iBAAiB;QAClB,kBAAkB;OAkF9C;IAtBC,qBAAuB;IAUvB,qCAAqC;IACrC,8DAQE;IAKJ,yCAKC;IAED;;;OAGG;IACH,gCAHW,IAAI,KACF,QAAQ;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CA4BrE;IAED;;;OAGG;IACH,8BAFa,QAAQ,IAAI,CAAC,CAsBzB;IAED;;;OAGG;IACH,2BAFa,QAAQ,IAAI,CAAC,CAqBzB;IAED;;;OAGG;IACH,sDAMC;CAwBF"}
1
+ {"version":3,"file":"metricsDatabaseClient.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsDatabaseClient.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH;IACE;;;;;;;;;;;;;;;;OAgBG;IACH;QAf2B,WAAW,EAA3B,MAAM;QACU,YAAY,EAA5B,MAAM;QAC2B,wBAAwB;;;QACxC,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QACf,iBAAiB;QAClB,kBAAkB;OAkF9C;IAtBC,qBAAuB;IAUvB,qCAAqC;IACrC,8DAQE;IAKJ,yCAKC;IAED;;;OAGG;IACH,gCAHW,IAAI,KACF,QAAQ;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CA6BrE;IAED;;;OAGG;IACH,8BAFa,QAAQ,IAAI,CAAC,CAsBzB;IAED;;;OAGG;IACH,2BAFa,QAAQ,IAAI,CAAC,CAqBzB;IAED;;;OAGG;IACH,sDAMC;CAwBF"}
@@ -109,29 +109,30 @@ class DatabaseMetricsClient extends BaseMetricsClient {
109
109
  */
110
110
  getDBConnectionsAndName = async pool => {
111
111
  try {
112
- const currentRes = await pool.query('SELECT COUNT(*) AS current FROM pg_stat_activity WHERE datname = current_database()');
112
+ const currentRes = await pool.query(`SELECT COUNT(*) AS current, current_database() AS db_name
113
+ FROM pg_stat_activity WHERE datname = current_database()`);
113
114
  const current = parseInt(currentRes.rows[0]?.current || 0, 10);
115
+ let dbName = currentRes.rows[0]?.db_name || '';
114
116
  const maxRes = await pool.query("SELECT current_setting('max_connections') AS max");
115
117
  const max = parseInt(maxRes.rows[0]?.max || 0, 10);
116
- let dbName = pool.options?.database;
117
118
  if (!dbName && pool.options?.connectionString) {
118
119
  try {
119
120
  const url = new URL(pool.options.connectionString);
120
- dbName = url.pathname.replace(/^\//, '');
121
+ dbName = url.pathname.replace(/^\//, '') || dbName;
121
122
  } catch {
122
- dbName = pool.options.connectionString;
123
+ dbName = dbName || 'unknown';
123
124
  }
124
125
  }
125
126
  return {
126
127
  current,
127
128
  max,
128
- dbName
129
+ dbName: dbName || 'unknown'
129
130
  };
130
131
  } catch (err) {
131
132
  return {
132
133
  current: 0,
133
134
  max: 0,
134
- dbName: pool.options?.database || ''
135
+ dbName: ''
135
136
  };
136
137
  }
137
138
  };
@@ -1 +1 @@
1
- {"version":3,"file":"metricsDatabaseClient.js","names":["Pool","require","BaseMetricsClient","DatabaseMetricsClient","constructor","databaseUrl","databaseName","additional_database_urls","metricsConfig","intervalSec","parseInt","process","env","METRICS_DATABASE_INTERVAL_SEC","tmpAppName","appName","BUILD_APP_NAME","mainDbName","startupValidation","console","error","mainPool","connectionString","query","end","info","err","message","dbKey","url","Object","entries","p","processType","databasePools","_addPool","databaseConnectionsGauge","createGauge","name","help","labelNames","withDefaultLabels","_setCleanupHandlers","pool","push","getDBConnectionsAndName","currentRes","current","rows","maxRes","max","dbName","options","database","URL","pathname","replace","collectDatabaseMetrics","set","getDefaultLabels","database_name","max_connections","String","db_key","warn","pushDatabaseMetrics","gatewayPush","clearAllCounters","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","startPush","_startPush","catch","cleanup","exit","on","module","exports"],"sources":["../../src/metrics/metricsDatabaseClient.js"],"sourcesContent":["const { Pool } = require('pg')\nconst { BaseMetricsClient } = require('./baseMetricsClient')\n\n/**\n * DatabaseMetricsClient collects Postgres connection metrics\n * and pushes them to Prometheus Pushgateway.\n *\n * @extends BaseMetricsClient\n */\nclass DatabaseMetricsClient extends BaseMetricsClient {\n /**\n * @param {Object} options\n * @param {string} options.databaseUrl - Required main database URL\n * @param {string} options.databaseName - Main database name for metrics\n * @param {Object<string, string>} [options.additional_database_urls] - Optional additional DBs, keyed by custom name with URL as value\n * @param {string} [options.appName] - Application name (from BaseMetricsClient)\n * @param {string} [options.dynoId] - Dyno/instance ID (from BaseMetricsClient)\n * @param {string} [options.processType] - Process type (from BaseMetricsClient)\n * @param {boolean} [options.enabled] - Enable metrics collection (from BaseMetricsClient)\n * @param {boolean} [options.logValues] - Log metrics values (from BaseMetricsClient)\n * @param {string} [options.pushgatewayUrl] - PushGateway URL (from BaseMetricsClient)\n * @param {string} [options.pushgatewaySecret] - PushGateway secret token (from BaseMetricsClient)\n * @param {number} [options.intervalSec] - Interval in seconds for pushing metrics\n * @param {boolean} [options.removeOldMetrics] - Remove old metrics by service\n * @param {function} [options.startupValidation] - Function to validate startup\n * @param {boolean} [options.disablePushgateway] - Disable pushing to Pushgateway (use HTTP scraping instead)\n */\n constructor({\n databaseUrl,\n databaseName,\n additional_database_urls = {},\n ...metricsConfig\n } = {}) {\n const intervalSec =\n metricsConfig.intervalSec ||\n parseInt(process.env.METRICS_DATABASE_INTERVAL_SEC || '', 10) ||\n 60\n\n const tmpAppName =\n metricsConfig.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n const mainDbName = databaseName || `${tmpAppName}_db`\n\n const startupValidation = async () => {\n if (!databaseUrl) {\n console.error(`[database-metrics] ❌ METRICS_DATABASE_URL is required`)\n return false\n }\n\n try {\n const mainPool = new Pool({ connectionString: databaseUrl })\n await mainPool.query('SELECT 1')\n await mainPool.end()\n console.info(`[database-metrics] ✓ Main database OK: ${mainDbName}`)\n } catch (err) {\n console.error(\n `[database-metrics] ❌ Cannot connect to main database: ${err.message}`\n )\n return false\n }\n\n for (const [dbKey, url] of Object.entries(additional_database_urls)) {\n try {\n const p = new Pool({ connectionString: url })\n await p.query('SELECT 1')\n await p.end()\n console.info(`[database-metrics] ✓ Additional database OK: ${dbKey}`)\n } catch (err) {\n console.error(\n `[database-metrics] ⚠ Skipping additional database: ${dbKey}`\n )\n console.error(`[database-metrics] ${err.message}`)\n }\n }\n\n console.info(`[database-metrics] Database metrics collection starting`)\n return true\n }\n\n super({\n ...metricsConfig,\n processType: metricsConfig.processType || 'database-metrics',\n intervalSec,\n startupValidation,\n })\n\n this.databasePools = []\n\n if (databaseUrl) {\n this._addPool(databaseUrl, mainDbName)\n }\n\n for (const [dbKey, url] of Object.entries(additional_database_urls)) {\n this._addPool(url, dbKey)\n }\n\n /** Gauge for Database connections */\n this.databaseConnectionsGauge = this.createGauge({\n name: 'app_database_connections',\n help: 'Postgres database connections',\n labelNames: this.withDefaultLabels([\n 'max_connections',\n 'database_name',\n 'db_key',\n ]),\n })\n\n this._setCleanupHandlers()\n }\n\n _addPool = (url, dbKey) => {\n const pool = new Pool({ connectionString: url })\n pool.dbKey = dbKey\n this.databasePools.push(pool)\n return pool\n }\n\n /**\n * @param {Pool} pool - PG connection pool\n * @returns {Promise<{ current: number, max: number, dbName: string }>}\n */\n getDBConnectionsAndName = async pool => {\n try {\n const currentRes = await pool.query(\n 'SELECT COUNT(*) AS current FROM pg_stat_activity WHERE datname = current_database()'\n )\n const current = parseInt(currentRes.rows[0]?.current || 0, 10)\n\n const maxRes = await pool.query(\n \"SELECT current_setting('max_connections') AS max\"\n )\n const max = parseInt(maxRes.rows[0]?.max || 0, 10)\n\n let dbName = pool.options?.database\n if (!dbName && pool.options?.connectionString) {\n try {\n const url = new URL(pool.options.connectionString)\n dbName = url.pathname.replace(/^\\//, '')\n } catch {\n dbName = pool.options.connectionString\n }\n }\n\n return { current, max, dbName }\n } catch (err) {\n return { current: 0, max: 0, dbName: pool.options?.database || '' }\n }\n }\n\n /**\n * Collect database connection metrics for all configured pools\n * @returns {Promise<void>}\n */\n collectDatabaseMetrics = async () => {\n for (const pool of this.databasePools) {\n try {\n const { current, max, dbName } = await this.getDBConnectionsAndName(\n pool\n )\n\n this.databaseConnectionsGauge.set(\n {\n ...this.getDefaultLabels(),\n database_name: pool.dbKey,\n max_connections: String(max),\n db_key: dbName,\n },\n current\n )\n } catch (err) {\n console.warn(`[database-metrics] Failed to collect: ${err.message}`)\n }\n }\n }\n\n /**\n * Push database metrics to Prometheus Pushgateway\n * @returns {Promise<void>}\n */\n pushDatabaseMetrics = async () => {\n try {\n await this.collectDatabaseMetrics()\n await this.gatewayPush()\n this.clearAllCounters()\n\n if (this.metricsLogValues) {\n const metricObjects = await this.registry.getMetricsAsJSON()\n console.info(\n `[database-metrics] Collected DB metrics`,\n JSON.stringify(metricObjects, null, 2)\n )\n }\n } catch (error) {\n console.error(\n `[database-metrics] Failed to collect DB metrics: ${error.message}`\n )\n throw error\n }\n }\n\n /**\n * Start periodic collection.\n * @param {number} [intervalSec=this.intervalSec] - Interval in seconds\n */\n startPush = (intervalSec = this.intervalSec) => {\n this._startPush(intervalSec, () => {\n this.pushDatabaseMetrics().catch(err => {\n console.error(`[database-metrics] Failed to push DB metrics:`, err)\n })\n })\n }\n\n /**\n * Cleanup database pools and exit process\n * @returns {Promise<void>}\n */\n cleanup = async () => {\n try {\n if (this.databasePools) {\n for (const pool of this.databasePools) {\n await pool.end()\n }\n }\n } catch (err) {\n console.error('[database-metrics] Error during cleanup:', err)\n }\n\n process.exit(0)\n }\n\n _setCleanupHandlers = () => {\n process.on('SIGINT', this.cleanup)\n process.on('SIGTERM', this.cleanup)\n }\n}\n\nmodule.exports = { DatabaseMetricsClient }\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"metricsDatabaseClient.js","names":["Pool","require","BaseMetricsClient","DatabaseMetricsClient","constructor","databaseUrl","databaseName","additional_database_urls","metricsConfig","intervalSec","parseInt","process","env","METRICS_DATABASE_INTERVAL_SEC","tmpAppName","appName","BUILD_APP_NAME","mainDbName","startupValidation","console","error","mainPool","connectionString","query","end","info","err","message","dbKey","url","Object","entries","p","processType","databasePools","_addPool","databaseConnectionsGauge","createGauge","name","help","labelNames","withDefaultLabels","_setCleanupHandlers","pool","push","getDBConnectionsAndName","currentRes","current","rows","dbName","db_name","maxRes","max","options","URL","pathname","replace","collectDatabaseMetrics","set","getDefaultLabels","database_name","max_connections","String","db_key","warn","pushDatabaseMetrics","gatewayPush","clearAllCounters","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","startPush","_startPush","catch","cleanup","exit","on","module","exports"],"sources":["../../src/metrics/metricsDatabaseClient.js"],"sourcesContent":["const { Pool } = require('pg')\nconst { BaseMetricsClient } = require('./baseMetricsClient')\n\n/**\n * DatabaseMetricsClient collects Postgres connection metrics\n * and pushes them to Prometheus Pushgateway.\n *\n * @extends BaseMetricsClient\n */\nclass DatabaseMetricsClient extends BaseMetricsClient {\n /**\n * @param {Object} options\n * @param {string} options.databaseUrl - Required main database URL\n * @param {string} options.databaseName - Main database name for metrics\n * @param {Object<string, string>} [options.additional_database_urls] - Optional additional DBs, keyed by custom name with URL as value\n * @param {string} [options.appName] - Application name (from BaseMetricsClient)\n * @param {string} [options.dynoId] - Dyno/instance ID (from BaseMetricsClient)\n * @param {string} [options.processType] - Process type (from BaseMetricsClient)\n * @param {boolean} [options.enabled] - Enable metrics collection (from BaseMetricsClient)\n * @param {boolean} [options.logValues] - Log metrics values (from BaseMetricsClient)\n * @param {string} [options.pushgatewayUrl] - PushGateway URL (from BaseMetricsClient)\n * @param {string} [options.pushgatewaySecret] - PushGateway secret token (from BaseMetricsClient)\n * @param {number} [options.intervalSec] - Interval in seconds for pushing metrics\n * @param {boolean} [options.removeOldMetrics] - Remove old metrics by service\n * @param {function} [options.startupValidation] - Function to validate startup\n * @param {boolean} [options.disablePushgateway] - Disable pushing to Pushgateway (use HTTP scraping instead)\n */\n constructor({\n databaseUrl,\n databaseName,\n additional_database_urls = {},\n ...metricsConfig\n } = {}) {\n const intervalSec =\n metricsConfig.intervalSec ||\n parseInt(process.env.METRICS_DATABASE_INTERVAL_SEC || '', 10) ||\n 60\n\n const tmpAppName =\n metricsConfig.appName || process.env.BUILD_APP_NAME || 'unknown-app'\n const mainDbName = databaseName || `${tmpAppName}_db`\n\n const startupValidation = async () => {\n if (!databaseUrl) {\n console.error(`[database-metrics] ❌ METRICS_DATABASE_URL is required`)\n return false\n }\n\n try {\n const mainPool = new Pool({ connectionString: databaseUrl })\n await mainPool.query('SELECT 1')\n await mainPool.end()\n console.info(`[database-metrics] ✓ Main database OK: ${mainDbName}`)\n } catch (err) {\n console.error(\n `[database-metrics] ❌ Cannot connect to main database: ${err.message}`\n )\n return false\n }\n\n for (const [dbKey, url] of Object.entries(additional_database_urls)) {\n try {\n const p = new Pool({ connectionString: url })\n await p.query('SELECT 1')\n await p.end()\n console.info(`[database-metrics] ✓ Additional database OK: ${dbKey}`)\n } catch (err) {\n console.error(\n `[database-metrics] ⚠ Skipping additional database: ${dbKey}`\n )\n console.error(`[database-metrics] ${err.message}`)\n }\n }\n\n console.info(`[database-metrics] Database metrics collection starting`)\n return true\n }\n\n super({\n ...metricsConfig,\n processType: metricsConfig.processType || 'database-metrics',\n intervalSec,\n startupValidation,\n })\n\n this.databasePools = []\n\n if (databaseUrl) {\n this._addPool(databaseUrl, mainDbName)\n }\n\n for (const [dbKey, url] of Object.entries(additional_database_urls)) {\n this._addPool(url, dbKey)\n }\n\n /** Gauge for Database connections */\n this.databaseConnectionsGauge = this.createGauge({\n name: 'app_database_connections',\n help: 'Postgres database connections',\n labelNames: this.withDefaultLabels([\n 'max_connections',\n 'database_name',\n 'db_key',\n ]),\n })\n\n this._setCleanupHandlers()\n }\n\n _addPool = (url, dbKey) => {\n const pool = new Pool({ connectionString: url })\n pool.dbKey = dbKey\n this.databasePools.push(pool)\n return pool\n }\n\n /**\n * @param {Pool} pool - PG connection pool\n * @returns {Promise<{ current: number, max: number, dbName: string }>}\n */\n getDBConnectionsAndName = async pool => {\n try {\n const currentRes = await pool.query(\n `SELECT COUNT(*) AS current, current_database() AS db_name\n FROM pg_stat_activity WHERE datname = current_database()`\n )\n const current = parseInt(currentRes.rows[0]?.current || 0, 10)\n let dbName = currentRes.rows[0]?.db_name || ''\n\n const maxRes = await pool.query(\n \"SELECT current_setting('max_connections') AS max\"\n )\n const max = parseInt(maxRes.rows[0]?.max || 0, 10)\n\n if (!dbName && pool.options?.connectionString) {\n try {\n const url = new URL(pool.options.connectionString)\n dbName = url.pathname.replace(/^\\//, '') || dbName\n } catch {\n dbName = dbName || 'unknown'\n }\n }\n\n return { current, max, dbName: dbName || 'unknown' }\n } catch (err) {\n return { current: 0, max: 0, dbName: '' }\n }\n }\n\n /**\n * Collect database connection metrics for all configured pools\n * @returns {Promise<void>}\n */\n collectDatabaseMetrics = async () => {\n for (const pool of this.databasePools) {\n try {\n const { current, max, dbName } = await this.getDBConnectionsAndName(\n pool\n )\n\n this.databaseConnectionsGauge.set(\n {\n ...this.getDefaultLabels(),\n database_name: pool.dbKey,\n max_connections: String(max),\n db_key: dbName,\n },\n current\n )\n } catch (err) {\n console.warn(`[database-metrics] Failed to collect: ${err.message}`)\n }\n }\n }\n\n /**\n * Push database metrics to Prometheus Pushgateway\n * @returns {Promise<void>}\n */\n pushDatabaseMetrics = async () => {\n try {\n await this.collectDatabaseMetrics()\n await this.gatewayPush()\n this.clearAllCounters()\n\n if (this.metricsLogValues) {\n const metricObjects = await this.registry.getMetricsAsJSON()\n console.info(\n `[database-metrics] Collected DB metrics`,\n JSON.stringify(metricObjects, null, 2)\n )\n }\n } catch (error) {\n console.error(\n `[database-metrics] Failed to collect DB metrics: ${error.message}`\n )\n throw error\n }\n }\n\n /**\n * Start periodic collection.\n * @param {number} [intervalSec=this.intervalSec] - Interval in seconds\n */\n startPush = (intervalSec = this.intervalSec) => {\n this._startPush(intervalSec, () => {\n this.pushDatabaseMetrics().catch(err => {\n console.error(`[database-metrics] Failed to push DB metrics:`, err)\n })\n })\n }\n\n /**\n * Cleanup database pools and exit process\n * @returns {Promise<void>}\n */\n cleanup = async () => {\n try {\n if (this.databasePools) {\n for (const pool of this.databasePools) {\n await pool.end()\n }\n }\n } catch (err) {\n console.error('[database-metrics] Error during cleanup:', err)\n }\n\n process.exit(0)\n }\n\n _setCleanupHandlers = () => {\n process.on('SIGINT', this.cleanup)\n process.on('SIGTERM', this.cleanup)\n }\n}\n\nmodule.exports = { DatabaseMetricsClient }\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adalo/metrics",
3
- "version": "0.0.0-staging.1",
3
+ "version": "0.0.0-staging.10",
4
4
  "description": "Reusable metrics utilities for Node.js and Laravel apps",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -31,13 +31,13 @@
31
31
  "@types/pg": "^8.15.6"
32
32
  },
33
33
  "devDependencies": {
34
- "@babel/cli": "^7.24.0",
35
- "@babel/core": "^7.24.0",
36
- "@babel/preset-env": "^7.24.0",
37
- "@babel/preset-typescript": "^7.24.0",
34
+ "@babel/cli": "7.18.6",
35
+ "@babel/core": "7.18.6",
36
+ "@babel/preset-env": "7.18.6",
37
+ "@babel/preset-typescript": "7.18.6",
38
38
  "@types/ioredis": "^5.0.0",
39
39
  "@types/jest": "28.1.6",
40
- "@types/node": "18.0.4",
40
+ "@types/node": "17.0.38",
41
41
  "@types/redis": "2",
42
42
  "@typescript-eslint/eslint-plugin": "5.30.6",
43
43
  "@typescript-eslint/parser": "5.30.6",
@@ -202,43 +202,60 @@ class BaseMetricsClient {
202
202
  this._idleInterval = null
203
203
  }
204
204
 
205
- if (this.startupValidation && !this.startupValidation()) {
206
- return
207
- }
205
+ const doStart = () => {
206
+ const runPush = () => {
207
+ if (customPushMetics && typeof customPushMetics === 'function') {
208
+ return Promise.resolve(customPushMetics())
209
+ }
210
+ return this.pushMetrics()
211
+ }
208
212
 
209
- const runPush = () => {
210
213
  if (customPushMetics && typeof customPushMetics === 'function') {
211
- return Promise.resolve(customPushMetics())
214
+ setInterval(() => customPushMetics(), interval * 1000)
215
+ } else {
216
+ setInterval(() => {
217
+ runPush().catch(err => {
218
+ console.error(`${this.prefixLogs} Failed to push metrics:`, err)
219
+ })
220
+ }, interval * 1000)
212
221
  }
213
- return this.pushMetrics()
214
- }
215
222
 
216
- if (customPushMetics && typeof customPushMetics === 'function') {
217
- setInterval(() => customPushMetics(), interval * 1000)
218
- } else {
219
- setInterval(() => {
220
- runPush().catch(err => {
221
- console.error(`${this.prefixLogs} Failed to push metrics:`, err)
222
- })
223
- }, interval * 1000)
224
- }
223
+ // First push immediately so metrics appear without waiting for the first interval
224
+ runPush().catch(err => {
225
+ console.error(`${this.prefixLogs} Failed to push metrics (initial):`, err)
226
+ })
225
227
 
226
- // First push immediately so metrics appear without waiting for the first interval
227
- runPush().catch(err => {
228
- console.error(`${this.prefixLogs} Failed to push metrics (initial):`, err)
229
- })
228
+ let pushOrigin = 'none'
229
+ try {
230
+ if (this.pushgatewayUrl && this.pushgatewayUrl.trim()) {
231
+ pushOrigin = new URL(this.pushgatewayUrl.trim()).origin
232
+ }
233
+ } catch {
234
+ pushOrigin = 'invalid URL'
235
+ }
236
+ console.warn(
237
+ `${this.prefixLogs} Metrics collection started. (interval: ${this.intervalSec}s, push: ${pushOrigin})`
238
+ )
239
+ }
230
240
 
231
- let pushOrigin = 'none'
232
- try {
233
- if (this.pushgatewayUrl && this.pushgatewayUrl.trim()) {
234
- pushOrigin = new URL(this.pushgatewayUrl.trim()).origin
241
+ if (this.startupValidation) {
242
+ const result = this.startupValidation()
243
+ if (result != null && typeof result.then === 'function') {
244
+ result
245
+ .then(ok => {
246
+ if (ok !== false) doStart()
247
+ })
248
+ .catch(err => {
249
+ console.error(
250
+ `${this.prefixLogs} Startup validation failed:`,
251
+ err
252
+ )
253
+ })
254
+ return
235
255
  }
236
- } catch {
237
- pushOrigin = 'invalid URL'
256
+ if (result === false) return
238
257
  }
239
- console.warn(
240
- `${this.prefixLogs} Metrics collection started. (interval: ${this.intervalSec}s, push: ${pushOrigin})`
241
- )
258
+ doStart()
242
259
  }
243
260
 
244
261
  pushMetrics = async () => {
@@ -121,28 +121,29 @@ class DatabaseMetricsClient extends BaseMetricsClient {
121
121
  getDBConnectionsAndName = async pool => {
122
122
  try {
123
123
  const currentRes = await pool.query(
124
- 'SELECT COUNT(*) AS current FROM pg_stat_activity WHERE datname = current_database()'
124
+ `SELECT COUNT(*) AS current, current_database() AS db_name
125
+ FROM pg_stat_activity WHERE datname = current_database()`
125
126
  )
126
127
  const current = parseInt(currentRes.rows[0]?.current || 0, 10)
128
+ let dbName = currentRes.rows[0]?.db_name || ''
127
129
 
128
130
  const maxRes = await pool.query(
129
131
  "SELECT current_setting('max_connections') AS max"
130
132
  )
131
133
  const max = parseInt(maxRes.rows[0]?.max || 0, 10)
132
134
 
133
- let dbName = pool.options?.database
134
135
  if (!dbName && pool.options?.connectionString) {
135
136
  try {
136
137
  const url = new URL(pool.options.connectionString)
137
- dbName = url.pathname.replace(/^\//, '')
138
+ dbName = url.pathname.replace(/^\//, '') || dbName
138
139
  } catch {
139
- dbName = pool.options.connectionString
140
+ dbName = dbName || 'unknown'
140
141
  }
141
142
  }
142
143
 
143
- return { current, max, dbName }
144
+ return { current, max, dbName: dbName || 'unknown' }
144
145
  } catch (err) {
145
- return { current: 0, max: 0, dbName: pool.options?.database || '' }
146
+ return { current: 0, max: 0, dbName: '' }
146
147
  }
147
148
  }
148
149