Update test framework: fix run_tests.py to support all test files, add auto-import-check for test files

This commit is contained in:
qiaoxinjiu
2026-05-09 15:11:30 +08:00
parent eb053a347f
commit eaba8328da
21739 changed files with 2236758 additions and 719 deletions

201
node_modules/prom-client/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2015 Simon Nyberg
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

619
node_modules/prom-client/README.md generated vendored Normal file
View File

@@ -0,0 +1,619 @@
# Prometheus client for node.js [![Actions Status](https://github.com/siimon/prom-client/workflows/Node.js%20CI/badge.svg?branch=master)](https://github.com/siimon/prom-client/actions)
A prometheus client for Node.js that supports histogram, summaries, gauges and
counters.
## Usage
See example folder for a sample usage. The library does not bundle any web
framework. To expose the metrics, respond to Prometheus's scrape requests with
the result of `await registry.metrics()`.
### Usage with Node.js's `cluster` module
Node.js's `cluster` module spawns multiple processes and hands off socket
connections to those workers. Returning metrics from a worker's local registry
will only reveal that individual worker's metrics, which is generally
undesirable. To solve this, you can aggregate all of the workers' metrics in the
master process. See `example/cluster.js` for an example.
Default metrics use sensible aggregation methods. (Note, however, that the event
loop lag mean and percentiles are averaged, which is not perfectly accurate.)
Custom metrics are summed across workers by default. To use a different
aggregation method, set the `aggregator` property in the metric config to one of
'sum', 'first', 'min', 'max', 'average' or 'omit'. (See `lib/metrics/version.js`
for an example.)
If you need to expose metrics about an individual worker, you can include a
value that is unique to the worker (such as the worker ID or process ID) in a
label. (See `example/server.js` for an example using
`worker_${cluster.worker.id}` as a label value.)
Metrics are aggregated from the global registry by default. To use a different
registry, call
`client.AggregatorRegistry.setRegistries(registryOrArrayOfRegistries)` from the
worker processes.
## API
### Default metrics
There are some default metrics recommended by Prometheus
[itself](https://prometheus.io/docs/instrumenting/writing_clientlibs/#standard-and-runtime-collectors).
To collect these, call `collectDefaultMetrics`. In addition, some
Node.js-specific metrics are included, such as event loop lag, active handles,
GC and Node.js version. See [lib/metrics](lib/metrics) for a list of all
metrics.
NOTE: Some of the metrics, concerning File Descriptors and Memory, are only
available on Linux.
`collectDefaultMetrics` optionally accepts a config object with following entries:
- `prefix` an optional prefix for metric names. Default: no prefix.
- `register` to which registry the metrics should be registered. Default: the global default registry.
- `gcDurationBuckets` with custom buckets for GC duration histogram. Default buckets of GC duration histogram are `[0.001, 0.01, 0.1, 1, 2, 5]` (in seconds).
- `eventLoopMonitoringPrecision` with sampling rate in milliseconds. Must be greater than zero. Default: 10.
To register metrics to another registry, pass it in as `register`:
```js
const client = require('prom-client');
const collectDefaultMetrics = client.collectDefaultMetrics;
const Registry = client.Registry;
const register = new Registry();
collectDefaultMetrics({ register });
```
To use custom buckets for GC duration histogram, pass it in as `gcDurationBuckets`:
```js
const client = require('prom-client');
const collectDefaultMetrics = client.collectDefaultMetrics;
collectDefaultMetrics({ gcDurationBuckets: [0.1, 0.2, 0.3] });
```
To prefix metric names with your own arbitrary string, pass in a `prefix`:
```js
const client = require('prom-client');
const collectDefaultMetrics = client.collectDefaultMetrics;
const prefix = 'my_application_';
collectDefaultMetrics({ prefix });
```
To apply generic labels to all default metrics, pass an object to the `labels` property (useful if you're working in a clustered environment):
```js
const client = require('prom-client');
const collectDefaultMetrics = client.collectDefaultMetrics;
collectDefaultMetrics({
labels: { NODE_APP_INSTANCE: process.env.NODE_APP_INSTANCE },
});
```
You can get the full list of metrics by inspecting
`client.collectDefaultMetrics.metricsList`.
Default metrics are collected on scrape of metrics endpoint,
not on an interval.
```js
const client = require('prom-client');
const collectDefaultMetrics = client.collectDefaultMetrics;
collectDefaultMetrics();
```
### Custom Metrics
All metric types have two mandatory parameters: `name` and `help`. Refer to
<https://prometheus.io/docs/practices/naming/> for guidance on naming metrics.
For metrics based on point-in-time observations (e.g. current memory usage, as
opposed to HTTP request durations observed continuously in a histogram), you
should provide a `collect()` function, which will be invoked when Prometheus
scrapes your metrics endpoint. `collect()` can either be synchronous or return a
promise. See **Gauge** below for an example. (Note that you should not update
metric values in a `setInterval` callback; do so in this `collect` function
instead.)
See [**Labels**](#labels) for information on how to configure labels for all
metric types.
#### Counter
Counters go up, and reset when the process restarts.
```js
const client = require('prom-client');
const counter = new client.Counter({
name: 'metric_name',
help: 'metric_help',
});
counter.inc(); // Increment by 1
counter.inc(10); // Increment by 10
```
#### Gauge
Gauges are similar to Counters but a Gauge's value can be decreased.
```js
const client = require('prom-client');
const gauge = new client.Gauge({ name: 'metric_name', help: 'metric_help' });
gauge.set(10); // Set to 10
gauge.inc(); // Increment 1
gauge.inc(10); // Increment 10
gauge.dec(); // Decrement by 1
gauge.dec(10); // Decrement by 10
```
##### Configuration
If the gauge is used for a point-in-time observation, you should provide a
`collect` function:
```js
const client = require('prom-client');
new client.Gauge({
name: 'metric_name',
help: 'metric_help',
collect() {
// Invoked when the registry collects its metrics' values.
// This can be synchronous or it can return a promise/be an async function.
this.set(/* the current value */);
},
});
```
```js
// Async version:
const client = require('prom-client');
new client.Gauge({
name: 'metric_name',
help: 'metric_help',
async collect() {
// Invoked when the registry collects its metrics' values.
const currentValue = await somethingAsync();
this.set(currentValue);
},
});
```
Note that you should not use arrow functions for `collect` because arrow
functions will not have the correct value for `this`.
##### Utility Functions
```js
// Set value to current time in seconds:
gauge.setToCurrentTime();
// Record durations:
const end = gauge.startTimer();
http.get('url', res => {
end();
});
```
#### Histogram
Histograms track sizes and frequency of events.
##### Configuration
The defaults buckets are intended to cover usual web/RPC requests, but they can
be overridden. (See also [**Bucket Generators**](#bucket-generators).)
```js
const client = require('prom-client');
new client.Histogram({
name: 'metric_name',
help: 'metric_help',
buckets: [0.1, 5, 15, 50, 100, 500],
});
```
##### Examples
```js
const client = require('prom-client');
const histogram = new client.Histogram({
name: 'metric_name',
help: 'metric_help',
});
histogram.observe(10); // Observe value in histogram
```
##### Utility Methods
```js
const end = histogram.startTimer();
xhrRequest(function (err, res) {
const seconds = end(); // Observes and returns the value to xhrRequests duration in seconds
});
```
#### Summary
Summaries calculate percentiles of observed values.
##### Configuration
The default percentiles are: 0.01, 0.05, 0.5, 0.9, 0.95, 0.99, 0.999. But they
can be overridden by specifying a `percentiles` array. (See also
[**Bucket Generators**](#bucket-generators).)
```js
const client = require('prom-client');
new client.Summary({
name: 'metric_name',
help: 'metric_help',
percentiles: [0.01, 0.1, 0.9, 0.99],
});
```
To enable the sliding window functionality for summaries you need to add
`maxAgeSeconds` and `ageBuckets` to the config like this:
```js
const client = require('prom-client');
new client.Summary({
name: 'metric_name',
help: 'metric_help',
maxAgeSeconds: 600,
ageBuckets: 5,
pruneAgedBuckets: false,
});
```
The `maxAgeSeconds` will tell how old a bucket can be before it is reset and
`ageBuckets` configures how many buckets we will have in our sliding window for
the summary. If `pruneAgedBuckets` is `false` (default), the metric value will
always be present, even when empty (its percentile values will be `0`). Set
`pruneAgedBuckets` to `true` if you don't want to export it when it is empty.
##### Examples
```js
const client = require('prom-client');
const summary = new client.Summary({
name: 'metric_name',
help: 'metric_help',
});
summary.observe(10);
```
##### Utility Methods
```js
const end = summary.startTimer();
xhrRequest(function (err, res) {
end(); // Observes the value to xhrRequests duration in seconds
});
```
### Labels
All metrics can take a `labelNames` property in the configuration object. All
label names that the metric support needs to be declared here. There are two
ways to add values to the labels:
```js
const client = require('prom-client');
const gauge = new client.Gauge({
name: 'metric_name',
help: 'metric_help',
labelNames: ['method', 'statusCode'],
});
// 1st version: Set value to 100 with "method" set to "GET" and "statusCode" to "200"
gauge.set({ method: 'GET', statusCode: '200' }, 100);
// 2nd version: Same effect as above
gauge.labels({ method: 'GET', statusCode: '200' }).set(100);
// 3rd version: And again the same effect as above
gauge.labels('GET', '200').set(100);
```
It is also possible to use timers with labels, both before and after the timer
is created:
```js
const end = startTimer({ method: 'GET' }); // Set method to GET, we don't know statusCode yet
xhrRequest(function (err, res) {
if (err) {
end({ statusCode: '500' }); // Sets value to xhrRequest duration in seconds with statusCode 500
} else {
end({ statusCode: '200' }); // Sets value to xhrRequest duration in seconds with statusCode 200
}
});
```
#### Zeroing metrics with Labels
Metrics with labels can not be exported before they have been observed at least
once since the possible label values are not known before they're observed.
For histograms, this can be solved by explicitly zeroing all expected label values:
```js
const histogram = new client.Histogram({
name: 'metric_name',
help: 'metric_help',
buckets: [0.1, 5, 15, 50, 100, 500],
labels: ['method'],
});
histogram.zero({ method: 'GET' });
histogram.zero({ method: 'POST' });
```
#### Strongly typed Labels
Typescript can also enforce label names using `as const`
```typescript
import * as client from 'prom-client';
const counter = new client.Counter({
name: 'metric_name',
help: 'metric_help',
// add `as const` here to enforce label names
labelNames: ['method'] as const,
});
// Ok
counter.inc({ method: 1 });
// this is an error since `'methods'` is not a valid `labelName`
// @ts-expect-error
counter.inc({ methods: 1 });
```
#### Default Labels (segmented by registry)
Static labels may be applied to every metric emitted by a registry:
```js
const client = require('prom-client');
const defaultLabels = { serviceName: 'api-v1' };
client.register.setDefaultLabels(defaultLabels);
```
This will output metrics in the following way:
```
# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes{serviceName="api-v1"} 33853440 1498510040309
```
Default labels will be overridden if there is a name conflict.
`register.clear()` will clear default labels.
### Exemplars
The exemplars defined in the OpenMetrics specification can be enabled on Counter
and Histogram metric types. The default metrics have support for OpenTelemetry,
they will populate the exemplars with the labels `{traceId, spanId}` and their
corresponding values.
The format for `inc()` and `observe()` calls are different if exemplars are
enabled. They get a single object with the format
`{labels, value, exemplarLabels}`.
When using exemplars, the registry used for metrics should be set to OpenMetrics
type (including the global or default registry if no registries are specified).
### Registry type
The library supports both the old Prometheus format and the OpenMetrics format.
The format can be set per registry. For default metrics:
```js
const Prometheus = require('prom-client');
Prometheus.register.setContentType(
Prometheus.Registry.OPENMETRICS_CONTENT_TYPE,
);
```
Currently available registry types are defined by the content types:
**PROMETHEUS_CONTENT_TYPE** - version 0.0.4 of the original Prometheus metrics,
this is currently the default registry type.
**OPENMETRICS_CONTENT_TYPE** - defaults to version 1.0.0 of the
[OpenMetrics standard](https://github.com/OpenObservability/OpenMetrics/blob/d99b705f611b75fec8f450b05e344e02eea6921d/specification/OpenMetrics.md).
The HTTP Content-Type string for each registry type is exposed both at module
level (`prometheusContentType` and `openMetricsContentType`) and as static
properties on the `Registry` object.
The `contentType` constant exposed by the module returns the default content
type when creating a new registry, currently defaults to Prometheus type.
### Multiple registries
By default, metrics are automatically registered to the global registry (located
at `require('prom-client').register`). You can prevent this by specifying
`registers: []` in the metric constructor configuration.
Using non-global registries requires creating a Registry instance and passing it
inside `registers` in the metric configuration object. Alternatively you can
pass an empty `registers` array and register it manually.
Registry has a `merge` function that enables you to expose multiple registries
on the same endpoint. If the same metric name exists in both registries, an
error will be thrown.
Merging registries of different types is undefined. The user needs to make sure
all used registries have the same type (Prometheus or OpenMetrics versions).
```js
const client = require('prom-client');
const registry = new client.Registry();
const counter = new client.Counter({
name: 'metric_name',
help: 'metric_help',
registers: [registry], // specify a non-default registry
});
const histogram = new client.Histogram({
name: 'metric_name',
help: 'metric_help',
registers: [], // don't automatically register this metric
});
registry.registerMetric(histogram); // register metric manually
counter.inc();
const mergedRegistries = client.Registry.merge([registry, client.register]);
```
If you want to use multiple or non-default registries with the Node.js `cluster`
module, you will need to set the registry/registries to aggregate from:
```js
const AggregatorRegistry = client.AggregatorRegistry;
AggregatorRegistry.setRegistries(registry);
// or for multiple registries:
AggregatorRegistry.setRegistries([registry1, registry2]);
```
### Register
You can get all metrics by running `await register.metrics()`, which will return
a string in the Prometheus exposition format.
#### Getting a single metric value in Prometheus exposition format
If you need to output a single metric in the Prometheus exposition format, you
can use `await register.getSingleMetricAsString(*name of metric*)`, which will
return a string for Prometheus to consume.
#### Getting a single metric
If you need to get a reference to a previously registered metric, you can use
`register.getSingleMetric(*name of metric*)`.
#### Removing metrics
You can remove all metrics by calling `register.clear()`. You can also remove a
single metric by calling `register.removeSingleMetric(*name of metric*)`.
#### Resetting metrics
If you need to reset all metrics, you can use `register.resetMetrics()`. The
metrics will remain present in the register and can be used without the need to
instantiate them again, like you would need to do after `register.clear()`.
#### Cluster metrics
You can get aggregated metrics for all workers in a Node.js cluster with
`await register.clusterMetrics()`. This method returns a promise that resolves
with a metrics string suitable for Prometheus to consume.
```js
const metrics = await register.clusterMetrics();
// - or -
register
.clusterMetrics()
.then(metrics => {
/* ... */
})
.catch(err => {
/* ... */
});
```
### Pushgateway
It is possible to push metrics via a
[Pushgateway](https://github.com/prometheus/pushgateway).
```js
const client = require('prom-client');
let gateway = new client.Pushgateway('http://127.0.0.1:9091');
gateway.pushAdd({ jobName: 'test' })
.then(({resp, body}) => {
/* ... */
})
.catch(err => {
/* ... */
})); //Add metric and overwrite old ones
gateway.push({ jobName: 'test' })
.then(({resp, body}) => {
/* ... */
})
.catch(err => {
/* ... */
})); //Overwrite all metrics (use PUT)
gateway.delete({ jobName: 'test' })
.then(({resp, body}) => {
/* ... */
})
.catch(err => {
/* ... */
})); //Delete all metrics for jobName
//All gateway requests can have groupings on it
gateway.pushAdd({ jobName: 'test', groupings: { key: 'value' } })
.then(({resp, body}) => {
/* ... */
})
.catch(err => {
/* ... */
}));
// It's possible to extend the Pushgateway with request options from nodes core
// http/https library. In particular, you might want to provide an agent so that
// TCP connections are reused.
gateway = new client.Pushgateway('http://127.0.0.1:9091', {
timeout: 5000, //Set the request timeout to 5000ms
agent: new http.Agent({
keepAlive: true,
keepAliveMsec: 10000,
maxSockets: 5,
}),
});
```
Some gateways such as [Gravel Gateway](https://github.com/sinkingpoint/prometheus-gravel-gateway) do not support grouping by job name, exposing a plain `/metrics` endpoint instead of `/metrics/job/<jobName>`. It's possible to configure a gateway instance to not require a jobName in the options argument.
```js
gravelGateway = new client.Pushgateway('http://127.0.0.1:9091', {
timeout: 5000,
requireJobName: false,
});
gravelGateway.pushAdd();
```
### Bucket Generators
For convenience, there are two bucket generator functions - linear and
exponential.
```js
const client = require('prom-client');
new client.Histogram({
name: 'metric_name',
help: 'metric_help',
buckets: client.linearBuckets(0, 10, 20), //Create 20 buckets, starting on 0 and a width of 10
});
new client.Histogram({
name: 'metric_name',
help: 'metric_help',
buckets: client.exponentialBuckets(1, 2, 5), //Create 5 buckets, starting on 1 and with a factor of 2
});
```
### Garbage Collection Metrics
To avoid native dependencies in this module, GC statistics for bytes reclaimed
in each GC sweep are kept in a separate module:
https://github.com/SimenB/node-prometheus-gc-stats. (Note that that metric may
no longer be accurate now that v8 uses parallel garbage collection.)

795
node_modules/prom-client/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,795 @@
// Type definitions for prom-client
// Definitions by: Simon Nyberg http://twitter.com/siimon_nyberg
export type Charset = 'utf-8';
export type PrometheusMIME = 'text/plain';
export type PrometheusMetricsVersion = '0.0.4';
export type OpenMetricsMIME = 'application/openmetrics-text';
export type OpenMetricsVersion = '1.0.0';
export type OpenMetricsContentType =
`${OpenMetricsMIME}; version=${OpenMetricsVersion}; charset=${Charset}`;
export type PrometheusContentType =
`${PrometheusMIME}; version=${PrometheusMetricsVersion}; charset=${Charset}`;
export type RegistryContentType =
| PrometheusContentType
| OpenMetricsContentType;
/**
* Container for all registered metrics
*/
export class Registry<
BoundRegistryContentType extends RegistryContentType = PrometheusContentType,
> {
/**
* Get string representation for all metrics
*/
metrics(): Promise<string>;
/**
* Remove all metrics from the registry
*/
clear(): void;
/**
* Reset all metrics in the registry
*/
resetMetrics(): void;
/**
* Register metric to register
* @param metric Metric to add to register
*/
registerMetric<T extends string>(metric: Metric<T>): void;
/**
* Get all metrics as objects
*/
getMetricsAsJSON(): Promise<MetricObjectWithValues<MetricValue<string>>[]>;
/**
* Get all metrics as objects
*/
getMetricsAsArray(): MetricObject[];
/**
* Remove a single metric
* @param name The name of the metric to remove
*/
removeSingleMetric(name: string): void;
/**
* Get a single metric
* @param name The name of the metric
*/
getSingleMetric<T extends string>(name: string): Metric<T> | undefined;
/**
* Set static labels to every metric emitted by this registry
* @param labels of name/value pairs:
* { defaultLabel: "value", anotherLabel: "value 2" }
*/
setDefaultLabels(labels: object): void;
/**
* Get a string representation of a single metric by name
* @param name The name of the metric
*/
getSingleMetricAsString(name: string): Promise<string>;
/**
* Gets the Content-Type of the metrics for use in the response headers.
*/
readonly contentType: BoundRegistryContentType;
/**
* Set the content type of a registry. Used to change between Prometheus and
* OpenMetrics versions.
* @param contentType The type of the registry
*/
setContentType(contentType: BoundRegistryContentType): void;
/**
* Merge registers
* @param registers The registers you want to merge together
*/
static merge(registers: Registry[]): Registry;
/**
* HTTP Prometheus Content-Type for metrics response headers.
*/
static PROMETHEUS_CONTENT_TYPE: PrometheusContentType;
/**
* HTTP OpenMetrics Content-Type for metrics response headers.
*/
static OPENMETRICS_CONTENT_TYPE: OpenMetricsContentType;
}
export type Collector = () => void;
/**
* The register that contains all metrics
*/
export const register: Registry;
/**
* HTTP Content-Type for metrics response headers for the default registry,
* defaults to Prometheus text format.
*/
export const contentType: RegistryContentType;
/**
* HTTP Prometheus Content-Type for metrics response headers.
*/
export const prometheusContentType: PrometheusContentType;
/**
* HTTP OpenMetrics Content-Type for metrics response headers.
*/
export const openMetricsContentType: OpenMetricsContentType;
export class AggregatorRegistry<
T extends RegistryContentType,
> extends Registry<T> {
/**
* Gets aggregated metrics for all workers.
* @return {Promise<string>} Promise that resolves with the aggregated
* metrics.
*/
clusterMetrics(): Promise<string>;
/**
* Creates a new Registry instance from an array of metrics that were
* created by `registry.getMetricsAsJSON()`. Metrics are aggregated using
* the method specified by their `aggregator` property, or by summation if
* `aggregator` is undefined.
* @param {Array} metricsArr Array of metrics, each of which created by
* `registry.getMetricsAsJSON()`.
* @return {Registry} aggregated registry.
*/
static aggregate<T extends RegistryContentType>(
metricsArr: Array<object>,
): Registry<T>; // TODO Promise?
/**
* Sets the registry or registries to be aggregated. Call from workers to
* use a registry/registries other than the default global registry.
* @param {Array<Registry>|Registry} regs Registry or registries to be
* aggregated.
* @return {void}
*/
static setRegistries(
regs:
| Array<
Registry<PrometheusContentType> | Registry<OpenMetricsContentType>
>
| Registry<PrometheusContentType>
| Registry<OpenMetricsContentType>,
): void;
}
/**
* General metric type
*/
export type Metric<T extends string = string> =
| Counter<T>
| Gauge<T>
| Summary<T>
| Histogram<T>;
/**
* Aggregation methods, used for aggregating metrics in a Node.js cluster.
*/
export type Aggregator = 'omit' | 'sum' | 'first' | 'min' | 'max' | 'average';
export enum MetricType {
Counter,
Gauge,
Histogram,
Summary,
}
type CollectFunction<T> = (this: T) => void | Promise<void>;
interface MetricObject {
name: string;
help: string;
type: MetricType;
aggregator: Aggregator;
collect: CollectFunction<any>;
}
interface MetricObjectWithValues<T extends MetricValue<string>>
extends MetricObject {
values: T[];
}
type MetricValue<T extends string> = {
value: number;
labels: LabelValues<T>;
};
type MetricValueWithName<T extends string> = MetricValue<T> & {
metricName?: string;
};
type LabelValues<T extends string> = Partial<Record<T, string | number>>;
interface MetricConfiguration<T extends string> {
name: string;
help: string;
labelNames?: T[] | readonly T[];
registers?: (
| Registry<PrometheusContentType>
| Registry<OpenMetricsContentType>
)[];
aggregator?: Aggregator;
collect?: CollectFunction<any>;
enableExemplars?: boolean;
}
export interface CounterConfiguration<T extends string>
extends MetricConfiguration<T> {
collect?: CollectFunction<Counter<T>>;
}
export interface IncreaseDataWithExemplar<T extends string> {
value?: number;
labels?: LabelValues<T>;
exemplarLabels?: LabelValues<T>;
}
export interface ObserveDataWithExemplar<T extends string> {
value: number;
labels?: LabelValues<T>;
exemplarLabels?: LabelValues<T>;
}
/**
* A counter is a cumulative metric that represents a single numerical value that only ever goes up
*/
export class Counter<T extends string = string> {
/**
* @param configuration Configuration when creating a Counter metric. Name and Help is required.
*/
constructor(configuration: CounterConfiguration<T>);
/**
* Increment for given labels
* @param labels Object with label keys and values
* @param value The number to increment with
*/
inc(labels: LabelValues<T>, value?: number): void;
/**
* Increment with value
* @param value The value to increment with
*/
inc(value?: number): void;
/**
* Increment with exemplars
* @param incData Object with labels, value and exemplars for an increase
*/
inc(incData: IncreaseDataWithExemplar<T>): void;
/**
* Get counter metric object
*/
get(): Promise<MetricObjectWithValues<MetricValue<T>>>;
/**
* Return the child for given labels
* @param values Label values
* @return Configured counter with given labels
*/
labels(...values: string[]): Counter.Internal;
/**
* Return the child for given labels
* @param labels Object with label keys and values
* @return Configured counter with given labels
*/
labels(labels: LabelValues<T>): Counter.Internal;
/**
* Reset counter values
*/
reset(): void;
/**
* Remove metrics for the given label values
* @param values Label values
*/
remove(...values: string[]): void;
/**
* Remove metrics for the given label values
* @param labels Object with label keys and values
*/
remove(labels: LabelValues<T>): void;
}
export namespace Counter {
interface Internal {
/**
* Increment with value
* @param value The value to increment with
*/
inc(value?: number): void;
}
}
export interface GaugeConfiguration<T extends string>
extends MetricConfiguration<T> {
collect?: CollectFunction<Gauge<T>>;
}
/**
* A gauge is a metric that represents a single numerical value that can arbitrarily go up and down.
*/
export class Gauge<T extends string = string> {
/**
* @param configuration Configuration when creating a Gauge metric. Name and Help is mandatory
*/
constructor(configuration: GaugeConfiguration<T>);
/**
* Increment gauge for given labels
* @param labels Object with label keys and values
* @param value The value to increment with
*/
inc(labels: LabelValues<T>, value?: number): void;
/**
* Increment gauge
* @param value The value to increment with
*/
inc(value?: number): void;
/**
* Decrement gauge
* @param labels Object with label keys and values
* @param value Value to decrement with
*/
dec(labels: LabelValues<T>, value?: number): void;
/**
* Decrement gauge
* @param value The value to decrement with
*/
dec(value?: number): void;
/**
* Set gauge value for labels
* @param labels Object with label keys and values
* @param value The value to set
*/
set(labels: LabelValues<T>, value: number): void;
/**
* Set gauge value
* @param value The value to set
*/
set(value: number): void;
/**
* Get gauge metric object
*/
get(): Promise<MetricObjectWithValues<MetricValue<T>>>;
/**
* Set gauge value to current epoch time in seconds
* @param labels Object with label keys and values
*/
setToCurrentTime(labels?: LabelValues<T>): void;
/**
* Start a timer. Calling the returned function will set the gauge's value
* to the observed duration in seconds.
* @param labels Object with label keys and values
* @return Function to invoke when timer should be stopped. The value it
* returns is the timed duration.
*/
startTimer(labels?: LabelValues<T>): (labels?: LabelValues<T>) => number;
/**
* Return the child for given labels
* @param values Label values
* @return Configured gauge with given labels
*/
labels(...values: string[]): Gauge.Internal<T>;
/**
* Return the child for given labels
* @param labels Object with label keys and values
* @return Configured counter with given labels
*/
labels(labels: LabelValues<T>): Gauge.Internal<T>;
/**
* Reset gauge values
*/
reset(): void;
/**
* Remove metrics for the given label values
* @param values Label values
*/
remove(...values: string[]): void;
/**
* Remove metrics for the given label values
* @param labels Object with label keys and values
*/
remove(labels: LabelValues<T>): void;
}
export namespace Gauge {
interface Internal<T extends string> {
/**
* Increment gauge with value
* @param value The value to increment with
*/
inc(value?: number): void;
/**
* Decrement with value
* @param value The value to decrement with
*/
dec(value?: number): void;
/**
* Set gauges value
* @param value The value to set
*/
set(value: number): void;
/**
* Set gauge value to current epoch time in ms
*/
setToCurrentTime(): void;
/**
* Start a timer. Calling the returned function will set the gauge's value
* to the observed duration in seconds.
* @return Function to invoke when timer should be stopped. The value it
* returns is the timed duration.
*/
startTimer(): (labels?: LabelValues<T>) => number;
}
}
export interface HistogramConfiguration<T extends string>
extends MetricConfiguration<T> {
buckets?: number[];
collect?: CollectFunction<Histogram<T>>;
}
/**
* A histogram samples observations (usually things like request durations or response sizes) and counts them in configurable buckets
*/
export class Histogram<T extends string = string> {
/**
* @param configuration Configuration when creating the Histogram. Name and Help is mandatory
*/
constructor(configuration: HistogramConfiguration<T>);
/**
* Observe value
* @param value The value to observe
*/
observe(value: number): void;
/**
* Observe value for given labels
* @param labels Object with label keys and values
* @param value The value to observe
*/
observe(labels: LabelValues<T>, value: number): void;
/**
* Observe with exemplars
* @param observeData Object with labels, value and exemplars for an observation
*/
observe(observeData: ObserveDataWithExemplar<T>): void;
/**
* Get histogram metric object
*/
get(): Promise<MetricObjectWithValues<MetricValueWithName<T>>>;
/**
* Start a timer. Calling the returned function will observe the duration in
* seconds in the histogram.
* @param labels Object with label keys and values
* @return Function to invoke when timer should be stopped. The value it
* returns is the timed duration.
*/
startTimer(labels?: LabelValues<T>): (labels?: LabelValues<T>) => number;
/**
* Start a timer with exemplar. Calling the returned function will observe the duration in
* seconds in the histogram.
* @param labels Object with label keys and values
* @param exemplarLabels Object with label keys and values for exemplars
* @return Function to invoke when timer should be stopped. The value it
* returns is the timed duration.
*/
startTimer(
labels?: LabelValues<T>,
exemplarLabels?: LabelValues<T>,
): (labels?: LabelValues<T>, exemplarLabels?: LabelValues<T>) => number;
/**
* Reset histogram values
*/
reset(): void;
/**
* Initialize the metrics for the given combination of labels to zero
*/
zero(labels: LabelValues<T>): void;
/**
* Return the child for given labels
* @param values Label values
* @return Configured histogram with given labels
*/
labels(...values: string[]): Histogram.Internal<T>;
/**
* Return the child for given labels
* @param labels Object with label keys and values
* @return Configured counter with given labels
*/
labels(labels: LabelValues<T>): Histogram.Internal<T>;
/**
* Remove metrics for the given label values
* @param values Label values
*/
remove(...values: string[]): void;
/**
* Remove metrics for the given label values
* @param labels Object with label keys and values
*/
remove(labels: LabelValues<T>): void;
}
export namespace Histogram {
interface Internal<T extends string> {
/**
* Observe value
* @param value The value to observe
*/
observe(value: number): void;
/**
* Start a timer. Calling the returned function will observe the
* duration in seconds in the histogram.
* @param labels Object with label keys and values
* @return Function to invoke when timer should be stopped. The value it
* returns is the timed duration.
*/
startTimer(): (labels?: LabelValues<T>) => void;
}
interface Config {
/**
* Buckets used in the histogram
*/
buckets?: number[];
}
}
export interface SummaryConfiguration<T extends string>
extends MetricConfiguration<T> {
percentiles?: number[];
maxAgeSeconds?: number;
ageBuckets?: number;
pruneAgedBuckets?: boolean;
compressCount?: number;
collect?: CollectFunction<Summary<T>>;
}
/**
* A summary samples observations
*/
export class Summary<T extends string = string> {
/**
* @param configuration Configuration when creating Summary metric. Name and Help is mandatory
*/
constructor(configuration: SummaryConfiguration<T>);
/**
* Observe value in summary
* @param value The value to observe
*/
observe(value: number): void;
/**
* Observe value for given labels
* @param labels Object with label keys and values
* @param value Value to observe
*/
observe(labels: LabelValues<T>, value: number): void;
/**
* Get summary metric object
*/
get(): Promise<MetricObjectWithValues<MetricValueWithName<T>>>;
/**
* Start a timer. Calling the returned function will observe the duration in
* seconds in the summary.
* @param labels Object with label keys and values
* @return Function to invoke when timer should be stopped
*/
startTimer(labels?: LabelValues<T>): (labels?: LabelValues<T>) => number;
/**
* Reset all values in the summary
*/
reset(): void;
/**
* Return the child for given labels
* @param values Label values
* @return Configured summary with given labels
*/
labels(...values: string[]): Summary.Internal<T>;
/**
* Return the child for given labels
* @param labels Object with label keys and values
* @return Configured counter with given labels
*/
labels(labels: LabelValues<T>): Summary.Internal<T>;
/**
* Remove metrics for the given label values
* @param values Label values
*/
remove(...values: string[]): void;
/**
* Remove metrics for the given label values
* @param labels Object with label keys and values
*/
remove(labels: LabelValues<T>): void;
}
export namespace Summary {
interface Internal<T extends string> {
/**
* Observe value in summary
* @param value The value to observe
*/
observe(value: number): void;
/**
* Start a timer. Calling the returned function will observe the
* duration in seconds in the summary.
* @param labels Object with label keys and values
* @return Function to invoke when timer should be stopped. The value it
* returns is the timed duration.
*/
startTimer(): (labels?: LabelValues<T>) => number;
}
interface Config {
/**
* Configurable percentiles, values should never be greater than 1
*/
percentiles?: number[];
}
}
/**
* Push metrics to a Pushgateway
*/
export class Pushgateway<T extends RegistryContentType> {
/**
* @param url Complete url to the Pushgateway. If port is needed append url with :port
* @param options Options
* @param registry Registry
*/
constructor(url: string, options?: any, registry?: Registry<T>);
/**
* Add metric and overwrite old ones
* @param params Push parameters
*/
pushAdd(
params: Pushgateway.Parameters,
): Promise<{ resp?: unknown; body?: unknown }>;
/**
* Overwrite all metric (using PUT to Pushgateway)
* @param params Push parameters
*/
push(
params: Pushgateway.Parameters,
): Promise<{ resp?: unknown; body?: unknown }>;
/**
* Delete all metrics for jobName
* @param params Push parameters
*/
delete(
params: Pushgateway.Parameters,
): Promise<{ resp?: unknown; body?: unknown }>;
}
export namespace Pushgateway {
interface Parameters {
/**
* Jobname that is pushing the metric
*/
jobName: string;
/**
* Label sets used in the url when making a request to the Pushgateway,
*/
groupings?: {
[key: string]: string;
};
}
}
/**
* Create an array with equal spacing between the elements
* @param start The first value in the array
* @param width The spacing between the elements
* @param count The number of items in array
* @return An array with the requested number of elements
*/
export function linearBuckets(
start: number,
width: number,
count: number,
): number[];
/**
* Create an array that grows exponentially
* @param start The first value in the array
* @param factor The exponential factor
* @param count The number of items in array
* @return An array with the requested number of elements
*/
export function exponentialBuckets(
start: number,
factor: number,
count: number,
): number[];
export interface DefaultMetricsCollectorConfiguration<
T extends RegistryContentType,
> {
register?: Registry<T>;
prefix?: string;
gcDurationBuckets?: number[];
eventLoopMonitoringPrecision?: number;
labels?: object;
}
export const collectDefaultMetrics: {
/**
* Configure default metrics
* @param config Configuration object for default metrics collector
*/
<T extends RegistryContentType>(
config?: DefaultMetricsCollectorConfiguration<T>,
): void;
/** All available default metrics */
metricsList: string[];
};
/**
* Validate a metric name
* @param name The name to validate
* @return True if the metric name is valid, false if not
*/
export function validateMetricName(name: string): boolean;

37
node_modules/prom-client/index.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
/**
* Prometheus client
* @module Prometheus client
*/
'use strict';
exports.register = require('./lib/registry').globalRegistry;
exports.Registry = require('./lib/registry');
Object.defineProperty(exports, 'contentType', {
configurable: false,
enumerable: true,
get() {
return exports.register.contentType;
},
set(value) {
exports.register.setContentType(value);
},
});
exports.prometheusContentType = exports.Registry.PROMETHEUS_CONTENT_TYPE;
exports.openMetricsContentType = exports.Registry.OPENMETRICS_CONTENT_TYPE;
exports.validateMetricName = require('./lib/validation').validateMetricName;
exports.Counter = require('./lib/counter');
exports.Gauge = require('./lib/gauge');
exports.Histogram = require('./lib/histogram');
exports.Summary = require('./lib/summary');
exports.Pushgateway = require('./lib/pushgateway');
exports.linearBuckets = require('./lib/bucketGenerators').linearBuckets;
exports.exponentialBuckets =
require('./lib/bucketGenerators').exponentialBuckets;
exports.collectDefaultMetrics = require('./lib/defaultMetrics');
exports.aggregators = require('./lib/metricAggregators').aggregators;
exports.AggregatorRegistry = require('./lib/cluster');

31
node_modules/prom-client/lib/bucketGenerators.js generated vendored Normal file
View File

@@ -0,0 +1,31 @@
'use strict';
exports.linearBuckets = (start, width, count) => {
if (count < 1) {
throw new Error('Linear buckets needs a positive count');
}
const buckets = new Array(count);
for (let i = 0; i < count; i++) {
buckets[i] = start + i * width;
}
return buckets;
};
exports.exponentialBuckets = (start, factor, count) => {
if (start <= 0) {
throw new Error('Exponential buckets needs a positive start');
}
if (count < 1) {
throw new Error('Exponential buckets needs a positive count');
}
if (factor <= 1) {
throw new Error('Exponential buckets needs a factor greater than 1');
}
const buckets = new Array(count);
for (let i = 0; i < count; i++) {
buckets[i] = start;
start *= factor;
}
return buckets;
};

219
node_modules/prom-client/lib/cluster.js generated vendored Normal file
View File

@@ -0,0 +1,219 @@
'use strict';
/**
* Extends the Registry class with a `clusterMetrics` method that returns
* aggregated metrics for all workers.
*
* In cluster workers, listens for and responds to requests for metrics by the
* cluster master.
*/
const Registry = require('./registry');
const { Grouper } = require('./util');
const { aggregators } = require('./metricAggregators');
// We need to lazy-load the 'cluster' module as some application servers -
// namely Passenger - crash when it is imported.
let cluster = () => {
const data = require('cluster');
cluster = () => data;
return data;
};
const GET_METRICS_REQ = 'prom-client:getMetricsReq';
const GET_METRICS_RES = 'prom-client:getMetricsRes';
let registries = [Registry.globalRegistry];
let requestCtr = 0; // Concurrency control
let listenersAdded = false;
const requests = new Map(); // Pending requests for workers' local metrics.
class AggregatorRegistry extends Registry {
constructor(regContentType = Registry.PROMETHEUS_CONTENT_TYPE) {
super(regContentType);
addListeners();
}
/**
* Gets aggregated metrics for all workers. The optional callback and
* returned Promise resolve with the same value; either may be used.
* @return {Promise<string>} Promise that resolves with the aggregated
* metrics.
*/
clusterMetrics() {
const requestId = requestCtr++;
return new Promise((resolve, reject) => {
let settled = false;
function done(err, result) {
if (settled) return;
settled = true;
if (err) reject(err);
else resolve(result);
}
const request = {
responses: [],
pending: 0,
done,
errorTimeout: setTimeout(() => {
const err = new Error('Operation timed out.');
request.done(err);
}, 5000),
};
requests.set(requestId, request);
const message = {
type: GET_METRICS_REQ,
requestId,
};
for (const id in cluster().workers) {
// If the worker exits abruptly, it may still be in the workers
// list but not able to communicate.
if (cluster().workers[id].isConnected()) {
cluster().workers[id].send(message);
request.pending++;
}
}
if (request.pending === 0) {
// No workers were up
clearTimeout(request.errorTimeout);
process.nextTick(() => done(null, ''));
}
});
}
get contentType() {
return super.contentType;
}
/**
* Creates a new Registry instance from an array of metrics that were
* created by `registry.getMetricsAsJSON()`. Metrics are aggregated using
* the method specified by their `aggregator` property, or by summation if
* `aggregator` is undefined.
* @param {Array} metricsArr Array of metrics, each of which created by
* `registry.getMetricsAsJSON()`.
* @param {string} registryType content type of the new registry. Defaults
* to PROMETHEUS_CONTENT_TYPE.
* @return {Registry} aggregated registry.
*/
static aggregate(
metricsArr,
registryType = Registry.PROMETHEUS_CONTENT_TYPE,
) {
const aggregatedRegistry = new Registry();
const metricsByName = new Grouper();
aggregatedRegistry.setContentType(registryType);
// Gather by name
metricsArr.forEach(metrics => {
metrics.forEach(metric => {
metricsByName.add(metric.name, metric);
});
});
// Aggregate gathered metrics.
metricsByName.forEach(metrics => {
const aggregatorName = metrics[0].aggregator;
const aggregatorFn = aggregators[aggregatorName];
if (typeof aggregatorFn !== 'function') {
throw new Error(`'${aggregatorName}' is not a defined aggregator.`);
}
const aggregatedMetric = aggregatorFn(metrics);
// NB: The 'omit' aggregator returns undefined.
if (aggregatedMetric) {
const aggregatedMetricWrapper = Object.assign(
{
get: () => aggregatedMetric,
},
aggregatedMetric,
);
aggregatedRegistry.registerMetric(aggregatedMetricWrapper);
}
});
return aggregatedRegistry;
}
/**
* Sets the registry or registries to be aggregated. Call from workers to
* use a registry/registries other than the default global registry.
* @param {Array<Registry>|Registry} regs Registry or registries to be
* aggregated.
* @return {void}
*/
static setRegistries(regs) {
if (!Array.isArray(regs)) regs = [regs];
regs.forEach(reg => {
if (!(reg instanceof Registry)) {
throw new TypeError(`Expected Registry, got ${typeof reg}`);
}
});
registries = regs;
}
}
/**
* Adds event listeners for cluster aggregation. Idempotent (safe to call more
* than once).
* @return {void}
*/
function addListeners() {
if (listenersAdded) return;
listenersAdded = true;
if (cluster().isMaster) {
// Listen for worker responses to requests for local metrics
cluster().on('message', (worker, message) => {
if (message.type === GET_METRICS_RES) {
const request = requests.get(message.requestId);
if (message.error) {
request.done(new Error(message.error));
return;
}
message.metrics.forEach(registry => request.responses.push(registry));
request.pending--;
if (request.pending === 0) {
// finalize
requests.delete(message.requestId);
clearTimeout(request.errorTimeout);
const registry = AggregatorRegistry.aggregate(request.responses);
const promString = registry.metrics();
request.done(null, promString);
}
}
});
}
if (cluster().isWorker) {
// Respond to master's requests for worker's local metrics.
process.on('message', message => {
if (message.type === GET_METRICS_REQ) {
Promise.all(registries.map(r => r.getMetricsAsJSON()))
.then(metrics => {
process.send({
type: GET_METRICS_RES,
requestId: message.requestId,
metrics,
});
})
.catch(error => {
process.send({
type: GET_METRICS_RES,
requestId: message.requestId,
error: error.message,
});
});
}
});
}
}
module.exports = AggregatorRegistry;

147
node_modules/prom-client/lib/counter.js generated vendored Normal file
View File

@@ -0,0 +1,147 @@
/**
* Counter metric
*/
'use strict';
const util = require('util');
const {
hashObject,
isObject,
getLabels,
removeLabels,
nowTimestamp,
} = require('./util');
const { validateLabel } = require('./validation');
const { Metric } = require('./metric');
const Exemplar = require('./exemplar');
class Counter extends Metric {
constructor(config) {
super(config);
this.type = 'counter';
this.defaultLabels = {};
this.defaultValue = 1;
this.defaultExemplarLabelSet = {};
if (config.enableExemplars) {
this.enableExemplars = true;
this.inc = this.incWithExemplar;
} else {
this.inc = this.incWithoutExemplar;
}
}
/**
* Increment counter
* @param {object} labels - What label you want to be incremented
* @param {Number} value - Value to increment, if omitted increment with 1
* @returns {object} results - object with information about the inc operation
* @returns {string} results.labelHash - hash representation of the labels
*/
incWithoutExemplar(labels, value) {
let hash = '';
if (isObject(labels)) {
hash = hashObject(labels, this.sortedLabelNames);
validateLabel(this.labelNames, labels);
} else {
value = labels;
labels = {};
}
if (value && !Number.isFinite(value)) {
throw new TypeError(`Value is not a valid number: ${util.format(value)}`);
}
if (value < 0) {
throw new Error('It is not possible to decrease a counter');
}
if (value === null || value === undefined) value = 1;
setValue(this.hashMap, value, labels, hash);
return { labelHash: hash };
}
/**
* Increment counter with exemplar, same as inc but accepts labels for an
* exemplar.
* If no label is provided the current exemplar labels are kept unchanged
* (defaults to empty set).
*
* @param {object} incOpts - Object with options about what metric to increase
* @param {object} incOpts.labels - What label you want to be incremented,
* defaults to null (metric with no labels)
* @param {Number} incOpts.value - Value to increment, defaults to 1
* @param {object} incOpts.exemplarLabels - Key-value labels for the
* exemplar, defaults to empty set {}
* @returns {void}
*/
incWithExemplar({
labels = this.defaultLabels,
value = this.defaultValue,
exemplarLabels = this.defaultExemplarLabelSet,
} = {}) {
const res = this.incWithoutExemplar(labels, value);
this.updateExemplar(exemplarLabels, value, res.labelHash);
}
updateExemplar(exemplarLabels, value, hash) {
if (exemplarLabels === this.defaultExemplarLabelSet) return;
if (!isObject(this.hashMap[hash].exemplar)) {
this.hashMap[hash].exemplar = new Exemplar();
}
this.hashMap[hash].exemplar.validateExemplarLabelSet(exemplarLabels);
this.hashMap[hash].exemplar.labelSet = exemplarLabels;
this.hashMap[hash].exemplar.value = value ? value : 1;
this.hashMap[hash].exemplar.timestamp = nowTimestamp();
}
/**
* Reset counter
* @returns {void}
*/
reset() {
this.hashMap = {};
if (this.labelNames.length === 0) {
setValue(this.hashMap, 0);
}
}
async get() {
if (this.collect) {
const v = this.collect();
if (v instanceof Promise) await v;
}
return {
help: this.help,
name: this.name,
type: this.type,
values: Object.values(this.hashMap),
aggregator: this.aggregator,
};
}
labels(...args) {
const labels = getLabels(this.labelNames, args) || {};
return {
inc: this.inc.bind(this, labels),
};
}
remove(...args) {
const labels = getLabels(this.labelNames, args) || {};
validateLabel(this.labelNames, labels);
return removeLabels.call(this, this.hashMap, labels, this.sortedLabelNames);
}
}
function setValue(hashMap, value, labels = {}, hash = '') {
if (hashMap[hash]) {
hashMap[hash].value += value;
} else {
hashMap[hash] = { value, labels };
}
return hashMap;
}
module.exports = Counter;

51
node_modules/prom-client/lib/defaultMetrics.js generated vendored Normal file
View File

@@ -0,0 +1,51 @@
'use strict';
const { isObject } = require('./util');
// Default metrics.
const processCpuTotal = require('./metrics/processCpuTotal');
const processStartTime = require('./metrics/processStartTime');
const osMemoryHeap = require('./metrics/osMemoryHeap');
const processOpenFileDescriptors = require('./metrics/processOpenFileDescriptors');
const processMaxFileDescriptors = require('./metrics/processMaxFileDescriptors');
const eventLoopLag = require('./metrics/eventLoopLag');
const processHandles = require('./metrics/processHandles');
const processRequests = require('./metrics/processRequests');
const processResources = require('./metrics/processResources');
const heapSizeAndUsed = require('./metrics/heapSizeAndUsed');
const heapSpacesSizeAndUsed = require('./metrics/heapSpacesSizeAndUsed');
const version = require('./metrics/version');
const gc = require('./metrics/gc');
const metrics = {
processCpuTotal,
processStartTime,
osMemoryHeap,
processOpenFileDescriptors,
processMaxFileDescriptors,
eventLoopLag,
...(typeof process.getActiveResourcesInfo === 'function'
? { processResources }
: {}),
processHandles,
processRequests,
heapSizeAndUsed,
heapSpacesSizeAndUsed,
version,
gc,
};
const metricsList = Object.keys(metrics);
module.exports = function collectDefaultMetrics(config) {
if (config !== null && config !== undefined && !isObject(config)) {
throw new TypeError('config must be null, undefined, or an object');
}
config = { eventLoopMonitoringPrecision: 10, ...config };
for (const metric of Object.values(metrics)) {
metric(config.register, config);
}
};
module.exports.metricsList = metricsList;

37
node_modules/prom-client/lib/exemplar.js generated vendored Normal file
View File

@@ -0,0 +1,37 @@
'use strict';
/**
* Class representing an OpenMetrics exemplar.
*
* @property {object} labelSet
* @property {number} value
* @property {number} [timestamp]
* */
class Exemplar {
constructor(labelSet = {}, value = null) {
this.labelSet = labelSet;
this.value = value;
}
/**
* Validation for the label set format.
* https://github.com/OpenObservability/OpenMetrics/blob/d99b705f611b75fec8f450b05e344e02eea6921d/specification/OpenMetrics.md#exemplars
*
* @param {object} labelSet - Exemplar labels.
* @throws {RangeError}
* @return {void}
*/
validateExemplarLabelSet(labelSet) {
let res = '';
for (const [labelName, labelValue] of Object.entries(labelSet)) {
res += `${labelName}${labelValue}`;
}
if (res.length > 128) {
throw new RangeError(
'Label set size must be smaller than 128 UTF-8 chars',
);
}
}
}
module.exports = Exemplar;

173
node_modules/prom-client/lib/gauge.js generated vendored Normal file
View File

@@ -0,0 +1,173 @@
/**
* Gauge metric
*/
'use strict';
const util = require('util');
const {
setValue,
setValueDelta,
getLabels,
hashObject,
isObject,
removeLabels,
} = require('./util');
const { validateLabel } = require('./validation');
const { Metric } = require('./metric');
class Gauge extends Metric {
constructor(config) {
super(config);
this.type = 'gauge';
}
/**
* Set a gauge to a value
* @param {object} labels - Object with labels and their values
* @param {Number} value - Value to set the gauge to, must be positive
* @returns {void}
*/
set(labels, value) {
value = getValueArg(labels, value);
labels = getLabelArg(labels);
set(this, labels, value);
}
/**
* Reset gauge
* @returns {void}
*/
reset() {
this.hashMap = {};
if (this.labelNames.length === 0) {
setValue(this.hashMap, 0, {});
}
}
/**
* Increment a gauge value
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {Number} value - Value to increment - if omitted, increment with 1
* @returns {void}
*/
inc(labels, value) {
value = getValueArg(labels, value);
labels = getLabelArg(labels);
if (value === undefined) value = 1;
setDelta(this, labels, value);
}
/**
* Decrement a gauge value
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {Number} value - Value to decrement - if omitted, decrement with 1
* @returns {void}
*/
dec(labels, value) {
value = getValueArg(labels, value);
labels = getLabelArg(labels);
if (value === undefined) value = 1;
setDelta(this, labels, -value);
}
/**
* Set the gauge to current unix epoch
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @returns {void}
*/
setToCurrentTime(labels) {
const now = Date.now() / 1000;
if (labels === undefined) {
this.set(now);
} else {
this.set(labels, now);
}
}
/**
* Start a timer
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @returns {function} - Invoke this function to set the duration in seconds since you started the timer.
* @example
* var done = gauge.startTimer();
* makeXHRRequest(function(err, response) {
* done(); //Duration of the request will be saved
* });
*/
startTimer(labels) {
const start = process.hrtime();
return endLabels => {
const delta = process.hrtime(start);
const value = delta[0] + delta[1] / 1e9;
this.set(Object.assign({}, labels, endLabels), value);
return value;
};
}
async get() {
if (this.collect) {
const v = this.collect();
if (v instanceof Promise) await v;
}
return {
help: this.help,
name: this.name,
type: this.type,
values: Object.values(this.hashMap),
aggregator: this.aggregator,
};
}
_getValue(labels) {
const hash = hashObject(labels || {}, this.sortedLabelNames);
return this.hashMap[hash] ? this.hashMap[hash].value : 0;
}
labels(...args) {
const labels = getLabels(this.labelNames, args);
validateLabel(this.labelNames, labels);
return {
inc: this.inc.bind(this, labels),
dec: this.dec.bind(this, labels),
set: this.set.bind(this, labels),
setToCurrentTime: this.setToCurrentTime.bind(this, labels),
startTimer: this.startTimer.bind(this, labels),
};
}
remove(...args) {
const labels = getLabels(this.labelNames, args);
validateLabel(this.labelNames, labels);
removeLabels.call(this, this.hashMap, labels, this.sortedLabelNames);
}
}
function set(gauge, labels, value) {
if (typeof value !== 'number') {
throw new TypeError(`Value is not a valid number: ${util.format(value)}`);
}
validateLabel(gauge.labelNames, labels);
setValue(gauge.hashMap, value, labels);
}
function setDelta(gauge, labels, delta) {
if (typeof delta !== 'number') {
throw new TypeError(`Delta is not a valid number: ${util.format(delta)}`);
}
validateLabel(gauge.labelNames, labels);
const hash = hashObject(labels, gauge.sortedLabelNames);
setValueDelta(gauge.hashMap, delta, labels, hash);
}
function getLabelArg(labels) {
return isObject(labels) ? labels : {};
}
function getValueArg(labels, value) {
return isObject(labels) ? value : labels;
}
module.exports = Gauge;

351
node_modules/prom-client/lib/histogram.js generated vendored Normal file
View File

@@ -0,0 +1,351 @@
/**
* Histogram
*/
'use strict';
const util = require('util');
const {
getLabels,
hashObject,
isObject,
removeLabels,
nowTimestamp,
} = require('./util');
const { validateLabel } = require('./validation');
const { Metric } = require('./metric');
const Exemplar = require('./exemplar');
class Histogram extends Metric {
constructor(config) {
super(config, {
buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10],
});
this.type = 'histogram';
this.defaultLabels = {};
this.defaultExemplarLabelSet = {};
this.enableExemplars = false;
for (const label of this.labelNames) {
if (label === 'le') {
throw new Error('le is a reserved label keyword');
}
}
this.upperBounds = this.buckets;
this.bucketValues = this.upperBounds.reduce((acc, upperBound) => {
acc[upperBound] = 0;
return acc;
}, {});
if (config.enableExemplars) {
this.enableExemplars = true;
this.bucketExemplars = this.upperBounds.reduce((acc, upperBound) => {
acc[upperBound] = null;
return acc;
}, {});
Object.freeze(this.bucketExemplars);
this.observe = this.observeWithExemplar;
} else {
this.observe = this.observeWithoutExemplar;
}
Object.freeze(this.bucketValues);
Object.freeze(this.upperBounds);
if (this.labelNames.length === 0) {
this.hashMap = {
[hashObject({})]: createBaseValues(
{},
this.bucketValues,
this.bucketExemplars,
),
};
}
}
/**
* Observe a value in histogram
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {Number} value - Value to observe in the histogram
* @returns {void}
*/
observeWithoutExemplar(labels, value) {
observe.call(this, labels === 0 ? 0 : labels || {})(value);
}
observeWithExemplar({
labels = this.defaultLabels,
value,
exemplarLabels = this.defaultExemplarLabelSet,
} = {}) {
observe.call(this, labels === 0 ? 0 : labels || {})(value);
this.updateExemplar(labels, value, exemplarLabels);
}
updateExemplar(labels, value, exemplarLabels) {
if (Object.keys(exemplarLabels).length === 0) return;
const hash = hashObject(labels, this.sortedLabelNames);
const bound = findBound(this.upperBounds, value);
const { bucketExemplars } = this.hashMap[hash];
let exemplar = bucketExemplars[bound];
if (!isObject(exemplar)) {
exemplar = new Exemplar();
bucketExemplars[bound] = exemplar;
}
exemplar.validateExemplarLabelSet(exemplarLabels);
exemplar.labelSet = exemplarLabels;
exemplar.value = value;
exemplar.timestamp = nowTimestamp();
}
async get() {
const data = await this.getForPromString();
data.values = data.values.map(splayLabels);
return data;
}
async getForPromString() {
if (this.collect) {
const v = this.collect();
if (v instanceof Promise) await v;
}
const data = Object.values(this.hashMap);
const values = data
.map(extractBucketValuesForExport(this))
.reduce(addSumAndCountForExport(this), []);
return {
name: this.name,
help: this.help,
type: this.type,
values,
aggregator: this.aggregator,
};
}
reset() {
this.hashMap = {};
}
/**
* Initialize the metrics for the given combination of labels to zero
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @returns {void}
*/
zero(labels) {
const hash = hashObject(labels, this.sortedLabelNames);
this.hashMap[hash] = createBaseValues(
labels,
this.bucketValues,
this.bucketExemplars,
);
}
/**
* Start a timer that could be used to logging durations
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {object} exemplarLabels - Object with labels for exemplar where key is the label key and value is label value. Can only be one level deep
* @returns {function} - Function to invoke when you want to stop the timer and observe the duration in seconds
* @example
* var end = histogram.startTimer();
* makeExpensiveXHRRequest(function(err, res) {
* const duration = end(); //Observe the duration of expensiveXHRRequest and returns duration in seconds
* console.log('Duration', duration);
* });
*/
startTimer(labels, exemplarLabels) {
return this.enableExemplars
? startTimerWithExemplar.call(this, labels, exemplarLabels)()
: startTimer.call(this, labels)();
}
labels(...args) {
const labels = getLabels(this.labelNames, args);
validateLabel(this.labelNames, labels);
return {
observe: observe.call(this, labels),
startTimer: startTimer.call(this, labels),
};
}
remove(...args) {
const labels = getLabels(this.labelNames, args);
validateLabel(this.labelNames, labels);
removeLabels.call(this, this.hashMap, labels, this.sortedLabelNames);
}
}
function startTimer(startLabels) {
return () => {
const start = process.hrtime();
return endLabels => {
const delta = process.hrtime(start);
const value = delta[0] + delta[1] / 1e9;
this.observe(Object.assign({}, startLabels, endLabels), value);
return value;
};
};
}
function startTimerWithExemplar(startLabels, startExemplarLabels) {
return () => {
const start = process.hrtime();
return (endLabels, endExemplarLabels) => {
const delta = process.hrtime(start);
const value = delta[0] + delta[1] / 1e9;
this.observe({
labels: Object.assign({}, startLabels, endLabels),
value,
exemplarLabels: Object.assign(
{},
startExemplarLabels,
endExemplarLabels,
),
});
return value;
};
};
}
function setValuePair(labels, value, metricName, exemplar, sharedLabels = {}) {
return {
labels,
sharedLabels,
value,
metricName,
exemplar,
};
}
function findBound(upperBounds, value) {
for (let i = 0; i < upperBounds.length; i++) {
const bound = upperBounds[i];
if (value <= bound) {
return bound;
}
}
return -1;
}
function observe(labels) {
return value => {
const labelValuePair = convertLabelsAndValues(labels, value);
validateLabel(this.labelNames, labelValuePair.labels);
if (!Number.isFinite(labelValuePair.value)) {
throw new TypeError(
`Value is not a valid number: ${util.format(labelValuePair.value)}`,
);
}
const hash = hashObject(labelValuePair.labels, this.sortedLabelNames);
let valueFromMap = this.hashMap[hash];
if (!valueFromMap) {
valueFromMap = createBaseValues(
labelValuePair.labels,
this.bucketValues,
this.bucketExemplars,
);
}
const b = findBound(this.upperBounds, labelValuePair.value);
valueFromMap.sum += labelValuePair.value;
valueFromMap.count += 1;
if (Object.prototype.hasOwnProperty.call(valueFromMap.bucketValues, b)) {
valueFromMap.bucketValues[b] += 1;
}
this.hashMap[hash] = valueFromMap;
};
}
function createBaseValues(labels, bucketValues, bucketExemplars) {
const result = {
labels,
bucketValues: { ...bucketValues },
sum: 0,
count: 0,
};
if (bucketExemplars) {
result.bucketExemplars = { ...bucketExemplars };
}
return result;
}
function convertLabelsAndValues(labels, value) {
return isObject(labels)
? {
labels,
value,
}
: {
value: labels,
labels: {},
};
}
function extractBucketValuesForExport(histogram) {
const name = `${histogram.name}_bucket`;
return bucketData => {
let acc = 0;
const buckets = histogram.upperBounds.map(upperBound => {
acc += bucketData.bucketValues[upperBound];
return setValuePair(
{ le: upperBound },
acc,
name,
bucketData.bucketExemplars
? bucketData.bucketExemplars[upperBound]
: null,
bucketData.labels,
);
});
return { buckets, data: bucketData };
};
}
function addSumAndCountForExport(histogram) {
return (acc, d) => {
acc.push(...d.buckets);
const infLabel = { le: '+Inf' };
acc.push(
setValuePair(
infLabel,
d.data.count,
`${histogram.name}_bucket`,
d.data.bucketExemplars ? d.data.bucketExemplars['-1'] : null,
d.data.labels,
),
setValuePair(
{},
d.data.sum,
`${histogram.name}_sum`,
undefined,
d.data.labels,
),
setValuePair(
{},
d.data.count,
`${histogram.name}_count`,
undefined,
d.data.labels,
),
);
return acc;
};
}
function splayLabels(bucket) {
const { sharedLabels, labels, ...newBucket } = bucket;
for (const label of Object.keys(sharedLabels)) {
labels[label] = sharedLabels[label];
}
newBucket.labels = labels;
return newBucket;
}
module.exports = Histogram;

73
node_modules/prom-client/lib/metric.js generated vendored Normal file
View File

@@ -0,0 +1,73 @@
'use strict';
const Registry = require('./registry');
const { isObject } = require('./util');
const { validateMetricName, validateLabelName } = require('./validation');
/**
* @abstract
*/
class Metric {
constructor(config, defaults = {}) {
if (!isObject(config)) {
throw new TypeError('constructor expected a config object');
}
Object.assign(
this,
{
labelNames: [],
registers: [Registry.globalRegistry],
aggregator: 'sum',
enableExemplars: false,
},
defaults,
config,
);
if (!this.registers) {
// in case config.registers is `undefined`
this.registers = [Registry.globalRegistry];
}
if (!this.help) {
throw new Error('Missing mandatory help parameter');
}
if (!this.name) {
throw new Error('Missing mandatory name parameter');
}
if (!validateMetricName(this.name)) {
throw new Error('Invalid metric name');
}
if (!validateLabelName(this.labelNames)) {
throw new Error('Invalid label name');
}
if (this.collect && typeof this.collect !== 'function') {
throw new Error('Optional "collect" parameter must be a function');
}
if (this.labelNames) {
this.sortedLabelNames = [...this.labelNames].sort();
} else {
this.sortedLabelNames = [];
}
this.reset();
for (const register of this.registers) {
if (
this.enableExemplars &&
register.contentType === Registry.PROMETHEUS_CONTENT_TYPE
) {
throw new TypeError(
'Exemplars are supported only on OpenMetrics registries',
);
}
register.registerMetric(this);
}
}
reset() {
/* abstract */
}
}
module.exports = { Metric };

81
node_modules/prom-client/lib/metricAggregators.js generated vendored Normal file
View File

@@ -0,0 +1,81 @@
'use strict';
const { Grouper, hashObject } = require('./util');
/**
* Returns a new function that applies the `aggregatorFn` to the values.
* @param {Function} aggregatorFn function to apply to values.
* @return {Function} aggregator function
*/
function AggregatorFactory(aggregatorFn) {
return metrics => {
if (metrics.length === 0) return;
const result = {
help: metrics[0].help,
name: metrics[0].name,
type: metrics[0].type,
values: [],
aggregator: metrics[0].aggregator,
};
// Gather metrics by metricName and labels.
const byLabels = new Grouper();
metrics.forEach(metric => {
metric.values.forEach(value => {
const key = hashObject(value.labels);
byLabels.add(`${value.metricName}_${key}`, value);
});
});
// Apply aggregator function to gathered metrics.
byLabels.forEach(values => {
if (values.length === 0) return;
const valObj = {
value: aggregatorFn(values),
labels: values[0].labels,
};
if (values[0].metricName) {
valObj.metricName = values[0].metricName;
}
// NB: Timestamps are omitted.
result.values.push(valObj);
});
return result;
};
}
// Export for users to define their own aggregation methods.
exports.AggregatorFactory = AggregatorFactory;
/**
* Functions that can be used to aggregate metrics from multiple registries.
*/
exports.aggregators = {
/**
* @return The sum of values.
*/
sum: AggregatorFactory(v => v.reduce((p, c) => p + c.value, 0)),
/**
* @return The first value.
*/
first: AggregatorFactory(v => v[0].value),
/**
* @return {undefined} Undefined; omits the metric.
*/
omit: () => {},
/**
* @return The arithmetic mean of the values.
*/
average: AggregatorFactory(
v => v.reduce((p, c) => p + c.value, 0) / v.length,
),
/**
* @return The minimum of the values.
*/
min: AggregatorFactory(v =>
v.reduce((p, c) => Math.min(p, c.value), Infinity),
),
/**
* @return The maximum of the values.
*/
max: AggregatorFactory(v =>
v.reduce((p, c) => Math.max(p, c.value), -Infinity),
),
};

143
node_modules/prom-client/lib/metrics/eventLoopLag.js generated vendored Normal file
View File

@@ -0,0 +1,143 @@
'use strict';
const Gauge = require('../gauge');
// Check if perf_hooks module is available
let perf_hooks;
try {
perf_hooks = require('perf_hooks');
} catch {
// node version is too old
}
// Reported always.
const NODEJS_EVENTLOOP_LAG = 'nodejs_eventloop_lag_seconds';
// Reported only when perf_hooks is available.
const NODEJS_EVENTLOOP_LAG_MIN = 'nodejs_eventloop_lag_min_seconds';
const NODEJS_EVENTLOOP_LAG_MAX = 'nodejs_eventloop_lag_max_seconds';
const NODEJS_EVENTLOOP_LAG_MEAN = 'nodejs_eventloop_lag_mean_seconds';
const NODEJS_EVENTLOOP_LAG_STDDEV = 'nodejs_eventloop_lag_stddev_seconds';
const NODEJS_EVENTLOOP_LAG_P50 = 'nodejs_eventloop_lag_p50_seconds';
const NODEJS_EVENTLOOP_LAG_P90 = 'nodejs_eventloop_lag_p90_seconds';
const NODEJS_EVENTLOOP_LAG_P99 = 'nodejs_eventloop_lag_p99_seconds';
function reportEventloopLag(start, gauge, labels) {
const delta = process.hrtime(start);
const nanosec = delta[0] * 1e9 + delta[1];
const seconds = nanosec / 1e9;
gauge.set(labels, seconds);
}
module.exports = (registry, config = {}) => {
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
const registers = registry ? [registry] : undefined;
let collect = () => {
const start = process.hrtime();
setImmediate(reportEventloopLag, start, lag, labels);
};
if (perf_hooks && perf_hooks.monitorEventLoopDelay) {
try {
const histogram = perf_hooks.monitorEventLoopDelay({
resolution: config.eventLoopMonitoringPrecision,
});
histogram.enable();
collect = () => {
const start = process.hrtime();
setImmediate(reportEventloopLag, start, lag, labels);
lagMin.set(labels, histogram.min / 1e9);
lagMax.set(labels, histogram.max / 1e9);
lagMean.set(labels, histogram.mean / 1e9);
lagStddev.set(labels, histogram.stddev / 1e9);
lagP50.set(labels, histogram.percentile(50) / 1e9);
lagP90.set(labels, histogram.percentile(90) / 1e9);
lagP99.set(labels, histogram.percentile(99) / 1e9);
histogram.reset();
};
} catch (e) {
if (e.code === 'ERR_NOT_IMPLEMENTED') {
return; // Bun
}
throw e;
}
}
const lag = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG,
help: 'Lag of event loop in seconds.',
registers,
labelNames,
aggregator: 'average',
// Use this one metric's `collect` to set all metrics' values.
collect,
});
const lagMin = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_MIN,
help: 'The minimum recorded event loop delay.',
registers,
labelNames,
aggregator: 'min',
});
const lagMax = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_MAX,
help: 'The maximum recorded event loop delay.',
registers,
labelNames,
aggregator: 'max',
});
const lagMean = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_MEAN,
help: 'The mean of the recorded event loop delays.',
registers,
labelNames,
aggregator: 'average',
});
const lagStddev = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_STDDEV,
help: 'The standard deviation of the recorded event loop delays.',
registers,
labelNames,
aggregator: 'average',
});
const lagP50 = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_P50,
help: 'The 50th percentile of the recorded event loop delays.',
registers,
labelNames,
aggregator: 'average',
});
const lagP90 = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_P90,
help: 'The 90th percentile of the recorded event loop delays.',
registers,
labelNames,
aggregator: 'average',
});
const lagP99 = new Gauge({
name: namePrefix + NODEJS_EVENTLOOP_LAG_P99,
help: 'The 99th percentile of the recorded event loop delays.',
registers,
labelNames,
aggregator: 'average',
});
};
module.exports.metricNames = [
NODEJS_EVENTLOOP_LAG,
NODEJS_EVENTLOOP_LAG_MIN,
NODEJS_EVENTLOOP_LAG_MAX,
NODEJS_EVENTLOOP_LAG_MEAN,
NODEJS_EVENTLOOP_LAG_STDDEV,
NODEJS_EVENTLOOP_LAG_P50,
NODEJS_EVENTLOOP_LAG_P90,
NODEJS_EVENTLOOP_LAG_P99,
];

58
node_modules/prom-client/lib/metrics/gc.js generated vendored Normal file
View File

@@ -0,0 +1,58 @@
'use strict';
const Histogram = require('../histogram');
let perf_hooks;
try {
// eslint-disable-next-line
perf_hooks = require('perf_hooks');
} catch {
// node version is too old
}
const NODEJS_GC_DURATION_SECONDS = 'nodejs_gc_duration_seconds';
const DEFAULT_GC_DURATION_BUCKETS = [0.001, 0.01, 0.1, 1, 2, 5];
const kinds = [];
if (perf_hooks && perf_hooks.constants) {
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_MAJOR] = 'major';
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_MINOR] = 'minor';
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_INCREMENTAL] = 'incremental';
kinds[perf_hooks.constants.NODE_PERFORMANCE_GC_WEAKCB] = 'weakcb';
}
module.exports = (registry, config = {}) => {
if (!perf_hooks) {
return;
}
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
const buckets = config.gcDurationBuckets
? config.gcDurationBuckets
: DEFAULT_GC_DURATION_BUCKETS;
const gcHistogram = new Histogram({
name: namePrefix + NODEJS_GC_DURATION_SECONDS,
help: 'Garbage collection duration by kind, one of major, minor, incremental or weakcb.',
labelNames: ['kind', ...labelNames],
enableExemplars: false,
buckets,
registers: registry ? [registry] : undefined,
});
const obs = new perf_hooks.PerformanceObserver(list => {
const entry = list.getEntries()[0];
// Node < 16 uses entry.kind
// Node >= 16 uses entry.detail.kind
// See: https://nodejs.org/docs/latest-v16.x/api/deprecations.html#deprecations_dep0152_extension_performanceentry_properties
const kind = entry.detail ? kinds[entry.detail.kind] : kinds[entry.kind];
// Convert duration from milliseconds to seconds
gcHistogram.observe(Object.assign({ kind }, labels), entry.duration / 1000);
});
obs.observe({ entryTypes: ['gc'] });
};
module.exports.metricNames = [NODEJS_GC_DURATION_SECONDS];

View File

@@ -0,0 +1,56 @@
'use strict';
const Gauge = require('../gauge');
const safeMemoryUsage = require('./helpers/safeMemoryUsage');
const NODEJS_HEAP_SIZE_TOTAL = 'nodejs_heap_size_total_bytes';
const NODEJS_HEAP_SIZE_USED = 'nodejs_heap_size_used_bytes';
const NODEJS_EXTERNAL_MEMORY = 'nodejs_external_memory_bytes';
module.exports = (registry, config = {}) => {
if (typeof process.memoryUsage !== 'function') {
return;
}
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
const registers = registry ? [registry] : undefined;
const namePrefix = config.prefix ? config.prefix : '';
const collect = () => {
const memUsage = safeMemoryUsage();
if (memUsage) {
heapSizeTotal.set(labels, memUsage.heapTotal);
heapSizeUsed.set(labels, memUsage.heapUsed);
if (memUsage.external !== undefined) {
externalMemUsed.set(labels, memUsage.external);
}
}
};
const heapSizeTotal = new Gauge({
name: namePrefix + NODEJS_HEAP_SIZE_TOTAL,
help: 'Process heap size from Node.js in bytes.',
registers,
labelNames,
// Use this one metric's `collect` to set all metrics' values.
collect,
});
const heapSizeUsed = new Gauge({
name: namePrefix + NODEJS_HEAP_SIZE_USED,
help: 'Process heap size used from Node.js in bytes.',
registers,
labelNames,
});
const externalMemUsed = new Gauge({
name: namePrefix + NODEJS_EXTERNAL_MEMORY,
help: 'Node.js external memory size in bytes.',
registers,
labelNames,
});
};
module.exports.metricNames = [
NODEJS_HEAP_SIZE_TOTAL,
NODEJS_HEAP_SIZE_USED,
NODEJS_EXTERNAL_MEMORY,
];

View File

@@ -0,0 +1,57 @@
'use strict';
const Gauge = require('../gauge');
const v8 = require('v8');
const METRICS = ['total', 'used', 'available'];
const NODEJS_HEAP_SIZE = {};
METRICS.forEach(metricType => {
NODEJS_HEAP_SIZE[metricType] = `nodejs_heap_space_size_${metricType}_bytes`;
});
module.exports = (registry, config = {}) => {
try {
v8.getHeapSpaceStatistics();
} catch (e) {
if (e.code === 'ERR_NOT_IMPLEMENTED') {
return; // Bun
}
throw e;
}
const registers = registry ? [registry] : undefined;
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = ['space', ...Object.keys(labels)];
const gauges = {};
METRICS.forEach(metricType => {
gauges[metricType] = new Gauge({
name: namePrefix + NODEJS_HEAP_SIZE[metricType],
help: `Process heap space size ${metricType} from Node.js in bytes.`,
labelNames,
registers,
});
});
// Use this one metric's `collect` to set all metrics' values.
gauges.total.collect = () => {
for (const space of v8.getHeapSpaceStatistics()) {
const spaceName = space.space_name.substr(
0,
space.space_name.indexOf('_space'),
);
gauges.total.set({ space: spaceName, ...labels }, space.space_size);
gauges.used.set({ space: spaceName, ...labels }, space.space_used_size);
gauges.available.set(
{ space: spaceName, ...labels },
space.space_available_size,
);
}
};
};
module.exports.metricNames = Object.values(NODEJS_HEAP_SIZE);

View File

@@ -0,0 +1,32 @@
'use strict';
function aggregateByObjectName(list) {
const data = {};
for (let i = 0; i < list.length; i++) {
const listElement = list[i];
if (!listElement || typeof listElement.constructor === 'undefined') {
continue;
}
if (Object.hasOwnProperty.call(data, listElement.constructor.name)) {
data[listElement.constructor.name] += 1;
} else {
data[listElement.constructor.name] = 1;
}
}
return data;
}
function updateMetrics(gauge, data, labels) {
gauge.reset();
for (const key in data) {
gauge.set(Object.assign({ type: key }, labels || {}), data[key]);
}
}
module.exports = {
aggregateByObjectName,
updateMetrics,
};

View File

@@ -0,0 +1,12 @@
'use strict';
// process.memoryUsage() can throw on some platforms, see #67
function safeMemoryUsage() {
try {
return process.memoryUsage();
} catch {
return;
}
}
module.exports = safeMemoryUsage;

38
node_modules/prom-client/lib/metrics/osMemoryHeap.js generated vendored Normal file
View File

@@ -0,0 +1,38 @@
'use strict';
const Gauge = require('../gauge');
const linuxVariant = require('./osMemoryHeapLinux');
const safeMemoryUsage = require('./helpers/safeMemoryUsage');
const PROCESS_RESIDENT_MEMORY = 'process_resident_memory_bytes';
function notLinuxVariant(registry, config = {}) {
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + PROCESS_RESIDENT_MEMORY,
help: 'Resident memory size in bytes.',
registers: registry ? [registry] : undefined,
labelNames,
collect() {
const memUsage = safeMemoryUsage();
// I don't think the other things returned from `process.memoryUsage()` is relevant to a standard export
if (memUsage) {
this.set(labels, memUsage.rss);
}
},
});
}
module.exports = (registry, config) =>
process.platform === 'linux'
? linuxVariant(registry, config)
: notLinuxVariant(registry, config);
module.exports.metricNames =
process.platform === 'linux'
? linuxVariant.metricNames
: [PROCESS_RESIDENT_MEMORY];

View File

@@ -0,0 +1,82 @@
'use strict';
const Gauge = require('../gauge');
const fs = require('fs');
const values = ['VmSize', 'VmRSS', 'VmData'];
const PROCESS_RESIDENT_MEMORY = 'process_resident_memory_bytes';
const PROCESS_VIRTUAL_MEMORY = 'process_virtual_memory_bytes';
const PROCESS_HEAP = 'process_heap_bytes';
function structureOutput(input) {
return input.split('\n').reduce((acc, string) => {
if (!values.some(value => string.startsWith(value))) {
return acc;
}
const split = string.split(':');
// Get the value
let value = split[1].trim();
// Remove trailing ` kb`
value = value.substr(0, value.length - 3);
// Make it into a number in bytes bytes
value = Number(value) * 1024;
acc[split[0]] = value;
return acc;
}, {});
}
module.exports = (registry, config = {}) => {
const registers = registry ? [registry] : undefined;
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
const residentMemGauge = new Gauge({
name: namePrefix + PROCESS_RESIDENT_MEMORY,
help: 'Resident memory size in bytes.',
registers,
labelNames,
// Use this one metric's `collect` to set all metrics' values.
collect() {
try {
// Sync I/O is often problematic, but /proc isn't really I/O, it
// a virtual filesystem that maps directly to in-kernel data
// structures and never blocks.
//
// Node.js/libuv do this already for process.memoryUsage(), see:
// - https://github.com/libuv/libuv/blob/a629688008694ed8022269e66826d4d6ec688b83/src/unix/linux-core.c#L506-L523
const stat = fs.readFileSync('/proc/self/status', 'utf8');
const structuredOutput = structureOutput(stat);
residentMemGauge.set(labels, structuredOutput.VmRSS);
virtualMemGauge.set(labels, structuredOutput.VmSize);
heapSizeMemGauge.set(labels, structuredOutput.VmData);
} catch {
// noop
}
},
});
const virtualMemGauge = new Gauge({
name: namePrefix + PROCESS_VIRTUAL_MEMORY,
help: 'Virtual memory size in bytes.',
registers,
labelNames,
});
const heapSizeMemGauge = new Gauge({
name: namePrefix + PROCESS_HEAP,
help: 'Process heap size in bytes.',
registers,
labelNames,
});
};
module.exports.metricNames = [
PROCESS_RESIDENT_MEMORY,
PROCESS_VIRTUAL_MEMORY,
PROCESS_HEAP,
];

View File

@@ -0,0 +1,88 @@
'use strict';
const OtelApi = require('@opentelemetry/api');
const Counter = require('../counter');
const PROCESS_CPU_USER_SECONDS = 'process_cpu_user_seconds_total';
const PROCESS_CPU_SYSTEM_SECONDS = 'process_cpu_system_seconds_total';
const PROCESS_CPU_SECONDS = 'process_cpu_seconds_total';
module.exports = (registry, config = {}) => {
const registers = registry ? [registry] : undefined;
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const exemplars = config.enableExemplars ? config.enableExemplars : false;
const labelNames = Object.keys(labels);
let lastCpuUsage = process.cpuUsage();
const cpuUserUsageCounter = new Counter({
name: namePrefix + PROCESS_CPU_USER_SECONDS,
help: 'Total user CPU time spent in seconds.',
enableExemplars: exemplars,
registers,
labelNames,
// Use this one metric's `collect` to set all metrics' values.
collect() {
const cpuUsage = process.cpuUsage();
const userUsageMicros = cpuUsage.user - lastCpuUsage.user;
const systemUsageMicros = cpuUsage.system - lastCpuUsage.system;
lastCpuUsage = cpuUsage;
if (this.enableExemplars) {
let exemplarLabels = {};
const currentSpan = OtelApi.trace.getSpan(OtelApi.context.active());
if (currentSpan) {
exemplarLabels = {
traceId: currentSpan.spanContext().traceId,
spanId: currentSpan.spanContext().spanId,
};
}
cpuUserUsageCounter.inc({
labels,
value: userUsageMicros / 1e6,
exemplarLabels,
});
cpuSystemUsageCounter.inc({
labels,
value: systemUsageMicros / 1e6,
exemplarLabels,
});
cpuUsageCounter.inc({
labels,
value: (userUsageMicros + systemUsageMicros) / 1e6,
exemplarLabels,
});
} else {
cpuUserUsageCounter.inc(labels, userUsageMicros / 1e6);
cpuSystemUsageCounter.inc(labels, systemUsageMicros / 1e6);
cpuUsageCounter.inc(
labels,
(userUsageMicros + systemUsageMicros) / 1e6,
);
}
},
});
const cpuSystemUsageCounter = new Counter({
name: namePrefix + PROCESS_CPU_SYSTEM_SECONDS,
help: 'Total system CPU time spent in seconds.',
enableExemplars: exemplars,
registers,
labelNames,
});
const cpuUsageCounter = new Counter({
name: namePrefix + PROCESS_CPU_SECONDS,
help: 'Total user and system CPU time spent in seconds.',
enableExemplars: exemplars,
registers,
labelNames,
});
};
module.exports.metricNames = [
PROCESS_CPU_USER_SECONDS,
PROCESS_CPU_SYSTEM_SECONDS,
PROCESS_CPU_SECONDS,
];

46
node_modules/prom-client/lib/metrics/processHandles.js generated vendored Normal file
View File

@@ -0,0 +1,46 @@
'use strict';
const { aggregateByObjectName } = require('./helpers/processMetricsHelpers');
const { updateMetrics } = require('./helpers/processMetricsHelpers');
const Gauge = require('../gauge');
const NODEJS_ACTIVE_HANDLES = 'nodejs_active_handles';
const NODEJS_ACTIVE_HANDLES_TOTAL = 'nodejs_active_handles_total';
module.exports = (registry, config = {}) => {
// Don't do anything if the function is removed in later nodes (exists in node@6-12...)
if (typeof process._getActiveHandles !== 'function') {
return;
}
const registers = registry ? [registry] : undefined;
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + NODEJS_ACTIVE_HANDLES,
help: 'Number of active libuv handles grouped by handle type. Every handle type is C++ class name.',
labelNames: ['type', ...labelNames],
registers,
collect() {
const handles = process._getActiveHandles();
updateMetrics(this, aggregateByObjectName(handles), labels);
},
});
new Gauge({
name: namePrefix + NODEJS_ACTIVE_HANDLES_TOTAL,
help: 'Total number of active handles.',
registers,
labelNames,
collect() {
const handles = process._getActiveHandles();
this.set(labels, handles.length);
},
});
};
module.exports.metricNames = [
NODEJS_ACTIVE_HANDLES,
NODEJS_ACTIVE_HANDLES_TOTAL,
];

View File

@@ -0,0 +1,45 @@
'use strict';
const Gauge = require('../gauge');
const fs = require('fs');
const PROCESS_MAX_FDS = 'process_max_fds';
let maxFds;
module.exports = (registry, config = {}) => {
if (maxFds === undefined) {
// This will fail if a linux-like procfs is not available.
try {
const limits = fs.readFileSync('/proc/self/limits', 'utf8');
const lines = limits.split('\n');
for (const line of lines) {
if (line.startsWith('Max open files')) {
const parts = line.split(/ +/);
maxFds = Number(parts[1]);
break;
}
}
} catch {
return;
}
}
if (maxFds === undefined) return;
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + PROCESS_MAX_FDS,
help: 'Maximum number of open file descriptors.',
registers: registry ? [registry] : undefined,
labelNames,
collect() {
if (maxFds !== undefined) this.set(labels, maxFds);
},
});
};
module.exports.metricNames = [PROCESS_MAX_FDS];

View File

@@ -0,0 +1,36 @@
'use strict';
const Gauge = require('../gauge');
const fs = require('fs');
const process = require('process');
const PROCESS_OPEN_FDS = 'process_open_fds';
module.exports = (registry, config = {}) => {
if (process.platform !== 'linux') {
return;
}
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + PROCESS_OPEN_FDS,
help: 'Number of open file descriptors.',
registers: registry ? [registry] : undefined,
labelNames,
collect() {
try {
const fds = fs.readdirSync('/proc/self/fd');
// Minus 1 to not count the fd that was used by readdirSync(),
// it's now closed.
this.set(labels, fds.length - 1);
} catch {
// noop
}
},
});
};
module.exports.metricNames = [PROCESS_OPEN_FDS];

View File

@@ -0,0 +1,45 @@
'use strict';
const Gauge = require('../gauge');
const { aggregateByObjectName } = require('./helpers/processMetricsHelpers');
const { updateMetrics } = require('./helpers/processMetricsHelpers');
const NODEJS_ACTIVE_REQUESTS = 'nodejs_active_requests';
const NODEJS_ACTIVE_REQUESTS_TOTAL = 'nodejs_active_requests_total';
module.exports = (registry, config = {}) => {
// Don't do anything if the function is removed in later nodes (exists in node@6)
if (typeof process._getActiveRequests !== 'function') {
return;
}
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + NODEJS_ACTIVE_REQUESTS,
help: 'Number of active libuv requests grouped by request type. Every request type is C++ class name.',
labelNames: ['type', ...labelNames],
registers: registry ? [registry] : undefined,
collect() {
const requests = process._getActiveRequests();
updateMetrics(this, aggregateByObjectName(requests), labels);
},
});
new Gauge({
name: namePrefix + NODEJS_ACTIVE_REQUESTS_TOTAL,
help: 'Total number of active requests.',
registers: registry ? [registry] : undefined,
labelNames,
collect() {
const requests = process._getActiveRequests();
this.set(labels, requests.length);
},
});
};
module.exports.metricNames = [
NODEJS_ACTIVE_REQUESTS,
NODEJS_ACTIVE_REQUESTS_TOTAL,
];

View File

@@ -0,0 +1,57 @@
'use strict';
const Gauge = require('../gauge');
const { updateMetrics } = require('./helpers/processMetricsHelpers');
const NODEJS_ACTIVE_RESOURCES = 'nodejs_active_resources';
const NODEJS_ACTIVE_RESOURCES_TOTAL = 'nodejs_active_resources_total';
module.exports = (registry, config = {}) => {
// Don't do anything if the function does not exist in previous nodes (exists in node@17.3.0)
if (typeof process.getActiveResourcesInfo !== 'function') {
return;
}
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + NODEJS_ACTIVE_RESOURCES,
help: 'Number of active resources that are currently keeping the event loop alive, grouped by async resource type.',
labelNames: ['type', ...labelNames],
registers: registry ? [registry] : undefined,
collect() {
const resources = process.getActiveResourcesInfo();
const data = {};
for (let i = 0; i < resources.length; i++) {
const resource = resources[i];
if (Object.hasOwn(data, resource)) {
data[resource] += 1;
} else {
data[resource] = 1;
}
}
updateMetrics(this, data, labels);
},
});
new Gauge({
name: namePrefix + NODEJS_ACTIVE_RESOURCES_TOTAL,
help: 'Total number of active resources.',
registers: registry ? [registry] : undefined,
labelNames,
collect() {
const resources = process.getActiveResourcesInfo();
this.set(labels, resources.length);
},
});
};
module.exports.metricNames = [
NODEJS_ACTIVE_RESOURCES,
NODEJS_ACTIVE_RESOURCES_TOTAL,
];

View File

@@ -0,0 +1,25 @@
'use strict';
const Gauge = require('../gauge');
const startInSeconds = Math.round(Date.now() / 1000 - process.uptime());
const PROCESS_START_TIME = 'process_start_time_seconds';
module.exports = (registry, config = {}) => {
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + PROCESS_START_TIME,
help: 'Start time of the process since unix epoch in seconds.',
registers: registry ? [registry] : undefined,
labelNames,
aggregator: 'omit',
collect() {
this.set(labels, startInSeconds);
},
});
};
module.exports.metricNames = [PROCESS_START_TIME];

33
node_modules/prom-client/lib/metrics/version.js generated vendored Normal file
View File

@@ -0,0 +1,33 @@
'use strict';
const Gauge = require('../gauge');
const version = process.version;
const versionSegments = version.slice(1).split('.').map(Number);
const NODE_VERSION_INFO = 'nodejs_version_info';
module.exports = (registry, config = {}) => {
const namePrefix = config.prefix ? config.prefix : '';
const labels = config.labels ? config.labels : {};
const labelNames = Object.keys(labels);
new Gauge({
name: namePrefix + NODE_VERSION_INFO,
help: 'Node.js version info.',
labelNames: ['version', 'major', 'minor', 'patch', ...labelNames],
registers: registry ? [registry] : undefined,
aggregator: 'first',
collect() {
// Needs to be in collect() so value is present even if reg is reset
this.labels(
version,
versionSegments[0],
versionSegments[1],
versionSegments[2],
...Object.values(labels),
).set(1);
},
});
};
module.exports.metricNames = [NODE_VERSION_INFO];

136
node_modules/prom-client/lib/pushgateway.js generated vendored Normal file
View File

@@ -0,0 +1,136 @@
'use strict';
const url = require('url');
const http = require('http');
const https = require('https');
const { gzipSync } = require('zlib');
const { globalRegistry } = require('./registry');
class Pushgateway {
constructor(gatewayUrl, options, registry) {
if (!registry) {
registry = globalRegistry;
}
this.registry = registry;
this.gatewayUrl = gatewayUrl;
const { requireJobName, ...requestOptions } = {
requireJobName: true,
...options,
};
this.requireJobName = requireJobName;
this.requestOptions = requestOptions;
}
pushAdd(params = {}) {
if (this.requireJobName && !params.jobName) {
throw new Error('Missing jobName parameter');
}
return useGateway.call(this, 'POST', params.jobName, params.groupings);
}
push(params = {}) {
if (this.requireJobName && !params.jobName) {
throw new Error('Missing jobName parameter');
}
return useGateway.call(this, 'PUT', params.jobName, params.groupings);
}
delete(params = {}) {
if (this.requireJobName && !params.jobName) {
throw new Error('Missing jobName parameter');
}
return useGateway.call(this, 'DELETE', params.jobName, params.groupings);
}
}
async function useGateway(method, job, groupings) {
// `URL` first added in v6.13.0
// eslint-disable-next-line n/no-deprecated-api
const gatewayUrlParsed = url.parse(this.gatewayUrl);
const gatewayUrlPath =
gatewayUrlParsed.pathname && gatewayUrlParsed.pathname !== '/'
? gatewayUrlParsed.pathname
: '';
const jobPath = job
? `/job/${encodeURIComponent(job)}${generateGroupings(groupings)}`
: '';
const path = `${gatewayUrlPath}/metrics${jobPath}`;
// eslint-disable-next-line n/no-deprecated-api
const target = url.resolve(this.gatewayUrl, path);
// eslint-disable-next-line n/no-deprecated-api
const requestParams = url.parse(target);
const httpModule = isHttps(requestParams.href) ? https : http;
const options = Object.assign(requestParams, this.requestOptions, {
method,
});
return new Promise((resolve, reject) => {
if (method === 'DELETE' && options.headers) {
delete options.headers['Content-Encoding'];
}
const req = httpModule.request(options, resp => {
let body = '';
resp.setEncoding('utf8');
resp.on('data', chunk => {
body += chunk;
});
resp.on('end', () => {
if (resp.statusCode >= 400) {
reject(
new Error(`push failed with status ${resp.statusCode}, ${body}`),
);
} else {
resolve({ resp, body });
}
});
});
req.on('error', err => {
reject(err);
});
req.on('timeout', () => {
req.destroy(new Error('Pushgateway request timed out'));
});
if (method !== 'DELETE') {
this.registry
.metrics()
.then(metrics => {
if (
options.headers &&
options.headers['Content-Encoding'] === 'gzip'
) {
metrics = gzipSync(metrics);
}
req.write(metrics);
req.end();
})
.catch(err => {
reject(err);
});
} else {
req.end();
}
});
}
function generateGroupings(groupings) {
if (!groupings) {
return '';
}
return Object.keys(groupings)
.map(
key =>
`/${encodeURIComponent(key)}/${encodeURIComponent(groupings[key])}`,
)
.join('');
}
function isHttps(href) {
return href.search(/^https/) !== -1;
}
module.exports = Pushgateway;

242
node_modules/prom-client/lib/registry.js generated vendored Normal file
View File

@@ -0,0 +1,242 @@
'use strict';
const { getValueAsString } = require('./util');
class Registry {
static get PROMETHEUS_CONTENT_TYPE() {
return 'text/plain; version=0.0.4; charset=utf-8';
}
static get OPENMETRICS_CONTENT_TYPE() {
return 'application/openmetrics-text; version=1.0.0; charset=utf-8';
}
constructor(regContentType = Registry.PROMETHEUS_CONTENT_TYPE) {
this._metrics = {};
this._collectors = [];
this._defaultLabels = {};
if (
regContentType !== Registry.PROMETHEUS_CONTENT_TYPE &&
regContentType !== Registry.OPENMETRICS_CONTENT_TYPE
) {
throw new TypeError(`Content type ${regContentType} is unsupported`);
}
this._contentType = regContentType;
}
getMetricsAsArray() {
return Object.values(this._metrics);
}
async getMetricsAsString(metrics) {
const metric =
typeof metrics.getForPromString === 'function'
? await metrics.getForPromString()
: await metrics.get();
const name = escapeString(metric.name);
const help = `# HELP ${name} ${escapeString(metric.help)}`;
const type = `# TYPE ${name} ${metric.type}`;
const values = [help, type];
const defaultLabels =
Object.keys(this._defaultLabels).length > 0 ? this._defaultLabels : null;
const isOpenMetrics =
this.contentType === Registry.OPENMETRICS_CONTENT_TYPE;
for (const val of metric.values || []) {
let { metricName = name, labels = {} } = val;
const { sharedLabels = {} } = val;
if (isOpenMetrics && metric.type === 'counter') {
metricName = `${metricName}_total`;
}
if (defaultLabels) {
labels = { ...labels, ...defaultLabels, ...labels };
}
// We have to flatten these separately to avoid duplicate labels appearing
// between the base labels and the shared labels
const formattedLabels = formatLabels(labels, sharedLabels);
const flattenedShared = flattenSharedLabels(sharedLabels);
const labelParts = [...formattedLabels, flattenedShared].filter(Boolean);
const labelsString = labelParts.length ? `{${labelParts.join(',')}}` : '';
let fullMetricLine = `${metricName}${labelsString} ${getValueAsString(
val.value,
)}`;
const { exemplar } = val;
if (exemplar && isOpenMetrics) {
const formattedExemplars = formatLabels(exemplar.labelSet);
fullMetricLine += ` # {${formattedExemplars.join(
',',
)}} ${getValueAsString(exemplar.value)} ${exemplar.timestamp}`;
}
values.push(fullMetricLine);
}
return values.join('\n');
}
async metrics() {
const isOpenMetrics =
this.contentType === Registry.OPENMETRICS_CONTENT_TYPE;
const promises = this.getMetricsAsArray().map(metric => {
if (isOpenMetrics && metric.type === 'counter') {
metric.name = standardizeCounterName(metric.name);
}
return this.getMetricsAsString(metric);
});
const resolves = await Promise.all(promises);
return isOpenMetrics
? `${resolves.join('\n')}\n# EOF\n`
: `${resolves.join('\n\n')}\n`;
}
registerMetric(metric) {
if (this._metrics[metric.name] && this._metrics[metric.name] !== metric) {
throw new Error(
`A metric with the name ${metric.name} has already been registered.`,
);
}
this._metrics[metric.name] = metric;
}
clear() {
this._metrics = {};
this._defaultLabels = {};
}
async getMetricsAsJSON() {
const metrics = [];
const defaultLabelNames = Object.keys(this._defaultLabels);
const promises = [];
for (const metric of this.getMetricsAsArray()) {
promises.push(metric.get());
}
const resolves = await Promise.all(promises);
for (const item of resolves) {
if (item.values && defaultLabelNames.length > 0) {
for (const val of item.values) {
// Make a copy before mutating
val.labels = Object.assign({}, val.labels);
for (const labelName of defaultLabelNames) {
val.labels[labelName] =
val.labels[labelName] || this._defaultLabels[labelName];
}
}
}
metrics.push(item);
}
return metrics;
}
removeSingleMetric(name) {
delete this._metrics[name];
}
getSingleMetricAsString(name) {
return this.getMetricsAsString(this._metrics[name]);
}
getSingleMetric(name) {
return this._metrics[name];
}
setDefaultLabels(labels) {
this._defaultLabels = labels;
}
resetMetrics() {
for (const metric in this._metrics) {
this._metrics[metric].reset();
}
}
get contentType() {
return this._contentType;
}
setContentType(metricsContentType) {
if (
metricsContentType === Registry.OPENMETRICS_CONTENT_TYPE ||
metricsContentType === Registry.PROMETHEUS_CONTENT_TYPE
) {
this._contentType = metricsContentType;
} else {
throw new Error(`Content type ${metricsContentType} is unsupported`);
}
}
static merge(registers) {
const regType = registers[0].contentType;
for (const reg of registers) {
if (reg.contentType !== regType) {
throw new Error(
'Registers can only be merged if they have the same content type',
);
}
}
const mergedRegistry = new Registry(regType);
const metricsToMerge = registers.reduce(
(acc, reg) => acc.concat(reg.getMetricsAsArray()),
[],
);
metricsToMerge.forEach(mergedRegistry.registerMetric, mergedRegistry);
return mergedRegistry;
}
}
function formatLabels(labels, exclude) {
const { hasOwnProperty } = Object.prototype;
const formatted = [];
for (const [name, value] of Object.entries(labels)) {
if (!exclude || !hasOwnProperty.call(exclude, name)) {
formatted.push(`${name}="${escapeLabelValue(value)}"`);
}
}
return formatted;
}
const sharedLabelCache = new WeakMap();
function flattenSharedLabels(labels) {
const cached = sharedLabelCache.get(labels);
if (cached) {
return cached;
}
const formattedLabels = formatLabels(labels);
const flattened = formattedLabels.join(',');
sharedLabelCache.set(labels, flattened);
return flattened;
}
function escapeLabelValue(str) {
if (typeof str !== 'string') {
return str;
}
return escapeString(str).replace(/"/g, '\\"');
}
function escapeString(str) {
return str.replace(/\\/g, '\\\\').replace(/\n/g, '\\n');
}
function standardizeCounterName(name) {
return name.replace(/_total$/, '');
}
module.exports = Registry;
module.exports.globalRegistry = new Registry();

208
node_modules/prom-client/lib/summary.js generated vendored Normal file
View File

@@ -0,0 +1,208 @@
/**
* Summary
*/
'use strict';
const util = require('util');
const { getLabels, hashObject, removeLabels } = require('./util');
const { validateLabel } = require('./validation');
const { Metric } = require('./metric');
const timeWindowQuantiles = require('./timeWindowQuantiles');
const DEFAULT_COMPRESS_COUNT = 1000; // every 1000 measurements
class Summary extends Metric {
constructor(config) {
super(config, {
percentiles: [0.01, 0.05, 0.5, 0.9, 0.95, 0.99, 0.999],
compressCount: DEFAULT_COMPRESS_COUNT,
hashMap: {},
});
this.type = 'summary';
for (const label of this.labelNames) {
if (label === 'quantile')
throw new Error('quantile is a reserved label keyword');
}
if (this.labelNames.length === 0) {
this.hashMap = {
[hashObject({})]: {
labels: {},
td: new timeWindowQuantiles(this.maxAgeSeconds, this.ageBuckets),
count: 0,
sum: 0,
},
};
}
}
/**
* Observe a value
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @param {Number} value - Value to observe
* @returns {void}
*/
observe(labels, value) {
observe.call(this, labels === 0 ? 0 : labels || {})(value);
}
async get() {
if (this.collect) {
const v = this.collect();
if (v instanceof Promise) await v;
}
const hashKeys = Object.keys(this.hashMap);
const values = [];
hashKeys.forEach(hashKey => {
const s = this.hashMap[hashKey];
if (s) {
if (this.pruneAgedBuckets && s.td.size() === 0) {
delete this.hashMap[hashKey];
} else {
extractSummariesForExport(s, this.percentiles).forEach(v => {
values.push(v);
});
values.push(getSumForExport(s, this));
values.push(getCountForExport(s, this));
}
}
});
return {
name: this.name,
help: this.help,
type: this.type,
values,
aggregator: this.aggregator,
};
}
reset() {
const data = Object.values(this.hashMap);
data.forEach(s => {
s.td.reset();
s.count = 0;
s.sum = 0;
});
}
/**
* Start a timer that could be used to logging durations
* @param {object} labels - Object with labels where key is the label key and value is label value. Can only be one level deep
* @returns {function} - Function to invoke when you want to stop the timer and observe the duration in seconds
* @example
* var end = summary.startTimer();
* makeExpensiveXHRRequest(function(err, res) {
* end(); //Observe the duration of expensiveXHRRequest
* });
*/
startTimer(labels) {
return startTimer.call(this, labels)();
}
labels(...args) {
const labels = getLabels(this.labelNames, args);
validateLabel(this.labelNames, labels);
return {
observe: observe.call(this, labels),
startTimer: startTimer.call(this, labels),
};
}
remove(...args) {
const labels = getLabels(this.labelNames, args);
validateLabel(this.labelNames, labels);
removeLabels.call(this, this.hashMap, labels, this.sortedLabelNames);
}
}
function extractSummariesForExport(summaryOfLabels, percentiles) {
summaryOfLabels.td.compress();
return percentiles.map(percentile => {
const percentileValue = summaryOfLabels.td.percentile(percentile);
return {
labels: Object.assign({ quantile: percentile }, summaryOfLabels.labels),
value: percentileValue ? percentileValue : 0,
};
});
}
function getCountForExport(value, summary) {
return {
metricName: `${summary.name}_count`,
labels: value.labels,
value: value.count,
};
}
function getSumForExport(value, summary) {
return {
metricName: `${summary.name}_sum`,
labels: value.labels,
value: value.sum,
};
}
function startTimer(startLabels) {
return () => {
const start = process.hrtime();
return endLabels => {
const delta = process.hrtime(start);
const value = delta[0] + delta[1] / 1e9;
this.observe(Object.assign({}, startLabels, endLabels), value);
return value;
};
};
}
function observe(labels) {
return value => {
const labelValuePair = convertLabelsAndValues(labels, value);
validateLabel(this.labelNames, labels);
if (!Number.isFinite(labelValuePair.value)) {
throw new TypeError(
`Value is not a valid number: ${util.format(labelValuePair.value)}`,
);
}
const hash = hashObject(labelValuePair.labels, this.sortedLabelNames);
let summaryOfLabel = this.hashMap[hash];
if (!summaryOfLabel) {
summaryOfLabel = {
labels: labelValuePair.labels,
td: new timeWindowQuantiles(this.maxAgeSeconds, this.ageBuckets),
count: 0,
sum: 0,
};
}
summaryOfLabel.td.push(labelValuePair.value);
summaryOfLabel.count++;
if (summaryOfLabel.count % this.compressCount === 0) {
summaryOfLabel.td.compress();
}
summaryOfLabel.sum += labelValuePair.value;
this.hashMap[hash] = summaryOfLabel;
};
}
function convertLabelsAndValues(labels, value) {
if (value === undefined) {
return {
value: labels,
labels: {},
};
}
return {
labels,
value,
};
}
module.exports = Summary;

67
node_modules/prom-client/lib/timeWindowQuantiles.js generated vendored Normal file
View File

@@ -0,0 +1,67 @@
'use strict';
const { TDigest } = require('tdigest');
class TimeWindowQuantiles {
constructor(maxAgeSeconds, ageBuckets) {
this.maxAgeSeconds = maxAgeSeconds || 0;
this.ageBuckets = ageBuckets || 0;
this.shouldRotate = maxAgeSeconds && ageBuckets;
this.ringBuffer = Array(ageBuckets).fill(new TDigest());
this.currentBuffer = 0;
this.lastRotateTimestampMillis = Date.now();
this.durationBetweenRotatesMillis =
(maxAgeSeconds * 1000) / ageBuckets || Infinity;
}
size() {
const bucket = rotate.call(this);
return bucket.size();
}
percentile(quantile) {
const bucket = rotate.call(this);
return bucket.percentile(quantile);
}
push(value) {
rotate.call(this);
this.ringBuffer.forEach(bucket => {
bucket.push(value);
});
}
reset() {
this.ringBuffer.forEach(bucket => {
bucket.reset();
});
}
compress() {
this.ringBuffer.forEach(bucket => {
bucket.compress();
});
}
}
function rotate() {
let timeSinceLastRotateMillis = Date.now() - this.lastRotateTimestampMillis;
while (
timeSinceLastRotateMillis > this.durationBetweenRotatesMillis &&
this.shouldRotate
) {
this.ringBuffer[this.currentBuffer] = new TDigest();
if (++this.currentBuffer >= this.ringBuffer.length) {
this.currentBuffer = 0;
}
timeSinceLastRotateMillis -= this.durationBetweenRotatesMillis;
this.lastRotateTimestampMillis += this.durationBetweenRotatesMillis;
}
return this.ringBuffer[this.currentBuffer];
}
module.exports = TimeWindowQuantiles;

130
node_modules/prom-client/lib/util.js generated vendored Normal file
View File

@@ -0,0 +1,130 @@
'use strict';
exports.getValueAsString = function getValueString(value) {
if (Number.isNaN(value)) {
return 'Nan';
} else if (!Number.isFinite(value)) {
if (value < 0) {
return '-Inf';
} else {
return '+Inf';
}
} else {
return `${value}`;
}
};
exports.removeLabels = function removeLabels(
hashMap,
labels,
sortedLabelNames,
) {
const hash = hashObject(labels, sortedLabelNames);
delete hashMap[hash];
};
exports.setValue = function setValue(hashMap, value, labels) {
const hash = hashObject(labels);
hashMap[hash] = {
value: typeof value === 'number' ? value : 0,
labels: labels || {},
};
return hashMap;
};
exports.setValueDelta = function setValueDelta(
hashMap,
deltaValue,
labels,
hash = '',
) {
const value = typeof deltaValue === 'number' ? deltaValue : 0;
if (hashMap[hash]) {
hashMap[hash].value += value;
} else {
hashMap[hash] = { value, labels };
}
return hashMap;
};
exports.getLabels = function (labelNames, args) {
if (typeof args[0] === 'object') {
return args[0];
}
if (labelNames.length !== args.length) {
throw new Error(
`Invalid number of arguments (${args.length}): "${args.join(
', ',
)}" for label names (${labelNames.length}): "${labelNames.join(', ')}".`,
);
}
const acc = {};
for (let i = 0; i < labelNames.length; i++) {
acc[labelNames[i]] = args[i];
}
return acc;
};
function fastHashObject(keys, labels) {
if (keys.length === 0) {
return '';
}
let hash = '';
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = labels[key];
if (value === undefined) continue;
hash += `${key}:${value},`;
}
return hash;
}
function hashObject(labels, labelNames) {
// We don't actually need a hash here. We just need a string that
// is unique for each possible labels object and consistent across
// calls with equivalent labels objects.
if (labelNames) {
return fastHashObject(labelNames, labels);
}
const keys = Object.keys(labels);
if (keys.length > 1) {
keys.sort(); // need consistency across calls
}
return fastHashObject(keys, labels);
}
exports.hashObject = hashObject;
exports.isObject = function isObject(obj) {
return obj !== null && typeof obj === 'object';
};
exports.nowTimestamp = function nowTimestamp() {
return Date.now() / 1000;
};
class Grouper extends Map {
/**
* Adds the `value` to the `key`'s array of values.
* @param {*} key Key to set.
* @param {*} value Value to add to `key`'s array.
* @returns {undefined} undefined.
*/
add(key, value) {
if (this.has(key)) {
this.get(key).push(value);
} else {
this.set(key, [value]);
}
}
}
exports.Grouper = Grouper;

27
node_modules/prom-client/lib/validation.js generated vendored Normal file
View File

@@ -0,0 +1,27 @@
'use strict';
const util = require('util');
// These are from https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels
const metricRegexp = /^[a-zA-Z_:][a-zA-Z0-9_:]*$/;
const labelRegexp = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
exports.validateMetricName = function (name) {
return metricRegexp.test(name);
};
exports.validateLabelName = function (names = []) {
return names.every(name => labelRegexp.test(name));
};
exports.validateLabel = function validateLabel(savedLabels, labels) {
for (const label in labels) {
if (!savedLabels.includes(label)) {
throw new Error(
`Added label "${label}" is not included in initial labelset: ${util.inspect(
savedLabels,
)}`,
);
}
}
};

87
node_modules/prom-client/package.json generated vendored Normal file
View File

@@ -0,0 +1,87 @@
{
"name": "prom-client",
"version": "15.1.3",
"description": "Client for prometheus",
"main": "index.js",
"files": [
"lib/",
"index.js",
"index.d.ts"
],
"engines": {
"node": "^16 || ^18 || >=20"
},
"scripts": {
"benchmarks": "node ./benchmarks/index.js",
"test": "npm run lint && npm run check-prettier && npm run compile-typescript && npm run test-unit",
"lint": "eslint .",
"test-unit": "jest",
"run-prettier": "prettier . .eslintrc",
"check-prettier": "npm run run-prettier -- --check",
"compile-typescript": "tsc --project .",
"prepare": "husky"
},
"repository": {
"type": "git",
"url": "git@github.com:siimon/prom-client.git"
},
"keywords": [
"Prometheus",
"Metrics",
"Client"
],
"author": "Simon Nyberg",
"license": "Apache-2.0",
"homepage": "https://github.com/siimon/prom-client",
"devDependencies": {
"@clevernature/benchmark-regression": "^1.0.0",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"eslint": "^8.32.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-n": "^16.0.0",
"eslint-plugin-prettier": "^5.0.1",
"express": "^4.13.3",
"husky": "^9.0.0",
"jest": "^29.3.1",
"lint-staged": "^13.1.0",
"nock": "^13.0.5",
"prettier": "3.3.2",
"typescript": "^5.0.4"
},
"dependencies": {
"@opentelemetry/api": "^1.4.0",
"tdigest": "^0.1.1"
},
"types": "./index.d.ts",
"jest": {
"testEnvironment": "node",
"testRegex": ".*Test\\.js$"
},
"lint-staged": {
"*.{js,ts}": "eslint --fix",
"*.{md,json,yml}": "prettier --write",
".travis.yml": "prettier --write"
},
"prettier": {
"singleQuote": true,
"useTabs": true,
"arrowParens": "avoid",
"trailingComma": "all",
"endOfLine": "auto",
"overrides": [
{
"files": "*.md",
"options": {
"useTabs": false
}
},
{
"files": ".eslintrc",
"options": {
"parser": "json"
}
}
]
}
}