Skip to content

Commit

Permalink
Add the officially recommended metrics (#25)
Browse files Browse the repository at this point in the history
* Set to Number param instead of float

* Add the officially recommended metrics
  • Loading branch information
SimenB authored and siimon committed Sep 7, 2016
1 parent af13f5c commit a74e5b3
Show file tree
Hide file tree
Showing 14 changed files with 334 additions and 6 deletions.
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,61 @@ See example folder for a sample usage. The library does not bundle any web frame

All metric types has 2 mandatory parameters, name and help.

#### Default metrics

There are some default metrics recommended by Prometheus
[itself](https://prometheus.io/docs/instrumenting/writing_clientlibs/#standard-and-runtime-collectors). These metrics are collected
automatically for you when you do `require('prom-client')`.

NOTE: Some of the metrics, concerning File Descriptors and Memory, are only available on Linux.

The function returned from `defaultMetrics` takes 2 options, a blacklist of metrics to skip, and a timeout for how often the probe should
be fired. By default all probes are launched every 10 seconds, but this can be modified like this:

```js
var client = require('prom-client');

var defaultMetrics = client.defaultMetrics;

// Skip `osMemoryHeap` probe, and probe every 5th second.
defaultMetrics(['osMemoryHeap'], 5000);
````

You can get the full list of metrics by inspecting `client.defaultMetrics.metricsList`.

`defaultMetrics` returns an identification when invoked, which is a reference to the `Timer` used to keep the probes going. This can be
passed to `clearInterval` in order to stop all probes.

NOTE: Existing intervals are automatically cleared when calling `defaultMetrics`.

```js
var client = require('prom-client');
var defaultMetrics = client.defaultMetrics;
var interval = defaultMetrics();
// ... some time later
clearInterval(interval);
````
NOTE: `unref` is called on the `interval` internally, so it will not keep your node process going indefinitely if it's the only thing
keeping it from shutting down.
##### Disabling default metrics
To disable collecting the default metrics, you have to call the function and pass it to `clearInterval`.
```js
var client = require('prom-client');

clearInterval(client.defaultMetrics());

// Clear the register
client.register.clear();
```

#### Counter

Counters go up, and reset when the process restarts.
Expand Down
6 changes: 6 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ exports.Pushgateway = require('./lib/pushgateway');

exports.linearBuckets = require('./lib/bucketGenerators').linearBuckets;
exports.exponentialBuckets = require('./lib/bucketGenerators').exponentialBuckets;

var defaultMetrics = require('./lib/defaultMetrics');

defaultMetrics();

exports.defaultMetrics = defaultMetrics;
2 changes: 1 addition & 1 deletion lib/counter.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ function Counter(name, help, labels) {
/**
* Increment counter
* @param {object} labels - What label you want to be incremented
* @param {float} value - Value to increment, if omitted increment with 1
* @param {Number} value - Value to increment, if omitted increment with 1
* @returns {void}
*/
Counter.prototype.inc = function(labels, value) {
Expand Down
48 changes: 48 additions & 0 deletions lib/defaultMetrics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';

var processCpuTotal = require('./metrics/processCpuTotal');
var processStartTime = require('./metrics/processStartTime');
var osMemoryHeap = require('./metrics/osMemoryHeap');
var processOpenFileDescriptors = require('./metrics/processOpenFileDescriptors');
var processMaxFileDescriptors = require('./metrics/processMaxFileDescriptors');

var metrics = {
processCpuTotal: processCpuTotal,
processStartTime: processStartTime,
osMemoryHeap: osMemoryHeap,
processOpenFileDescriptors: processOpenFileDescriptors,
processMaxFileDescriptors: processMaxFileDescriptors
};

var existingInterval = null;

module.exports = function startDefaultMetrics (disabledMetrics, interval) {
if(existingInterval !== null) {
clearInterval(existingInterval);
}

disabledMetrics = disabledMetrics || [];
interval = interval || 10000;

var metricsInUse = Object.keys(metrics)
.filter(function (metric) {
return disabledMetrics.indexOf(metric) < 0;
})
.map(function (metric) {
return metrics[metric]();
});

function updateAllMetrics () {
metricsInUse.forEach(function (metric) {
metric.call();
});
}

updateAllMetrics();

existingInterval = setInterval(updateAllMetrics, interval).unref();

return existingInterval;
};

module.exports.metricsList = Object.keys(metrics);
6 changes: 3 additions & 3 deletions lib/gauge.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function Gauge(name, help, labels) {
/**
* Set a gauge to a value
* @param {object} labels - Object with labels and their values
* @param {float} value - Value to set the gauge to, must be positive
* @param {Number} value - Value to set the gauge to, must be positive
* @returns {void}
*/
Gauge.prototype.set = function(labels, value) {
Expand All @@ -61,7 +61,7 @@ Gauge.prototype.set = function(labels, value) {
/**
* Increment a gauge value
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {float} value - Value to increment - if omitted, increment with 1
* @param {Number} value - Value to increment - if omitted, increment with 1
* @returns {void}
*/
Gauge.prototype.inc = function(labels, value) {
Expand All @@ -72,7 +72,7 @@ Gauge.prototype.inc = function(labels, value) {
/**
* Decrement a gauge value
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {float} value - Value to decrement - if omitted, decrement with 1
* @param {Number} value - Value to decrement - if omitted, decrement with 1
* @returns {void}
*/
Gauge.prototype.dec = function(labels, value) {
Expand Down
2 changes: 1 addition & 1 deletion lib/histogram.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function Histogram(name, help, labelsOrConf, conf) {
/**
* Observe a value in histogram
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {float} value - Value to observe in the histogram
* @param {Number} value - Value to observe in the histogram
* @returns {void}
*/
Histogram.prototype.observe = function(labels, value) {
Expand Down
18 changes: 18 additions & 0 deletions lib/metrics/osMemoryHeap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';

var Gauge = require('../gauge');
var linuxVariant = require('./osMemoryHeapLinux');
var notLinuxVariant = function () {
var residentMemGauge = new Gauge('process_resident_memory_bytes', 'Resident memory size in bytes.');

return function () {
var memoryUsage = process.memoryUsage();

// I don't think the other things returned from `process.memoryUsage()` is relevant to a standard export
residentMemGauge.set(null, memoryUsage.rss);
};
};

module.exports = function () {
return process.platform === 'linux' ? linuxVariant() : notLinuxVariant();
};
50 changes: 50 additions & 0 deletions lib/metrics/osMemoryHeapLinux.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use strict';

var Gauge = require('../gauge');
var fs = require('fs');

var values = ['VmSize', 'VmRSS', 'VmData'];

function structureOutput (input) {
var returnValue = {};

input.split('\n')
.filter(function (s) {
return values.some(function (value) {
return s.indexOf(value) === 0;
});
})
.forEach(function (string) {
var split = string.split(':');

// Get the value
var value = split[1].trim();
// Remove trailing ` kb`
value = value.substr(0, value.length - 3);
// Make it into a number in bytes bytes
value = Number(value) * 1000;

returnValue[split[0]] = value;
});

return returnValue;
}

module.exports = function () {
var residentMemGauge = new Gauge('process_resident_memory_bytes', 'Resident memory size in bytes.');
var virtualMemGauge = new Gauge('process_virtual_memory_bytes', 'Virtual memory size in bytes.');
var heapSizeMemGauge = new Gauge('process_heap_bytes', 'Process heap size in bytes.');

return function () {
fs.readFile('/proc/self/status', 'utf8', function (err, status) {
if(err) {
return;
}
var structuredOutput = structureOutput(status);

residentMemGauge.set(null, structuredOutput.VmRSS);
virtualMemGauge.set(null, structuredOutput.VmSize);
heapSizeMemGauge.set(null, structuredOutput.VmData);
});
};
};
20 changes: 20 additions & 0 deletions lib/metrics/processCpuTotal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

var Gauge = require('../gauge');

module.exports = function () {
// Don't do anything if the function doesn't exist (introduced in node@6.1.0)
if(typeof process.cpuUsage !== 'function') {
return function () {
};
}

var cpuUserGauge = new Gauge('process_cpu_seconds_total', 'Total user and system CPU time spent in seconds.');

return function () {
var cpuUsage = process.cpuUsage();
var totalUsageMicros = cpuUsage.user + cpuUsage.system;

cpuUserGauge.set(null, totalUsageMicros / 1e6);
};
};
26 changes: 26 additions & 0 deletions lib/metrics/processMaxFileDescriptors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict';

var Gauge = require('../gauge');
var fs = require('fs');

module.exports = function () {
var isSet = false;

return function () {
if(isSet || process.platform !== 'linux') {
return;
}

fs.readFile('/proc/sys/fs/file-max', 'utf8', function (err, maxFds) {
if(err) {
return;
}

isSet = true;

var fileDescriptorsGauge = new Gauge('process_max_fds', 'Maximum number of open file descriptors.');

fileDescriptorsGauge.set(null, maxFds);
});
};
};
24 changes: 24 additions & 0 deletions lib/metrics/processOpenFileDescriptors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';

var Gauge = require('../gauge');
var fs = require('fs');

module.exports = function () {
if(process !== 'linux') {
return function () {
};
}

var fileDescriptorsGauge = new Gauge('process_open_fds', 'Number of open file descriptors.');

return function () {
fs.readdir('/proc/self/fd', function (err, list) {
if(err) {
return;
}

// Minus 1, as this invocation created one
fileDescriptorsGauge.set(null, list.length - 1);
});
};
};
17 changes: 17 additions & 0 deletions lib/metrics/processStartTime.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
'use strict';

var Gauge = require('../gauge');
var nowInSeconds = Math.round(Date.now() / 1000 - process.uptime());

module.exports = function () {
var cpuUserGauge = new Gauge('process_start_time_seconds', 'Start time of the process since unix epoch in seconds.');
var isSet = false;

return function () {
if(isSet) {
return;
}
cpuUserGauge.set(null, nowInSeconds);
isSet = true;
};
};
2 changes: 1 addition & 1 deletion lib/summary.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function Summary(name, help, labelsOrConf, conf) {
/**
* Observe a value
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {float} value - Value to observe
* @param {Number} value - Value to observe
* @returns {void}
*/
Summary.prototype.observe = function(labels, value) {
Expand Down
Loading

0 comments on commit a74e5b3

Please sign in to comment.