Socket
Socket
Sign inDemoInstall

prom-client

Package Overview
Dependencies
Maintainers
3
Versions
84
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

prom-client - npm Package Compare versions

Comparing version 13.1.0 to 13.2.0

15

CHANGELOG.md

@@ -16,2 +16,17 @@ # Changelog

## [13.2.0] - 2021-08-08
### Changed
- Don't add event listener to `process` if cluster module is not used.
- fix: set labels for default memory metrics on linux.
- fix: fix DEP0152 deprecation warning in Node.js v16+.
- fix: Set aggregation mode for newer event loop metrics. (Fixes [#418](https://github.com/siimon/prom-client/issues/418))
- Improve performance of/reduce memory allocations in Gauge.
### Added
- feat: added `zero()` to `Histogram` for setting the metrics for a given label combination to zero
- fix: allow `Gauge.inc/dec(0)` without defaulting to 1
## [13.1.0] - 2021-01-24

@@ -18,0 +33,0 @@

@@ -393,2 +393,7 @@ // Type definitions for prom-client

/**
* Initialize the metrics for the given combination of labels to zero
*/
zero(labels: LabelValues<T>): void;
/**
* Return the child for given labels

@@ -395,0 +400,0 @@ * @param values Label values

42

lib/cluster.js

@@ -183,25 +183,27 @@ 'use strict';

}
}
// Respond to master's requests for worker's local metrics.
process.on('message', message => {
if (cluster().isWorker && message.type === GET_METRICS_REQ) {
Promise.all(registries.map(r => r.getMetricsAsJSON()))
.then(metrics => {
process.send({
type: GET_METRICS_RES,
requestId: message.requestId,
metrics,
});
})
.catch(error => {
process.send({
type: GET_METRICS_RES,
requestId: message.requestId,
error: error.message,
});
});
if (cluster().isWorker) {
// Respond to master's requests for worker's local metrics.
process.on('message', message => {
if (message.type === GET_METRICS_REQ) {
Promise.all(registries.map(r => r.getMetricsAsJSON()))
.then(metrics => {
process.send({
type: GET_METRICS_RES,
requestId: message.requestId,
metrics,
});
})
.catch(error => {
process.send({
type: GET_METRICS_RES,
requestId: message.requestId,
error: error.message,
});
});
}
});
}
});
}
module.exports = AggregatorRegistry;

@@ -20,8 +20,21 @@ /**

inc(labels, value) {
if (!isObject(labels)) {
return inc.call(this, null)(labels, value);
let hash;
if (isObject(labels)) {
hash = hashObject(labels);
validateLabel(this.labelNames, labels);
} else {
value = labels;
labels = {};
}
const hash = hashObject(labels);
return inc.call(this, labels, hash)(value);
if (value && !Number.isFinite(value)) {
throw new TypeError(`Value is not a valid number: ${util.format(value)}`);
}
if (value < 0) {
throw new Error('It is not possible to decrease a counter');
}
if (value === null || value === undefined) value = 1;
setValue(this.hashMap, value, labels, hash);
}

@@ -34,3 +47,6 @@

reset() {
return reset.call(this);
this.hashMap = {};
if (this.labelNames.length === 0) {
setValue(this.hashMap, 0);
}
}

@@ -52,8 +68,6 @@

labels() {
const labels = getLabels(this.labelNames, arguments) || {};
validateLabel(this.labelNames, labels);
const hash = hashObject(labels);
labels(...args) {
const labels = getLabels(this.labelNames, args) || {};
return {
inc: inc.call(this, labels, hash),
inc: this.inc.bind(this, labels),
};

@@ -69,34 +83,7 @@ }

const reset = function () {
this.hashMap = {};
if (this.labelNames.length === 0) {
this.hashMap = setValue({}, 0);
}
};
const inc = function (labels, hash) {
return value => {
if (value && !Number.isFinite(value)) {
throw new TypeError(`Value is not a valid number: ${util.format(value)}`);
}
if (value < 0) {
throw new Error('It is not possible to decrease a counter');
}
labels = labels || {};
validateLabel(this.labelNames, labels);
const incValue = value === null || value === undefined ? 1 : value;
this.hashMap = setValue(this.hashMap, incValue, labels, hash);
};
};
function setValue(hashMap, value, labels, hash) {
hash = hash || '';
function setValue(hashMap, value, labels = {}, hash = '') {
if (hashMap[hash]) {
hashMap[hash].value += value;
} else {
hashMap[hash] = { value, labels: labels || {} };
hashMap[hash] = { value, labels };
}

@@ -103,0 +90,0 @@ return hashMap;

@@ -27,6 +27,5 @@ /**

set(labels, value) {
if (!isObject(labels)) {
return set.call(this, null)(labels, value);
}
return set.call(this, labels)(value);
value = getValueArg(labels, value);
labels = getLabelArg(labels);
set(this, labels, value);
}

@@ -39,3 +38,6 @@

reset() {
return reset.call(this);
this.hashMap = {};
if (this.labelNames.length === 0) {
setValue(this.hashMap, 0, {});
}
}

@@ -50,3 +52,6 @@

inc(labels, value) {
inc.call(this, labels)(value);
value = getValueArg(labels, value);
labels = getLabelArg(labels);
if (value === undefined) value = 1;
set(this, labels, this._getValue(labels) + value);
}

@@ -61,3 +66,6 @@

dec(labels, value) {
dec.call(this, labels)(value);
value = getValueArg(labels, value);
labels = getLabelArg(labels);
if (value === undefined) value = 1;
set(this, labels, this._getValue(labels) - value);
}

@@ -71,3 +79,8 @@

setToCurrentTime(labels) {
return setToCurrentTime.call(this, labels)();
const now = Date.now() / 1000;
if (labels === undefined) {
this.set(now);
} else {
this.set(labels, now);
}
}

@@ -86,3 +99,7 @@

startTimer(labels) {
return startTimer.call(this, labels)();
const start = process.hrtime();
return endLabels => {
const delta = process.hrtime(start);
this.set(Object.assign({}, labels, endLabels), delta[0] + delta[1] / 1e9);
};
}

@@ -113,7 +130,7 @@

return {
inc: inc.call(this, labels),
dec: dec.call(this, labels),
set: set.call(this, labels),
setToCurrentTime: setToCurrentTime.call(this, labels),
startTimer: startTimer.call(this, labels),
inc: this.inc.bind(this, labels),
dec: this.dec.bind(this, labels),
set: this.set.bind(this, labels),
setToCurrentTime: this.setToCurrentTime.bind(this, labels),
startTimer: this.startTimer.bind(this, labels),
};

@@ -129,74 +146,19 @@ }

function setToCurrentTime(labels) {
return () => {
const now = Date.now() / 1000;
if (labels === undefined) {
this.set(now);
} else {
this.set(labels, now);
}
};
}
function set(gauge, labels, value) {
if (typeof value !== 'number') {
throw new TypeError(`Value is not a valid number: ${util.format(value)}`);
}
function startTimer(startLabels) {
return () => {
const start = process.hrtime();
return endLabels => {
const delta = process.hrtime(start);
this.set(
Object.assign({}, startLabels, endLabels),
delta[0] + delta[1] / 1e9,
);
};
};
validateLabel(gauge.labelNames, labels);
setValue(gauge.hashMap, value, labels);
}
function dec(labels) {
return value => {
const data = convertLabelsAndValues(labels, value);
this.set(data.labels, this._getValue(data.labels) - (data.value || 1));
};
function getLabelArg(labels) {
return isObject(labels) ? labels : {};
}
function inc(labels) {
return value => {
const data = convertLabelsAndValues(labels, value);
this.set(data.labels, this._getValue(data.labels) + (data.value || 1));
};
function getValueArg(labels, value) {
return isObject(labels) ? value : labels;
}
function set(labels) {
return value => {
if (typeof value !== 'number') {
throw new TypeError(`Value is not a valid number: ${util.format(value)}`);
}
labels = labels || {};
validateLabel(this.labelNames, labels);
this.hashMap = setValue(this.hashMap, value, labels);
};
}
function reset() {
this.hashMap = {};
if (this.labelNames.length === 0) {
this.hashMap = setValue({}, 0, {});
}
}
function convertLabelsAndValues(labels, value) {
if (!isObject(labels)) {
return {
value: labels,
labels: {},
};
}
return {
labels,
value,
};
}
module.exports = Gauge;

@@ -77,2 +77,15 @@ /**

/**
* Initialize the metrics for the given combination of labels to zero
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @returns {void}
*/
zero(labels) {
const hash = hashObject(labels);
this.hashMap[hash] = createBaseValues(
labels,
Object.assign({}, this.bucketValues),
);
}
/**
* Start a timer that could be used to logging durations

@@ -79,0 +92,0 @@ * @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep

@@ -80,2 +80,3 @@ 'use strict';

labelNames,
aggregator: 'min',
});

@@ -87,2 +88,3 @@ const lagMax = new Gauge({

labelNames,
aggregator: 'max',
});

@@ -94,2 +96,3 @@ const lagMean = new Gauge({

labelNames,
aggregator: 'average',
});

@@ -101,2 +104,3 @@ const lagStddev = new Gauge({

labelNames,
aggregator: 'average',
});

@@ -108,2 +112,3 @@ const lagP50 = new Gauge({

labelNames,
aggregator: 'average',
});

@@ -115,2 +120,3 @@ const lagP90 = new Gauge({

labelNames,
aggregator: 'average',
});

@@ -122,2 +128,3 @@ const lagP99 = new Gauge({

labelNames,
aggregator: 'average',
});

@@ -124,0 +131,0 @@ };

@@ -44,14 +44,14 @@ 'use strict';

const entry = list.getEntries()[0];
// Node < 16 uses entry.kind
// Node >= 16 uses entry.detail.kind
// See: https://nodejs.org/docs/latest-v16.x/api/deprecations.html#deprecations_dep0152_extension_performanceentry_properties
const kind = entry.detail ? kinds[entry.detail.kind] : kinds[entry.kind];
// Convert duration from milliseconds to seconds
gcHistogram.observe(
Object.assign({ kind: kinds[entry.kind] }, labels),
entry.duration / 1000,
);
gcHistogram.observe(Object.assign({ kind }, labels), entry.duration / 1000);
});
// We do not expect too many gc events per second, so we do not use buffering
obs.observe({ entryTypes: ['gc'], buffered: false });
obs.observe({ entryTypes: ['gc'] });
};
module.exports.metricNames = [NODEJS_GC_DURATION_SECONDS];

@@ -37,2 +37,4 @@ 'use strict';

const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);

@@ -43,2 +45,3 @@ const residentMemGauge = new Gauge({

registers,
labelNames,
// Use this one metric's `collect` to set all metrics' values.

@@ -56,5 +59,5 @@ collect() {

residentMemGauge.set(structuredOutput.VmRSS);
virtualMemGauge.set(structuredOutput.VmSize);
heapSizeMemGauge.set(structuredOutput.VmData);
residentMemGauge.set(labels, structuredOutput.VmRSS);
virtualMemGauge.set(labels, structuredOutput.VmSize);
heapSizeMemGauge.set(labels, structuredOutput.VmData);
} catch {

@@ -69,2 +72,3 @@ // noop

registers,
labelNames,
});

@@ -75,2 +79,3 @@ const heapSizeMemGauge = new Gauge({

registers,
labelNames,
});

@@ -77,0 +82,0 @@ };

@@ -13,15 +13,9 @@ 'use strict';

exports.validateLabelName = function (names) {
let valid = true;
(names || []).forEach(name => {
if (!labelRegexp.test(name)) {
valid = false;
}
});
return valid;
exports.validateLabelName = function (names = []) {
return names.every(name => labelRegexp.test(name));
};
exports.validateLabel = function validateLabel(savedLabels, labels) {
Object.keys(labels).forEach(label => {
if (savedLabels.indexOf(label) === -1) {
for (const label in labels) {
if (!savedLabels.includes(label)) {
throw new Error(

@@ -33,3 +27,3 @@ `Added label "${label}" is not included in initial labelset: ${util.inspect(

}
});
}
};
{
"name": "prom-client",
"version": "13.1.0",
"version": "13.2.0",
"description": "Client for prometheus",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -20,6 +20,8 @@ # Prometheus client for node.js [![Actions Status](https://github.com/siimon/prom-client/workflows/Node.js%20CI/badge.svg?branch=master)](https://github.com/siimon/prom-client/actions)

Default metrics use sensible aggregation methods. Custom metrics are summed
across workers by default. To use a different aggregation method, set the
`aggregator` property in the metric config to one of 'sum', 'first', 'min',
'max', 'average' or 'omit'. (See `lib/metrics/version.js` for an example.)
Default metrics use sensible aggregation methods. (Note, however, that the event
loop lag mean and percentiles are averaged, which is not perfectly accurate.)
Custom metrics are summed across workers by default. To use a different
aggregation method, set the `aggregator` property in the metric config to one of
'sum', 'first', 'min', 'max', 'average' or 'omit'. (See `lib/metrics/version.js`
for an example.)

@@ -53,3 +55,3 @@ If you need to expose metrics about an individual worker, you can include a

- `prefix` an optional prefix for metric names. Default: no prefix.
- `registry` to which metrics should be registered. Default: the global default registry.
- `register` to which metrics should be registered. Default: the global default registry.
- `gcDurationBuckets` with custom buckets for GC duration histogram. Default buckets of GC duration histogram are `[0.001, 0.01, 0.1, 1, 2, 5]` (in seconds).

@@ -331,2 +333,20 @@ - `eventLoopMonitoringPrecision` with sampling rate in milliseconds. Must be greater than zero. Default: 10.

#### Zeroing metrics with Labels
Metrics with labels can not be exported before they have been observed at least
once since the possible label values are not known before they're observed.
For histograms, this can be solved by explicitly zeroing all expected label values:
```js
const histogram = new client.Histogram({
name: 'metric_name',
help: 'metric_help',
buckets: [0.1, 5, 15, 50, 100, 500],
labels: ['method'],
});
histogram.zero({ method: 'GET' });
histogram.zero({ method: 'POST' });
```
#### Strongly typed Labels

@@ -333,0 +353,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc