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

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,
)}`,
);
}
}
};