Home

Awesome

<p align="center"><img src="./logo.png" alt="logo" width="250px" /></p> <h1 align="center">fastify-metrics</h1>

Release code style: prettier semantic-release npm (scoped) license

A slighlty opinionated Fastify plugin that collects metrics and dispatches them to 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 client to send them to a stasd collector.

⚠️ Fastify 4 introduced some breaking changes, please refer to this version support table to find what works best for you!

Table of Content

<!-- toc --> <!-- tocstop -->

Fastify Version Support

Node.jsfastify@immobiliarelabs/fastify-metrics
<1433
>1444

Installation

npm

# lastest stable version
$ npm i -S @immobiliarelabs/fastify-metrics
# latest development version
$ npm i -S @immobiliarelabs/fastify-metrics@next

yarn

# lastest stable version
$ yarn add @immobiliarelabs/fastify-metrics
# latest development version
$ yarn @immobiliarelabs/fastify-metrics@next

Migrating from version 1

See the migration guide if you have to migrate from the version 1 to 2 of this plugin.

Usage

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 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

Metrics collected

These are the metrics that can be collected with their respective label.

NameTypeUnit of measureDescription
<METRICS_NAMESPACE>.process.cpugaugepercentageprocess cpu usage
<METRICS_NAMESPACE>.process.mem.externalgaugebytesprocess external memory
<METRICS_NAMESPACE>.process.mem.rssgaugebytesprocess rss memory
<METRICS_NAMESPACE>.process.mem.heapUsedgaugebytesprocess heap used memory
<METRICS_NAMESPACE>.process.mem.heapTotalgaugebytesprocess heap total memory
<METRICS_NAMESPACE>.process.eventLoopDelaygaugemillisecondsprocess event loop delay
<METRICS_NAMESPACE>.process.eventLoopUtilizationgaugeabsolute number between 0 and 1process event loop utilization
<METRICS_NAMESPACE>.<computedPrefix>.<routeId>.requestscounterunitrequests count per service
<METRICS_NAMESPACE>.<computedPrefix>.<routeId>.errors.<statusCode>counteruniterrors count per service
<METRICS_NAMESPACE>.<computedPrefix>.<routeId>.request_sizetimingbytesrequest size
<METRICS_NAMESPACE>.<computedPrefix>.<routeId>.response_timetimingmillisecondsresponse time
<METRICS_NAMESPACE>.<computedPrefix>.<routeId>.response_sizetimingbytesresponse size

To know more about how the computedPrefix and the route label are built see here.

Decorators

The plugin adds some decorators to both the fastify instance and the reply object.

Fastify decorators

metrics

An object containing the following properties:

metrics.namespace

The namespace passed to the plugin configuration option.

metrics.fastifyPrefix

The normalized fastify instance prefix.

metrics.routesPrefix

The normalized routes prefix passed to the routes.prefix option.

metrics.client

The Dats instance.

metrics.sampler

The sampler 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.

metrics.hrtime2ns

A utility function to convert the legacy process.hrtime([time]) value to nanoseconds.

See hrtime-utils.

metrics.hrtime2ms

A utility function to convert the legacy process.hrtime([time]) value to milliseconds.

See hrtime-utils.

metrics.hrtime2s

A utility function to convert the legacy process.hrtime([time]) value to seconds.

See hrtime-utils.

Request and Reply decorators

getMetricLabel()

sendTimingMetric(name[, value])

It 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])

It 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)

It 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)

It 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:

Request and Reply routeConfig

The plugin adds a metrics object to the Request.routeOptions and Reply.request.routeOptions for convenience with the following properties:

These properties can be useful when using a custom getLabel function.

API

This module exports a plugin registration function.

Configuration options

The plugin is configured with an object with the following properties

Routes labels generation modes

There are two different modes to generate the label for each route:

computedPrefix

In both modes by default the plugin generates a prefix using:

Generating a computed prefix like this:

<fastifyPrefix>.<routesPrefix>

static mode

In this mode a onRoute hook is registered in the fastify instance and the plugin generates a label at startup time combining the following strings:

The getLabel function in this mode will have the following signature:

getLabel(options)

Pay attention to avoid returing empty strings or strings with leading and trailing ..

dynamic mode

In this mode a onRequest hook 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 getLabel function in this mode will have the following signature:

getLabel(request, reply)

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
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, 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.

Support & Contribute

Made with ❤️ by ImmobiliareLabs & 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.

License

fastify-metrics is licensed under the MIT license.
See the LICENSE file for more information.