@balena/node-metrics-gatherer

中文文档
8.0.4发布于 2026/11/03ISCTypeScriptES Module构建来源已验证近 30 天下载 1.3万

gather / export prometheus metrics easily in nodejs

原始使用文档
文档目录16 节
  1. Diagram
  2. Basic Usage:
  3. Writing metrics
  4. Exporting metrics (simple)
  5. Exporting metrics (cluster)
  6. A warning about metrics.describe and Node's cluster module
  7. Metric types
  8. Gauge
  9. Labels
  10. Labels and time-series cardinality
  11. Descriptions
  12. Help text
  13. Defining the label-set
  14. Histogram buckets and Summary percentiles
  15. Clustered aggregation strategy
  16. Internal errors

node-metrics-gatherer

gather and expose prometheus metrics with a simple syntax

Diagram

diagram of module

Basic Usage:

Writing metrics

Basic usage involves adding a single import and a single line.

import { metrics } from '@balena/node-metrics-gatherer';

...

// inside some loop which checks sensors...
metrics.gauge('temperature', 28);

Exporting metrics (simple)

Then, you need to make sure the metrics can be served when a request arrives. This library does not depend on express at runtime - instead it provides a request handler which you mount on your own express app (or a dedicated one you create just for metrics):

// create a request handler to respond to prometheus pulls, and mount it on
// your express app
app.use('/metrics', metrics.requestHandler());

// OR, create a dedicated app just for metrics (using port 9337, arbitrarily)
import express from 'express';
express().use('/metrics', metrics.requestHandler()).listen(9337);

Optionally, you can provide a function which validates whether the request should be served or given a 403, by returning a boolean (an "authFunc"):

const authFunc = (req) => req.get('Authorization') === 'Basic 123456';
app.use('/metrics', metrics.requestHandler(authFunc));

Exporting metrics (cluster)

If an application forks several child workers and they each listen on :80/metrics, each worker will have a random chance of being hit, only exporting their own metrics each time, causing instability and confusion. Thankfully, @prometheus-io/client provides a way to handle this, with a registry you create just after you fork. Internally, it handles message-passing between the workers and the main process, where the metrics are aggregated. The example/ folder in that repo has (as of this writing) an example. This requires a separate express app to be created, listening on a port which isn't participating in the worker cluster pooling.

Each worker must opt in to aggregation by calling metrics.aggregateRequestWorker(), otherwise the primary will not be able to collect its metrics and the aggregate request will time out. Any metrics recorded in the primary process itself are included in the aggregated output as well.

See below an example usage:

(port 9337 chosen arbitrarily)

if (cluster.isPrimary) {
    for (let i = 0; i < 4; i++) {
        cluster.fork();
    }
    express()
        .use('/cluster_metrics', metrics.aggregateRequestHandler())
        .listen(9337);
} else {
    metrics.aggregateRequestWorker();
}

A warning about metrics.describe and Node's cluster module

When you make a call to metrics.describe.counter() (or metrics.describe.histogram(), etc.) , a global registry object is updated with the metric's description. If you call metrics.describe.counter() in the master before forking, the registries inside the workers will not have the metric definition, and because you can write to a metric without first describing it, this means it will simply have all defaults, and not your custom description, labels, buckets, percentiles, etc...

To avoid this, metrics should be described inside the code path that the workers will follow, so that the global registry objects in the workers will have the metric descriptions.

Metric types

See the prometheus documentation

The metric types available as method calls (eg., metrics.summary) correspond to the Prometheus metric types (eg., Prometheus's "Summary")

Gauge

Used to record a value which can vary over time, like temperature.

metrics.gauge('greenhouse_temperature', temp [, labelObject ]);

Counter

Used to record increases to a monotonic counter, like requests served.

metrics.counter('requests_served_total', 1 [, labelObject ]);

Summary

Used to calculate (pre-defined) quantiles on a stream of data.

metrics.summary('db_query_duration_milliseconds', queryTime, [, labelObject ]);

Histogram

Used to calculate a histogram on a stream of data.

metrics.histogram('db_query_duration_milliseconds', queryTime, [, labelObject ]);

HistogramSummary

There's a convenience method to observe both a histogram and a summary, which will suffix _hist and _summary to the metrics.

metrics.histogramSummary('db_query_duration_milliseconds', queryTime, [, labelObject ]);

Labels

Labels can be used to add some more granularity to a metric:

metrics.counter('application_deployments_total', 1, { app: userRequest.appId, method: 'git-push' });
metrics.counter('application_deployments_total', 1, { app: userRequest.appId, method: 'balena-cli-push' });

You must use the full set of labels you intend to attach with each call; for example, you can't have one line add the labels { method: 'git-push' } and another line add the label { result: 'success' }. Labels should be used for things which apply to the metric every time it's invoked (clever use of values like 'unknown' or 'default' can handle cases where you might think you shouldn't add the label).

Labels and time-series cardinality

Use labels sparingly - there will be one time-series created for every pair of label names / values which you create. It would be bad to create a label to track source IP, for example, unless you were really sure that the tsdb could handle it.

As an example, let's say we have two labels, A and B, and they can have values [x, y], and [q, r, s], respectively. Then there would be the following time series:

A=x, B=q
A=x, B=r
A=x, B=s
A=y, B=q
A=y, B=r
A=y, B=s

Descriptions

Descriptions can be used to inform prometheus / the client library about the metric:

Help text

Metrics description calls specify the type of the metric, its name, and a description, at minimum:

metrics.describe.histogram(
    'api_request_duration_milliseconds',
    'histogram of total time taken to service requests to the api including queue wait (all queues and all userAgents together)',
);

Defining the label-set

This is where labels would be specified as well:

metrics.describe.histogram(
    'api_request_duration_milliseconds',
    'histogram of total time taken to service requests to the api including queue wait (all queues and all userAgents together)',
    {
        labelNames: ['requestType']
    },
);

Histogram buckets and Summary percentiles

You can also declare buckets for histogram-type metrics or percentiles for summary-type metrics, if you'd like them to differ from the defaults:

histogram buckets

metrics.describe.histogram(
    'api_request_duration_milliseconds',
    'histogram of total time taken to service requests to the api including queue wait',
    {
        buckets: [4, 10, 100, 500, 1000, 5000, 15000, 30000],
        labelNames: ['queue', 'userAgent'],
    },
);

summary percentiles

metrics.describe.summary(
    'api_request_duration_milliseconds',
    'summary of total time taken to service requests to the api including queue wait',
    {
        percentiles: [0.9, 0.99, 0.999, 0.9999, 0.99999],
        labelNames: ['queue', 'userAgent'],
    },
);

Clustered aggregation strategy

There are several aggregation strategies which @prometheus-io/client's ClusterRegistry can use to combine the metrics recorded by cluster workers. They are:

  • sum
  • first
  • min
  • max
  • average
  • omit

(You can see how they work in the source of @prometheus-io/client/lib/metricAggregators.js)

In order to control the aggregation strategy (which defaults to 'sum' if unspecified) for a given metric, you can supply an aggregator property to describe() which will determine the method used by the aggregator registry when multiple clustered workers write to that metric.

Let's say we forked 4 workers, each of which observed a given number of connections being active at a time, writing to a Gauge-type metric:

on('connect', () => {
    nConnections++;
    metrics.gauge('active_connections', nConnections);
});
on('disconnect', () => {
    nConnections--;
    metrics.gauge('active_connections', nConnections);
});

We would probably be content with the default aggregation behaviour (which is to sum the individual results from each worker) to find out the total number of active connections.

However, if we wanted to also know the number of connections which the most-busy worker was handling, to get a measure of how far the busiest worker deviated from the average (which would be total / nWorkers), we could also record a gauge which had been described with the max aggregation strategy:

metrics.describe.gauge(
    'active_connections_max',
    'the number of connections being handled by the busiest worker',
    {
        aggregator: 'max'
    }
);

...

on('connect', () => {
    nConnections++;
    metrics.gauge('active_connections', nConnections);
    metrics.gauge('active_connections_max', nConnections);
});
on('disconnect', () => {
    nConnections--;
    metrics.gauge('active_connections', nConnections);
    metrics.gauge('active_connections_max', nConnections);
});

Internal errors

Errors occuring in calls to this library are caught, logged to stderr if the DEBUG env var is defined, and a public property of the MetricsGatherer object will be incremented: internalErrorCount.

版本与发布记录

@balena/node-metrics-gatherer 共发布 182 个版本,首次发布于 2019/01/22;过去 12 个月发布了 37 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
8.0.4latest2026/11/034 天前
8.0.4-build-renovate-balena-lint-11-x-6a2c09a69c3badd5d5ecdf6b5d04fab3a9dd8f3e-12026/11/034 天前
8.0.32026/10/1622 天前
8.0.3-build-renovate-flowzonify-0-x-e4eed5994b40bee472a1c44b67beba8548403331-12026/10/1622 天前
8.0.22026/10/1523 天前
8.0.2-build-idempotent-collect-default-metrics-784b374cfa4c16937e6a5d81435132bd1f749eee-12026/10/1523 天前

其他发布通道:build-updates → 6.0.4-build-updates-8ba69f4cad2b51c018e51e78f8df23de9f672bdf-1,build-suffix-check → 6.0.5-build-suffix-check-0876d476b4d2244b11dcab9cf6c26dafd42ce5ab-1,build-prom-client-15 → 6.1.0-build-prom-client-15-1791a4e229c522f308e47a5ee497744065ecffaa-1,build-avoid-closures → 6.1.1-build-avoid-closures-7eb4b51838a970f1629ed95d5bf82a700844a923-1,build-tsconfig → 6.1.2-build-tsconfig-a2f651f1bcdd4b4078dc0e9c64d3fffc8843f581-1,build-joshbwlng-typings → 6.1.3-build-joshbwlng-typings-3454c6f8060ee50cdefeabe32bd3feead8d12725-1。

关于 @balena/node-metrics-gatherer 的常见问题

@balena/node-metrics-gatherer 是什么?

gather / export prometheus metrics easily in nodejs

如何安装 @balena/node-metrics-gatherer?

在项目目录运行 npm install @balena/node-metrics-gatherer,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:@types/express(^4.17.21 || ^5.0.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@balena/node-metrics-gatherer 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@balena/node-metrics-gatherer 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@balena/node-metrics-gatherer 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js ^24 || >=26。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@balena/node-metrics-gatherer 可以商用吗?

@balena/node-metrics-gatherer 使用 ISC 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@balena/node-metrics-gatherer 的下载量有多少?

根据 npm 官方下载统计,@balena/node-metrics-gatherer 在 2026/10/08 – 2026/11/06 这 30 天内被下载 13,485 次,日均约 450 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@balena/node-metrics-gatherer 还在维护吗?

最新版本 8.0.4 发布于 2026/11/03(4 天前);至今共发布 182 个版本,其中过去 12 个月发布了 37 个;首次发布于 2019/01/22。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。