Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/immobiliare/fastify-metrics
📊 Fastify plugin that integrates metrics collection and dispatch to statsd
https://github.com/immobiliare/fastify-metrics
fastify grafana javascript metrics monitoring nodejs npm statsd
Last synced: 7 days ago
JSON representation
📊 Fastify plugin that integrates metrics collection and dispatch to statsd
- Host: GitHub
- URL: https://github.com/immobiliare/fastify-metrics
- Owner: immobiliare
- License: mit
- Created: 2021-10-21T08:40:26.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2024-04-25T01:48:26.000Z (6 months ago)
- Last Synced: 2024-04-25T04:57:46.663Z (6 months ago)
- Topics: fastify, grafana, javascript, metrics, monitoring, nodejs, npm, statsd
- Language: JavaScript
- Homepage:
- Size: 1.7 MB
- Stars: 71
- Watchers: 8
- Forks: 3
- Open Issues: 28
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
fastify-metrics
[![Release](https://github.com/immobiliare/fastify-metrics/actions/workflows/release.yml/badge.svg)](https://github.com/immobiliare/fastify-metrics/actions/workflows/release.yml)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier?style=flat-square)
[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg?style=flat-square)](https://github.com/semantic-release/semantic-release)
![npm (scoped)](https://img.shields.io/npm/v/@immobiliarelabs/fastify-metrics)
![license](https://img.shields.io/github/license/immobiliare/fastify-metrics)> A slighlty opinionated [Fastify](https://www.fastify.io/) plugin that collects metrics and dispatches them to [statsd](https://github.com/statsd/statsd).
If you write your services and apps using `Fastify` and also use `statsd`, this plugin might be for you!
It automatically collects Node.js process metrics along with routes stats like hit count, timings and errors and uses the [`Dats`](https://github.com/immobiliare/dats) client to send them to a `stasd` collector.
> ⚠️ Fastify 4 introduced some breaking changes, please refer to [this](#fastify-version-support) version support table to find what works best for you!
## Table of Content
* [Fastify Version Support](#fastify-version-support)
- [Installation](#installation)
* [`npm`](#npm)
* [`yarn`](#yarn)
- [Migrating from version 1](#migrating-from-version-1)
- [Usage](#usage)
- [Route Configuration](#route-configuration)
* [Note](#note)
- [Metrics collected](#metrics-collected)
- [Decorators](#decorators)
* [Fastify decorators](#fastify-decorators)
+ [`metrics`](#metrics)
- [`metrics.namespace`](#metricsnamespace)
- [`metrics.fastifyPrefix`](#metricsfastifyprefix)
- [`metrics.routesPrefix`](#metricsroutesprefix)
- [`metrics.client`](#metricsclient)
- [`metrics.sampler`](#metricssampler)
- [`metrics.hrtime2us`](#metricshrtime2us)
- [`metrics.hrtime2ns`](#metricshrtime2ns)
- [`metrics.hrtime2ms`](#metricshrtime2ms)
- [`metrics.hrtime2s`](#metricshrtime2s)
* [Request and Reply decorators](#request-and-reply-decorators)
+ [`getMetricLabel()`](#getmetriclabel)
+ [`sendTimingMetric(name[, value])`](#sendtimingmetricname-value)
+ [`sendCounterMetric(name[, value])`](#sendcountermetricname-value)
+ [`sendGaugeMetric(name, value)`](#sendgaugemetricname-value)
+ [`sendSetMetric(name, value)`](#sendsetmetricname-value)
- [Hooks](#hooks)
- [Request and Reply routeConfig](#request-and-reply-routeconfig)
- [API](#api)
* [Configuration `options`](#configuration-options)
* [Routes labels generation modes](#routes-labels-generation-modes)
- [computedPrefix](#computedprefix)
+ [`static` mode](#static-mode)
- [`getLabel(options)`](#getlabeloptions)
+ [`dynamic` mode](#dynamic-mode)
- [`getLabel(request, reply)`](#getlabelrequest-reply)
- [Example](#example)
- [Powered Apps](#powered-apps)
- [Support & Contribute](#support--contribute)
- [License](#license)### Fastify Version Support
| `Node.js` | `fastify` | `@immobiliarelabs/fastify-metrics` |
| --------- | --------- | ---------------------------------- |
| `<14` | `3` | `3` |
| `>14` | `4` | `4` |## Installation
### `npm`
```bash
# lastest stable version
$ npm i -S @immobiliarelabs/fastify-metrics
# latest development version
$ npm i -S @immobiliarelabs/fastify-metrics@next
```### `yarn`
```bash
# lastest stable version
$ yarn add @immobiliarelabs/fastify-metrics
# latest development version
$ yarn @immobiliarelabs/fastify-metrics@next
```## Migrating from version 1
See the [migration guide](./MIGRATION_GUIDE.md) if you have to migrate from the version 1 to 2 of this plugin.
## Usage
```js
const fastify = require('fastify')();await fastify.register(require('@immobiliarelabs/fastify-metrics'), {
client: {
host: 'udp://someip:someport',
namespace: 'ns',
},
});const route = {
// This is required in order to associate a metric to a route
// If an object `metrics` with a `routeId` is not passed the route stats will be
// ignored.
config: {
metrics: {
routeId: 'root.getStatus',
},
},
url: '/',
method: 'GET',
handler(request, reply) {
reply.send({ ok: true });
},
};
fastify.route(route);fastify.listen(3000);
```## Route Configuration
To configure a route, you have to pass a `metrics` object with the `routeId` key set with a not empty string.
If the `routeId` is not passed or is set with a falsy value, the route will not be metricated, and all route metrics methods will be disabled.
There are more usage examples in the [`examples`](./examples) folder.
### Note
The plugin internally uses the `routeId` key in the `metrics` object of the `Request.routeOptions` or `Reply.request.routeOptions` object to build the label of the metric of a route.
See
- https://www.fastify.io/docs/latest/Reference/Routes/#config
- https://www.fastify.io/docs/latest/Reference/Reply/
- https://www.fastify.io/docs/latest/Reference/Request/## Metrics collected
These are the metrics that can be collected with their respective label.
| Name | Type | Unit of measure | Description |
| :------------------------------------------------------------------- | :-------- | :------------------------------ | :----------------------------- |
| `.process.cpu` | `gauge` | percentage | process cpu usage |
| `.process.mem.external` | `gauge` | bytes | process external memory |
| `.process.mem.rss` | `gauge` | bytes | process rss memory |
| `.process.mem.heapUsed` | `gauge` | bytes | process heap used memory |
| `.process.mem.heapTotal` | `gauge` | bytes | process heap total memory |
| `.process.eventLoopDelay` | `gauge` | milliseconds | process event loop delay |
| `.process.eventLoopUtilization` | `gauge` | absolute number between 0 and 1 | process event loop utilization |
| `...requests` | `counter` | unit | requests count per service |
| `...errors.` | `counter` | unit | errors count per service |
| `...request_size` | `timing` | bytes | request size |
| `...response_time` | `timing` | milliseconds | response time |
| `...response_size` | `timing` | bytes | response size |**To know more about how the `computedPrefix` and the route label are built see [here](#routes-labels-generation-modes)**.
## Decorators
The plugin adds some decorators to both the fastify instance and the reply object.
### Fastify decorators
#### `metrics`
- <`object`>
An object containing the following properties:
##### `metrics.namespace`
- <`string`>
The `namespace` passed to the plugin configuration option.
##### `metrics.fastifyPrefix`
- <`string`>
The normalized fastify instance `prefix`.
##### `metrics.routesPrefix`
- <`string`>
The normalized routes `prefix` passed to the `routes.prefix` option.
##### `metrics.client`
The [Dats](https://github.com/immobiliare/dats) instance.
##### `metrics.sampler`
The [sampler](https://github.com/dnlup/doc) instance used to sample process metrics, if `options.health` is `true`.
##### `metrics.hrtime2us`
A utility function to convert the legacy `process.hrtime([time])` value to microseconds.
See [hrtime-utils](https://github.com/dnlup/hrtime-utils#hrtime2ustime).
##### `metrics.hrtime2ns`
A utility function to convert the legacy `process.hrtime([time])` value to nanoseconds.
See [hrtime-utils](https://github.com/dnlup/hrtime-utils#hrtime2nstime).
##### `metrics.hrtime2ms`
A utility function to convert the legacy `process.hrtime([time])` value to milliseconds.
See [hrtime-utils](https://github.com/dnlup/hrtime-utils#hrtime2mstime).
##### `metrics.hrtime2s`
A utility function to convert the legacy `process.hrtime([time])` value to seconds.
See [hrtime-utils](https://github.com/dnlup/hrtime-utils#hrtime2stime).
### Request and Reply decorators
#### `getMetricLabel()`
- **Returns** <`string`> The computed metric label of the route.
#### `sendTimingMetric(name[, value])`
- name <`string`> The name of the metric
- value <`number`> The value of the metricIt sends a timing metric. It automatically prepends the route label to the passed `name`. It is just a small wrapper of the native `Dats` client method.
#### `sendCounterMetric(name[, value])`
- name <`string`> The name of the metric
- value <`number`> The value of the metricIt sends a counter metric. It automatically prepends the route label to the passed `name`. It is just a small wrapper of the native `Dats` client method.
#### `sendGaugeMetric(name, value)`
- name <`string`> The name of the metric
- value <`number`> The value of the metricIt sends a gauge metric. It automatically prepends the route label to the passed `name`. It is just a small wrapper of the native `Dats` client method.
#### `sendSetMetric(name, value)`
- name <`string`> The name of the metric
- value <`number`> The value of the metricIt sends a timing metric. It automatically prepends the route label to the passed `name`. It is just a small wrapper of the native `Dats` client method.
## Hooks
The plugin uses the following hooks:
- `onRoute`: to generate the route labels at startup time if `routes.mode` is set to `'static'`.
- `onClose`: to close the [Dats](https://github.com/immobiliare/dats) instance and the [sampler](https://github.com/dnlup/doc#new-docsampleroptions) instance.
- `onRequest`: it registers a hook to count requests and, if `routes.mode` is set to `'dynamic'`, it adds another one to generate the route label.
- `onResponse`: to measure response time
- `onError`: to count errors## Request and Reply routeConfig
The plugin adds a `metrics` object to the `Request.routeOptions` and `Reply.request.routeOptions` for convenience with the following properties:
- `routeId` <`string`> The id for the current route
- `fastifyPrefix` <`string`> The prefix of the fastify instance registering the route, with the `/` replaced with `.` and without the `.` at the beginning.
- `routesPrefix` <`string`> The routes prefix passed to the plugin options and without `.` at the beginning and end.These properties can be useful when using a custom [`getLabel`](routes-labels-generation-modes) function.
## API
This module exports a [plugin registration function](https://github.com/fastify/fastify/blob/master/docs/Plugins-Guide.md#register).
### Configuration `options`
> The plugin is configured with an object with the following properties
- `client` <`Object`|`Client`> The statsd client [configuration](https://github.com/immobiliare/dats#new-clientoptions) object or a [`Client`](https://github.com/immobiliare/dats) instance. When using the options object, a default `onError` function is used to log with level `error` the event with the app logger.
- `routes` <`boolean`|`Object`> Routes metrics configuration. If set to `false` it disables the collection of all the default routes metrics.
- `mode` <`'static'`|`'dynamic'`> The [strategy](#routes-labels-generation-modes) to generate the route metric label.
- `prefix` <`string`> The prefix to use for the routes labels (`...*`). It defaults to `''` (no prefix).
- `getLabel` <`Function`> A custom function to generate the route label. It has a different signature depending on the [mode](#routes-labels-generation-modes).
- `timing` <`boolean`> Collect response timings (`..`). Default: `true`.
- `requestSize` <`boolean`> Collect request size (`..requests.`). Default: `false`.
- `responseSize` <`boolean`> Collect response size (`..requests.`). Default: `false`.
- `hits` <`boolean`> Collect requests count (`..requests.`). Default: `true`.
- `errors` <`boolean`> Collect errors count (`..errors..`). Default: `true`.
- `health` <`boolean`|`Object`> Flag to enable/disable the collection of the process health data(`.process.*`) or an object to configure a subset of the health metrics provided by the [sampler](https://github.com/dnlup/doc#new-docsampleroptions). Default: `true`.
- `sampleInterval` <`number`> The number of milliseconds of the interval to get the metrics sample.
- `eventLoopOptions` <`Object`> The options object used to configure the core [`monitorEventLoopDelay`](https://nodejs.org/docs/latest-v16.x/api/perf_hooks.html#perf_hooksmonitoreventloopdelayoptions).### Routes labels generation modes
There are two different modes to generate the label for each route:
- `static`
- `dynamic`##### computedPrefix
In both modes by default the plugin generates a prefix using:
- the [`fastify` prefix](https://www.fastify.io/docs/latest/Reference/Server/#prefix) used to register the plugin (normalized replacing `/` with `.`), we call it `fastifyPrefix`
- the routes prefix passed to the plugin option `routes.prefix`, we call it `routesPrefix`Generating a computed prefix like this:
`.`
#### `static` mode
In this mode a [`onRoute` hook](https://www.fastify.io/docs/latest/Reference/Hooks/#onroute) is registered in the `fastify` instance and the plugin generates a label at startup time combining the following strings:
- the [`fastify` prefix](https://www.fastify.io/docs/latest/Reference/Server/#prefix) used to register the plugin, accessible via the `prefix` key of the route registration options.
- the routes prefix passed in the plugin options, accessible as a parameter of the internal `getLabel` function.
- the `config.metrics.routeId` string used to configure the route, acessible via the `config` key of the route registration options.The `getLabel` function in this mode will have the following signature:
##### `getLabel(options)`
- `options` [<`Object`>](https://www.fastify.io/docs/latest/Reference/Hooks/#onroute) The route registration
- `config`
- `metrics`
- `routeId` <`string`> The id used to initialize the route.
- `fastifyPrefix` <`string`> The normalized prefix of the fastify instance registering the route.
- `routesPrefix` <`string`> The normalized routes prefix passed to the plugin options.
- **Returns:** <`string`> The route label string without any `.` at the beginning or end.Pay attention to avoid returing empty strings or strings with leading and trailing `.`.
#### `dynamic` mode
In this mode a [`onRequest` hook](https://www.fastify.io/docs/latest/Reference/Hooks/#onrequest) is registerd in the `fastify` instance and the plugin generates a label and attaches it to each request and reply combining the following strings:
- the [`fastify` prefix](https://www.fastify.io/docs/latest/Reference/Server/#prefix) used to register the plugin, accessible via the `prefix` key of the `fastify` instance.
- the routes prefix passed in the plugin options, accessible via the `metricsRoutesPrefix` decorator of the `fastify` instance.
- the `config.metrics.routeId` string used to configure the route, acessible via the `config` key of the `request` or `reply` context.The `getLabel` function in this mode will have the following signature:
##### `getLabel(request, reply)`
- `request`
- `reply`
- **Returns:** <`string`> The route label string without any `.` at the beginning or end.The `this` context of the function is bound to the fastify instance of the request. Pay attention to avoid returing empty strings or strings with leading and trailing `.`. Also, don't use arrow functions otherwhise the `this` context won't refer to the fastify instance.
If you don't pass your custom function, the default one returns the same string computed in `static` mode. Hence, the `dynamic` mode is not very useful if you don't define your own `getLabel` function.
##### Example
```js
const fastify = require('fastify')();await fastify.register(require('@immobiliarelabs/fastify-metrics'), {
client: {
host: 'udp://someip:someport',
namespace: 'ns',
},
routes: {
mode: 'dynamic',
getLabel: function (request, reply) {
const auth = request.user ? 'user' : 'anonim';
const { metrics } = request.routeOptions.config;
const routesPrefix = metrics.routesPrefix
? `${metrics.routesPrefix}.`
: '';
const fastifyPrefix = metrics.fastifyPrefix
? `${metrics.fastifyPrefix}.`
: '';
const routeId = metrics.routeId ? `${metrics.routeId}.` : '';
return `${fastifyPrefix}${routesPrefix}${routeId}${auth}`;
},
},
});const route = {
config: {
metrics: {
routeId: 'root.getStatus',
},
},
url: '/',
method: 'GET',
handler(request, reply) {
reply.send({ ok: true });
},
};
fastify.route(route);fastify.listen(3000);
```## Powered Apps
`fastify-metrics` was created by the amazing Node.js team at ImmobiliareLabs, the Tech dept of [Immobiliare.it](https://www.immobiliare.it), the #1 real estate company in Italy.
We are currently using `fastify-metrics` in our products as well as our internal toolings.
**If you are using fastify-metrics in production [drop us a message](mailto://[email protected])**.
## Support & Contribute
Made with ❤️ by [ImmobiliareLabs](https://github.com/immobiliare) & [Contributors](./CONTRIBUTING.md#contributors)
We'd love for you to contribute to `fastify-metrics`!
If you have any questions on how to use `fastify-metrics`, bugs and enhancement please feel free to reach out by opening a [GitHub Issue](https://github.com/immobiliare/fastify-metrics/issues).## License
`fastify-metrics` is licensed under the MIT license.
See the [LICENSE](./LICENSE) file for more information.