mirror of
https://github.com/BreizhHardware/express-prom-bundle.git
synced 2026-01-19 00:37:36 +01:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0bda9934c1 | ||
|
|
5bf6b153e6 | ||
|
|
1171fb5be1 | ||
|
|
ea9f34aa3e | ||
|
|
93b0e8fafe | ||
|
|
2d2b253567 | ||
|
|
1b52c6f46e | ||
|
|
2293d1cd40 | ||
|
|
1ee094eb04 | ||
|
|
a1284ec3e5 | ||
|
|
5a1efd7fe6 | ||
|
|
bcac9f523a | ||
|
|
c4be86b651 | ||
|
|
5e5a47a500 | ||
|
|
426e4d4556 | ||
|
|
78ebb06a6d | ||
|
|
c6d2210062 | ||
|
|
c436fa3c88 | ||
|
|
77b02c7302 | ||
|
|
068ed93a61 | ||
|
|
c5aa3f7388 | ||
|
|
ca28ff4fe1 | ||
|
|
52f47ed9f4 | ||
|
|
a60996ebc3 | ||
|
|
e7c94ff307 | ||
|
|
884617bffe | ||
|
|
32b76d0970 | ||
|
|
845cd6ee46 | ||
|
|
0c3c949425 | ||
|
|
1ec800cde7 | ||
|
|
661e7d5092 | ||
|
|
b569a71130 | ||
|
|
10c372c438 | ||
|
|
3599c32876 | ||
|
|
59ec729cb2 | ||
|
|
c95cd1834c | ||
|
|
d18316fee8 | ||
|
|
ccb74f99b0 | ||
|
|
2e093bc14d | ||
|
|
3f6b2746b9 | ||
|
|
83ee0ce06e | ||
|
|
5300d0ef82 | ||
|
|
62abb62772 | ||
|
|
bef92b77e1 | ||
|
|
99d8fc1ea9 |
6
Makefile
6
Makefile
@@ -1,10 +1,10 @@
|
||||
.PHONY: coverage
|
||||
|
||||
test:
|
||||
./node_modules/jasme/run.js
|
||||
npm test
|
||||
lint:
|
||||
node_modules/eslint/bin/eslint.js src
|
||||
node_modules/.bin/dtslint types
|
||||
npx eslint src
|
||||
npm run dtslint-next
|
||||
coverage:
|
||||
node_modules/istanbul/lib/cli.js cover \
|
||||
-i 'src/*' \
|
||||
|
||||
23
README.md
23
README.md
@@ -52,7 +52,7 @@ Which labels to include in `http_request_duration_seconds` metric:
|
||||
* **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**
|
||||
* **httpDurationMetricName**: Allows you change the name of HTTP duration metric, default: **`http_request_duration_seconds`**.
|
||||
|
||||
### metricType option ###
|
||||
|
||||
@@ -79,14 +79,29 @@ Additional options for **summary**:
|
||||
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**
|
||||
* **urlPathReplacement**: replacement string for the values (default: "#val")
|
||||
|
||||
### Other options ###
|
||||
|
||||
* **autoregister**: if `/metrics` endpoint should be registered. (Default: **true**)
|
||||
* **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`.
|
||||
* **metricsApp**: Allows you to attach the metrics endpoint to a different express app. You probably want to use it in combination with `autoregister: false`.
|
||||
* **bypass**: An object that takes onRequest and onFinish callbacks that determines whether the given request should be excluded in the metrics. Default:
|
||||
|
||||
```js
|
||||
{
|
||||
onRequest: (req) => false,
|
||||
onFinish: (req, res) => false
|
||||
}
|
||||
```
|
||||
|
||||
`onRequest` is run directly in the middleware chain, before the request is processed. `onFinish` is run after the request has been processed, and has access to the express response object in addition to the request object. Both callbacks are optional, and if one or both returns true the request is excluded.
|
||||
|
||||
As a shorthand, just the onRequest callback can be used instead of the object.
|
||||
|
||||
|
||||
### More details on includePath option
|
||||
|
||||
@@ -197,7 +212,8 @@ which returns an aggregate of all metrics from all the workers.
|
||||
|
||||
``` javascript
|
||||
const cluster = require('cluster');
|
||||
const promBundle = require('./src/index');
|
||||
const promBundle = require('express-prom-bundle');
|
||||
const promClient = require('prom-client');
|
||||
const numCPUs = Math.max(2, require('os').cpus().length);
|
||||
const express = require('express');
|
||||
|
||||
@@ -213,6 +229,7 @@ if (cluster.isMaster) {
|
||||
console.log('cluster metrics listening on 9999');
|
||||
console.log('call localhost:9999/metrics for aggregated metrics');
|
||||
} else {
|
||||
new promClient.AggregatorRegistry();
|
||||
const app = express();
|
||||
app.use(promBundle({
|
||||
autoregister: false, // disable /metrics for single workers
|
||||
|
||||
@@ -19,7 +19,8 @@ const bundle = promBundle({
|
||||
urlValueParser: {
|
||||
minHexLength: 5,
|
||||
extraMasks: [
|
||||
"^[0-9]+\\.[0-9]+\\.[0-9]+$" // replace dot-separated dates with #val
|
||||
"^[0-9]+\\.[0-9]+\\.[0-9]+$", // replace dot-separated dates with #val, (regex as string)
|
||||
/^[0-9]+\-[0-9]+\-[0-9]+$/ // replace dash-separated dates with #val (actual regex)
|
||||
]
|
||||
},
|
||||
normalizePath: [
|
||||
|
||||
26
package-lock.json
generated
26
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "express-prom-bundle",
|
||||
"version": "6.3.2",
|
||||
"version": "6.6.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1265,9 +1265,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"handlebars": {
|
||||
"version": "4.7.6",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz",
|
||||
"integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==",
|
||||
"version": "4.7.7",
|
||||
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz",
|
||||
"integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5",
|
||||
@@ -1826,9 +1826,9 @@
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.19",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
||||
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"dev": true
|
||||
},
|
||||
"log-driver": {
|
||||
@@ -2156,9 +2156,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true
|
||||
},
|
||||
"path-to-regexp": {
|
||||
@@ -2980,9 +2980,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
|
||||
"integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "express-prom-bundle",
|
||||
"version": "6.3.2",
|
||||
"version": "6.6.0",
|
||||
"description": "express middleware with popular prometheus metrics in one bundle",
|
||||
"main": "src/index.js",
|
||||
"keywords": [
|
||||
@@ -16,7 +16,7 @@
|
||||
],
|
||||
"types": "types",
|
||||
"scripts": {
|
||||
"test": "node_modules/jasme/run.js",
|
||||
"test": "NODE_ENV=test node_modules/jasme/run.js",
|
||||
"lint": "eslint src",
|
||||
"coverage": "make coverage",
|
||||
"dtslint": "dtslint types",
|
||||
|
||||
@@ -206,7 +206,7 @@ describe('index', () => {
|
||||
bypass: (req)=> {
|
||||
// metrics added here to attempt skipping /metrics
|
||||
// this should fail though, because serving /metrics preceeds bypassing
|
||||
return !!req.url.match(/test|bad.word|metrics/)
|
||||
return !!req.url.match(/test|bad.word|metrics/);
|
||||
}
|
||||
});
|
||||
app.use(instance);
|
||||
@@ -240,6 +240,46 @@ describe('index', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('bypass requests, checking res', done => {
|
||||
const app = express();
|
||||
const instance = bundle({
|
||||
bypass: {
|
||||
onFinish: (req, res) => res.statusCode === 404
|
||||
}
|
||||
});
|
||||
app.use(instance);
|
||||
app.use('/200', (req, res) => res.send(''));
|
||||
app.use('/404', (req, res) => res.status(404).send(''));
|
||||
app.use('/500', (req, res) => res.status(500).send(''));
|
||||
|
||||
const agent = supertest(app);
|
||||
agent.get('/200')
|
||||
.expect(200)
|
||||
.then(() => {
|
||||
return agent
|
||||
.get('/404')
|
||||
.expect(404);
|
||||
})
|
||||
.then(() => {
|
||||
return agent
|
||||
.get('/500')
|
||||
.expect(500);
|
||||
})
|
||||
.then(() => {
|
||||
const metricHashMap = instance.metrics.http_request_duration_seconds.hashMap;
|
||||
|
||||
// only /200 and /500 should be counted
|
||||
expect(metricHashMap['status_code:200'].count).toBe(1);
|
||||
expect(metricHashMap['status_code:404']).not.toBeDefined();
|
||||
expect(metricHashMap['status_code:500'].count).toBe(1);
|
||||
|
||||
return agent
|
||||
.get('/metrics')
|
||||
.expect(200);
|
||||
})
|
||||
.then(done);
|
||||
});
|
||||
|
||||
it('complains about deprecated options', () => {
|
||||
expect(() => bundle({prefix: 'hello'})).toThrow();
|
||||
});
|
||||
@@ -409,6 +449,27 @@ describe('index', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('handles errors in collectors', done => {
|
||||
const app = express();
|
||||
const instance = bundle({});
|
||||
app.use(instance);
|
||||
|
||||
new promClient.Gauge({
|
||||
name: 'kaboom',
|
||||
help: 'this metric explodes',
|
||||
collect() {
|
||||
throw new Error('kaboom!');
|
||||
}
|
||||
});
|
||||
|
||||
// the error will NOT be displayed if NODE_ENV=test (as defined in package.json)
|
||||
|
||||
supertest(app)
|
||||
.get('/metrics')
|
||||
.expect(500)
|
||||
.end((err) => done(err));
|
||||
});
|
||||
|
||||
it('customLabels={foo: "bar"} adds foo="bar" label to metrics', done => {
|
||||
const app = express();
|
||||
const instance = bundle({
|
||||
@@ -491,18 +552,18 @@ describe('index', () => {
|
||||
|
||||
describe('usage of clusterMetrics()', () => {
|
||||
it('clusterMetrics returns 200 even without a cluster', (done) => {
|
||||
const app = express();
|
||||
const app = express();
|
||||
|
||||
cluster.workers = [];
|
||||
cluster.workers = [];
|
||||
|
||||
app.use('/cluster_metrics', bundle.clusterMetrics());
|
||||
const agent = supertest(app);
|
||||
agent
|
||||
.get('/cluster_metrics')
|
||||
.end((err, res) => {
|
||||
expect(res.status).toBe(200);
|
||||
done();
|
||||
});
|
||||
app.use('/cluster_metrics', bundle.clusterMetrics());
|
||||
const agent = supertest(app);
|
||||
agent
|
||||
.get('/cluster_metrics')
|
||||
.end((err, res) => {
|
||||
expect(res.status).toBe(200);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('clusterMetrics returns 500 in case of an error', (done) => {
|
||||
@@ -569,5 +630,23 @@ describe('index', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('additional metricsApp can be used', done => {
|
||||
const app = express();
|
||||
const metricsApp = express();
|
||||
const bundled = bundle({metricsApp});
|
||||
|
||||
app.use(bundled);
|
||||
|
||||
const agent = supertest(app);
|
||||
const metricsAgent = supertest(metricsApp);
|
||||
agent.get('/').end(() => {
|
||||
metricsAgent.get('/metrics').end((err, res) => {
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.text).toMatch(/status_code="404"/);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,4 +28,9 @@ describe('normalizePath', () => {
|
||||
});
|
||||
expect(subject).toThrow();
|
||||
});
|
||||
|
||||
it('uses urlPathReplacement when passed to transform the path', () => {
|
||||
expect(normalizePath({url: '/a/12345'}, {urlPathReplacement: ':id'}))
|
||||
.toBe('/a/:id');
|
||||
});
|
||||
});
|
||||
|
||||
36
src/index.js
36
src/index.js
@@ -68,7 +68,8 @@ function main(opts) {
|
||||
formatStatusCode: main.normalizeStatusCode,
|
||||
metricType: 'histogram',
|
||||
promClient: {},
|
||||
promRegistry: promClient.register
|
||||
promRegistry: promClient.register,
|
||||
metricsApp: null,
|
||||
}, opts
|
||||
);
|
||||
|
||||
@@ -140,33 +141,44 @@ function main(opts) {
|
||||
}
|
||||
|
||||
const metricsMiddleware = function(req, res, next) {
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
const sendSuccesss = (output) => {
|
||||
res.writeHead(200, {'Content-Type': 'text/plain'});
|
||||
res.end(output);
|
||||
};
|
||||
|
||||
const metricsResponse = opts.promRegistry.metrics();
|
||||
// starting from prom-client@13 .metrics() returns a Promise
|
||||
if (metricsResponse.then) {
|
||||
metricsResponse
|
||||
.then(output => res.end(output))
|
||||
.then(output => sendSuccesss(output))
|
||||
.catch(err => next(err));
|
||||
} else {
|
||||
// compatibility fallback for previous versions of prom-client@<=12
|
||||
res.end(metricsResponse);
|
||||
sendSuccesss(metricsResponse);
|
||||
}
|
||||
};
|
||||
|
||||
const metricsMatch = opts.metricsPath instanceof RegExp ? opts.metricsPath
|
||||
: new RegExp('^' + (opts.metricsPath || '/metrics') + '/?$');
|
||||
|
||||
if (typeof opts.bypass === 'function') {
|
||||
opts.bypass = {
|
||||
onRequest: opts.bypass
|
||||
};
|
||||
} else if (!opts.bypass) {
|
||||
opts.bypass = {};
|
||||
}
|
||||
|
||||
const middleware = function (req, res, next) {
|
||||
const path = req.originalUrl || req.url; // originalUrl gets lost in koa-connect?
|
||||
|
||||
if (opts.autoregister && path.match(metricsMatch)) {
|
||||
return metricsMiddleware(req, res);
|
||||
return metricsMiddleware(req, res, next);
|
||||
}
|
||||
|
||||
// bypass() is checked only after /metrics was processed
|
||||
// if you wish to disable /metrics use autoregister:false instead
|
||||
if (opts.bypass && opts.bypass(req)) {
|
||||
if (opts.bypass.onRequest && opts.bypass.onRequest(req)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -178,6 +190,10 @@ function main(opts) {
|
||||
const timer = metrics[httpMetricName].startTimer(labels);
|
||||
|
||||
onFinished(res, () => {
|
||||
if (opts.bypass.onFinish && opts.bypass.onFinish(req, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (opts.includeStatusCode) {
|
||||
labels.status_code = opts.formatStatusCode(res, opts);
|
||||
}
|
||||
@@ -201,6 +217,14 @@ function main(opts) {
|
||||
next();
|
||||
};
|
||||
|
||||
if (opts.metricsApp) {
|
||||
opts.metricsApp.get(opts.metricsPath || '/metrics', (req, res) => {
|
||||
res.set('Content-Type', opts.promRegistry.contentType);
|
||||
opts.promRegistry.metrics()
|
||||
.then(metrics => res.end(metrics));
|
||||
});
|
||||
}
|
||||
|
||||
middleware.metrics = metrics;
|
||||
middleware.promClient = promClient;
|
||||
middleware.metricsMiddleware = metricsMiddleware;
|
||||
|
||||
@@ -11,6 +11,7 @@ module.exports = function(req, opts) {
|
||||
// by middlewares such as 'router'. Note: this function is called onFinish
|
||||
/// i.e. always in the tail of the middleware chain
|
||||
let path = url.parse(req.originalUrl || req.url).pathname;
|
||||
const urlPathReplacement = opts ? opts.urlPathReplacement : '#val';
|
||||
|
||||
const normalizePath = opts && opts.normalizePath;
|
||||
if (Array.isArray(normalizePath)) {
|
||||
@@ -26,5 +27,5 @@ module.exports = function(req, opts) {
|
||||
if (!urlValueParser) {
|
||||
urlValueParser = new UrlValueParser(opts && opts.urlValueParser);
|
||||
}
|
||||
return urlValueParser.replacePathValues(path);
|
||||
return urlValueParser.replacePathValues(path, urlPathReplacement);
|
||||
};
|
||||
|
||||
27
types/index.d.ts
vendored
27
types/index.d.ts
vendored
@@ -1,6 +1,6 @@
|
||||
// TypeScript Version: 2.8
|
||||
|
||||
import { Request, RequestHandler, Response } from 'express';
|
||||
import { Request, RequestHandler, Response, Express } from 'express';
|
||||
import { DefaultMetricsCollectorConfiguration, Registry } from 'prom-client';
|
||||
|
||||
export {};
|
||||
@@ -19,7 +19,6 @@ declare namespace express_prom_bundle {
|
||||
|
||||
interface Opts {
|
||||
autoregister?: boolean;
|
||||
buckets?: number[];
|
||||
|
||||
customLabels?: { [key: string]: any };
|
||||
|
||||
@@ -28,9 +27,25 @@ declare namespace express_prom_bundle {
|
||||
includePath?: boolean;
|
||||
includeUp?: boolean;
|
||||
|
||||
bypass?: (req: Request) => boolean;
|
||||
bypass?:
|
||||
| ((req: Request) => boolean)
|
||||
| {
|
||||
onRequest?: (req: Request) => boolean;
|
||||
onFinish?: (req: Request, res: Response) => boolean;
|
||||
};
|
||||
|
||||
excludeRoutes?: Array<string | RegExp>;
|
||||
|
||||
metricType?: 'summary' | 'histogram';
|
||||
|
||||
// https://github.com/siimon/prom-client#histogram
|
||||
buckets?: number[];
|
||||
|
||||
// https://github.com/siimon/prom-client#summary
|
||||
percentiles?: number[];
|
||||
maxAgeSeconds?: number;
|
||||
ageBuckets?: number;
|
||||
|
||||
metricsPath?: string;
|
||||
httpDurationMetricName?: string;
|
||||
promClient?: { collectDefaultMetrics?: DefaultMetricsCollectorConfiguration };
|
||||
@@ -38,13 +53,15 @@ declare namespace express_prom_bundle {
|
||||
normalizePath?: NormalizePathEntry[] | NormalizePathFn;
|
||||
formatStatusCode?: NormalizeStatusCodeFn;
|
||||
transformLabels?: TransformLabelsFn;
|
||||
urlPathReplacement?: string;
|
||||
metricsApp?: Express;
|
||||
|
||||
// https://github.com/disjunction/url-value-parser#options
|
||||
urlValueParser?: {
|
||||
minHexLength?: number;
|
||||
minBase64Length?: number;
|
||||
replaceMasks?: string[];
|
||||
extraMasks?: string[];
|
||||
replaceMasks?: Array<RegExp | string>;
|
||||
extraMasks?: Array<RegExp | string>;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Request, RequestHandler, Response } from 'express';
|
||||
import * as express from 'express';
|
||||
|
||||
import * as promClient from 'prom-client';
|
||||
|
||||
import * as promBundle from 'express-prom-bundle';
|
||||
|
||||
// $ExpectType Middleware
|
||||
const middleware: RequestHandler = promBundle({ includeMethod: true });
|
||||
const middleware: express.RequestHandler = promBundle({ includeMethod: true });
|
||||
|
||||
// $ExpectType: string
|
||||
middleware.name;
|
||||
@@ -35,12 +36,13 @@ promBundle({
|
||||
buckets: [0.1, 0.4, 0.7],
|
||||
includeMethod: true,
|
||||
includePath: true,
|
||||
excludeRoutes: ['/foo', /^\/bar\/?$/],
|
||||
customLabels: { year: null },
|
||||
transformLabels: (labels: promBundle.Labels) => ({
|
||||
...labels,
|
||||
year: new Date().getFullYear()
|
||||
}),
|
||||
metricType: 'summary',
|
||||
metricType: 'histogram',
|
||||
metricsPath: '/prometheus',
|
||||
promClient: {
|
||||
collectDefaultMetrics: {
|
||||
@@ -56,21 +58,37 @@ promBundle({
|
||||
normalizePath: [
|
||||
['^/foo', '/example'] // replace /foo with /example
|
||||
],
|
||||
formatStatusCode: (res: Response) => res.statusCode + 100
|
||||
formatStatusCode: (res: express.Response) => res.statusCode + 100,
|
||||
metricsApp: express()
|
||||
});
|
||||
|
||||
promClient.register.clear();
|
||||
|
||||
promBundle({
|
||||
metricType: 'summary',
|
||||
maxAgeSeconds: 600,
|
||||
ageBuckets: 5
|
||||
});
|
||||
|
||||
promClient.register.clear();
|
||||
|
||||
promBundle({
|
||||
metricType: 'summary',
|
||||
percentiles: [0.01, 0.1, 0.9, 0.99]
|
||||
});
|
||||
|
||||
// TypeScript workaround to write a readonly field
|
||||
type Writable<T> = { -readonly [K in keyof T]: T[K] };
|
||||
const wPromBundle: Writable<promBundle> = promBundle;
|
||||
|
||||
wPromBundle.normalizePath = (req: Request, opts: promBundle.Opts) => {
|
||||
wPromBundle.normalizePath = (req: express.Request, opts: promBundle.Opts) => {
|
||||
const path = promBundle.normalizePath(req, opts);
|
||||
|
||||
// count all docs as one path, but /docs/login as a separate one
|
||||
return path.match(/^\/docs/) && !path.match(/^\/login/) ? '/docs/*' : path;
|
||||
};
|
||||
|
||||
wPromBundle.normalizeStatusCode = (res: Response) => res.statusCode.toString();
|
||||
wPromBundle.normalizeStatusCode = (res: express.Response) => res.statusCode.toString();
|
||||
|
||||
// $ExpectType RequestHandler
|
||||
promBundle.clusterMetrics();
|
||||
|
||||
Reference in New Issue
Block a user