@adalo/metrics 0.1.80 → 0.1.82
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.
|
@@ -40,10 +40,10 @@ export class DatabaseMetricsClient extends MetricsClient {
|
|
|
40
40
|
/** Gauge for Database connections */
|
|
41
41
|
databaseConnectionsGauge: import("prom-client").Gauge<string>;
|
|
42
42
|
/**
|
|
43
|
-
* @param {
|
|
43
|
+
* @param {Pool} pool - PG connection pool
|
|
44
44
|
* @returns {Promise<{ current: number, max: number, dbName: string }>}
|
|
45
45
|
*/
|
|
46
|
-
getDBConnectionsAndName: (pool:
|
|
46
|
+
getDBConnectionsAndName: (pool: Pool) => Promise<{
|
|
47
47
|
current: number;
|
|
48
48
|
max: number;
|
|
49
49
|
dbName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metricsDatabaseClient.d.ts","sourceRoot":"","sources":["../src/metricsDatabaseClient.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH;IACE;;;;;;;;;;;;;;;OAeG;IACH;QAd2B,WAAW,EAA3B,MAAM;QACa,wBAAwB;QAC1B,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QAChB,mBAAmB;QAClB,iBAAiB;OA0E9C;IAlBC,sBAAuB;IAUvB,qCAAqC;IACrC,8DAIE;IAKJ;;;OAGG;IACH,gCAHW,
|
|
1
|
+
{"version":3,"file":"metricsDatabaseClient.d.ts","sourceRoot":"","sources":["../src/metricsDatabaseClient.js"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH;IACE;;;;;;;;;;;;;;;OAeG;IACH;QAd2B,WAAW,EAA3B,MAAM;QACa,wBAAwB;QAC1B,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QAChB,mBAAmB;QAClB,iBAAiB;OA0E9C;IAlBC,sBAAuB;IAUvB,qCAAqC;IACrC,8DAIE;IAKJ;;;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,CAqBzB;IAED;;;OAGG;IACH,2BAFa,QAAQ,IAAI,CAAC,CAoBzB;IAED;;;OAGG;IACH,sDAMC;CAwBF"}
|
|
@@ -97,7 +97,7 @@ class DatabaseMetricsClient extends MetricsClient {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
|
-
* @param {
|
|
100
|
+
* @param {Pool} pool - PG connection pool
|
|
101
101
|
* @returns {Promise<{ current: number, max: number, dbName: string }>}
|
|
102
102
|
*/
|
|
103
103
|
getDBConnectionsAndName = async pool => {
|
|
@@ -106,19 +106,13 @@ class DatabaseMetricsClient extends MetricsClient {
|
|
|
106
106
|
const current = parseInt(currentRes.rows[0]?.current || 0, 10);
|
|
107
107
|
const maxRes = await pool.query("SELECT current_setting('max_connections') AS max");
|
|
108
108
|
const max = parseInt(maxRes.rows[0]?.max || 0, 10);
|
|
109
|
-
|
|
110
|
-
// Try .database first, then parse connectionString
|
|
111
109
|
let dbName = pool.options?.database;
|
|
112
|
-
console.log("getDBConnectionsAndName pool.options", pool.options);
|
|
113
|
-
console.log("getDBConnectionsAndName dbName", dbName);
|
|
114
110
|
if (!dbName && pool.options?.connectionString) {
|
|
115
|
-
console.log("getDBConnectionsAndName pool.options?.connectionString", pool.options?.connectionString);
|
|
116
111
|
try {
|
|
117
112
|
const url = new URL(pool.options.connectionString);
|
|
118
|
-
dbName = url.pathname.replace(/^\//, '');
|
|
119
|
-
console.log("getDBConnectionsAndName dbName 2", dbName);
|
|
113
|
+
dbName = url.pathname.replace(/^\//, '');
|
|
120
114
|
} catch {
|
|
121
|
-
dbName = pool.options.connectionString;
|
|
115
|
+
dbName = pool.options.connectionString;
|
|
122
116
|
}
|
|
123
117
|
}
|
|
124
118
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metricsDatabaseClient.js","names":["Pool","require","MetricsClient","DatabaseMetricsClient","constructor","databaseUrl","additional_database_urls","metricsConfig","intervalSec","parseInt","process","env","METRICS_DATABASE_INTERVAL_SEC","startupValidation","console","error","mainPool","connectionString","query","end","info","err","message","url","p","scripDefaultMetrics","processType","databasePools","push","databaseConnectionsGauge","createGauge","name","help","labelNames","withDefaultLabels","_setCleanupHandlers","getDBConnectionsAndName","pool","currentRes","current","rows","maxRes","max","dbName","options","database","log","URL","pathname","replace","collectDatabaseMetrics","set","getDefaultLabels","database_name","max_connections","String","warn","pushDatabaseMetrics","gatewayPush","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","startPush","_startPush","catch","cleanup","exit","on","module","exports"],"sources":["../src/metricsDatabaseClient.js"],"sourcesContent":["const { Pool } = require('pg')\nconst { MetricsClient } = require('.')\n\n/**\n * DatabaseMetricsClient collects Postgres connection metrics\n * and pushes them to Prometheus Pushgateway.\n *\n * @extends MetricsClient\n */\nclass DatabaseMetricsClient extends MetricsClient {\n /**\n * @param {Object} options\n * @param {string} options.databaseUrl - Required main database URL\n * @param {string[]} [options.additional_database_urls] - Optional additional DB URLs\n * @param {string} [options.appName] - Application name (from MetricsClient)\n * @param {string} [options.dynoId] - Dyno/instance ID (from MetricsClient)\n * @param {string} [options.processType] - Process type (from MetricsClient)\n * @param {boolean} [options.enabled] - Enable metrics collection (from MetricsClient)\n * @param {boolean} [options.logValues] - Log metrics values (from MetricsClient)\n * @param {string} [options.pushgatewayUrl] - PushGateway URL (from MetricsClient)\n * @param {string} [options.pushgatewaySecret] - PushGateway secret token (from MetricsClient)\n * @param {number} [options.intervalSec] - Interval in seconds for pushing metrics\n * @param {boolean} [options.removeOldMetrics] - Remove old metrics by service\n * @param {boolean} [options.scripDefaultMetrics] - Skip default metrics creation\n * @param {function} [options.startupValidation] - Function to validate startup\n */\n constructor({\n databaseUrl,\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 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`)\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 url of 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: ${url}`)\n } catch (err) {\n console.error(\n `[database-metrics] ⚠ Skipping additional database: ${url}`\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 scripDefaultMetrics: true,\n processType: metricsConfig.processType || 'database-metrics',\n intervalSec,\n startupValidation,\n })\n\n this.databasePools = []\n\n if (databaseUrl) {\n this.databasePools.push(new Pool({ connectionString: databaseUrl }))\n }\n\n for (const url of additional_database_urls) {\n this.databasePools.push(new Pool({ connectionString: url }))\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(['max_connections', 'database_name']),\n })\n\n this._setCleanupHandlers()\n }\n\n /**\n * @param {any} 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 // Try .database first, then parse connectionString\n let dbName = pool.options?.database;\n console.log(\"getDBConnectionsAndName pool.options\", pool.options)\n console.log(\"getDBConnectionsAndName dbName\", dbName)\n if (!dbName && pool.options?.connectionString) {\n console.log(\"getDBConnectionsAndName pool.options?.connectionString\", pool.options?.connectionString)\n try {\n const url = new URL(pool.options.connectionString);\n dbName = url.pathname.replace(/^\\//, ''); // remove leading /\n console.log(\"getDBConnectionsAndName dbName 2\", dbName)\n } catch {\n dbName = pool.options.connectionString; // fallback\n }\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: dbName,\n max_connections: String(max),\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\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","MetricsClient","DatabaseMetricsClient","constructor","databaseUrl","additional_database_urls","metricsConfig","intervalSec","parseInt","process","env","METRICS_DATABASE_INTERVAL_SEC","startupValidation","console","error","mainPool","connectionString","query","end","info","err","message","url","p","scripDefaultMetrics","processType","databasePools","push","databaseConnectionsGauge","createGauge","name","help","labelNames","withDefaultLabels","_setCleanupHandlers","getDBConnectionsAndName","pool","currentRes","current","rows","maxRes","max","dbName","options","database","URL","pathname","replace","collectDatabaseMetrics","set","getDefaultLabels","database_name","max_connections","String","warn","pushDatabaseMetrics","gatewayPush","metricsLogValues","metricObjects","registry","getMetricsAsJSON","JSON","stringify","startPush","_startPush","catch","cleanup","exit","on","module","exports"],"sources":["../src/metricsDatabaseClient.js"],"sourcesContent":["const { Pool } = require('pg')\nconst { MetricsClient } = require('.')\n\n/**\n * DatabaseMetricsClient collects Postgres connection metrics\n * and pushes them to Prometheus Pushgateway.\n *\n * @extends MetricsClient\n */\nclass DatabaseMetricsClient extends MetricsClient {\n /**\n * @param {Object} options\n * @param {string} options.databaseUrl - Required main database URL\n * @param {string[]} [options.additional_database_urls] - Optional additional DB URLs\n * @param {string} [options.appName] - Application name (from MetricsClient)\n * @param {string} [options.dynoId] - Dyno/instance ID (from MetricsClient)\n * @param {string} [options.processType] - Process type (from MetricsClient)\n * @param {boolean} [options.enabled] - Enable metrics collection (from MetricsClient)\n * @param {boolean} [options.logValues] - Log metrics values (from MetricsClient)\n * @param {string} [options.pushgatewayUrl] - PushGateway URL (from MetricsClient)\n * @param {string} [options.pushgatewaySecret] - PushGateway secret token (from MetricsClient)\n * @param {number} [options.intervalSec] - Interval in seconds for pushing metrics\n * @param {boolean} [options.removeOldMetrics] - Remove old metrics by service\n * @param {boolean} [options.scripDefaultMetrics] - Skip default metrics creation\n * @param {function} [options.startupValidation] - Function to validate startup\n */\n constructor({\n databaseUrl,\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 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`)\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 url of 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: ${url}`)\n } catch (err) {\n console.error(\n `[database-metrics] ⚠ Skipping additional database: ${url}`\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 scripDefaultMetrics: true,\n processType: metricsConfig.processType || 'database-metrics',\n intervalSec,\n startupValidation,\n })\n\n this.databasePools = []\n\n if (databaseUrl) {\n this.databasePools.push(new Pool({ connectionString: databaseUrl }))\n }\n\n for (const url of additional_database_urls) {\n this.databasePools.push(new Pool({ connectionString: url }))\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(['max_connections', 'database_name']),\n })\n\n this._setCleanupHandlers()\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: dbName,\n max_connections: String(max),\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\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
|
@@ -99,7 +99,7 @@ class DatabaseMetricsClient extends MetricsClient {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
|
-
* @param {
|
|
102
|
+
* @param {Pool} pool - PG connection pool
|
|
103
103
|
* @returns {Promise<{ current: number, max: number, dbName: string }>}
|
|
104
104
|
*/
|
|
105
105
|
getDBConnectionsAndName = async pool => {
|
|
@@ -114,22 +114,16 @@ class DatabaseMetricsClient extends MetricsClient {
|
|
|
114
114
|
)
|
|
115
115
|
const max = parseInt(maxRes.rows[0]?.max || 0, 10)
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
let dbName = pool.options?.database;
|
|
119
|
-
console.log("getDBConnectionsAndName pool.options", pool.options)
|
|
120
|
-
console.log("getDBConnectionsAndName dbName", dbName)
|
|
117
|
+
let dbName = pool.options?.database
|
|
121
118
|
if (!dbName && pool.options?.connectionString) {
|
|
122
|
-
console.log("getDBConnectionsAndName pool.options?.connectionString", pool.options?.connectionString)
|
|
123
119
|
try {
|
|
124
|
-
const url = new URL(pool.options.connectionString)
|
|
125
|
-
dbName = url.pathname.replace(/^\//, '')
|
|
126
|
-
console.log("getDBConnectionsAndName dbName 2", dbName)
|
|
120
|
+
const url = new URL(pool.options.connectionString)
|
|
121
|
+
dbName = url.pathname.replace(/^\//, '')
|
|
127
122
|
} catch {
|
|
128
|
-
dbName = pool.options.connectionString
|
|
123
|
+
dbName = pool.options.connectionString
|
|
129
124
|
}
|
|
130
125
|
}
|
|
131
126
|
|
|
132
|
-
|
|
133
127
|
return { current, max, dbName }
|
|
134
128
|
} catch (err) {
|
|
135
129
|
return { current: 0, max: 0, dbName: pool.options?.database || '' }
|