From 5ead531a59906fc9d51a92e8d060420de57d5f6a Mon Sep 17 00:00:00 2001 From: Konstantin Pogorelov Date: Tue, 19 Apr 2016 00:48:23 +0200 Subject: [PATCH] added whie-black-listing, http counter is a histogram now --- .npmignore | 3 +- advanced-example.js | 28 +++++++ package.json | 2 +- spec/indexSpec.js | 2 +- src/{PrometheusHelper.js => PromFactory.js} | 3 +- src/index.js | 90 +++++++++++++++------ 6 files changed, 100 insertions(+), 28 deletions(-) create mode 100644 advanced-example.js rename src/{PrometheusHelper.js => PromFactory.js} (92%) diff --git a/.npmignore b/.npmignore index fb58e0d..c02dd96 100644 --- a/.npmignore +++ b/.npmignore @@ -1,4 +1,5 @@ +advanced-example.js docker-compose.yml spec .travis.yml -.eslintrc \ No newline at end of file +.eslintrc diff --git a/advanced-example.js b/advanced-example.js new file mode 100644 index 0000000..8f50bf5 --- /dev/null +++ b/advanced-example.js @@ -0,0 +1,28 @@ +"use strict"; + +const express = require("express"), + app = express(), + promBundle = require("express-prom-bundle"), + promClient = require("prom-client"); + +const bundle = promBundle({ + prefix: "demo_app:something:", + blacklist: [/up/] +}); + +app.use(bundle); + +let c1 = new bundle.promClient.Counter("c1", "c1 help"); +c1.inc(10); + +let c2 = bundle.factory.newCounter("c2", "c2 help"); +c2.inc(20); + +app.get("/foo", (req, res) => { + setTimeout(() => { + res.send("foo response"); + }, 500); +}); +app.get("/bar", (req, res) => res.send("bar response")); + +app.listen(3001); diff --git a/package.json b/package.json index be54d4b..87f37d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "express-prom-bundle", - "version": "1.0.0", + "version": "1.1.0", "description": "express middleware with popular prometheus metrics in one bundle", "main": "src/index.js", "keywords": [ diff --git a/spec/indexSpec.js b/spec/indexSpec.js index d5afd16..3ade247 100644 --- a/spec/indexSpec.js +++ b/spec/indexSpec.js @@ -9,7 +9,7 @@ describe("index", () => { const app = express(); app.use(bundle({ - prefix: "hello" + prefix: "hello:" })); it("/metrics returns up=1", done => { diff --git a/src/PrometheusHelper.js b/src/PromFactory.js similarity index 92% rename from src/PrometheusHelper.js rename to src/PromFactory.js index aa8f04c..b598619 100644 --- a/src/PrometheusHelper.js +++ b/src/PromFactory.js @@ -18,8 +18,7 @@ module.exports = class { } makeRealName(name) { - const prefix = this.opts.prefix ? (this.opts.prefix + ":") : ""; - return prefix + name; + return (this.opts.prefix || "") + name; } makeMetric(TheClass, name, description, param) { diff --git a/src/index.js b/src/index.js index 6be8e29..7c30157 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,7 @@ "use strict"; const - PrometheusHelper = require("./PrometheusHelper"), + PromFactory = require("./PromFactory"), onFinished = require("on-finished"); function filterArrayByRegExps(array, regexps) { @@ -16,6 +16,21 @@ function filterArrayByRegExps(array, regexps) { }); } +function prepareMetricNames(opts, metricTemplates) { + let names = Object.keys(metricTemplates); + if (opts.whitelist) { + if (opts.blacklist) { + throw new Error("you cannot have whitelist and blacklist at the same time"); + } + return filterArrayByRegExps(names, opts.whitelist); + } + if (opts.blacklist) { + const blacklisted = filterArrayByRegExps(names, opts.blacklist); + return names.filter(name => blacklisted.indexOf(name) === -1); + } + return names; +} + function main(opts) { if (arguments[2] && arguments[1] && arguments[1].send) { arguments[1].status(500) @@ -26,57 +41,86 @@ function main(opts) { return; } - let helper = new PrometheusHelper(opts); - let metricTemplates = { - "up": () => helper.newGauge( + let factory = new PromFactory(opts); + + const metricTemplates = { + "up": () => factory.newGauge( "up", "1 = up, 0 = not up" ), - "nodejs_memory_heap_total_bytes": () => helper.newGauge( + "nodejs_memory_heap_total_bytes": () => factory.newGauge( "nodejs_memory_heap_total_bytes", "value of process.memoryUsage().heapTotal" ), - "nodejs_memory_heap_used_bytes": () => helper.newGauge( + "nodejs_memory_heap_used_bytes": () => factory.newGauge( "nodejs_memory_heap_used_bytes", "value of process.memoryUsage().heapUsed" ), - "http_request_total": () => helper.newCounter( - "http_request_total", - "number of http responses labeled with status code", - ["status_code"] - ) + "http_request_duration": () => { + const metric = factory.newHistogram( + "http_request_duration", + "number of http responses labeled with status code", + { + labels: ["bar"], + buckets: [0.003, 0.03, 0.1, 0.3, 1.5, 10] + } + ); + metric.labelNames = ["status_code"]; + return metric; + } }; - const metrics = {}; + const + metrics = {}, + names = prepareMetricNames(opts, metricTemplates); - for (let name of Object.keys(metricTemplates)) { + + for (let name of names) { metrics[name] = metricTemplates[name](); } - metrics.up.set(1); + if (metrics.up) { + metrics.up.set(1); + } let middleware = function (req, res, next) { + let timer, labels; + + if (metrics["http_request_duration"]) { + timer = metrics["http_request_duration"].startTimer(labels); + labels = {"status_code": 0}; + } + if (req.path == "/metrics") { let memoryUsage = process.memoryUsage(); - metrics["nodejs_memory_heap_total_bytes"].set(memoryUsage.heapTotal); - metrics["nodejs_memory_heap_used_bytes"].set(memoryUsage.heapUsed); + if (metrics["nodejs_memory_heap_total_bytes"]) { + metrics["nodejs_memory_heap_total_bytes"].set(memoryUsage.heapTotal); + } + if (metrics["nodejs_memory_heap_used_bytes"]) { + metrics["nodejs_memory_heap_used_bytes"].set(memoryUsage.heapUsed); + } res.contentType("text/plain") - .send(helper.promClient.register.metrics()); + .send(factory.promClient.register.metrics()); return; } - onFinished(res, () => { - if (res.statusCode) { - metrics["http_request_total"].inc({"status_code": res.statusCode}); - } - }); + if (timer) { + onFinished(res, () => { + if (res.statusCode) { + labels["status_code"] = res.statusCode; + timer(); + } + }); + } next(); }; - middleware.helper = helper; + middleware.factory = factory; middleware.metricTemplates = metricTemplates; + middleware.metrics = metrics; + middleware.promClient = factory.promClient; return middleware; }