mirror of
https://github.com/BreizhHardware/express-prom-bundle.git
synced 2026-01-18 16:27:28 +01:00
267 lines
9.9 KiB
Markdown
267 lines
9.9 KiB
Markdown
[](https://travis-ci.org/jochen-schweizer/express-prom-bundle) [](https://coveralls.io/github/jochen-schweizer/express-prom-bundle?branch=master) [](https://www.tldrlegal.com/l/mit) [](http://badge.fury.io/js/express-prom-bundle)
|
|
|
|
# express prometheus bundle
|
|
|
|
Express middleware with popular prometheus metrics in one bundle. It's also compatible with koa v1 and v2 (see below).
|
|
|
|
Since version 5 it uses **prom-client** as a peer dependency. See: https://github.com/siimon/prom-client
|
|
|
|
Included metrics:
|
|
|
|
* `up`: normally is just 1
|
|
* `http_request_duration_seconds`: http latency histogram/summary labeled with `status_code`, `method` and `path`
|
|
|
|
## Install
|
|
|
|
```
|
|
npm install prom-client express-prom-bundle
|
|
```
|
|
|
|
## Sample Usage
|
|
|
|
```javascript
|
|
const promBundle = require("express-prom-bundle");
|
|
const app = require("express")();
|
|
const metricsMiddleware = promBundle({includeMethod: true});
|
|
|
|
app.use(metricsMiddleware);
|
|
app.use(/* your middleware */);
|
|
app.listen(3000);
|
|
```
|
|
|
|
* call your endpoints
|
|
* see your metrics here: [http://localhost:3000/metrics](http://localhost:3000/metrics)
|
|
|
|
**ALERT!**
|
|
|
|
The order in which the routes are registered is important, since
|
|
**only the routes registered after the express-prom-bundle will be measured**
|
|
|
|
You can use this to your advantage to bypass some of the routes.
|
|
See the example below.
|
|
|
|
## Options
|
|
|
|
Which labels to include in `http_request_duration_seconds` metric:
|
|
|
|
* **includeStatusCode**: HTTP status code (200, 400, 404 etc.), default: **true**
|
|
* **includeMethod**: HTTP method (GET, PUT, ...), default: **false**
|
|
* **includePath**: URL path (see important details below), default: **false**
|
|
* **customLabels**: an object containing extra labels, e.g. ```{project_name: 'hello_world'}```.
|
|
Most useful together with **transformLabels** callback, otherwise it's better to use native Prometheus relabeling.
|
|
* **includeUp**: include an auxiliary "up"-metric which always returns 1, default: **true**
|
|
* **metricsPath**: replace the `/metrics` route with a **regex** or exact **string**. Note: it is highly recommended to just stick to the default
|
|
* **metricType**: histogram/summary selection. See more details below
|
|
* **bypass**: function taking express request as an argument and determines whether the given request should be excluded in the metrics, default: **() => false**
|
|
|
|
### metricType option ###
|
|
|
|
Two metric types are supported for `http_request_duration_seconds` metric:
|
|
* [histogram](https://prometheus.io/docs/concepts/metric_types/#histogram) (default)
|
|
* [summary](https://prometheus.io/docs/concepts/metric_types/#summary)
|
|
|
|
Additional options for **histogram**:
|
|
* **buckets**: buckets used for the `http_request_duration_seconds` histogram
|
|
|
|
Additional options for **summary**:
|
|
* **percentiles**: percentiles used for `http_request_duration_seconds` summary
|
|
* **ageBuckets**: ageBuckets configures how many buckets we have in our sliding window for the summary
|
|
* **maxAgeSeconds**: the maxAgeSeconds will tell how old a bucket can be before it is reset
|
|
|
|
### Transformation callbacks ###
|
|
|
|
* **normalizePath**: `function(req)` or `Array`
|
|
* if function is provided, then it should generate path value from express `req`
|
|
* if array is provided, then it should be an array of tuples `[regex, replacement]`. The `regex` can be a string and is automatically converted into JS regex.
|
|
* ... see more details in the section below
|
|
* **urlValueParser**: options passed when instantiating [url-value-parser](https://github.com/disjunction/url-value-parser).
|
|
This is the easiest way to customize which parts of the URL should be replaced with "#val".
|
|
See the [docs](https://github.com/disjunction/url-value-parser) of url-value-parser module for details.
|
|
* **formatStatusCode**: `function(res)` producing final status code from express `res` object, e.g. you can combine `200`, `201` and `204` to just `2xx`.
|
|
* **transformLabels**: `function(labels, req, res)` transforms the **labels** object, e.g. setting dynamic values to **customLabels**
|
|
|
|
### Other options ###
|
|
|
|
* **autoregister**: if `/metrics` endpoint should be registered. (Default: **true**)
|
|
* **promClient**: options for promClient startup, e.g. **collectDefaultMetrics**. This option was added
|
|
to keep `express-prom-bundle` runnable using confit (e.g. with kraken.js) without writing any JS code,
|
|
see [advanced example](https://github.com/jochen-schweizer/express-prom-bundle/blob/master/advanced-example.js)
|
|
* **promRegistry**: Optional `promClient.Registry` instance to attach metrics to. Defaults to global `promClient.register`.
|
|
|
|
### More details on includePath option
|
|
|
|
Let's say you want to have latency statistics by URL path,
|
|
e.g. separate metrics for `/my-app/user/`, `/products/by-category` etc.
|
|
|
|
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.
|
|
These conversions are handled by `normalizePath` function.
|
|
|
|
You can extend this magical behavior by providing
|
|
additional RegExp rules to be performed,
|
|
or override `normalizePath` with your own function.
|
|
|
|
#### Example 1 (add custom RegExp):
|
|
|
|
```javascript
|
|
app.use(promBundle({
|
|
normalizePath: [
|
|
// collect paths like "/customer/johnbobson" as just one "/custom/#name"
|
|
['^/customer/.*', '/customer/#name'],
|
|
|
|
// collect paths like "/bobjohnson/order-list" as just one "/#name/order-list"
|
|
['^.*/order-list', '/#name/order-list']
|
|
],
|
|
urlValueParser: {
|
|
minHexLength: 5,
|
|
extraMasks: [
|
|
'ORD[0-9]{5,}' // replace strings like ORD1243423, ORD673562 as #val
|
|
]
|
|
}
|
|
}));
|
|
```
|
|
|
|
#### Example 2 (override normalizePath function):
|
|
|
|
```javascript
|
|
app.use(promBundle(/* options? */));
|
|
|
|
// let's reuse the existing one and just add some
|
|
// functionality on top
|
|
const originalNormalize = promBundle.normalizePath;
|
|
promBundle.normalizePath = (req, opts) => {
|
|
const path = originalNormalize(req, opts);
|
|
// count all docs as one path, but /docs/login as a separate one
|
|
return (path.match(/^\/docs/) && !path.match(/^\/login/)) ? '/docs/*' : path;
|
|
};
|
|
```
|
|
|
|
For more details:
|
|
* [url-value-parser](https://www.npmjs.com/package/url-value-parser) - magic behind automatic path normalization
|
|
* [normalizePath.js](https://github.com/jochen-schweizer/express-prom-bundle/blob/master/src/normalizePath.js) - source code for path processing
|
|
|
|
|
|
## express example
|
|
|
|
setup std. metrics but exclude `up`-metric:
|
|
|
|
```javascript
|
|
const express = require("express");
|
|
const app = express();
|
|
const promBundle = require("express-prom-bundle");
|
|
|
|
// calls to this route will not appear in metrics
|
|
// because it's applied before promBundle
|
|
app.get("/status", (req, res) => res.send("i am healthy"));
|
|
|
|
// register metrics collection for all routes
|
|
// ... except those starting with /foo
|
|
app.use("/((?!foo))*", promBundle({includePath: true}));
|
|
|
|
// this call will NOT appear in metrics,
|
|
// because express will skip the metrics middleware
|
|
app.get("/foo", (req, res) => res.send("bar"));
|
|
|
|
// calls to this route will appear in metrics
|
|
app.get("/hello", (req, res) => res.send("ok"));
|
|
|
|
app.listen(3000);
|
|
```
|
|
|
|
See an [advanced example on github](https://github.com/jochen-schweizer/express-prom-bundle/blob/master/advanced-example.js)
|
|
|
|
## koa v2 example
|
|
|
|
```javascript
|
|
const promBundle = require("express-prom-bundle");
|
|
const Koa = require("koa");
|
|
const c2k = require("koa-connect");
|
|
const metricsMiddleware = promBundle({/* options */ });
|
|
|
|
const app = new Koa();
|
|
|
|
app.use(c2k(metricsMiddleware));
|
|
app.use(/* your middleware */);
|
|
app.listen(3000);
|
|
```
|
|
|
|
## using with cluster
|
|
|
|
You'll need to use an additional **clusterMetrics()** middleware.
|
|
|
|
In the example below the master process will expose an API with a single endpoint `/metrics`
|
|
which returns an aggregate of all metrics from all the workers.
|
|
|
|
``` javascript
|
|
const cluster = require('cluster');
|
|
const promBundle = require('./src/index');
|
|
const numCPUs = Math.max(2, require('os').cpus().length);
|
|
const express = require('express');
|
|
|
|
if (cluster.isMaster) {
|
|
for (let i = 1; i < numCPUs; i++) {
|
|
cluster.fork();
|
|
}
|
|
|
|
const metricsApp = express();
|
|
metricsApp.use('/metrics', promBundle.clusterMetrics());
|
|
metricsApp.listen(9999);
|
|
|
|
console.log('cluster metrics listening on 9999');
|
|
console.log('call localhost:9999/metrics for aggregated metrics');
|
|
} else {
|
|
const app = express();
|
|
app.use(promBundle({
|
|
autoregister: false, // disable /metrics for single workers
|
|
includeMethod: true
|
|
}));
|
|
app.use((req, res) => res.send(`hello from pid ${process.pid}\n`));
|
|
app.listen(3000);
|
|
console.log(`worker ${process.pid} listening on 3000`);
|
|
}
|
|
```
|
|
|
|
## using with kraken.js
|
|
|
|
Here is meddleware config sample, which can be used in a standard **kraken.js** application.
|
|
In this case the stats for URI paths and HTTP methods are collected separately,
|
|
while replacing all HEX values starting from 5 characters and all IP addresses in the path as #val.
|
|
|
|
```json
|
|
{
|
|
"middleware": {
|
|
"expressPromBundle": {
|
|
"route": "/((?!status|favicon.ico|robots.txt))*",
|
|
"priority": 0,
|
|
"module": {
|
|
"name": "express-prom-bundle",
|
|
"arguments": [
|
|
{
|
|
"includeMethod": true,
|
|
"includePath": true,
|
|
"buckets": [0.1, 1, 5],
|
|
"promClient": {
|
|
"collectDefaultMetrics": {
|
|
}
|
|
},
|
|
"urlValueParser": {
|
|
"minHexLength": 5,
|
|
"extraMasks": [
|
|
"^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+$"
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## License
|
|
|
|
MIT
|