Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

express-prom-bundle

Package Overview
Dependencies
Maintainers
1
Versions
56
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

express-prom-bundle - npm Package Compare versions

Comparing version 1.2.3 to 2.0.0

4

package.json
{
"name": "express-prom-bundle",
"version": "1.2.3",
"version": "2.0.0",
"description": "express middleware with popular prometheus metrics in one bundle",

@@ -19,3 +19,3 @@ "main": "src/index.js",

"on-finished": "^2.3.0",
"prom-client": "^6.2.0",
"prom-client": "^6.3.0",
"url-value-parser": "^1.0.0"

@@ -22,0 +22,0 @@ },

@@ -12,5 +12,6 @@ [![build status](https://travis-ci.org/jochen-schweizer/express-prom-bundle.png)](https://travis-ci.org/jochen-schweizer/express-prom-bundle) [![Coverage Status](https://coveralls.io/repos/github/jochen-schweizer/express-prom-bundle/badge.svg?branch=master)](https://coveralls.io/github/jochen-schweizer/express-prom-bundle?branch=master) [![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)](https://www.tldrlegal.com/l/mit) [![NPM version](https://badge.fury.io/js/express-prom-bundle.png)](http://badge.fury.io/js/express-prom-bundle)

* `up`: normally is just 1
* `nodejs_memory_heap_total_bytes` and `nodejs_memory_heap_used_bytes`
* `http_request_seconds`: http latency histogram labeled with `status_code`
* `http_request_duration_seconds`: http latency histogram labeled with `status_code`, `method` and `path`
**Please not version 2.x is NOT compatible with 1.x**
## Install

@@ -25,5 +26,5 @@

```javascript
const promBundle = require("express-prom-bundle"),
metricsMiddleware = promBundle({/* options */ }),
app = require("express")();
const promBundle = require("express-prom-bundle");
const metricsMiddleware = promBundle({/* options */ });
const app = require("express")();

@@ -48,19 +49,17 @@ app.use(metricsMiddleware);

* **prefix**: prefix added to every metric name
* **whitelist**, **blacklist**: array of strings or regexp specifying which metrics to include/exclude
* **buckets**: buckets used for `http_request_seconds` histogram
* **includeMethod**: include HTTP method (GET, PUT, ...) as a label to `http_request_seconds`
* **includePath**: include URL path as a label - **EXPERIMENTAL!** (see below)
* **normalizePath**: boolean or `function(req)` - path normalization for `includePath` option
* **excludeRoutes**: array of strings or regexp specifying which routes should be skipped for `http_request_seconds` metric. It uses `req.path` as subject when checking
* **autoregister**: if `/metrics` endpoint should be registered. It is (Default: **true**)
* **keepDefaultMetrics**: if default metrics provided by **prom-client** should be probed and delivered. (Default: **false**)
* **buckets**: buckets used for `http_request_seconds` histogram
* **includeMethod**: include HTTP method (GET, PUT, ...) as a label to `http_request_duration_seconds`
* **includePath**: include URL path as a label (see below)
* **normalizePath**: boolean or `function(req)` - path normalization for `includePath` option
* **excludeRoutes**: array of strings or regexp specifying which routes should be skipped for `http_request_duration_seconds` metric. It uses `req.path` as subject when checking
* **autoregister**: if `/metrics` endpoint should be registered. (Default: **true**)
* **whitelist**, **blacklist**: array of strings or regexp specifying which metrics to include/exclude
### includePath option
### More details on includePath option
The goal is to have separate latency statistics by URL path, e.g. `/my-app/user/`, `/products/by-category` etc.
But just taking `req.path` as a label value won't work as IDs are often part of the URL, like `/user/12352/profile`. So what we actually need is a path template. The automatically module tries to figure out what parts of the path are values or IDs, and what is an actual path. The example mentioned before would be normalized to `/user/#val/profile` and that will become the value for the label.
Just taking `req.path` as a label value won't work as IDs are often part of the URL, like `/user/12352/profile`. So what we actually need is a path template. The module tries to figure out what parts of the path are values or IDs, and what is an actual path. The example mentioned before would be normalized to `/user/#val/profile` and that will become the value for the label.
You can override this magical behavior and create define your own function by providing an optional callback **normalizePath**.
You can override this magical behavior and define your own function by providing an optional callback using **normalizePath** option.

@@ -80,5 +79,5 @@ For more details:

const express = require("express"),
app = express(),
promBundle = require("express-prom-bundle");
const express = require("express");
const app = express();
const promBundle = require("express-prom-bundle");

@@ -91,3 +90,3 @@

app.use(promBundle({
prefix: "demo_app:something",
includePath: true,
excludeRoutes: ["/foo"]

@@ -110,6 +109,6 @@ }));

```javascript
const promBundle = require("express-prom-bundle"),
koa = require("koa"),
c2k = require("koa-connect"),
metricsMiddleware = promBundle({/* options */ });
const promBundle = require("express-prom-bundle");
const koa = require("koa");
const c2k = require("koa-connect");
const metricsMiddleware = promBundle({/* options */ });

@@ -125,2 +124,14 @@ const app = koa();

* **2.0.0**
* the reason for the version lift were:
* compliance to official naming recommendation: https://prometheus.io/docs/practices/naming/
* stopping promotion of an anti-pattern - see https://groups.google.com/d/msg/prometheus-developers/XjlOnDCK9qc/ovKzV3AIBwAJ
* dealing with **prom-client** being a singleton with a built-in registry
* main histogram metric renamed from `http_request_seconds` to `http_request_duration_seconds`
* options removed: **prefix**, **keepDefaultMetrics**
* factory removed (as the only reason of it was adding the prefix)
* upgrade prom-client to 6.3.0
* code style changed to the one closer to express
* **1.2.1**

@@ -127,0 +138,0 @@ * upgrade prom-client to 6.1.2

@@ -1,162 +0,140 @@

"use strict";
'use strict';
const onFinished = require('on-finished');
const url = require('url');
const promClient = require('prom-client');
const normalizePath = require('./normalizePath');
const PromFactory = require("./PromFactory");
const onFinished = require("on-finished");
const url = require("url");
const promClient = require("prom-client");
const normalizePath = require("./normalizePath");
function matchVsRegExps(element, regexps) {
for (let regexp of regexps) {
if (regexp instanceof RegExp) {
if (element.match(regexp)) {
return true;
}
} else if (element == regexp) {
return true;
}
for (let regexp of regexps) {
if (regexp instanceof RegExp) {
if (element.match(regexp)) {
return true;
}
} else if (element === regexp) {
return true;
}
return false;
}
return false;
}
function filterArrayByRegExps(array, regexps) {
return array.filter(element => {
return matchVsRegExps(element, regexps);
});
return array.filter(element => {
return matchVsRegExps(element, regexps);
});
}
function prepareMetricNames(opts, metricTemplates) {
const 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);
}
const names = Object.keys(metricTemplates);
if (opts.whitelist) {
if (opts.blacklist) {
const blacklisted = filterArrayByRegExps(names, opts.blacklist);
return names.filter(name => blacklisted.indexOf(name) === -1);
throw new Error('you cannot have whitelist and blacklist at the same time');
}
return names;
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) {
opts = Object.assign({ autoregister: true }, opts || {} );
if (arguments[2] && arguments[1] && arguments[1].send) {
arguments[1].status(500)
.send("<h1>500 Error</h1>\n"
+ "<p>Unexpected 3rd param in express-prom-bundle.\n"
+ "<p>Did you just put express-prom-bundle into app.use "
+ "without calling it as a function first?");
return;
}
opts = Object.assign({autoregister: true}, opts || {});
if (arguments[2] && arguments[1] && arguments[1].send) {
arguments[1].status(500)
.send('<h1>500 Error</h1>\n'
+ '<p>Unexpected 3rd param in express-prom-bundle.\n'
+ '<p>Did you just put express-prom-bundle into app.use '
+ 'without calling it as a function first?');
return;
}
// this is a really messy hack but needed for compatibility with v1
// will be completely removed in v2
if (!opts.keepDefaultMetrics) {
const metrics = promClient.register.getMetricsAsJSON();
clearInterval(promClient.defaultMetrics());
metrics.forEach(metric => {
if (!opts.prefix || metric.name.substr(0, opts.prefix.length) != opts.prefix) {
promClient.register.removeSingleMetric(metric.name);
}
});
}
if (opts.prefix || opts.keepDefaultMetrics !== undefined) {
throw new Error(
'express-prom-bundle detected obsolete options:'
+ 'prefix and/or keepDefaultMetrics. '
+ 'Please refer to oficial docs. '
+ 'Most likely you upgraded the module without necessary code changes'
);
}
const factory = new PromFactory(opts);
const httpMtricName = opts.httpDurationMetricName || 'http_request_duration_seconds';
const metricTemplates = {
"up": () => factory.newGauge(
"up",
"1 = up, 0 = not up"
),
"nodejs_memory_heap_total_bytes": () => factory.newGauge(
"nodejs_memory_heap_total_bytes",
"value of process.memoryUsage().heapTotal"
),
"nodejs_memory_heap_used_bytes": () => factory.newGauge(
"nodejs_memory_heap_used_bytes",
"value of process.memoryUsage().heapUsed"
),
"http_request_seconds": () => {
const labels = ["status_code"];
if (opts.includeMethod) {
labels.push("method");
}
if (opts.includePath) {
labels.push("path");
}
const metric = factory.newHistogram(
"http_request_seconds",
"number of http responses labeled with status code",
labels,
{
buckets: opts.buckets || [0.003, 0.03, 0.1, 0.3, 1.5, 10]
}
);
return metric;
const metricTemplates = {
'up': () => new promClient.Gauge(
'up',
'1 = up, 0 = not up'
),
'http_request_duration_seconds': () => {
const labels = ['status_code'];
if (opts.includeMethod) {
labels.push('method');
}
if (opts.includePath) {
labels.push('path');
}
const metric = new promClient.Histogram(
httpMtricName,
'duration histogram of http responses labeled with: ' + labels.join(', '),
labels,
{
buckets: opts.buckets || [0.003, 0.03, 0.1, 0.3, 1.5, 10]
}
};
);
return metric;
}
};
const metrics = {};
const names = prepareMetricNames(opts, metricTemplates);
const metrics = {};
const names = prepareMetricNames(opts, metricTemplates);
for (let name of names) {
metrics[name] = metricTemplates[name]();
}
for (let name of names) {
metrics[name] = metricTemplates[name]();
}
if (metrics.up) {
metrics.up.set(1);
}
if (metrics.up) {
metrics.up.set(1);
}
const metricsMiddleware = function(req,res) {
let memoryUsage = process.memoryUsage();
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);
}
const metricsMiddleware = function(req,res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end(promClient.register.metrics());
};
res.writeHead(200, {"Content-Type": "text/plain"});
res.end(factory.promClient.register.metrics());
};
const middleware = function (req, res, next) {
const middleware = function (req, res, next) {
const path = req.path || url.parse(req.url).pathname;
let labels;
const path = req.path || url.parse(req.url).pathname;
let labels;
if (opts.autoregister && path === '/metrics') {
return metricsMiddleware(req,res);
}
if (opts.autoregister && path == "/metrics") {
return metricsMiddleware(req,res);
}
if (opts.excludeRoutes && matchVsRegExps(path, opts.excludeRoutes)) {
return next();
}
if (opts.excludeRoutes && matchVsRegExps(path, opts.excludeRoutes)) {
return next();
if (metrics[httpMtricName]) {
labels = {'status_code': 0};
let timer = metrics[httpMtricName].startTimer(labels);
onFinished(res, () => {
labels.status_code = res.statusCode;
if (opts.includeMethod) {
labels.method = req.method;
}
if (metrics["http_request_seconds"]) {
labels = {"status_code": 0};
let timer = metrics["http_request_seconds"].startTimer(labels);
onFinished(res, () => {
labels.status_code = res.statusCode;
if (opts.includeMethod) {
labels.method = req.method;
}
if (opts.includePath) {
labels.path = normalizePath(req, opts);
}
timer();
});
if (opts.includePath) {
labels.path = normalizePath(req, opts);
}
timer();
});
}
next();
};
next();
};
middleware.factory = factory;
middleware.metricTemplates = metricTemplates;
middleware.metrics = metrics;
middleware.promClient = factory.promClient;
middleware.metricsMiddleware = metricsMiddleware;
return middleware;
middleware.metricTemplates = metricTemplates;
middleware.metrics = metrics;
middleware.promClient = promClient;
middleware.metricsMiddleware = metricsMiddleware;
return middleware;
}

@@ -163,0 +141,0 @@

@@ -1,26 +0,27 @@

"use strict";
'use strict';
const UrlValueParser = require("url-value-parser");
const url = require("url");
const UrlValueParser = require('url-value-parser');
const url = require('url');
let urlValueParser;
module.exports = function(req, opts) {
opts = opts || {};
opts = opts || {};
// originalUrl is taken, because url and path can be changed
// by middlewares such as "router". Note: this function is called onFinish
/// i.e. always in the tail of the middleware chain
const path = url.parse(req.originalUrl).pathname;
// originalUrl is taken, because url and path can be changed
// by middlewares such as 'router'. Note: this function is called onFinish
/// i.e. always in the tail of the middleware chain
const path = url.parse(req.originalUrl).pathname;
if (opts.normalizePath !== undefined && !opts.normalizePath) {
return path;
}
if (typeof opts.normalizePath === "function") {
return opts.normalizePath(req, opts);
}
if (opts.normalizePath !== undefined && !opts.normalizePath) {
return path;
}
if (typeof opts.normalizePath === 'function') {
return opts.normalizePath(req, opts);
}
if (!urlValueParser) {
urlValueParser = new UrlValueParser();
}
return urlValueParser.replacePathValues(path);
if (!urlValueParser) {
urlValueParser = new UrlValueParser();
}
return urlValueParser.replacePathValues(path);
};
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