Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for metric types histogram and summary, ability to reset, nop operation #19

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ Supported metric types:

* Counter
* Gauge
* Histogram
* Summary

Labels are supported for both metric types.
Labels are supported for all metric types.

## How to install

Expand Down Expand Up @@ -38,6 +40,7 @@ The node expects a `message.payload` as input with the following structure:

```json
{
"reset": false,
"op": "inc",
"val": 5,
"labels": {
Expand All @@ -46,6 +49,8 @@ The node expects a `message.payload` as input with the following structure:
}
```

Set `reset` if you want to reset the metric before the operation is executed. Defaults to `false`.

Choose the `op` property from one of the following values:

* For metrics of type __Counter__:
Expand All @@ -54,6 +59,9 @@ Choose the `op` property from one of the following values:
* `set` - Sets the gauge to `val` (which is mandatory for this operation)
* `inc` - Increases the gauge by `val` (without specifying `val`, defaults to `1`)
* `dec` - Decreases the gauge by `val` (without specifying `val`, defaults to `1`)
* For metrics of type __Histogram__ and __Summary__:
* `observe` - Observe `val` (which is mandatory for this operation)
* Additionally, all metrics support the `nop` 'no-operation' mode, which does nothing. Useful for resets

As described above, the `val` property is mandatory or optional depending on the selected `op` and the metric type.

Expand All @@ -74,6 +82,35 @@ example_counter 10
# TYPE example_gauge gauge
example_gauge{tag_1="computer_123"} 5
example_gauge 5

# HELP example_histogram This is an example Histogram
# TYPE example_histogram histogram
example_histogram_bucket{le="0.005"} 0
example_histogram_bucket{le="0.01"} 0
example_histogram_bucket{le="0.025"} 0
example_histogram_bucket{le="0.05"} 0
example_histogram_bucket{le="0.1"} 0
example_histogram_bucket{le="0.25"} 0
example_histogram_bucket{le="0.5"} 0
example_histogram_bucket{le="1"} 1
example_histogram_bucket{le="2.5"} 1
example_histogram_bucket{le="5"} 1
example_histogram_bucket{le="10"} 1
example_histogram_bucket{le="+Inf"} 1
example_histogram_sum 1
example_histogram_count 1

# HELP example_summary This is an example Summary
# TYPE example_summary summary
example_summary{quantile="0.01"} 0.1
example_summary{quantile="0.05"} 0.1
example_summary{quantile="0.5"} 0.1
example_summary{quantile="0.9"} 0.1
example_summary{quantile="0.95"} 0.1
example_summary{quantile="0.99"} 0.1
example_summary{quantile="0.999"} 0.1
example_summary_sum 0.1
example_summary_count 1
```

### Changing the path
Expand Down
29 changes: 29 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 5 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@
"url": "git+https://github.com/Docoyo/node-red-contrib-prometheus-exporter.git"
},
"author": "docoyo",
"contributors": [
{
"name": "Masanori Fujita"
}
],
"contributors": [{
"name": "Masanori Fujita"
}],
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/Docoyo/node-red-contrib-prometheus-exporter/issues"
Expand All @@ -30,6 +28,6 @@
}
},
"dependencies": {
"prom-client": "^13.1.0"
"prom-client": "^14.1.0"
}
}
}
27 changes: 26 additions & 1 deletion prometheus-exporter/prometheus-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ module.exports = {
if (customRegistry._metrics[metricConfig.name]) {
const metric = customRegistry._metrics[metricConfig.name];
red.log.info('Reusing Prometheus Counter ' + metricConfig.name);
metric.reset();
return metric;
} else {
const metric = new client.Counter(metricConfig);
Expand All @@ -60,6 +59,32 @@ module.exports = {
return metric;
}
},
addHistogram: function (metricConfig) {
metricConfig.registers = [customRegistry];
if (customRegistry._metrics[metricConfig.name]) {
const metric = customRegistry._metrics[metricConfig.name];
red.log.info('Reusing Prometheus Histogram ' + metricConfig.name);
metric.reset();
return metric;
} else {
const metric = new client.Histogram(metricConfig);
red.log.info('Added Prometheus Histogram ' + metricConfig.name);
return metric;
}
},
addSummary: function (metricConfig) {
metricConfig.registers = [customRegistry];
if (customRegistry._metrics[metricConfig.name]) {
const metric = customRegistry._metrics[metricConfig.name];
red.log.info('Reusing Prometheus Summary ' + metricConfig.name);
metric.reset();
return metric;
} else {
const metric = new client.Summary(metricConfig);
red.log.info('Added Prometheus Summary ' + metricConfig.name);
return metric;
}
},
removeMetric: function (metricName) {
customRegistry.removeSingleMetric(metricName);
red.log.info('Removed Prometheus metric ' + metricName);
Expand Down
37 changes: 37 additions & 0 deletions prometheus-exporter/prometheus-exporter.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ <h2>Using the metric in a flow</h2>
</p>
<pre>
{
"reset": false,
"op": "inc",
"val": 5,
"labels": {
"tag_1": "computer_123"
}
}
</pre>
<p>Set <code>reset</code> if you want to reset the metric before the operation is executed. Defaults to <code>false</code>.</p>
<p>Choose the <code>op</code> property from one of the following values:
<ul>
<li>For metrics of type <strong>Counter</strong>:
Expand All @@ -48,6 +50,12 @@ <h2>Using the metric in a flow</h2>
to <code>1</code>)</li>
</ul>
</li>
<li>For metrics of type <strong>Histogram</strong> and <strong>Summary</strong>:
<ul>
<li><code>observe</code> - Observe <code>val</code> (which is mandatory for this operation)</li>
</ul>
</li>
<li>Additionally, all metrics support the <code>nop</code> 'no-operation' mode, which does nothing. Useful for resets.</li>
</ul>
</p>
<h2>Check the metrics output</h2>
Expand All @@ -64,6 +72,35 @@ <h2>Check the metrics output</h2>
# TYPE example_gauge gauge
example_gauge{tag_1="computer_123"} 5
example_gauge 5

# HELP example_histogram This is an example Histogram
# TYPE example_histogram histogram
example_histogram_bucket{le="0.005"} 0
example_histogram_bucket{le="0.01"} 0
example_histogram_bucket{le="0.025"} 0
example_histogram_bucket{le="0.05"} 0
example_histogram_bucket{le="0.1"} 0
example_histogram_bucket{le="0.25"} 0
example_histogram_bucket{le="0.5"} 0
example_histogram_bucket{le="1"} 1
example_histogram_bucket{le="2.5"} 1
example_histogram_bucket{le="5"} 1
example_histogram_bucket{le="10"} 1
example_histogram_bucket{le="+Inf"} 1
example_histogram_sum 1
example_histogram_count 1

# HELP example_summary This is an example Summary
# TYPE example_summary summary
example_summary{quantile="0.01"} 0.1
example_summary{quantile="0.05"} 0.1
example_summary{quantile="0.5"} 0.1
example_summary{quantile="0.9"} 0.1
example_summary{quantile="0.95"} 0.1
example_summary{quantile="0.99"} 0.1
example_summary{quantile="0.999"} 0.1
example_summary_sum 0.1
example_summary_count 1
</pre>
</p>
</script>
Expand Down
23 changes: 19 additions & 4 deletions prometheus-exporter/prometheus-exporter.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,44 @@
const VALID_OPS = {
counter: ['inc'],
gauge: ['inc', 'dec', 'set']
gauge: ['inc', 'dec', 'set'],
histogram: ['observe'],
summary: ['observe'],
};

const DEFAULT_OPS = {
counter: 'inc',
gauge: 'set'
gauge: 'set',
histogram: 'observe',
summary: 'observe'
};

module.exports = function (RED) {
'use strict';

function PrometheusExporterNode(config) {
RED.nodes.createNode(this, config);
var node = this;
RED.log.info('Instanciating PrometheusExporterNode ' + this.id);
this.metricConfig = RED.nodes.getNode(config.metric);
if (this.metricConfig && this.metricConfig.mtype) {
this.on('input', function (msg, send, done) {
this.on('input', function (msg, _send, done) {
let metricLabels = {};
let metricVal = 1;
let metricOp = undefined;

if (msg.payload) {

if (msg.payload.reset === true) { // Including the type-check seems sensible
this.metricConfig.prometheusMetric.reset();
}

// determine operation
if (msg.payload.op) {
if (msg.payload.op === 'nop') {
// no operation
done();
return;
}

if (VALID_OPS[this.metricConfig.mtype].includes(msg.payload.op)) {
metricOp = msg.payload.op;
} else {
Expand Down
78 changes: 76 additions & 2 deletions prometheus-exporter/prometheus-metric-config.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,51 @@
<select id="node-config-input-mtype">
<option value="counter">Counter</option>
<option value="gauge">Gauge</option>
<option value="histogram">Histogram</option>
<option value="summary">Summary</option>
</select>
</div>
<div class="form-row">
<label for="node-config-input-buckets"><i class="fa fa-wrench"></i> Buckets</label>
<input id="node-config-input-buckets" disabled type="text" placeholder="0.005, 0.01, 0.05, 0.25, 0.5, 1, 5, 10 (optional)" />
</div>
<div class="form-row">
<label for="node-config-input-percentiles"><i class="fa fa-wrench"></i> Percentiles</label>
<input id="node-config-input-percentiles" disabled type="text" placeholder="0.01, 0.05, 0.5, 0.9, 0.95, 0.99, 0.999 (optional)" />
</div>
<div class="form-row">
<label for="node-config-input-maxAgeSeconds"><i class="fa fa-wrench"></i> Max bucket age in seconds</label>
<input id="node-config-input-maxAgeSeconds" disabled type="text" placeholder="(optional)" />
</div>
<div class="form-row">
<label for="node-config-input-ageBuckets"><i class="fa fa-wrench"></i> Number of buckets in sliding window</label>
<input id="node-config-input-ageBuckets" disabled type="text" placeholder="(optional)" />
</div>
<div class="form-row">
<label for="node-config-input-compressCount"><i class="fa fa-wrench"></i> Number of measurements after which data is compressed</label>
<input id="node-config-input-compressCount" disabled type="text" placeholder="1000 (optional)" />
</div>
</script>

<script type="text/javascript">
function onConfigEditPrepare() {
$('#node-config-input-mtype').change(function (event) {
console.log('mtype chanhged:', $( this ).val());
if ($(this).val() === 'histogram') {
$('#node-config-input-buckets').removeAttr('disabled');
} else {
$('#node-config-input-buckets').attr('disabled', 'disabled');
}
if ($(this).val() === 'summary') {
$('#node-config-input-percentiles').removeAttr('disabled');
$('#node-config-input-maxAgeSeconds').removeAttr('disabled');
$('#node-config-input-ageBuckets').removeAttr('disabled');
$('#node-config-input-compressCount').removeAttr('disabled');
} else {
$('#node-config-input-percentiles').attr('disabled', 'disabled');
$('#node-config-input-maxAgeSeconds').attr('disabled', 'disabled');
$('#node-config-input-ageBuckets').attr('disabled', 'disabled');
$('#node-config-input-compressCount').attr('disabled', 'disabled');
}
});
}

Expand All @@ -33,7 +70,6 @@
}

function validateMetricLabels(value) {
console.log('validateMetricLabels', value);
if (value) {
let splitLabels = value.split(',');
splitLabels = splitLabels.map((item) => item.trim());
Expand All @@ -43,6 +79,24 @@
}
}

function validateMetricNumber(value) {
if (value) {
return !isNaN(Number(value));
} else {
return true;
}
}

function validateMetricNumberList(value) {
if (value) {
let splitBuckets = value.split(',');
splitBuckets = splitBuckets.map((item) => Number(item.trim()));
return splitBuckets.every((item) => !isNaN(item));
} else {
return true;
}
}

RED.nodes.registerType('prometheus-metric-config', {
category: 'config',
defaults: {
Expand All @@ -61,6 +115,26 @@
mtype: {
value: 'counter',
required: true
},
buckets: {
value: [],
validate: validateMetricNumberList
},
percentiles: {
value: [],
validate: validateMetricNumberList
},
maxAgeSeconds: {
value: null,
validate: validateMetricNumber
},
ageBuckets: {
value: null,
validate: validateMetricNumber
},
compressCount: {
value: null,
validate: validateMetricNumber
}
},
inputs: 0,
Expand Down
Loading