@adalo/metrics 0.0.0-staging.14 → 0.0.0-staging.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"baseMetricsClient.d.ts","sourceRoot":"","sources":["../../src/metrics/baseMetricsClient.js"],"names":[],"mappings":"AAKA;;;;GAIG;AACH;IACE;;;;;;;;;;;;;;OAcG;IACH;QAb2B,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QACf,iBAAiB;QAClB,kBAAkB;QAClB,yBAAyB;OA8DpD;IA3DC,gBAA4E;IAC5E,eAAqE;IACrE,oBAG6B;IAC7B,mCAC4C;IAC5C,iBAAuE;IACvE,mBAC+D;IAC/D,uBACoE;IACpE,kBAC0E;IAC1E,oBAGI;IACJ,wCAAiD;IACjD,4BAEoD;IACpD,0BAEmD;IAEnD,mBAAyF;IAEzF,uEAAsC;IAKtC;;;;MAIC;IAED,oFAAoF;IACpF;;;MAGC;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;;;;;;;;;;;;OAYG;IACH;QAT0B,IAAI,EAAnB,MAAM;QACS,IAAI,EAAnB,MAAM;QACY,UAAU;QACX,sBAAsB;kBAE3B,MAAM,mBAAmB,MAAM,KAAK,IAAI,CAgC9D;IAED;;OAEG;IACH,6BAKC;IAED;;OAEG;IACH,kCAiCC;IAED,sEAmDC;IA/CK,+CAA0D;IAiDhE,8FAA8F;IAC9F,6BAEC;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;;;;;;OAMG;IACH,0CAHW,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;;;;;;;;;;;;;;OAcG;IACH;QAb2B,OAAO;QACP,MAAM;QACN,WAAW;QACV,OAAO;QACP,SAAS;QACV,cAAc;QACd,iBAAiB;QACjB,WAAW;QACV,gBAAgB;QACf,iBAAiB;QAClB,kBAAkB;QAClB,yBAAyB;OA8DpD;IA3DC,gBAA4E;IAC5E,eAAqE;IACrE,oBAG6B;IAC7B,mCAC4C;IAC5C,iBAAuE;IACvE,mBAC+D;IAC/D,uBACoE;IACpE,kBAC0E;IAC1E,oBAGI;IACJ,wCAAiD;IACjD,4BAEoD;IACpD,0BAEmD;IAEnD,mBAAyF;IAEzF,uEAAsC;IAKtC;;;;MAIC;IAED,oFAAoF;IACpF;;;MAGC;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;;;;;;;;;;;;OAYG;IACH;QAT0B,IAAI,EAAnB,MAAM;QACS,IAAI,EAAnB,MAAM;QACY,UAAU;QACX,sBAAsB;kBAE3B,MAAM,mBAAmB,MAAM,KAAK,IAAI,CAgC9D;IAED;;OAEG;IACH,6BAKC;IAED;;OAEG;IACH,kCAiCC;IAED,sEAmDC;IA/CK,+CAA0D;IAiDhE,8FAA8F;IAC9F,6BAEC;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,uBA0DC;IAED;;;;;;OAMG;IACH,6BAHW,MAAM,EAAE,KACN,MAAM,EAAE,CAIpB;IAED;;;;;;OAMG;IACH,0CAHW,MAAM,EAAE,KACN,MAAM,EAAE,CAIpB;IAED;;;;MAEC;IAED,gCAGC;IAID,8BAEC;IAED,gCAEC;IAED,4EAEC;IAED,sCAEC;IAED,2DAWC;CACF"}
@@ -361,6 +361,7 @@ class BaseMetricsClient {
361
361
  }
362
362
  return new Promise((resolve, reject) => {
363
363
  const u = new URL(pushUrl);
364
+ let payloadBytes = 0;
364
365
  const req = (u.protocol === 'https:' ? https : http).request({
365
366
  hostname: u.hostname,
366
367
  port: u.port || (u.protocol === 'https:' ? 443 : 80),
@@ -374,7 +375,9 @@ class BaseMetricsClient {
374
375
  keepAlive: true
375
376
  }) : undefined
376
377
  }, res => {
378
+ res.resume();
377
379
  if (res.statusCode >= 200 && res.statusCode < 300) {
380
+ console.warn(`${this.prefixLogs} metrics_pushed pid=${process.pid} bytes=${payloadBytes} status=${res.statusCode}`);
378
381
  resolve();
379
382
  } else {
380
383
  let data = '';
@@ -386,7 +389,8 @@ class BaseMetricsClient {
386
389
  });
387
390
  req.on('error', reject);
388
391
  this._registry.metrics().then(metrics => {
389
- req.setHeader('Content-Length', Buffer.byteLength(metrics, 'utf8'));
392
+ payloadBytes = Buffer.byteLength(metrics, 'utf8');
393
+ req.setHeader('Content-Length', payloadBytes);
390
394
  req.end(metrics, 'utf8');
391
395
  }).catch(reject);
392
396
  });
@@ -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","includeNodeDefaultMetrics","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","defaultLabelsWithoutDynoId","gateway","gauges","counters","countersFunctions","gaugeUpdaters","_clearOldWorkers","_setCleanupHandlers","keepProcessAliveWhenDisabled","createGauge","name","help","updateFn","labelNames","Object","keys","g","Gauge","registers","createCounter","useLabelsWithoutDynoId","defaultSet","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","withDefaultLabelsWithoutDynoId","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 * @param {boolean} [config.includeNodeDefaultMetrics] Register prom-client default process metrics (heap, etc.); defaults true unless MetricsClient forces false for web+Redis HTTP recording dynos.\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.includeNodeDefaultMetrics =\n config.includeNodeDefaultMetrics !== false\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 if (this.includeNodeDefaultMetrics) {\n client.collectDefaultMetrics({ register: this._registry })\n }\n\n this.defaultLabels = {\n app: this.appName,\n dyno_id: this.dynoId,\n process_type: this.processType,\n }\n\n /** Default labels without dyno_id (for HTTP metrics so dyno_id is not included). */\n this.defaultLabelsWithoutDynoId = {\n app: this.appName,\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 * @param {boolean} [params.useLabelsWithoutDynoId=false] - If true, counter uses app/process_type only (no dyno_id). Use for HTTP metrics.\n *\n * @returns {(labels?: Object, incrementValue?: number) => void}\n * A function to increment the counter.\n * Usage: (labels?, incrementValue?)\n */\n createCounter({\n name,\n help,\n labelNames = Object.keys(this.defaultLabels),\n useLabelsWithoutDynoId = false,\n }) {\n if (this.counters[name]) return this.countersFunctions[name]\n\n const defaultSet = useLabelsWithoutDynoId\n ? this.defaultLabelsWithoutDynoId\n : this.defaultLabels\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({ ...defaultSet, ...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 /** Must be a prototype method (not a class field) so subclasses can `super.pushMetrics()`. */\n async pushMetrics() {\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 /**\n * Merge default labels without dyno_id (`app`, `process_type`) with custom label names.\n * Use for HTTP metrics so dyno_id is not included.\n *\n * @param {string[]} labels Additional label names\n * @returns {string[]} Combined label names\n */\n withDefaultLabelsWithoutDynoId = (labels = []) => {\n return [...Object.keys(this.defaultLabelsWithoutDynoId), ...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","includeNodeDefaultMetrics","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","defaultLabelsWithoutDynoId","gateway","gauges","counters","countersFunctions","gaugeUpdaters","_clearOldWorkers","_setCleanupHandlers","keepProcessAliveWhenDisabled","createGauge","name","help","updateFn","labelNames","Object","keys","g","Gauge","registers","createCounter","useLabelsWithoutDynoId","defaultSet","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","payloadBytes","contentType","resume","pid","then","Buffer","byteLength","setHeader","withDefaultLabels","labels","withDefaultLabelsWithoutDynoId","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 * @param {boolean} [config.includeNodeDefaultMetrics] Register prom-client default process metrics (heap, etc.); defaults true unless MetricsClient forces false for web+Redis HTTP recording dynos.\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.includeNodeDefaultMetrics =\n config.includeNodeDefaultMetrics !== false\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 if (this.includeNodeDefaultMetrics) {\n client.collectDefaultMetrics({ register: this._registry })\n }\n\n this.defaultLabels = {\n app: this.appName,\n dyno_id: this.dynoId,\n process_type: this.processType,\n }\n\n /** Default labels without dyno_id (for HTTP metrics so dyno_id is not included). */\n this.defaultLabelsWithoutDynoId = {\n app: this.appName,\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 * @param {boolean} [params.useLabelsWithoutDynoId=false] - If true, counter uses app/process_type only (no dyno_id). Use for HTTP metrics.\n *\n * @returns {(labels?: Object, incrementValue?: number) => void}\n * A function to increment the counter.\n * Usage: (labels?, incrementValue?)\n */\n createCounter({\n name,\n help,\n labelNames = Object.keys(this.defaultLabels),\n useLabelsWithoutDynoId = false,\n }) {\n if (this.counters[name]) return this.countersFunctions[name]\n\n const defaultSet = useLabelsWithoutDynoId\n ? this.defaultLabelsWithoutDynoId\n : this.defaultLabels\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({ ...defaultSet, ...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 /** Must be a prototype method (not a class field) so subclasses can `super.pushMetrics()`. */\n async pushMetrics() {\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 let payloadBytes = 0\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 res.resume()\n if (res.statusCode >= 200 && res.statusCode < 300) {\n console.warn(\n `${this.prefixLogs} metrics_pushed pid=${process.pid} bytes=${payloadBytes} status=${res.statusCode}`\n )\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 payloadBytes = Buffer.byteLength(metrics, 'utf8')\n req.setHeader('Content-Length', payloadBytes)\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 /**\n * Merge default labels without dyno_id (`app`, `process_type`) with custom label names.\n * Use for HTTP metrics so dyno_id is not included.\n *\n * @param {string[]} labels Additional label names\n * @returns {string[]} Combined label names\n */\n withDefaultLabelsWithoutDynoId = (labels = []) => {\n return [...Object.keys(this.defaultLabelsWithoutDynoId), ...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":[]}
@@ -51,7 +51,16 @@ export class MetricsClient extends BaseMetricsClient {
51
51
  _skippedPushWorkerLogged: boolean;
52
52
  _lastUsageMicros: number;
53
53
  _lastCheckTime: number;
54
+ _metricsBootstrapLogged: boolean;
55
+ _httpLogWindowStart: number;
56
+ _httpLogBatchCount: number;
57
+ _httpLogLastSample: string;
54
58
  _logMetricsBootstrap: () => void;
59
+ /**
60
+ * Throttled HTTP recording log (not gated by METRICS_LOG_VALUES). Same pod can run many workers
61
+ * (same dyno_id); pid + cluster_worker_id distinguish processes.
62
+ */
63
+ _maybeLogHttpRecord(method: any, route: any, status_code: any): void;
55
64
  get isWebRedisHttpRecordingOnly(): boolean;
56
65
  _initDefaultMetrics: () => void;
57
66
  getCpuUsagePercent: () => number;
@@ -1 +1 @@
1
- {"version":3,"file":"metricsClient.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsClient.js"],"names":[],"mappings":"AA6BA;;;;;;GAMG;AACH;IACE;;;;;;;;;;;;;;;;;OAiBG;IACH;;;;;;;;;;;;;;;;mBAqFC;IA/CC,4BAA4C;IAC5C,qCAA8D;IAM9D,8CAOU;IAEV,oCAEC;IAED,6BAA2D;IAG3D,iCAAwE;IACxE,+BAA8C;IAgB9C,kCAAqC;IAErC,yBAAyB;IACzB,uBAAgC;IAMlC,iCAQC;IAED,2CAEC;IAED,gCAiFC;IAED,iCAyBC;IAED,2BAeC;IAED,kCASC;IAED,kCAcC;IAED,2BAKC;IAED;;;;;;;aAuCC;IAED;;;;;OAKG;IACH,sEAHiB,IAAI,GAAG,QAAQ,IAAI,CAAC,kBACxB,IAAI,CAoBhB;IAuBD,oEA+BC;CACF"}
1
+ {"version":3,"file":"metricsClient.d.ts","sourceRoot":"","sources":["../../src/metrics/metricsClient.js"],"names":[],"mappings":"AA4CA;;;;;;GAMG;AACH;IACE;;;;;;;;;;;;;;;;;OAiBG;IACH;;;;;;;;;;;;;;;;mBAiGC;IApDC,4BAA4C;IAC5C,qCAA8D;IAM9D,8CAOU;IAEV,oCAEC;IAED,6BAA2D;IAG3D,iCAAwE;IACxE,+BAA8C;IAgB9C,kCAAqC;IAErC,yBAAyB;IACzB,uBAAgC;IAEhC,iCAAoC;IACpC,4BAA4B;IAC5B,2BAA2B;IAC3B,2BAA4B;IAM9B,iCAmBC;IAED;;;OAGG;IACH,qEAmBC;IAED,2CAEC;IAED,gCAiFC;IAED,iCAyBC;IAED,2BAeC;IAED,kCASC;IAED,kCAcC;IAED,2BAKC;IAED;;;;;;;aAyCC;IAED;;;;;OAKG;IACH,sEAHiB,IAAI,GAAG,QAAQ,IAAI,CAAC,kBACxB,IAAI,CAoBhB;IA4BD,oEA+BC;CACF"}
@@ -32,6 +32,20 @@ function isMetricsExporterProcessType(processType) {
32
32
  return processType === 'metrics';
33
33
  }
34
34
 
35
+ /** Extra tag when `cluster` workers are used (same pod hostname, different PIDs). */
36
+ function clusterWorkerTag() {
37
+ try {
38
+ const cluster = require('cluster');
39
+ if (cluster.isWorker && cluster.worker) {
40
+ return ` cluster_worker_id=${cluster.worker.id}`;
41
+ }
42
+ } catch (_) {
43
+ /* ignore */
44
+ }
45
+ return '';
46
+ }
47
+ const HTTP_METRICS_LOG_INTERVAL_MS = 5000;
48
+
35
49
  /**
36
50
  * MetricsClient handles Prometheus metrics collection and push.
37
51
  *
@@ -60,8 +74,18 @@ class MetricsClient extends BaseMetricsClient {
60
74
  */
61
75
  constructor(config = {}) {
62
76
  const processTypeBeforeSuper = config.processType || process.env.BUILD_DYNO_PROCESS_TYPE || 'undefined_build_dyno_type';
63
- const httpMetricsEnabled = config.httpMetricsEnabled ?? process.env.METRICS_HTTP_ENABLED === 'true' ?? false;
64
77
  const redisUrl = config.httpMetricsRedisUrl || process.env.METRICS_HTTP_REDIS_URL || process.env.REDIS_URL || '';
78
+ let httpMetricsEnabled;
79
+ if (config.httpMetricsEnabled !== undefined) {
80
+ httpMetricsEnabled = config.httpMetricsEnabled;
81
+ } else if (process.env.METRICS_HTTP_ENABLED === 'true') {
82
+ httpMetricsEnabled = true;
83
+ } else if (isMetricsExporterProcessType(processTypeBeforeSuper) && redisUrl) {
84
+ // metrics: dyno must drain Redis HTTP keys; many stacks omit METRICS_HTTP_ENABLED here
85
+ httpMetricsEnabled = true;
86
+ } else {
87
+ httpMetricsEnabled = false;
88
+ }
65
89
  let httpMetricsRedisAggregation = Boolean(redisUrl) && Boolean(httpMetricsEnabled) && config.httpMetricsRedisAggregation !== false;
66
90
  if (httpMetricsRedisAggregation && !isRedisPeerInstalled()) {
67
91
  console.warn('[MetricsClient] METRICS_HTTP_ENABLED with REDIS_URL needs the peer package `redis` in your application (npm install redis@3). Falling back to in-process HTTP counters. @adalo/metrics does not bundle redis.');
@@ -104,6 +128,10 @@ class MetricsClient extends BaseMetricsClient {
104
128
  this._skippedPushWorkerLogged = false;
105
129
  this._lastUsageMicros = 0;
106
130
  this._lastCheckTime = Date.now();
131
+ this._metricsBootstrapLogged = false;
132
+ this._httpLogWindowStart = 0;
133
+ this._httpLogBatchCount = 0;
134
+ this._httpLogLastSample = '';
107
135
  this._initDefaultMetrics();
108
136
  this._logMetricsBootstrap();
109
137
  }
@@ -111,9 +139,39 @@ class MetricsClient extends BaseMetricsClient {
111
139
  if (!this.enabled) {
112
140
  return;
113
141
  }
142
+ if (this._metricsBootstrapLogged) {
143
+ return;
144
+ }
145
+ this._metricsBootstrapLogged = true;
114
146
  const wc = process.env.WEB_CONCURRENCY || '';
115
- console.warn(`${this.prefixLogs} metrics bootstrap WEB_CONCURRENCY=${wc || '(unset)'} host_cpus=${this._bootstrapHostCpuCount} dyno=${process.env.DYNO || '(unset)'} redis_http=${this.httpMetricsRedisAggregation} web_redis_only=${this._webRedisHttpRecordingOnly}`);
147
+ const httpMode = !this.httpMetricsEnabled ? 'off' : this._httpRedisStore ? 'redis' : 'local_counters';
148
+ const pidTag = `pid=${process.pid}`;
149
+ const clusterTag = clusterWorkerTag();
150
+ console.warn(`${this.prefixLogs} metrics bootstrap ${pidTag}${clusterTag} WEB_CONCURRENCY=${wc || '(unset)'} host_cpus=${this._bootstrapHostCpuCount} dyno_id=${this.dynoId} process_type=${this.processType} scheduling_id=${process.env.DYNO || 'n/a'} http_metrics=${httpMode} redis_http=${this.httpMetricsRedisAggregation} web_redis_only=${this._webRedisHttpRecordingOnly}`);
116
151
  };
152
+
153
+ /**
154
+ * Throttled HTTP recording log (not gated by METRICS_LOG_VALUES). Same pod can run many workers
155
+ * (same dyno_id); pid + cluster_worker_id distinguish processes.
156
+ */
157
+ _maybeLogHttpRecord(method, route, status_code) {
158
+ if (!this.enabled || !this.httpMetricsEnabled) {
159
+ return;
160
+ }
161
+ this._httpLogBatchCount += 1;
162
+ this._httpLogLastSample = `${method} ${route} ${status_code}`;
163
+ const now = Date.now();
164
+ if (!this._httpLogWindowStart) {
165
+ this._httpLogWindowStart = now;
166
+ }
167
+ if (now - this._httpLogWindowStart < HTTP_METRICS_LOG_INTERVAL_MS) {
168
+ return;
169
+ }
170
+ const clusterTag = clusterWorkerTag();
171
+ console.warn(`${this.prefixLogs} http_metrics_record pid=${process.pid}${clusterTag} process_type=${this.processType} dyno_id=${this.dynoId} count=${this._httpLogBatchCount} sample=${this._httpLogLastSample}`);
172
+ this._httpLogWindowStart = now;
173
+ this._httpLogBatchCount = 0;
174
+ }
117
175
  get isWebRedisHttpRecordingOnly() {
118
176
  return Boolean(this._webRedisHttpRecordingOnly);
119
177
  }
@@ -249,6 +307,7 @@ class MetricsClient extends BaseMetricsClient {
249
307
  if (!this.httpMetricsEnabled) return;
250
308
  if (this._httpRedisStore) {
251
309
  this._httpRedisStore.record(method, route, status_code, appId, databaseId, duration);
310
+ this._maybeLogHttpRecord(method, route, status_code);
252
311
  return;
253
312
  }
254
313
  this.countersFunctions?.app_requests_total({
@@ -265,6 +324,7 @@ class MetricsClient extends BaseMetricsClient {
265
324
  appId,
266
325
  databaseId
267
326
  }, duration);
327
+ this._maybeLogHttpRecord(method, route, status_code);
268
328
  }
269
329
 
270
330
  /**
@@ -291,7 +351,10 @@ class MetricsClient extends BaseMetricsClient {
291
351
  return;
292
352
  }
293
353
  if (this._httpRedisStore && shouldRunMetricsPush(this) && this.countersFunctions?.app_requests_total && this.countersFunctions?.app_requests_total_duration) {
294
- await this._httpRedisStore.flushToCounters((labels, value) => this.countersFunctions.app_requests_total(labels, value), (labels, value) => this.countersFunctions.app_requests_total_duration(labels, value));
354
+ const flushed = await this._httpRedisStore.flushToCounters((labels, value) => this.countersFunctions.app_requests_total(labels, value), (labels, value) => this.countersFunctions.app_requests_total_duration(labels, value));
355
+ if (flushed) {
356
+ console.warn(`${this.prefixLogs} http_metrics_redis_flush pid=${process.pid}${clusterWorkerTag()} process_type=${this.processType} dyno_id=${this.dynoId} drained=true`);
357
+ }
295
358
  }
296
359
  // eslint-disable-next-line consistent-return
297
360
  return super.pushMetrics();
@@ -1 +1 @@
1
- {"version":3,"file":"metricsClient.js","names":["fs","require","os","BaseMetricsClient","shouldRunMetricsPush","HttpMetricsRedisStore","isRedisPeerInstalled","isWebServingDynoContext","processType","dyno","process","env","DYNO","test","isMetricsExporterProcessType","MetricsClient","constructor","config","processTypeBeforeSuper","BUILD_DYNO_PROCESS_TYPE","httpMetricsEnabled","METRICS_HTTP_ENABLED","redisUrl","httpMetricsRedisUrl","METRICS_HTTP_REDIS_URL","REDIS_URL","httpMetricsRedisAggregation","Boolean","console","warn","isWebCtx","webRedisRecordingOnly","NODE_ENV","includeNodeDefaultMetrics","redisKeyProcessType","_httpRedisStore","appName","_webRedisHttpRecordingOnly","exportRuntimeGauges","wcParsed","parseInt","WEB_CONCURRENCY","_bootstrapWebConcurrency","Number","isFinite","_bootstrapHostCpuCount","cpus","length","defaultLabelsWithoutDynoId","app","process_type","_skippedPushWorkerLogged","_lastUsageMicros","_lastCheckTime","Date","now","_initDefaultMetrics","_logMetricsBootstrap","enabled","wc","prefixLogs","isWebRedisHttpRecordingOnly","createGauge","name","help","updateFn","getCpuUsagePercent","getAvailableCPUs","getContainerMemoryUsage","measureLag","getContainerMemoryLimit","uptime","registerHttpCounters","createCounter","labelNames","withDefaultLabelsWithoutDynoId","useLabelsWithoutDynoId","stat","readFileSync","match","currentUsage","deltaUsage","deltaTime","cpuMaxPath","existsSync","quotaStr","periodStr","trim","split","memoryUsage","rss","path","val","parsed","totalmem","Promise","resolve","start","setImmediate","trackHttpRequest","method","route","status_code","appId","databaseId","duration","record","countersFunctions","app_requests_total","app_requests_total_duration","startPush","interval","customPushMetrics","METRICS_SUPPRESS_SKIP_PUSH_LOG","_startPush","pushMetrics","flushToCounters","labels","value","trackHttpRequestMiddleware","req","res","next","on","params","body","query","datasourceId","statusCode","module","exports"],"sources":["../../src/metrics/metricsClient.js"],"sourcesContent":["const fs = require('fs')\nconst os = require('os')\nconst { BaseMetricsClient } = require('./baseMetricsClient')\nconst { shouldRunMetricsPush } = require('./shouldRunMetricsPush')\nconst {\n HttpMetricsRedisStore,\n isRedisPeerInstalled,\n} = require('./httpMetricsRedisStore')\n\n/**\n * Heroku-style: DYNO=web.1 → web serving; DYNO=metrics.1 → VM-agent exporter (Procfile `metrics:`).\n * Without DYNO (local Docker), falls back to BUILD_DYNO_PROCESS_TYPE === 'web'.\n */\nfunction isWebServingDynoContext(processType) {\n const dyno = process.env.DYNO || ''\n if (dyno) {\n return /^web\\./.test(dyno)\n }\n return processType === 'web'\n}\n\nfunction isMetricsExporterProcessType(processType) {\n const dyno = process.env.DYNO || ''\n if (dyno) {\n return /^metrics\\./.test(dyno)\n }\n return processType === 'metrics'\n}\n\n/**\n * MetricsClient handles Prometheus metrics collection and push.\n *\n * **Redis HTTP:** When `METRICS_HTTP_ENABLED` and a Redis URL exist, HTTP counts use Redis\n * (key segment from BUILD_APP_NAME + process type). Production **web.*** dynos only write Redis;\n * run Procfile **`metrics`** + **metrics-exporter.js** to drain and POST. Tests: `httpMetricsRedisAggregation: false`.\n */\nclass MetricsClient extends 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, metrics, etc.)\n * @param {boolean} [config.enabled] Enable metrics collection\n * @param {boolean} [config.httpMetricsEnabled] Enable HTTP request metrics (app_requests_total, app_requests_total_duration); defaults from METRICS_HTTP_ENABLED\n * @param {boolean} [config.logValues] Log metrics values to console\n * @param {string} [config.pushgatewayUrl] Push URL (VM-agent import endpoint)\n * @param {string} [config.pushgatewaySecret] Basic auth secret (Base64 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 VM-agent (use HTTP scraping instead)\n * @param {boolean} [config.includeNodeDefaultMetrics] Register prom-client default process metrics\n * @param {string} [config.httpMetricsRedisUrl] Overrides METRICS_HTTP_REDIS_URL / REDIS_URL for the HTTP Redis store\n * @param {boolean} [config.httpMetricsRedisAggregation] When unset, on if Redis + HTTP metrics; set false to force in-process HTTP counters only\n */\n constructor(config = {}) {\n const processTypeBeforeSuper =\n config.processType || process.env.BUILD_DYNO_PROCESS_TYPE || 'undefined_build_dyno_type'\n\n const httpMetricsEnabled =\n config.httpMetricsEnabled ??\n process.env.METRICS_HTTP_ENABLED === 'true' ??\n false\n\n const redisUrl =\n config.httpMetricsRedisUrl ||\n process.env.METRICS_HTTP_REDIS_URL ||\n process.env.REDIS_URL ||\n ''\n\n let httpMetricsRedisAggregation =\n Boolean(redisUrl) &&\n Boolean(httpMetricsEnabled) &&\n config.httpMetricsRedisAggregation !== false\n\n if (httpMetricsRedisAggregation && !isRedisPeerInstalled()) {\n console.warn(\n '[MetricsClient] METRICS_HTTP_ENABLED with REDIS_URL needs the peer package `redis` in your application (npm install redis@3). Falling back to in-process HTTP counters. @adalo/metrics does not bundle redis.'\n )\n httpMetricsRedisAggregation = false\n }\n\n const isWebCtx = isWebServingDynoContext(processTypeBeforeSuper)\n const webRedisRecordingOnly =\n Boolean(httpMetricsRedisAggregation) &&\n isWebCtx &&\n process.env.NODE_ENV === 'production'\n\n super({\n ...config,\n ...(webRedisRecordingOnly ? { includeNodeDefaultMetrics: false } : {}),\n })\n\n this.httpMetricsEnabled = httpMetricsEnabled\n this.httpMetricsRedisAggregation = httpMetricsRedisAggregation\n\n const redisKeyProcessType = isMetricsExporterProcessType(this.processType)\n ? 'web'\n : this.processType\n\n this._httpRedisStore =\n this.httpMetricsEnabled && this.httpMetricsRedisAggregation\n ? new HttpMetricsRedisStore({\n redisUrl,\n appName: this.appName,\n processType: redisKeyProcessType,\n })\n : null\n\n this._webRedisHttpRecordingOnly = Boolean(\n webRedisRecordingOnly && this._httpRedisStore\n )\n\n this.exportRuntimeGauges = !this._webRedisHttpRecordingOnly\n\n const wcParsed = parseInt(process.env.WEB_CONCURRENCY || '', 10)\n this._bootstrapWebConcurrency = Number.isFinite(wcParsed) ? wcParsed : 0\n this._bootstrapHostCpuCount = os.cpus().length\n\n if (this.httpMetricsRedisAggregation && redisUrl) {\n if (isMetricsExporterProcessType(this.processType)) {\n this.defaultLabelsWithoutDynoId = {\n app: this.appName,\n process_type: 'web',\n }\n } else if (!this._webRedisHttpRecordingOnly) {\n this.defaultLabelsWithoutDynoId = {\n app: this.appName,\n process_type: this.processType,\n }\n }\n }\n\n this._skippedPushWorkerLogged = false\n\n this._lastUsageMicros = 0\n this._lastCheckTime = Date.now()\n\n this._initDefaultMetrics()\n this._logMetricsBootstrap()\n }\n\n _logMetricsBootstrap = () => {\n if (!this.enabled) {\n return\n }\n const wc = process.env.WEB_CONCURRENCY || ''\n console.warn(\n `${this.prefixLogs} metrics bootstrap WEB_CONCURRENCY=${wc || '(unset)'} host_cpus=${this._bootstrapHostCpuCount} dyno=${process.env.DYNO || '(unset)'} redis_http=${this.httpMetricsRedisAggregation} web_redis_only=${this._webRedisHttpRecordingOnly}`\n )\n }\n\n get isWebRedisHttpRecordingOnly() {\n return Boolean(this._webRedisHttpRecordingOnly)\n }\n\n _initDefaultMetrics = () => {\n if (this.exportRuntimeGauges) {\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.createGauge({\n name: 'app_bootstrap_web_concurrency',\n help: 'WEB_CONCURRENCY at process start (diagnostic; 0 if unset/invalid)',\n updateFn: () => this._bootstrapWebConcurrency,\n })\n\n this.createGauge({\n name: 'app_bootstrap_host_cpu_count',\n help: 'os.cpus().length at process start (diagnostic)',\n updateFn: () => this._bootstrapHostCpuCount,\n })\n }\n\n const registerHttpCounters =\n this.httpMetricsEnabled && !this._webRedisHttpRecordingOnly\n\n if (registerHttpCounters) {\n this.createCounter({\n name: 'app_requests_total',\n help: 'Total number of HTTP requests',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n\n this.createCounter({\n name: 'app_requests_total_duration',\n help: 'Total duration of HTTP requests in milliseconds',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n }\n }\n\n 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 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 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 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 measureLag() {\n return new Promise(resolve => {\n const start = Date.now()\n setImmediate(() => resolve(Date.now() - start))\n })\n }\n\n trackHttpRequest({\n method,\n route,\n status_code,\n appId = '',\n databaseId = '',\n duration,\n }) {\n if (!this.httpMetricsEnabled) return\n\n if (this._httpRedisStore) {\n this._httpRedisStore.record(\n method,\n route,\n status_code,\n appId,\n databaseId,\n duration\n )\n return\n }\n\n this.countersFunctions?.app_requests_total({\n method,\n route,\n status_code,\n appId,\n databaseId,\n })\n this.countersFunctions?.app_requests_total_duration(\n {\n method,\n route,\n status_code,\n appId,\n databaseId,\n },\n duration\n )\n }\n\n /**\n * Start periodic push to VM-agent (`METRICS_PUSHGATEWAY_URL`). Interval defaults to `intervalSec` from config/env.\n * @param {number} [interval] Seconds between pushes; defaults to `this.intervalSec`\n * @param {() => void | Promise<void>} [customPushMetrics] Optional; replaces default push when provided\n * @returns {void}\n */\n startPush = (interval, customPushMetrics) => {\n if (this._webRedisHttpRecordingOnly) {\n return\n }\n if (!shouldRunMetricsPush(this)) {\n if (\n this.enabled &&\n !this._skippedPushWorkerLogged &&\n process.env.METRICS_SUPPRESS_SKIP_PUSH_LOG !== 'true'\n ) {\n console.warn(\n `${this.prefixLogs} Skipping VM-agent push on this worker. METRICS_PUSH_ALL_WORKERS=true restores per-worker push.`\n )\n this._skippedPushWorkerLogged = true\n }\n return\n }\n this._startPush(interval, customPushMetrics)\n }\n\n async pushMetrics() {\n if (this._webRedisHttpRecordingOnly) {\n return\n }\n if (\n this._httpRedisStore &&\n shouldRunMetricsPush(this) &&\n this.countersFunctions?.app_requests_total &&\n this.countersFunctions?.app_requests_total_duration\n ) {\n await this._httpRedisStore.flushToCounters(\n (labels, value) =>\n this.countersFunctions.app_requests_total(labels, value),\n (labels, value) =>\n this.countersFunctions.app_requests_total_duration(labels, value)\n )\n }\n // eslint-disable-next-line consistent-return\n return super.pushMetrics()\n }\n\n trackHttpRequestMiddleware = (req, res, next) => {\n if (!this.enabled || !this.httpMetricsEnabled || req.method === 'OPTIONS') {\n next()\n return\n }\n\n const start = Date.now()\n res.on('finish', () => {\n const route = req.route?.path || req.path || 'unknown'\n const appId =\n req.params?.appId || req.body?.appId || req.query?.appId || ''\n const databaseId =\n req.params?.databaseId ||\n req.body?.databaseId ||\n req.query?.databaseId ||\n req.params?.datasourceId ||\n req.body?.datasourceId ||\n req.query?.datasourceId ||\n ''\n\n this.trackHttpRequest({\n method: req.method,\n route,\n status_code: res.statusCode,\n appId,\n databaseId,\n duration: Date.now() - start,\n })\n })\n\n next()\n }\n}\n\nmodule.exports = { MetricsClient }\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"metricsClient.js","names":["fs","require","os","BaseMetricsClient","shouldRunMetricsPush","HttpMetricsRedisStore","isRedisPeerInstalled","isWebServingDynoContext","processType","dyno","process","env","DYNO","test","isMetricsExporterProcessType","clusterWorkerTag","cluster","isWorker","worker","id","_","HTTP_METRICS_LOG_INTERVAL_MS","MetricsClient","constructor","config","processTypeBeforeSuper","BUILD_DYNO_PROCESS_TYPE","redisUrl","httpMetricsRedisUrl","METRICS_HTTP_REDIS_URL","REDIS_URL","httpMetricsEnabled","undefined","METRICS_HTTP_ENABLED","httpMetricsRedisAggregation","Boolean","console","warn","isWebCtx","webRedisRecordingOnly","NODE_ENV","includeNodeDefaultMetrics","redisKeyProcessType","_httpRedisStore","appName","_webRedisHttpRecordingOnly","exportRuntimeGauges","wcParsed","parseInt","WEB_CONCURRENCY","_bootstrapWebConcurrency","Number","isFinite","_bootstrapHostCpuCount","cpus","length","defaultLabelsWithoutDynoId","app","process_type","_skippedPushWorkerLogged","_lastUsageMicros","_lastCheckTime","Date","now","_metricsBootstrapLogged","_httpLogWindowStart","_httpLogBatchCount","_httpLogLastSample","_initDefaultMetrics","_logMetricsBootstrap","enabled","wc","httpMode","pidTag","pid","clusterTag","prefixLogs","dynoId","_maybeLogHttpRecord","method","route","status_code","isWebRedisHttpRecordingOnly","createGauge","name","help","updateFn","getCpuUsagePercent","getAvailableCPUs","getContainerMemoryUsage","measureLag","getContainerMemoryLimit","uptime","registerHttpCounters","createCounter","labelNames","withDefaultLabelsWithoutDynoId","useLabelsWithoutDynoId","stat","readFileSync","match","currentUsage","deltaUsage","deltaTime","cpuMaxPath","existsSync","quotaStr","periodStr","trim","split","memoryUsage","rss","path","val","parsed","totalmem","Promise","resolve","start","setImmediate","trackHttpRequest","appId","databaseId","duration","record","countersFunctions","app_requests_total","app_requests_total_duration","startPush","interval","customPushMetrics","METRICS_SUPPRESS_SKIP_PUSH_LOG","_startPush","pushMetrics","flushed","flushToCounters","labels","value","trackHttpRequestMiddleware","req","res","next","on","params","body","query","datasourceId","statusCode","module","exports"],"sources":["../../src/metrics/metricsClient.js"],"sourcesContent":["const fs = require('fs')\nconst os = require('os')\nconst { BaseMetricsClient } = require('./baseMetricsClient')\nconst { shouldRunMetricsPush } = require('./shouldRunMetricsPush')\nconst {\n HttpMetricsRedisStore,\n isRedisPeerInstalled,\n} = require('./httpMetricsRedisStore')\n\n/**\n * Heroku-style: DYNO=web.1 → web serving; DYNO=metrics.1 → VM-agent exporter (Procfile `metrics:`).\n * Without DYNO (local Docker), falls back to BUILD_DYNO_PROCESS_TYPE === 'web'.\n */\nfunction isWebServingDynoContext(processType) {\n const dyno = process.env.DYNO || ''\n if (dyno) {\n return /^web\\./.test(dyno)\n }\n return processType === 'web'\n}\n\nfunction isMetricsExporterProcessType(processType) {\n const dyno = process.env.DYNO || ''\n if (dyno) {\n return /^metrics\\./.test(dyno)\n }\n return processType === 'metrics'\n}\n\n/** Extra tag when `cluster` workers are used (same pod hostname, different PIDs). */\nfunction clusterWorkerTag() {\n try {\n const cluster = require('cluster')\n if (cluster.isWorker && cluster.worker) {\n return ` cluster_worker_id=${cluster.worker.id}`\n }\n } catch (_) {\n /* ignore */\n }\n return ''\n}\n\nconst HTTP_METRICS_LOG_INTERVAL_MS = 5000\n\n/**\n * MetricsClient handles Prometheus metrics collection and push.\n *\n * **Redis HTTP:** When `METRICS_HTTP_ENABLED` and a Redis URL exist, HTTP counts use Redis\n * (key segment from BUILD_APP_NAME + process type). Production **web.*** dynos only write Redis;\n * run Procfile **`metrics`** + **metrics-exporter.js** to drain and POST. Tests: `httpMetricsRedisAggregation: false`.\n */\nclass MetricsClient extends 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, metrics, etc.)\n * @param {boolean} [config.enabled] Enable metrics collection\n * @param {boolean} [config.httpMetricsEnabled] Enable HTTP request metrics (app_requests_total, app_requests_total_duration); defaults from METRICS_HTTP_ENABLED\n * @param {boolean} [config.logValues] Log metrics values to console\n * @param {string} [config.pushgatewayUrl] Push URL (VM-agent import endpoint)\n * @param {string} [config.pushgatewaySecret] Basic auth secret (Base64 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 VM-agent (use HTTP scraping instead)\n * @param {boolean} [config.includeNodeDefaultMetrics] Register prom-client default process metrics\n * @param {string} [config.httpMetricsRedisUrl] Overrides METRICS_HTTP_REDIS_URL / REDIS_URL for the HTTP Redis store\n * @param {boolean} [config.httpMetricsRedisAggregation] When unset, on if Redis + HTTP metrics; set false to force in-process HTTP counters only\n */\n constructor(config = {}) {\n const processTypeBeforeSuper =\n config.processType || process.env.BUILD_DYNO_PROCESS_TYPE || 'undefined_build_dyno_type'\n\n const redisUrl =\n config.httpMetricsRedisUrl ||\n process.env.METRICS_HTTP_REDIS_URL ||\n process.env.REDIS_URL ||\n ''\n\n let httpMetricsEnabled\n if (config.httpMetricsEnabled !== undefined) {\n httpMetricsEnabled = config.httpMetricsEnabled\n } else if (process.env.METRICS_HTTP_ENABLED === 'true') {\n httpMetricsEnabled = true\n } else if (isMetricsExporterProcessType(processTypeBeforeSuper) && redisUrl) {\n // metrics: dyno must drain Redis HTTP keys; many stacks omit METRICS_HTTP_ENABLED here\n httpMetricsEnabled = true\n } else {\n httpMetricsEnabled = false\n }\n\n let httpMetricsRedisAggregation =\n Boolean(redisUrl) &&\n Boolean(httpMetricsEnabled) &&\n config.httpMetricsRedisAggregation !== false\n\n if (httpMetricsRedisAggregation && !isRedisPeerInstalled()) {\n console.warn(\n '[MetricsClient] METRICS_HTTP_ENABLED with REDIS_URL needs the peer package `redis` in your application (npm install redis@3). Falling back to in-process HTTP counters. @adalo/metrics does not bundle redis.'\n )\n httpMetricsRedisAggregation = false\n }\n\n const isWebCtx = isWebServingDynoContext(processTypeBeforeSuper)\n const webRedisRecordingOnly =\n Boolean(httpMetricsRedisAggregation) &&\n isWebCtx &&\n process.env.NODE_ENV === 'production'\n\n super({\n ...config,\n ...(webRedisRecordingOnly ? { includeNodeDefaultMetrics: false } : {}),\n })\n\n this.httpMetricsEnabled = httpMetricsEnabled\n this.httpMetricsRedisAggregation = httpMetricsRedisAggregation\n\n const redisKeyProcessType = isMetricsExporterProcessType(this.processType)\n ? 'web'\n : this.processType\n\n this._httpRedisStore =\n this.httpMetricsEnabled && this.httpMetricsRedisAggregation\n ? new HttpMetricsRedisStore({\n redisUrl,\n appName: this.appName,\n processType: redisKeyProcessType,\n })\n : null\n\n this._webRedisHttpRecordingOnly = Boolean(\n webRedisRecordingOnly && this._httpRedisStore\n )\n\n this.exportRuntimeGauges = !this._webRedisHttpRecordingOnly\n\n const wcParsed = parseInt(process.env.WEB_CONCURRENCY || '', 10)\n this._bootstrapWebConcurrency = Number.isFinite(wcParsed) ? wcParsed : 0\n this._bootstrapHostCpuCount = os.cpus().length\n\n if (this.httpMetricsRedisAggregation && redisUrl) {\n if (isMetricsExporterProcessType(this.processType)) {\n this.defaultLabelsWithoutDynoId = {\n app: this.appName,\n process_type: 'web',\n }\n } else if (!this._webRedisHttpRecordingOnly) {\n this.defaultLabelsWithoutDynoId = {\n app: this.appName,\n process_type: this.processType,\n }\n }\n }\n\n this._skippedPushWorkerLogged = false\n\n this._lastUsageMicros = 0\n this._lastCheckTime = Date.now()\n\n this._metricsBootstrapLogged = false\n this._httpLogWindowStart = 0\n this._httpLogBatchCount = 0\n this._httpLogLastSample = ''\n\n this._initDefaultMetrics()\n this._logMetricsBootstrap()\n }\n\n _logMetricsBootstrap = () => {\n if (!this.enabled) {\n return\n }\n if (this._metricsBootstrapLogged) {\n return\n }\n this._metricsBootstrapLogged = true\n const wc = process.env.WEB_CONCURRENCY || ''\n const httpMode = !this.httpMetricsEnabled\n ? 'off'\n : this._httpRedisStore\n ? 'redis'\n : 'local_counters'\n const pidTag = `pid=${process.pid}`\n const clusterTag = clusterWorkerTag()\n console.warn(\n `${this.prefixLogs} metrics bootstrap ${pidTag}${clusterTag} WEB_CONCURRENCY=${wc || '(unset)'} host_cpus=${this._bootstrapHostCpuCount} dyno_id=${this.dynoId} process_type=${this.processType} scheduling_id=${process.env.DYNO || 'n/a'} http_metrics=${httpMode} redis_http=${this.httpMetricsRedisAggregation} web_redis_only=${this._webRedisHttpRecordingOnly}`\n )\n }\n\n /**\n * Throttled HTTP recording log (not gated by METRICS_LOG_VALUES). Same pod can run many workers\n * (same dyno_id); pid + cluster_worker_id distinguish processes.\n */\n _maybeLogHttpRecord(method, route, status_code) {\n if (!this.enabled || !this.httpMetricsEnabled) {\n return\n }\n this._httpLogBatchCount += 1\n this._httpLogLastSample = `${method} ${route} ${status_code}`\n const now = Date.now()\n if (!this._httpLogWindowStart) {\n this._httpLogWindowStart = now\n }\n if (now - this._httpLogWindowStart < HTTP_METRICS_LOG_INTERVAL_MS) {\n return\n }\n const clusterTag = clusterWorkerTag()\n console.warn(\n `${this.prefixLogs} http_metrics_record pid=${process.pid}${clusterTag} process_type=${this.processType} dyno_id=${this.dynoId} count=${this._httpLogBatchCount} sample=${this._httpLogLastSample}`\n )\n this._httpLogWindowStart = now\n this._httpLogBatchCount = 0\n }\n\n get isWebRedisHttpRecordingOnly() {\n return Boolean(this._webRedisHttpRecordingOnly)\n }\n\n _initDefaultMetrics = () => {\n if (this.exportRuntimeGauges) {\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.createGauge({\n name: 'app_bootstrap_web_concurrency',\n help: 'WEB_CONCURRENCY at process start (diagnostic; 0 if unset/invalid)',\n updateFn: () => this._bootstrapWebConcurrency,\n })\n\n this.createGauge({\n name: 'app_bootstrap_host_cpu_count',\n help: 'os.cpus().length at process start (diagnostic)',\n updateFn: () => this._bootstrapHostCpuCount,\n })\n }\n\n const registerHttpCounters =\n this.httpMetricsEnabled && !this._webRedisHttpRecordingOnly\n\n if (registerHttpCounters) {\n this.createCounter({\n name: 'app_requests_total',\n help: 'Total number of HTTP requests',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n\n this.createCounter({\n name: 'app_requests_total_duration',\n help: 'Total duration of HTTP requests in milliseconds',\n labelNames: this.withDefaultLabelsWithoutDynoId([\n 'method',\n 'route',\n 'appId',\n 'databaseId',\n 'status_code',\n ]),\n useLabelsWithoutDynoId: true,\n })\n }\n }\n\n 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 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 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 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 measureLag() {\n return new Promise(resolve => {\n const start = Date.now()\n setImmediate(() => resolve(Date.now() - start))\n })\n }\n\n trackHttpRequest({\n method,\n route,\n status_code,\n appId = '',\n databaseId = '',\n duration,\n }) {\n if (!this.httpMetricsEnabled) return\n\n if (this._httpRedisStore) {\n this._httpRedisStore.record(\n method,\n route,\n status_code,\n appId,\n databaseId,\n duration\n )\n this._maybeLogHttpRecord(method, route, status_code)\n return\n }\n\n this.countersFunctions?.app_requests_total({\n method,\n route,\n status_code,\n appId,\n databaseId,\n })\n this.countersFunctions?.app_requests_total_duration(\n {\n method,\n route,\n status_code,\n appId,\n databaseId,\n },\n duration\n )\n this._maybeLogHttpRecord(method, route, status_code)\n }\n\n /**\n * Start periodic push to VM-agent (`METRICS_PUSHGATEWAY_URL`). Interval defaults to `intervalSec` from config/env.\n * @param {number} [interval] Seconds between pushes; defaults to `this.intervalSec`\n * @param {() => void | Promise<void>} [customPushMetrics] Optional; replaces default push when provided\n * @returns {void}\n */\n startPush = (interval, customPushMetrics) => {\n if (this._webRedisHttpRecordingOnly) {\n return\n }\n if (!shouldRunMetricsPush(this)) {\n if (\n this.enabled &&\n !this._skippedPushWorkerLogged &&\n process.env.METRICS_SUPPRESS_SKIP_PUSH_LOG !== 'true'\n ) {\n console.warn(\n `${this.prefixLogs} Skipping VM-agent push on this worker. METRICS_PUSH_ALL_WORKERS=true restores per-worker push.`\n )\n this._skippedPushWorkerLogged = true\n }\n return\n }\n this._startPush(interval, customPushMetrics)\n }\n\n async pushMetrics() {\n if (this._webRedisHttpRecordingOnly) {\n return\n }\n if (\n this._httpRedisStore &&\n shouldRunMetricsPush(this) &&\n this.countersFunctions?.app_requests_total &&\n this.countersFunctions?.app_requests_total_duration\n ) {\n const flushed = await this._httpRedisStore.flushToCounters(\n (labels, value) =>\n this.countersFunctions.app_requests_total(labels, value),\n (labels, value) =>\n this.countersFunctions.app_requests_total_duration(labels, value)\n )\n if (flushed) {\n console.warn(\n `${this.prefixLogs} http_metrics_redis_flush pid=${process.pid}${clusterWorkerTag()} process_type=${this.processType} dyno_id=${this.dynoId} drained=true`\n )\n }\n }\n // eslint-disable-next-line consistent-return\n return super.pushMetrics()\n }\n\n trackHttpRequestMiddleware = (req, res, next) => {\n if (!this.enabled || !this.httpMetricsEnabled || req.method === 'OPTIONS') {\n next()\n return\n }\n\n const start = Date.now()\n res.on('finish', () => {\n const route = req.route?.path || req.path || 'unknown'\n const appId =\n req.params?.appId || req.body?.appId || req.query?.appId || ''\n const databaseId =\n req.params?.databaseId ||\n req.body?.databaseId ||\n req.query?.databaseId ||\n req.params?.datasourceId ||\n req.body?.datasourceId ||\n req.query?.datasourceId ||\n ''\n\n this.trackHttpRequest({\n method: req.method,\n route,\n status_code: res.statusCode,\n appId,\n databaseId,\n duration: Date.now() - start,\n })\n })\n\n next()\n }\n}\n\nmodule.exports = { MetricsClient }\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.14",
3
+ "version": "0.0.0-staging.16",
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",
@@ -419,6 +419,7 @@ class BaseMetricsClient {
419
419
  }
420
420
  return new Promise((resolve, reject) => {
421
421
  const u = new URL(pushUrl)
422
+ let payloadBytes = 0
422
423
  const req = (u.protocol === 'https:' ? https : http).request(
423
424
  {
424
425
  hostname: u.hostname,
@@ -437,7 +438,11 @@ class BaseMetricsClient {
437
438
  : undefined,
438
439
  },
439
440
  res => {
441
+ res.resume()
440
442
  if (res.statusCode >= 200 && res.statusCode < 300) {
443
+ console.warn(
444
+ `${this.prefixLogs} metrics_pushed pid=${process.pid} bytes=${payloadBytes} status=${res.statusCode}`
445
+ )
441
446
  resolve()
442
447
  } else {
443
448
  let data = ''
@@ -454,7 +459,8 @@ class BaseMetricsClient {
454
459
  this._registry
455
460
  .metrics()
456
461
  .then(metrics => {
457
- req.setHeader('Content-Length', Buffer.byteLength(metrics, 'utf8'))
462
+ payloadBytes = Buffer.byteLength(metrics, 'utf8')
463
+ req.setHeader('Content-Length', payloadBytes)
458
464
  req.end(metrics, 'utf8')
459
465
  })
460
466
  .catch(reject)
@@ -27,6 +27,21 @@ function isMetricsExporterProcessType(processType) {
27
27
  return processType === 'metrics'
28
28
  }
29
29
 
30
+ /** Extra tag when `cluster` workers are used (same pod hostname, different PIDs). */
31
+ function clusterWorkerTag() {
32
+ try {
33
+ const cluster = require('cluster')
34
+ if (cluster.isWorker && cluster.worker) {
35
+ return ` cluster_worker_id=${cluster.worker.id}`
36
+ }
37
+ } catch (_) {
38
+ /* ignore */
39
+ }
40
+ return ''
41
+ }
42
+
43
+ const HTTP_METRICS_LOG_INTERVAL_MS = 5000
44
+
30
45
  /**
31
46
  * MetricsClient handles Prometheus metrics collection and push.
32
47
  *
@@ -57,17 +72,24 @@ class MetricsClient extends BaseMetricsClient {
57
72
  const processTypeBeforeSuper =
58
73
  config.processType || process.env.BUILD_DYNO_PROCESS_TYPE || 'undefined_build_dyno_type'
59
74
 
60
- const httpMetricsEnabled =
61
- config.httpMetricsEnabled ??
62
- process.env.METRICS_HTTP_ENABLED === 'true' ??
63
- false
64
-
65
75
  const redisUrl =
66
76
  config.httpMetricsRedisUrl ||
67
77
  process.env.METRICS_HTTP_REDIS_URL ||
68
78
  process.env.REDIS_URL ||
69
79
  ''
70
80
 
81
+ let httpMetricsEnabled
82
+ if (config.httpMetricsEnabled !== undefined) {
83
+ httpMetricsEnabled = config.httpMetricsEnabled
84
+ } else if (process.env.METRICS_HTTP_ENABLED === 'true') {
85
+ httpMetricsEnabled = true
86
+ } else if (isMetricsExporterProcessType(processTypeBeforeSuper) && redisUrl) {
87
+ // metrics: dyno must drain Redis HTTP keys; many stacks omit METRICS_HTTP_ENABLED here
88
+ httpMetricsEnabled = true
89
+ } else {
90
+ httpMetricsEnabled = false
91
+ }
92
+
71
93
  let httpMetricsRedisAggregation =
72
94
  Boolean(redisUrl) &&
73
95
  Boolean(httpMetricsEnabled) &&
@@ -136,6 +158,11 @@ class MetricsClient extends BaseMetricsClient {
136
158
  this._lastUsageMicros = 0
137
159
  this._lastCheckTime = Date.now()
138
160
 
161
+ this._metricsBootstrapLogged = false
162
+ this._httpLogWindowStart = 0
163
+ this._httpLogBatchCount = 0
164
+ this._httpLogLastSample = ''
165
+
139
166
  this._initDefaultMetrics()
140
167
  this._logMetricsBootstrap()
141
168
  }
@@ -144,12 +171,48 @@ class MetricsClient extends BaseMetricsClient {
144
171
  if (!this.enabled) {
145
172
  return
146
173
  }
174
+ if (this._metricsBootstrapLogged) {
175
+ return
176
+ }
177
+ this._metricsBootstrapLogged = true
147
178
  const wc = process.env.WEB_CONCURRENCY || ''
179
+ const httpMode = !this.httpMetricsEnabled
180
+ ? 'off'
181
+ : this._httpRedisStore
182
+ ? 'redis'
183
+ : 'local_counters'
184
+ const pidTag = `pid=${process.pid}`
185
+ const clusterTag = clusterWorkerTag()
148
186
  console.warn(
149
- `${this.prefixLogs} metrics bootstrap WEB_CONCURRENCY=${wc || '(unset)'} host_cpus=${this._bootstrapHostCpuCount} dyno=${process.env.DYNO || '(unset)'} redis_http=${this.httpMetricsRedisAggregation} web_redis_only=${this._webRedisHttpRecordingOnly}`
187
+ `${this.prefixLogs} metrics bootstrap ${pidTag}${clusterTag} WEB_CONCURRENCY=${wc || '(unset)'} host_cpus=${this._bootstrapHostCpuCount} dyno_id=${this.dynoId} process_type=${this.processType} scheduling_id=${process.env.DYNO || 'n/a'} http_metrics=${httpMode} redis_http=${this.httpMetricsRedisAggregation} web_redis_only=${this._webRedisHttpRecordingOnly}`
150
188
  )
151
189
  }
152
190
 
191
+ /**
192
+ * Throttled HTTP recording log (not gated by METRICS_LOG_VALUES). Same pod can run many workers
193
+ * (same dyno_id); pid + cluster_worker_id distinguish processes.
194
+ */
195
+ _maybeLogHttpRecord(method, route, status_code) {
196
+ if (!this.enabled || !this.httpMetricsEnabled) {
197
+ return
198
+ }
199
+ this._httpLogBatchCount += 1
200
+ this._httpLogLastSample = `${method} ${route} ${status_code}`
201
+ const now = Date.now()
202
+ if (!this._httpLogWindowStart) {
203
+ this._httpLogWindowStart = now
204
+ }
205
+ if (now - this._httpLogWindowStart < HTTP_METRICS_LOG_INTERVAL_MS) {
206
+ return
207
+ }
208
+ const clusterTag = clusterWorkerTag()
209
+ console.warn(
210
+ `${this.prefixLogs} http_metrics_record pid=${process.pid}${clusterTag} process_type=${this.processType} dyno_id=${this.dynoId} count=${this._httpLogBatchCount} sample=${this._httpLogLastSample}`
211
+ )
212
+ this._httpLogWindowStart = now
213
+ this._httpLogBatchCount = 0
214
+ }
215
+
153
216
  get isWebRedisHttpRecordingOnly() {
154
217
  return Boolean(this._webRedisHttpRecordingOnly)
155
218
  }
@@ -334,6 +397,7 @@ class MetricsClient extends BaseMetricsClient {
334
397
  databaseId,
335
398
  duration
336
399
  )
400
+ this._maybeLogHttpRecord(method, route, status_code)
337
401
  return
338
402
  }
339
403
 
@@ -354,6 +418,7 @@ class MetricsClient extends BaseMetricsClient {
354
418
  },
355
419
  duration
356
420
  )
421
+ this._maybeLogHttpRecord(method, route, status_code)
357
422
  }
358
423
 
359
424
  /**
@@ -392,12 +457,17 @@ class MetricsClient extends BaseMetricsClient {
392
457
  this.countersFunctions?.app_requests_total &&
393
458
  this.countersFunctions?.app_requests_total_duration
394
459
  ) {
395
- await this._httpRedisStore.flushToCounters(
460
+ const flushed = await this._httpRedisStore.flushToCounters(
396
461
  (labels, value) =>
397
462
  this.countersFunctions.app_requests_total(labels, value),
398
463
  (labels, value) =>
399
464
  this.countersFunctions.app_requests_total_duration(labels, value)
400
465
  )
466
+ if (flushed) {
467
+ console.warn(
468
+ `${this.prefixLogs} http_metrics_redis_flush pid=${process.pid}${clusterWorkerTag()} process_type=${this.processType} dyno_id=${this.dynoId} drained=true`
469
+ )
470
+ }
401
471
  }
402
472
  // eslint-disable-next-line consistent-return
403
473
  return super.pushMetrics()