API and process monitoring with Prometheus for Node.js micro-service
Note: Prometheus (prom-client
) is a peer dependency since 1.x version
${path}.json
)const apiMetrics = require('prometheus-api-metrics');
app.use(apiMetrics())
Option | Type | Description | Default Value |
---|---|---|---|
metricsPath |
String |
Path to access the metrics | /metrics |
defaultMetricsInterval |
Number |
Interval to collect the process metrics in milliseconds | 10000 |
durationBuckets |
Array<Number> |
Buckets for response time in seconds | [0.001, 0.005, 0.015, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5] |
requestSizeBuckets |
Array<Number> |
Buckets for request size in bytes | [5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000] |
responseSizeBuckets |
Array<Number> |
Buckets for response size in bytes | [5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000, 10000] |
useUniqueHistogramName |
Boolean |
Add to metrics names the project name as a prefix (from package.json) | false |
metricsPrefix |
String |
A custom metrics names prefix, the package will add underscore between your prefix to the metric name | |
excludeRoutes |
Array<String> |
Array of routes to exclude. Routes should be in your framework syntax | |
includeQueryParams |
Boolean |
Indicate if to include query params in route, the query parameters will be sorted in order to eliminate the number of unique labels | false |
additionalLabels |
Array<String> |
Indicating custom labels that can be included on each http_* metric. Use in conjunction with extractAdditionalLabelValuesFn . |
|
extractAdditionalLabelValuesFn |
Function |
A function that can be use to generate the value of custom labels for each of the http_* metrics. When using koa, the function takes ctx , when using express, it takes req, res as arguments |
To get the metrics in Prometheus format use:
curl http[s]://<host>:[port]/metrics
To get the metrics in JSON format use:
curl http[s]://<host>:[port]/metrics.json
Note:
If you pass to the middleware the metricsPath
option the path will be the one that you chose.
If you are using express framework and no route was found for the request (e.g: 404 status code), the request will not be collected. that's because we'll risk memory leak since the route is not a pattern but a hardcoded string.
You can expand the API metrics with more metrics that you would like to expose. All you have to do is:
Require prometheus client
const Prometheus = require('prom-client');
Create new metric from the kind that you like
const checkoutsTotal = new Prometheus.Counter({
name: 'checkouts_total',
help: 'Total number of checkouts',
labelNames: ['payment_method']
});
Update it:
checkoutsTotal.inc({
payment_method: paymentMethod
})
The custom metrics will be exposed under the same endpoint as the API metrics.
For more info about the Node.js Prometheus client you can read here
This will work only if you use the default Prometheus registry - do not use new Prometheus.Registry()
You can define additional metric labels by using additionalLabels
and extractAdditionalLabelValuesFn
options.
For instance:
const apiMetrics = require('prometheus-api-metrics');
app.use(apiMetrics({
additionalLabels: ['customer', 'cluster'],
extractAdditionalLabelValuesFn: (req, res) => {
const { headers } = req.headers;
return {
customer: headers['x-custom-header-customer'],
cluster: headers['x-custom-header-cluster']
}
}
}))
This feature enables you to easily process the result of Request.js timings feature.
You can choose to initialized this functionality as a Class or not
Class:
const HttpMetricsCollector = require('prometheus-api-metrics').HttpMetricsCollector;
const collector = new HttpMetricsCollector();
collector.init();
Singleton:
const HttpMetricsCollector = require('prometheus-api-metrics').HttpMetricsCollector;
HttpMetricsCollector.init();
For Example:
request({ url: 'http://www.google.com', time: true }, (err, response) => {
Collector.collect(err || response);
});
return requestPromise({ method: 'POST', url: 'http://www.mocky.io/v2/5bd9984b2f00006d0006d1fd', route: 'v2/:id', time: true, resolveWithFullResponse: true }).then((response) => {
Collector.collect(response);
}).catch((error) => {
Collector.collect(error);
});
Notes:
{ time: true }
as part of your request configuration and then pass to the collector the response or error you got.resolveWithFullResponse: true
route
and target
attribute instead of taking them from the request object. In order to do that you should set a metrics
object on your request with those attribute:
request({ method: 'POST', url: 'http://www.mocky.io/v2/5bd9984b2f00006d0006d1fd', metrics: { target: 'www.google.com', route: 'v2/:id' }, time: true }, (err, response) => {...};
});
const axios = require('axios');
const axiosTime = require('axios-time');
axiosTime(axios);
try {
const response = await axios({ baseURL: 'http://www.google.com', method: 'get', url: '/' });
Collector.collect(response);
} catch (error) {
Collector.collect(error);
}
Notes:
axios-time
package is required.This package supports koa server that uses koa-router
and koa-bodyparser
const { koaMiddleware } = require('prometheus-api-metrics')
app.use(koaMiddleware())
npm test
(sum(rate(http_request_duration_seconds_bucket{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>">, route="<ROUTE_NAME>", le="0.05"}[10m])) by (<SERVICE_LABEL_FIELD>) + sum(rate(http_request_duration_seconds_bucket{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>", route="<ROUTE_NAME>", le="0.1"}[10m])) by (<SERVICE_LABEL_FIELD>)) / 2 / sum(rate(http_request_duration_seconds_count{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>", route="<ROUTE_NAME>"}[10m])) by (<SERVICE_LABEL_FIELD>)
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>", route="<ROUTE_NAME>", code="200"}[10m])) by (le))
histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>"}[10m])) by (le))
histogram_quantile(0.50, sum(rate(http_request_size_bytes_bucket{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>"}[10m])) by (le))
histogram_quantile(0.50, sum(rate(http_response_size_bytes_bucket{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>"}[10m])) by (le))
avg(nodejs_external_memory_bytes / 1024 / 1024) by (<SERVICE_LABEL_FIELD)
avg(nodejs_eventloop_lag_seconds) by (<SERVICE_LABEL_FIELD)