UNPKG

prometheus-api-metrics

Version:

API and process monitoring with Prometheus for Node.js micro-service

319 lines (234 loc) 11.7 kB
# Prometheus API Monitoring [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Build Status][travis-image]][travis-url] [![Test Coverage][coveralls-image]][coveralls-url] [![Known Vulnerabilities][snyk-image]][snyk-url] [![Apache 2.0 License][license-image]][license-url] <!-- **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* --> <!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> - [Goal](#goal) - [Features](#features) - [Usage](#usage) - [Options](#options) - [Access the metrics](#access-the-metrics) - [Custom Metrics](#custom-metrics) - [Note](#note) - [Additional Metric Labels](#additional-metric-labels) - [Request.js HTTP request duration collector](#requestjs-http-request-duration-collector) - [Usage](#usage-1) - [Initialize](#initialize) - [Options](#options-1) - [request](#request) - [request-promise-native](#request-promise-native) - [axios](#axios) - [Usage in koa](#usage-in-koa) - [Test](#test) - [Prometheus Examples Queries](#prometheus-examples-queries) - [Apdex](#apdex) - [95th Response Time by specific route and status code](#95th-response-time-by-specific-route-and-status-code) - [Median Response Time Overall](#median-response-time-overall) - [Median Request Size Overall](#median-request-size-overall) - [Median Response Size Overall](#median-response-size-overall) - [Average Memory Usage - All services](#average-memory-usage---all-services) - [Average Eventloop Latency - All services](#average-eventloop-latency---all-services) <!-- END doctoc generated TOC please keep comment here to allow auto update --> ## Goal API and process monitoring with [Prometheus](https://prometheus.io) for Node.js micro-service **Note: Prometheus (`prom-client`) is a peer dependency since 1.x version** ## Features - [Collect API metrics for each call](#usage) - Response time in seconds - Request size in bytes - Response size in bytes - Add prefix to metrics names - custom or project name - Exclude specific routes from being collect - Number of open connections to the server - Process Metrics as recommended by Prometheus [itself](https://prometheus.io/docs/instrumenting/writing_clientlibs/#standard-and-runtime-collectors) - Endpoint to retrieve the metrics - used for Prometheus scraping - Prometheus format - JSON format (`${path}.json`) - Support custom metrics - [Http function to collect request.js HTTP request duration](#requestjs-http-request-duration-collector) ## Usage ```js const apiMetrics = require('prometheus-api-metrics'); app.use(apiMetrics()) ``` ### Options | 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 | | ### Access the metrics To get the metrics in Prometheus format use: ```sh curl http[s]://<host>:[port]/metrics ``` To get the metrics in JSON format use: ```sh curl http[s]://<host>:[port]/metrics.json ``` **Note:** 1. If you pass to the middleware the `metricsPath` option the path will be the one that you chose. 2. 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. ## Custom Metrics You can expand the API metrics with more metrics that you would like to expose. All you have to do is: Require prometheus client ```js const Prometheus = require('prom-client'); ``` Create new metric from the kind that you like ```js const checkoutsTotal = new Prometheus.Counter({ name: 'checkouts_total', help: 'Total number of checkouts', labelNames: ['payment_method'] }); ``` Update it: ```js 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](https://github.com/siimon/prom-client#prometheus-client-for-nodejs--) ### Note This will work only if you use the default Prometheus registry - do not use `new Prometheus.Registry()` ## Additional Metric Labels You can define additional metric labels by using `additionalLabels` and `extractAdditionalLabelValuesFn` options. For instance: ```js 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'] } } })) ``` ## Request.js HTTP request duration collector This feature enables you to easily process the result of Request.js timings feature. ### Usage #### Initialize You can choose to initialized this functionality as a Class or not **Class:** ```js const HttpMetricsCollector = require('prometheus-api-metrics').HttpMetricsCollector; const collector = new HttpMetricsCollector(); collector.init(); ``` **Singleton:** ```js const HttpMetricsCollector = require('prometheus-api-metrics').HttpMetricsCollector; HttpMetricsCollector.init(); ``` #### Options - durationBuckets - the histogram buckets for request duration. - countClientErrors - Boolean that indicates whether to collect client errors as Counter, this counter will have target and error code labels. - useUniqueHistogramName - Add to metrics names the project name as a prefix (from package.json) - prefix - A custom metrics names prefix, the package will add underscore between your prefix to the metric name. For Example: #### request ```js request({ url: 'http://www.google.com', time: true }, (err, response) => { Collector.collect(err || response); }); ``` #### request-promise-native ```js 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:** 1. In order to use this feature you must use `{ time: true }` as part of your request configuration and then pass to the collector the response or error you got. 2. In order to use the timing feature in request-promise/request-promise-native you must also use `resolveWithFullResponse: true` 3. Override - you can override the `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: ```js request({ method: 'POST', url: 'http://www.mocky.io/v2/5bd9984b2f00006d0006d1fd', metrics: { target: 'www.google.com', route: 'v2/:id' }, time: true }, (err, response) => {...}; }); ``` #### axios ```js 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:** - In order to collect metrics from axios client the [`axios-time`](https://www.npmjs.com/package/axios-time) package is required. ## Usage in koa This package supports koa server that uses [`koa-router`](https://www.npmjs.com/package/koa-router) and [`koa-bodyparser`](https://www.npmjs.com/package/koa-bodyparser) ```js const { koaMiddleware } = require('prometheus-api-metrics') app.use(koaMiddleware()) ``` ## Test ```sh npm test ``` ## Prometheus Examples Queries ### [Apdex](https://en.wikipedia.org/wiki/Apdex) ``` (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>) ``` ### 95th Response Time by specific route and status code ``` histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>", route="<ROUTE_NAME>", code="200"}[10m])) by (le)) ``` ### Median Response Time Overall ``` histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>"}[10m])) by (le)) ``` ### Median Request Size Overall ``` histogram_quantile(0.50, sum(rate(http_request_size_bytes_bucket{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>"}[10m])) by (le)) ``` ### Median Response Size Overall ``` histogram_quantile(0.50, sum(rate(http_response_size_bytes_bucket{<SERVICE_LABEL_FIELD>="<SERVICE_LABEL>"}[10m])) by (le)) ``` ### Average Memory Usage - All services ``` avg(nodejs_external_memory_bytes / 1024 / 1024) by (<SERVICE_LABEL_FIELD) ``` ### Average Eventloop Latency - All services ``` avg(nodejs_eventloop_lag_seconds) by (<SERVICE_LABEL_FIELD) ``` [npm-image]: https://img.shields.io/npm/v/prometheus-api-metrics.svg?style=flat [npm-url]: https://npmjs.org/package/prometheus-api-metrics [travis-image]: https://travis-ci.org/PayU/prometheus-api-metrics.svg?branch=master [travis-url]: https://travis-ci.org/PayU/prometheus-api-metrics [coveralls-image]: https://coveralls.io/repos/github/PayU/prometheus-api-metrics/badge.svg?branch=master [coveralls-url]: https://coveralls.io/github/PayU/prometheus-api-metrics?branch=master [downloads-image]: http://img.shields.io/npm/dm/prometheus-api-metrics.svg?style=flat [downloads-url]: https://npmjs.org/package/prometheus-api-metrics [license-image]: https://img.shields.io/badge/license-Apache_2.0-green.svg?style=flat [license-url]: LICENSE [snyk-image]: https://snyk.io/test/npm/prometheus-api-metrics/badge.svg [snyk-url]: https://snyk.io/test/npm/prometheus-api-metrics