Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
express-prom-bundle
Advanced tools
express middleware with popular prometheus metrics in one bundle
The express-prom-bundle package is a middleware for Express.js that integrates Prometheus metrics collection and reporting. It allows you to easily monitor the performance and health of your Express applications by exposing various metrics that Prometheus can scrape.
Basic Setup
This code demonstrates the basic setup of express-prom-bundle in an Express application. It initializes the middleware and includes it in the app, which will start collecting and exposing metrics.
const express = require('express');
const promBundle = require('express-prom-bundle');
const app = express();
const metricsMiddleware = promBundle({ includeMethod: true });
app.use(metricsMiddleware);
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Custom Metrics
This code demonstrates how to create and use custom metrics with express-prom-bundle. It shows how to define a custom counter and increment it in a route handler.
const express = require('express');
const promBundle = require('express-prom-bundle');
const promClient = require('prom-client');
const app = express();
const metricsMiddleware = promBundle({ includeMethod: true });
app.use(metricsMiddleware);
const customCounter = new promClient.Counter({
name: 'custom_counter',
help: 'Example of a custom counter',
});
app.get('/increment', (req, res) => {
customCounter.inc();
res.send('Counter incremented');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Custom Metrics Endpoint
This code demonstrates how to customize the endpoint where Prometheus metrics are exposed. By setting the `metricsPath` option, you can change the default `/metrics` endpoint to a custom path.
const express = require('express');
const promBundle = require('express-prom-bundle');
const app = express();
const metricsMiddleware = promBundle({ includeMethod: true, metricsPath: '/custom-metrics' });
app.use(metricsMiddleware);
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
prom-client is a Prometheus client for Node.js that allows you to create and expose custom metrics. Unlike express-prom-bundle, it does not provide middleware for Express, so you need to manually integrate it into your application.
express-status-monitor is a simple, self-hosted module based on Socket.io and Chart.js to report realtime server metrics for Express-based applications. It provides a web-based dashboard for monitoring but does not integrate with Prometheus.
Express middleware with popular prometheus metrics in one bundle. It's also compatible with koa v1 and v2 (see below).
This library uses prom-client v15+ as a peer dependency. See: https://github.com/siimon/prom-client
If you need a support for older versions of prom-client (v12-v14), downgrade to express-prom-bundle v6.6.0
Included metrics:
up
: normally is just 1http_request_duration_seconds
: http latency histogram/summary labeled with status_code
, method
and path
npm install prom-client express-prom-bundle
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);
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.
Which labels to include in http_request_duration_seconds
metric:
{project_name: 'hello_world'}
.
Most useful together with transformLabels callback, otherwise it's better to use native Prometheus relabeling./metrics
route with a regex or exact string. Note: it is highly recommended to just stick to the defaulthttp_request_duration_seconds
.Two metric types are supported for http_request_duration_seconds
metric:
Additional options for histogram:
http_request_duration_seconds
histogramAdditional options for summary:
http_request_duration_seconds
summaryfunction(req)
or Array
req
[regex, replacement]
. The regex
can be a string and is automatically converted into JS regex.function(res)
producing final status code from express res
object, e.g. you can combine 200
, 201
and 204
to just 2xx
.function(labels, req, res)
transforms the labels object, e.g. setting dynamic values to customLabelsautoregister: 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
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:
{
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.
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.
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
]
}
}));
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:
app.use(promBundle(/* options? */));
promBundle.normalizePath = (req, opts) => {
// Return the path of the express route (i.e. /v1/user/:id or /v1/timer/automated/:userid/:timerid")
return req.route?.path ?? "NULL";
};
setup std. metrics but exclude up
-metric:
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
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);
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.
const cluster = require('cluster');
const promBundle = require('express-prom-bundle');
const promClient = require('prom-client');
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 {
new promClient.AggregatorRegistry();
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`);
}
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.
{
"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]+$"
]
}
}
]
}
}
}
}
MIT
FAQs
express middleware with popular prometheus metrics in one bundle
The npm package express-prom-bundle receives a total of 458,031 weekly downloads. As such, express-prom-bundle popularity was classified as popular.
We found that express-prom-bundle demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.