Skip to content

Commit

Permalink
Consul telemetry exporter 11 (#12)
Browse files Browse the repository at this point in the history
* #11 metrics name should not contain the metric statistic

* #11 metrics name should not contain the metric statistic

* #11 metrics name should not contain the metric statistic

* #11 metrics name should not contain the metric statistic

* #11 metrics name should not contain the metric statistic

* #11 metrics name should not contain the metric statistic

* #11 metrics name should not contain the metric statistic
  • Loading branch information
lchayoun authored and ezraroi committed Nov 23, 2017
1 parent 854b4a2 commit 16218af
Show file tree
Hide file tree
Showing 3 changed files with 273 additions and 12 deletions.
102 changes: 99 additions & 3 deletions lib/prometheus.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,103 @@
module.exports = {
sanitize,
normalize
};

function sanitize(name) {
return name.replace(/[^a-zA-Z0-9:_]/g, '_');
}

module.exports = {
sanitize
};
function normalize(name) {
let normalizedName = name;
const labels = {};

/**
* handling the following metrics patterns:
* consul.dns.ptr_query.<node>
* consul.dns.domain_query.<node>
* consul.raft.replication.appendEntries.logs.<node>
* consul.raft.replication.appendEntries.rpc.<node>
* consul.raft.replication.heartbeat.<node>
* @type {RegExp}
*/
const $1 = new RegExp('(consul.(?:dns.(?:ptr_query|domain_query)|raft.replication.(?:appendEntries.(?:logs|rpc)|heartbeat)))[.](.*)');
if ($1.test(normalizedName)) {
const match = $1.exec(normalizedName);
normalizedName = match[1];
labels.node = sanitize(match[2]);
}

/**
* handling the following metrics patterns:
* consul.http.<verb>.<path>
* @type {RegExp}
*/
const $2 = new RegExp('(consul.http)[.]([^.]*)[.](.*)');
if ($2.test(normalizedName)) {
const match = $2.exec(normalizedName);
normalizedName = match[1];
labels.verb = sanitize(match[2]);
labels.path = sanitize(match[3]);
}

/**
* handling the following metrics patterns:
* consul.fsm.acl.<op>
* consul.fsm.session.<op>
* consul.fsm.kvs.<op>
* consul.fsm.tombstone.<op>
* consul.fsm.prepared-query.<op>
* @type {RegExp}
*/
const $3 = new RegExp('(consul.fsm.(?:acl|session|kvs|tombstone|prepared-query))[.](.*)');
if ($3.test(normalizedName)) {
const match = $3.exec(normalizedName);
normalizedName = match[1];
labels.op = sanitize(match[2]);
}

/**
* handling the following metrics patterns:
* consul.catalog.service.query.<service>
* consul.catalog.service.not-found.<service>
* consul.health.service.query.<service>
* consul.health.service.not-found.<service>
* @type {RegExp}
*/
const $4 = new RegExp('(consul.(?:catalog|health).service.(?:query|not-found))[.](.*)');
if ($4.test(normalizedName)) {
const match = $4.exec(normalizedName);
normalizedName = match[1];
labels.service = sanitize(match[2]);
}

/**
* handling the following metrics patterns:
* consul.catalog.service.query-tag.<service>.<tag>
* consul.health.service.query-tag.<service>.<tag>
* @type {RegExp}
*/
const $5 = new RegExp('(consul.(?:catalog|health).service.query-tag)[.]([^.]*)[.](.*)');
if ($5.test(normalizedName)) {
const match = $5.exec(normalizedName);
normalizedName = match[1];
labels.service = sanitize(match[2]);
labels.tag = sanitize(match[3]);
}

/**
* handling the following metrics patterns:
* consul.consul.* - in order to remove the duplication
* @type {RegExp}
*/
const $6 = new RegExp('consul[.](consul.*)');
if ($6.test(normalizedName)) {
const match = $6.exec(normalizedName);
normalizedName = match[1];
}

return {
name: normalizedName,
labels: labels
};
}
20 changes: 11 additions & 9 deletions routes/metrics.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,27 +46,29 @@ function createPrometheusMetrics(result) {
return res;

function _handleCounter(counter) {
_setGauge(counter.Name + '_count', _.extend({'statistic': 'count'}, counter.Labels), counter.Count);
_setGauge(counter.Name + '_sum', _.extend({'statistic': 'sum'}, counter.Labels), counter.Sum);
_setGauge(counter.Name + '_min', _.extend({'statistic': 'min'}, counter.Labels), counter.Min);
_setGauge(counter.Name + '_max', _.extend({'statistic': 'max'}, counter.Labels), counter.Max);
_setGauge(counter.Name + '_mean', _.extend({'statistic': 'mean'}, counter.Labels), counter.Mean);
_setGauge(counter.Name + '_stddev', _.extend({'statistic': 'stddev'}, counter.Labels), counter.Stddev);
_setGauge(counter.Name, _.extend({'statistic': 'count'}, counter.Labels), counter.Count);
_setGauge(counter.Name, _.extend({'statistic': 'sum'}, counter.Labels), counter.Sum);
_setGauge(counter.Name, _.extend({'statistic': 'min'}, counter.Labels), counter.Min);
_setGauge(counter.Name, _.extend({'statistic': 'max'}, counter.Labels), counter.Max);
_setGauge(counter.Name, _.extend({'statistic': 'mean'}, counter.Labels), counter.Mean);
_setGauge(counter.Name, _.extend({'statistic': 'stddev'}, counter.Labels), counter.Stddev);
}

function _handleGauge(gauge) {
_setGauge(gauge.Name, gauge.Labels, gauge.Value);
}

function _setGauge(name, labels, value) {
let metricName = prometheus.sanitize(name);
const metric = prometheus.normalize(name);
const normalizedLabels = _.extend({}, metric.labels, labels);
const metricName = prometheus.sanitize(metric.name);
const gaugeMetric = metrics[metricName] || new client.Gauge({
name: metricName.toLowerCase(),
help: metricName.toLowerCase() + '_help',
labelNames: Object.keys(labels),
labelNames: Object.keys(normalizedLabels),
registers: [registry]
});
gaugeMetric.set(labels, value);
gaugeMetric.set(normalizedLabels, value);
metrics[metricName] = gaugeMetric;
}
}
163 changes: 163 additions & 0 deletions test/lib/prometheus.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,167 @@ describe('prometheus test', function () {
expect(prometheus.sanitize('.')).to.be('_');
});

it('should return consul.dns.ptr_query with node tag', function () {
expect(prometheus.normalize('consul.dns.ptr_query.a')).to.be.eql({
name: 'consul.dns.ptr_query',
labels: {
node: 'a'
}
});
});

it('should return consul.raft.replication.appendEntries.rpc with node tag', function () {
expect(prometheus.normalize('consul.raft.replication.appendEntries.rpc.58deeea6-0cc7-fc76-720f-b187be80f900')).to.be.eql({
name: 'consul.raft.replication.appendEntries.rpc',
labels: {
node: '58deeea6_0cc7_fc76_720f_b187be80f900'
}
});
});

it('should return consul.raft.replication.appendEntries.logs with node tag', function () {
expect(prometheus.normalize('consul.raft.replication.appendEntries.logs.58deeea6-0cc7-fc76-720f-b187be80f900')).to.be.eql({
name: 'consul.raft.replication.appendEntries.logs',
labels: {
node: '58deeea6_0cc7_fc76_720f_b187be80f900'
}
});
});

it('should return consul.raft.replication.heartbeat with node tag', function () {
expect(prometheus.normalize('consul.raft.replication.heartbeat.58deeea6-0cc7-fc76-720f-b187be80f900')).to.be.eql({
name: 'consul.raft.replication.heartbeat',
labels: {
node: '58deeea6_0cc7_fc76_720f_b187be80f900'
}
});
});

it('should return consul.dns.domain_query with node tag', function () {
expect(prometheus.normalize('consul.dns.domain_query.a')).to.be.eql({
name: 'consul.dns.domain_query',
labels: {
node: 'a'
}
});
});

it('should return consul.http with verb tag and path tag', function () {
expect(prometheus.normalize('consul.http.GET.v1.agent.metrics')).to.be.eql({
name: 'consul.http',
labels: {
verb: 'GET',
path: 'v1_agent_metrics',
}
});
});

it('should return consul.fsm.acl with op tag', function () {
expect(prometheus.normalize('consul.fsm.acl.a')).to.be.eql({
name: 'consul.fsm.acl',
labels: {
op: 'a'
}
});
});

it('should return consul.fsm.session with op tag', function () {
expect(prometheus.normalize('consul.fsm.session.a')).to.be.eql({
name: 'consul.fsm.session',
labels: {
op: 'a'
}
});
});

it('should return consul.fsm.kvs with op tag', function () {
expect(prometheus.normalize('consul.fsm.kvs.a')).to.be.eql({
name: 'consul.fsm.kvs',
labels: {
op: 'a'
}
});
});

it('should return consul.fsm.tombstone with op tag', function () {
expect(prometheus.normalize('consul.fsm.tombstone.a')).to.be.eql({
name: 'consul.fsm.tombstone',
labels: {
op: 'a'
}
});
});

it('should return consul.fsm.prepared-query with op tag', function () {
expect(prometheus.normalize('consul.fsm.prepared-query.a')).to.be.eql({
name: 'consul.fsm.prepared-query',
labels: {
op: 'a'
}
});
});

it('should return consul.catalog.service.query with service tag', function () {
expect(prometheus.normalize('consul.catalog.service.query.a')).to.be.eql({
name: 'consul.catalog.service.query',
labels: {
service: 'a'
}
});
});

it('should return consul.catalog.service.query-tag with service tag and tag tag', function () {
expect(prometheus.normalize('consul.catalog.service.query-tag.a.b')).to.be.eql({
name: 'consul.catalog.service.query-tag',
labels: {
service: 'a',
tag: 'b'
}
});
});

it('should return consul.catalog.service.not-found with service tag', function () {
expect(prometheus.normalize('consul.catalog.service.not-found.a')).to.be.eql({
name: 'consul.catalog.service.not-found',
labels: {
service: 'a'
}
});
});

it('should return consul.health.service.query with service tag', function () {
expect(prometheus.normalize('consul.health.service.query.a')).to.be.eql({
name: 'consul.health.service.query',
labels: {
service: 'a'
}
});
});

it('should return consul.health.service.query-tag with service tag and tag tag', function () {
expect(prometheus.normalize('consul.health.service.query-tag.a.b')).to.be.eql({
name: 'consul.health.service.query-tag',
labels: {
service: 'a',
tag: 'b'
}
});
});

it('should return consul.health.service.not-found with service tag', function () {
expect(prometheus.normalize('consul.health.service.not-found.a')).to.be.eql({
name: 'consul.health.service.not-found',
labels: {
service: 'a'
}
});
});

it('should return single consul prefix', function () {
expect(prometheus.normalize('consul.consul.health.service')).to.be.eql({
name: 'consul.health.service',
labels: {}
});
});

});

0 comments on commit 16218af

Please sign in to comment.