Promster
โฐA Prometheus exporter for Hapi, express, Apollo, undici and Marble.js servers to automatically measure request timings ๐
Install / Use
/learn @tdeekens/PromsterREADME
Table of Contents
- Why another Prometheus exporter?
- Package Status
- Installation
- Getting Started
- Configuration
- Metrics Reference
- Example PromQL Queries
โฏ Why another Prometheus exporter for Express and Hapi?
These packages are a combination of observations and experiences I have had with other exporters which I tried to fix.
- ๐ Use
process.hrtime.bigint()for high-resolution real time in metrics in seconds (converting from nanoseconds)process.hrtime.bigint()calls libuv'suv_hrtime, without system call likenew Date
- โ๏ธ Allow normalization of all pre-defined label values
- ๐ฅ Expose Garbage Collection among other metric of the Node.js process by default
- ๐จ Expose a built-in server to expose metrics quickly (on a different port) while also allowing users to integrate with existing servers
- ๐ Define two metrics one histogram for buckets and a summary for percentiles for performant graphs in e.g. Grafana
- ๐ฉโ๐ฉโ๐ง One library to integrate with Hapi, Express and potentially more (managed as a mono repository)
- ๐ฆ Allow customization of labels while sorting them internally before reporting
- ๐ผ Expose Prometheus client on Express locals or Hapi app to easily allow adding more app metrics
โฏ Package Status
| Package | Version | Downloads |
| ----------------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------- |
| promster/hapi | [![hapi Version][hapi-icon]][hapi-version] | [![hapi Downloads][hapi-downloads]][hapi-downloads] |
| promster/express | [![express Version][express-icon]][express-version] | [![express Downloads][express-downloads]][express-downloads] |
| promster/marblejs | [![marblejs Version][marblejs-icon]][marblejs-version] | [![marblejs Downloads][marblejs-downloads]][marblejs-downloads] |
| promster/fastify | [![fastify Version][fastify-icon]][fastify-version] | [![fastify Downloads][fastify-downloads]][fastify-downloads] |
| promster/apollo | [![apollo Version][apollo-icon]][apollo-version] | [![apollo Downloads][apollo-downloads]][apollo-downloads] |
| promster/undici | [![undici Version][undici-icon]][undici-version] | [![undici Downloads][undici-downloads]][undici-downloads] |
| promster/server | [![server Version][server-icon]][server-version] | [![server Downloads][server-downloads]][server-downloads] |
| promster/metrics | [![metrics Version][metrics-icon]][metrics-version] | [![metrics Downloads][metrics-downloads]][metrics-downloads] |
| promster/types | [![types Version][types-icon]][types-version] | [![types Downloads][types-downloads]][types-downloads] |
โฏ Installation
This is a mono repository maintained using
changesets. It contains
multiple packages including framework integrations (express,
hapi, fastify, marblejs, apollo, undici), a core metrics library,
a standalone server for exposing metrics, and shared types.
Depending on the preferred integration use:
yarn add @promster/express or npm i @promster/express --save
or
yarn add @promster/hapi or npm i @promster/hapi --save
Please additionally make sure you have prom-client installed. It is a peer dependency of @promster as some projects might already have an existing prom-client installed, which otherwise would result in different default registries.
yarn add prom-client or npm i prom-client --save
โฏ Getting Started
Promster has to be set up with your server -- either as an Express middleware, a Hapi plugin, a Fastify plugin, or similar. You can expose the gathered metrics via a built-in small server or through your own.
Note: Do not be scared by the variety of options.
@promstercan be set up without any additional configuration options and has sensible defaults. However, trying to suit many needs and different existing setups (e.g. metrics having recording rules over histograms) it comes with all those options listed in Configuration.
Express
import app from './your-express-app';
import { createMiddleware } from '@promster/express';
// Note: This should be done BEFORE other routes
// Pass 'app' as middleware parameter to additionally expose Prometheus under 'app.locals'
app.use(createMiddleware({ app, options }));
Passing the app into the createMiddleware call attaches the internal prom-client to your Express app's locals. This may come in handy as later you can:
// Create an e.g. custom counter
const counter = new app.locals.Prometheus.Counter({
name: 'metric_name',
help: 'metric_help',
});
// to later increment it
counter.inc();
Fastify
import app from './your-fastify-app';
import { plugin as promsterPlugin } from '@promster/fastify';
fastify.register(promsterPlugin);
The plugin attaches the internal prom-client to your Fastify instance. This may come in handy as later you can:
// Create an e.g. custom counter
const counter = new fastify.Prometheus.Counter({
name: 'metric_name',
help: 'metric_help',
});
// to later increment it
counter.inc();
Hapi
import { createPlugin } from '@promster/hapi';
import app from './your-hapi-app';
app.register(createPlugin({ options }));
Here you do not have to pass in the app into the createPlugin call as the internal prom-client will be exposed onto Hapi as in:
// Create an e.g. custom counter
const counter = new app.Prometheus.Counter({
name: 'metric_name',
help: 'metric_help',
});
// to later increment it
counter.inc();
Marble.js
import {
createMiddleware,
getContentType,
getSummary,
} from '@promster/marblejs';
const middlewares = [
createMiddleware(),
//...
];
const serveMetrics$ = r
.matchPath('/metrics')
.matchType('GET')
.use(async (req$) =>
req$.pipe(
mapTo({
headers: { 'Content-Type': getContentType() },
body: await getSummary(),
}),
),
);
Apollo
import { createPlugin as createPromsterMetricsPlugin } from '@promster/apollo';
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [createPromsterMetricsPlugin()],
});
await server.listen();
Undici
Depending on the undici version used you have to use a pool metrics exporter or can use the agent metrics exporter.
Agent metrics (undici v7.9.0 or later)
import { createAgentMetricsExporter } from '@promster/undici';
createAgentMetricsExporter([agentA, agentB]);
You can then also always add additional agents:
import { addObservedAgent } from '@promster/undici';
addObservedAgent(agent);
Pool metrics (undici before v7.9.0)
import { createPoolMetricsExporter } from '@promster/undici';
createPoolMetricsExporter({ poolA, poolB });
You can then also always add additional pools:
import { addObservedPool } from '@promster/undici';
addObservedPool(origin, pool);
To integrate this with an undici agent you can use the factory function:
const agent = new Agent({
factory(origin: s
