From 1fe6ef6da04c30fa052c76c8392412ec8b28af5d Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Fri, 24 Dec 2021 12:12:31 +0530 Subject: [PATCH] Use process.getActiveResourcesInfo() in process metrics We should use process.getActiveResourcesInfo() here because it is a public alternative of the private APIs process._getActiveHandles() and process._getActiveRequests(). Refs: https://nodejs.org/api/process.html#processgetactiveresourcesinfo Signed-off-by: Darshan Sen --- CHANGELOG.md | 7 ++++ example/.default-metrics.js.swp | Bin 0 -> 16384 bytes lib/defaultMetrics.js | 6 ++- lib/metrics/processResources.js | 58 +++++++++++++++++++++++++++ test/metrics/processResourcesTest.js | 37 +++++++++++++++++ 5 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 example/.default-metrics.js.swp create mode 100644 lib/metrics/processResources.js create mode 100644 test/metrics/processResourcesTest.js diff --git a/CHANGELOG.md b/CHANGELOG.md index c27e15bf..10cd57f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,13 @@ project adheres to [Semantic Versioning](http://semver.org/). causing vscode to think that push/pushAdd and delete didn't promise resulting in incorrect behavior. +- The `processHandles` and `processRequests` metrics were replaced by the more + advanced `processResources` metric, which keeps a track of all sorts of active + resources. It consists of the following gauges: + - `nodejs_active_resources` - Number of active resources that are currently + keeping the event loop alive, grouped by async resource type. + - `nodejs_active_resources_total` - Total number of active resources. + ### Added ## [14.0.0] - 2021-09-18 diff --git a/example/.default-metrics.js.swp b/example/.default-metrics.js.swp new file mode 100644 index 0000000000000000000000000000000000000000..cc5c4b61aea1ec3c2419b927923ce7eade3c964e GIT binary patch literal 16384 zcmeI2ON<;x9mdNh5KQ7YzyTo?RICxZcE+>aJ=62Bvq;3di6gx0O>iPb5DhimwX?n3 zk8yX;csG{eh7^PZ2Z#Ux3I{j^fq>)yha4iJ937McF}ENEi3{N=B_fEg`Z3efQ}Z%8 zfYdAhc0Aqxs`~n?e^*!4c3XMup-tK-YXm=c6Y|F4Z`f_mKYQfQPY~iWE3n* zz6HJsHo!-~-*1Oj@B`q1hrk8!hxZZkbMRI06gUn3^Ut*M3tGFJ zbz6b;ByWXY$m~}8FysO4uwI9glk~#I#qH^lcs;}5K|n=4RMbOv@CpXqLhX3bf?6(( zYAh3yG{}t5>hqS*uk?5j263}gL1S52@+g@sdE{krG1TojZSGTVhsIGfZ548+wkI_p z*Am^z*KS+wUVqRA?f71=%S{-N9;4x5mzU{ApZlZ7s1?xj_ufklo54$$Ieg48ycA>F ze1>16K~wA+4V>Us5;V!IJ6Yt}tAKUY~Et_>(-Da~D zaKm%WU@W;NN6H+V9DRA0(`GZ=rCshDXxy^7$h8oMeKhTx==`WO6|$(wZFU$8J2qdj zGPmGiXP2L}vyv5wyATX*r7d~5f zu7J=Ugt%*&Z=&z}77H!Uz4ZifICJL~KN37!`bfACE)M(`QrW>k>GafaGLdu0%tS{j z*@5NsoXL0Fq?^sLhE_H;tx~CLN~KnA=+%0?HZ?15+ExCpVMM8a|Z0E_dmU7&T2E z7{1jFJztEEX-RQDYPQ)(`(*5tw8=ri;d5tJThTlyp8V(vvY}oZYcmrfZ8dX#du&#e znFyuV(6{`shbfoC9nU{ZW?pc&T4`u?Eq}$*XsHfq)J(;!!p# zs#1p~jppFaNUuO1EfpmXeI#jamMn~;e%J~vhY!XTRd47@bnoV!k_1a7N#Z^y+M%MI zbnr_q6^6M6r#;s?puFoDyJBWLexDfKUMeu1*p?OCGs;*pjYk?@5AR3&xSD9zoq9YT z7|+&v0cN&%y3pV-2)RSgZ$BCf4{+CnQ{+YA5|s)Dzr2-EV@527_W1Zcgj~zoh7TM@s>O9Sm+JEJUCtsce%R~gg#yS zG{tl@@NACv**oIUsZ-|&xsG4QI>vg>kLCyW_l$iW250H#6=aTDqWFk@;}`yEC8|GB zqN}SEkCT8?=JRO2nY`mp+i~$SIVO_W>xE}AX5*(s49~W?5w6EC!*9m#!mnH-IN=5% zy#{fTfD)dq@g>pgbPZpw^emsRESJm6Li{pZ#3To=)rZUHh*b5!?sPfj93W++|;Cb$Sb2HuALpMjr(`@r4c z1Q6GC2Pl3D2MPxY2MPx!J8*h6zs1%uT4wT)OCRcV7f(yC<1;6n^(32wve*Zi-)0@2 zKBvT5I;~G^vF%%~S)!gB&DN-sT5VVwxnwKuc#3Jtu2+hEHrlU-=*txFdIqKXDgKMj*SGQ zTcycCsNL~~;-W=G&}9Z@zG9b_^_gX}w$0=9!nNqs+LFt!V%y8_@Rrx!!((2kQmXZ; zUQ72~=d8%U#%;bPWx+afLKZFVurA~;T1p7Ng<_sY^6= zoS7}pwGSh?qx;Y^8kc6%tTNjpi&2qE@$zqwO0%}?^9^9qXUl#MP_Ygjlf&{&*t*mQ!#1zJn;P1mc{1xjUet)gYt zp%apQXzg67mI~%t|+YfF-({-+5CtO>HQZoM?IM$bM3CdGg4u { + // Don't do anything if the function does not exist in previous nodes (exists in node@17.3.0) + if (typeof process.getActiveResourcesInfo !== 'function') { + return; + } + + const namePrefix = config.prefix ? config.prefix : ''; + const labels = config.labels ? config.labels : {}; + const labelNames = Object.keys(labels); + + new Gauge({ + name: namePrefix + NODEJS_ACTIVE_RESOURCES, + help: + 'Number of active resources that are currently keeping the event loop alive, grouped by async resource type.', + labelNames: ['type', ...labelNames], + registers: registry ? [registry] : undefined, + collect() { + const resources = process.getActiveResourcesInfo(); + + const data = {}; + + for (let i = 0; i < resources.length; i++) { + const resource = resources[i]; + + if (Object.hasOwn(data, resource)) { + data[resource] += 1; + } else { + data[resource] = 1; + } + } + + updateMetrics(this, data, labels); + }, + }); + + new Gauge({ + name: namePrefix + NODEJS_ACTIVE_RESOURCES_TOTAL, + help: 'Total number of active resources.', + registers: registry ? [registry] : undefined, + labelNames, + collect() { + const resources = process.getActiveResourcesInfo(); + this.set(labels, resources.length); + }, + }); +}; + +module.exports.metricNames = [ + NODEJS_ACTIVE_RESOURCES, + NODEJS_ACTIVE_RESOURCES_TOTAL, +]; diff --git a/test/metrics/processResourcesTest.js b/test/metrics/processResourcesTest.js new file mode 100644 index 00000000..42b4f540 --- /dev/null +++ b/test/metrics/processResourcesTest.js @@ -0,0 +1,37 @@ +'use strict'; + +describe('processRequests', () => { + const register = require('../../index').register; + const processResources = require('../../lib/metrics/processResources'); + + beforeAll(() => { + register.clear(); + }); + + afterEach(() => { + register.clear(); + }); + + it('should add metric to the registry', async () => { + if (typeof process.getActiveResourcesInfo !== 'function') { + return; + } + + expect(await register.getMetricsAsJSON()).toHaveLength(0); + + processResources(); + + const metrics = await register.getMetricsAsJSON(); + + expect(metrics).toHaveLength(2); + expect(metrics[0].help).toEqual( + 'Number of active resources that are currently keeping the event loop alive, grouped by async resource type.', + ); + expect(metrics[0].type).toEqual('gauge'); + expect(metrics[0].name).toEqual('nodejs_active_resources'); + + expect(metrics[1].help).toEqual('Total number of active resources.'); + expect(metrics[1].type).toEqual('gauge'); + expect(metrics[1].name).toEqual('nodejs_active_resources_total'); + }); +});