mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
Merge pull request #11679 from overleaf/em-upgrade-prom-client
Upgrade prom-client library in metrics GitOrigin-RevId: 4ff60d94b17e7f236d4b40103365ed6fe1515148
This commit is contained in:
parent
b0841592c7
commit
5683cebf71
4 changed files with 130 additions and 137 deletions
|
@ -24,11 +24,16 @@ function configure(opts = {}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let initialized = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the metrics module and start the default metrics collectors and
|
* Configure the metrics module and start the default metrics collectors and
|
||||||
* profiling agents.
|
* profiling agents.
|
||||||
*/
|
*/
|
||||||
function initialize(appName, opts = {}) {
|
function initialize(appName, opts = {}) {
|
||||||
|
if (initialized) {
|
||||||
|
return
|
||||||
|
}
|
||||||
appName = appName || DEFAULT_APP_NAME
|
appName = appName || DEFAULT_APP_NAME
|
||||||
if (tracing.tracingEnabled()) {
|
if (tracing.tracingEnabled()) {
|
||||||
tracing.initialize(appName)
|
tracing.initialize(appName)
|
||||||
|
@ -72,6 +77,7 @@ function initialize(appName, opts = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inc('process_startup')
|
inc('process_startup')
|
||||||
|
initialized = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerDestructor(func) {
|
function registerDestructor(func) {
|
||||||
|
@ -84,9 +90,16 @@ function injectMetricsRoute(app) {
|
||||||
ExpressCompression({
|
ExpressCompression({
|
||||||
level: parseInt(process.env.METRICS_COMPRESSION_LEVEL || '1', 10),
|
level: parseInt(process.env.METRICS_COMPRESSION_LEVEL || '1', 10),
|
||||||
}),
|
}),
|
||||||
function (req, res) {
|
function (req, res, next) {
|
||||||
res.set('Content-Type', promWrapper.registry.contentType)
|
res.set('Content-Type', promWrapper.registry.contentType)
|
||||||
res.end(promWrapper.registry.metrics())
|
promWrapper.registry
|
||||||
|
.metrics()
|
||||||
|
.then(metrics => {
|
||||||
|
res.end(metrics)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
next(err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -103,70 +116,70 @@ function set(key, value, sampleRate = 1) {
|
||||||
console.log('counts are not currently supported')
|
console.log('counts are not currently supported')
|
||||||
}
|
}
|
||||||
|
|
||||||
function inc(key, sampleRate = 1, opts = {}) {
|
function inc(key, sampleRate = 1, labels = {}) {
|
||||||
if (arguments.length === 2 && typeof sampleRate === 'object') {
|
if (arguments.length === 2 && typeof sampleRate === 'object') {
|
||||||
opts = sampleRate
|
labels = sampleRate
|
||||||
}
|
}
|
||||||
|
|
||||||
key = buildPromKey(key)
|
key = buildPromKey(key)
|
||||||
promWrapper.metric('counter', key).inc(opts)
|
promWrapper.metric('counter', key, labels).inc(labels)
|
||||||
if (process.env.DEBUG_METRICS) {
|
if (process.env.DEBUG_METRICS) {
|
||||||
console.log('doing inc', key, opts)
|
console.log('doing inc', key, labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function count(key, count, sampleRate = 1, opts = {}) {
|
function count(key, count, sampleRate = 1, labels = {}) {
|
||||||
if (arguments.length === 3 && typeof sampleRate === 'object') {
|
if (arguments.length === 3 && typeof sampleRate === 'object') {
|
||||||
opts = sampleRate
|
labels = sampleRate
|
||||||
}
|
}
|
||||||
|
|
||||||
key = buildPromKey(key)
|
key = buildPromKey(key)
|
||||||
promWrapper.metric('counter', key).inc(opts, count)
|
promWrapper.metric('counter', key, labels).inc(labels, count)
|
||||||
if (process.env.DEBUG_METRICS) {
|
if (process.env.DEBUG_METRICS) {
|
||||||
console.log('doing count/inc', key, opts)
|
console.log('doing count/inc', key, labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function summary(key, value, opts = {}) {
|
function summary(key, value, labels = {}) {
|
||||||
key = buildPromKey(key)
|
key = buildPromKey(key)
|
||||||
promWrapper.metric('summary', key).observe(opts, value)
|
promWrapper.metric('summary', key, labels).observe(labels, value)
|
||||||
if (process.env.DEBUG_METRICS) {
|
if (process.env.DEBUG_METRICS) {
|
||||||
console.log('doing summary', key, value, opts)
|
console.log('doing summary', key, value, labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function timing(key, timeSpan, sampleRate = 1, opts = {}) {
|
function timing(key, timeSpan, sampleRate = 1, labels = {}) {
|
||||||
if (arguments.length === 3 && typeof sampleRate === 'object') {
|
if (arguments.length === 3 && typeof sampleRate === 'object') {
|
||||||
opts = sampleRate
|
labels = sampleRate
|
||||||
}
|
}
|
||||||
|
|
||||||
key = buildPromKey('timer_' + key)
|
key = buildPromKey('timer_' + key)
|
||||||
promWrapper.metric('summary', key).observe(opts, timeSpan)
|
promWrapper.metric('summary', key, labels).observe(labels, timeSpan)
|
||||||
if (process.env.DEBUG_METRICS) {
|
if (process.env.DEBUG_METRICS) {
|
||||||
console.log('doing timing', key, opts)
|
console.log('doing timing', key, labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function histogram(key, value, buckets, opts = {}) {
|
function histogram(key, value, buckets, labels = {}) {
|
||||||
key = buildPromKey('histogram_' + key)
|
key = buildPromKey('histogram_' + key)
|
||||||
promWrapper.metric('histogram', key, buckets).observe(opts, value)
|
promWrapper.metric('histogram', key, labels, buckets).observe(labels, value)
|
||||||
if (process.env.DEBUG_METRICS) {
|
if (process.env.DEBUG_METRICS) {
|
||||||
console.log('doing histogram', key, buckets, opts)
|
console.log('doing histogram', key, buckets, labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Timer {
|
class Timer {
|
||||||
constructor(key, sampleRate = 1, opts = {}, buckets) {
|
constructor(key, sampleRate = 1, labels = {}, buckets) {
|
||||||
if (typeof sampleRate === 'object') {
|
if (typeof sampleRate === 'object') {
|
||||||
// called with (key, opts, buckets)
|
// called with (key, labels, buckets)
|
||||||
if (arguments.length === 3) {
|
if (arguments.length === 3) {
|
||||||
buckets = opts
|
buckets = labels
|
||||||
opts = sampleRate
|
labels = sampleRate
|
||||||
}
|
}
|
||||||
|
|
||||||
// called with (key, opts)
|
// called with (key, labels)
|
||||||
if (arguments.length === 2) {
|
if (arguments.length === 2) {
|
||||||
opts = sampleRate
|
labels = sampleRate
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleRate = 1 // default value to pass to timing function
|
sampleRate = 1 // default value to pass to timing function
|
||||||
|
@ -176,40 +189,37 @@ class Timer {
|
||||||
key = buildPromKey(key)
|
key = buildPromKey(key)
|
||||||
this.key = key
|
this.key = key
|
||||||
this.sampleRate = sampleRate
|
this.sampleRate = sampleRate
|
||||||
this.opts = opts
|
this.labels = labels
|
||||||
this.buckets = buckets
|
this.buckets = buckets
|
||||||
}
|
}
|
||||||
|
|
||||||
done() {
|
done() {
|
||||||
const timeSpan = new Date() - this.start
|
const timeSpan = new Date() - this.start
|
||||||
if (this.buckets) {
|
if (this.buckets) {
|
||||||
histogram(this.key, timeSpan, this.buckets, this.opts)
|
histogram(this.key, timeSpan, this.buckets, this.labels)
|
||||||
} else {
|
} else {
|
||||||
timing(this.key, timeSpan, this.sampleRate, this.opts)
|
timing(this.key, timeSpan, this.sampleRate, this.labels)
|
||||||
}
|
}
|
||||||
return timeSpan
|
return timeSpan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function gauge(key, value, sampleRate = 1, opts = {}) {
|
function gauge(key, value, sampleRate = 1, labels = {}) {
|
||||||
if (arguments.length === 3 && typeof sampleRate === 'object') {
|
if (arguments.length === 3 && typeof sampleRate === 'object') {
|
||||||
opts = sampleRate
|
labels = sampleRate
|
||||||
}
|
}
|
||||||
|
|
||||||
key = buildPromKey(key)
|
key = buildPromKey(key)
|
||||||
promWrapper
|
promWrapper.metric('gauge', key, labels).set(labels, sanitizeValue(value))
|
||||||
.metric('gauge', key)
|
|
||||||
.set({ status: opts.status }, sanitizeValue(value))
|
|
||||||
if (process.env.DEBUG_METRICS) {
|
if (process.env.DEBUG_METRICS) {
|
||||||
console.log('doing gauge', key, opts)
|
console.log('doing gauge', key, labels)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function globalGauge(key, value, sampleRate = 1, opts = {}) {
|
function globalGauge(key, value, sampleRate = 1, labels = {}) {
|
||||||
key = buildPromKey(key)
|
key = buildPromKey(key)
|
||||||
promWrapper
|
labels = { host: 'global', ...labels }
|
||||||
.metric('gauge', key)
|
promWrapper.metric('gauge', key, labels).set(labels, sanitizeValue(value))
|
||||||
.set({ host: 'global', status: opts.status }, sanitizeValue(value))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
"@opentelemetry/sdk-node": "^0.28.0",
|
"@opentelemetry/sdk-node": "^0.28.0",
|
||||||
"@opentelemetry/semantic-conventions": "^1.2.0",
|
"@opentelemetry/semantic-conventions": "^1.2.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"prom-client": "^11.1.3",
|
"prom-client": "^14.1.1",
|
||||||
"yn": "^3.1.1"
|
"yn": "^3.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -1,16 +1,10 @@
|
||||||
/*
|
const logger = require('@overleaf/logger')
|
||||||
* decaffeinate suggestions:
|
|
||||||
* DS101: Remove unnecessary use of Array.from
|
|
||||||
* DS102: Remove unnecessary code created because of implicit returns
|
|
||||||
* DS205: Consider reworking code to avoid use of IIFEs
|
|
||||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
|
||||||
*/
|
|
||||||
const prom = require('prom-client')
|
const prom = require('prom-client')
|
||||||
const registry = require('prom-client').register
|
const registry = require('prom-client').register
|
||||||
const metrics = new Map()
|
const metrics = new Map()
|
||||||
|
|
||||||
const optsKey = function (opts) {
|
const labelsKey = function (labels) {
|
||||||
let keys = Object.keys(opts)
|
let keys = Object.keys(labels)
|
||||||
if (keys.length === 0) {
|
if (keys.length === 0) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
@ -18,31 +12,20 @@ const optsKey = function (opts) {
|
||||||
keys = keys.sort()
|
keys = keys.sort()
|
||||||
|
|
||||||
let hash = ''
|
let hash = ''
|
||||||
for (const key of Array.from(keys)) {
|
for (const key of keys) {
|
||||||
if (hash.length) {
|
if (hash.length) {
|
||||||
hash += ','
|
hash += ','
|
||||||
}
|
}
|
||||||
hash += `${key}:${opts[key]}`
|
hash += `${key}:${labels[key]}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
const extendOpts = function (opts, labelNames) {
|
const labelsAsArgs = function (labels, labelNames) {
|
||||||
// Make a clone in order to be able to re-use opts for other kinds of metrics.
|
|
||||||
opts = Object.assign({}, opts)
|
|
||||||
for (const label of Array.from(labelNames)) {
|
|
||||||
if (!opts[label]) {
|
|
||||||
opts[label] = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return opts
|
|
||||||
}
|
|
||||||
|
|
||||||
const optsAsArgs = function (opts, labelNames) {
|
|
||||||
const args = []
|
const args = []
|
||||||
for (const label of Array.from(labelNames)) {
|
for (const label of labelNames) {
|
||||||
args.push(opts[label] || '')
|
args.push(labels[label] || '')
|
||||||
}
|
}
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
@ -51,74 +34,68 @@ const PromWrapper = {
|
||||||
ttlInMinutes: 0,
|
ttlInMinutes: 0,
|
||||||
registry,
|
registry,
|
||||||
|
|
||||||
metric(type, name, buckets) {
|
metric(type, name, labels, buckets) {
|
||||||
return metrics.get(name) || new MetricWrapper(type, name, buckets)
|
return metrics.get(name) || new MetricWrapper(type, name, labels, buckets)
|
||||||
},
|
},
|
||||||
|
|
||||||
collectDefaultMetrics: prom.collectDefaultMetrics,
|
collectDefaultMetrics: prom.collectDefaultMetrics,
|
||||||
}
|
}
|
||||||
|
|
||||||
class MetricWrapper {
|
class MetricWrapper {
|
||||||
constructor(type, name, buckets) {
|
constructor(type, name, labels, buckets) {
|
||||||
metrics.set(name, this)
|
metrics.set(name, this)
|
||||||
this.name = name
|
this.name = name
|
||||||
this.instances = new Map()
|
this.instances = new Map()
|
||||||
this.lastAccess = new Date()
|
this.lastAccess = new Date()
|
||||||
this.metric = (() => {
|
|
||||||
switch (type) {
|
const labelNames = labels ? Object.keys(labels) : []
|
||||||
case 'counter':
|
switch (type) {
|
||||||
return new prom.Counter({
|
case 'counter':
|
||||||
name,
|
this.metric = new prom.Counter({
|
||||||
help: name,
|
name,
|
||||||
labelNames: ['status', 'method', 'path'],
|
help: name,
|
||||||
})
|
labelNames,
|
||||||
case 'histogram':
|
})
|
||||||
return new prom.Histogram({
|
break
|
||||||
name,
|
case 'histogram':
|
||||||
help: name,
|
this.metric = new prom.Histogram({
|
||||||
labelNames: [
|
name,
|
||||||
'path',
|
help: name,
|
||||||
'status_code',
|
labelNames,
|
||||||
'method',
|
buckets,
|
||||||
'collection',
|
})
|
||||||
'query',
|
break
|
||||||
],
|
case 'summary':
|
||||||
buckets,
|
this.metric = new prom.Summary({
|
||||||
})
|
name,
|
||||||
case 'summary':
|
help: name,
|
||||||
return new prom.Summary({
|
maxAgeSeconds: 60,
|
||||||
name,
|
ageBuckets: 10,
|
||||||
help: name,
|
labelNames,
|
||||||
maxAgeSeconds: 60,
|
})
|
||||||
ageBuckets: 10,
|
break
|
||||||
labelNames: [
|
case 'gauge':
|
||||||
'path',
|
this.metric = new prom.Gauge({
|
||||||
'status_code',
|
name,
|
||||||
'method',
|
help: name,
|
||||||
'collection',
|
labelNames,
|
||||||
'query',
|
})
|
||||||
],
|
break
|
||||||
})
|
default:
|
||||||
case 'gauge':
|
throw new Error(`Unknown metric type: ${type}`)
|
||||||
return new prom.Gauge({
|
}
|
||||||
name,
|
|
||||||
help: name,
|
|
||||||
labelNames: ['host', 'status'],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inc(opts, value) {
|
inc(labels, value) {
|
||||||
return this._execMethod('inc', opts, value)
|
this._execMethod('inc', labels, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(opts, value) {
|
observe(labels, value) {
|
||||||
return this._execMethod('observe', opts, value)
|
this._execMethod('observe', labels, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
set(opts, value) {
|
set(labels, value) {
|
||||||
return this._execMethod('set', opts, value)
|
this._execMethod('set', labels, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
sweep() {
|
sweep() {
|
||||||
|
@ -130,12 +107,12 @@ class MetricWrapper {
|
||||||
console.log(
|
console.log(
|
||||||
'Sweeping stale metric instance',
|
'Sweeping stale metric instance',
|
||||||
this.name,
|
this.name,
|
||||||
{ opts: instance.opts },
|
{ labels: instance.labels },
|
||||||
key
|
key
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return this.metric.remove(
|
this.metric.remove(
|
||||||
...Array.from(optsAsArgs(instance.opts, this.metric.labelNames) || [])
|
...labelsAsArgs(instance.labels, this.metric.labelNames)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -146,18 +123,24 @@ class MetricWrapper {
|
||||||
console.log('Sweeping stale metric', this.name, thresh, this.lastAccess)
|
console.log('Sweeping stale metric', this.name, thresh, this.lastAccess)
|
||||||
}
|
}
|
||||||
metrics.delete(this.name)
|
metrics.delete(this.name)
|
||||||
return registry.removeSingleMetric(this.name)
|
registry.removeSingleMetric(this.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_execMethod(method, opts, value) {
|
_execMethod(method, labels, value) {
|
||||||
opts = extendOpts(opts, this.metric.labelNames)
|
const key = labelsKey(labels)
|
||||||
const key = optsKey(opts)
|
|
||||||
if (key !== '') {
|
if (key !== '') {
|
||||||
this.instances.set(key, { time: new Date(), opts })
|
this.instances.set(key, { time: new Date(), labels })
|
||||||
}
|
}
|
||||||
this.lastAccess = new Date()
|
this.lastAccess = new Date()
|
||||||
return this.metric[method](opts, value)
|
try {
|
||||||
|
this.metric[method](labels, value)
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(
|
||||||
|
{ err, metric: this.metric.name, labels },
|
||||||
|
'failed to record metric'
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,8 +165,8 @@ PromWrapper.setupSweeping = function () {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log('Sweeping metrics')
|
console.log('Sweeping metrics')
|
||||||
}
|
}
|
||||||
return metrics.forEach((metric, key) => {
|
metrics.forEach((metric, key) => {
|
||||||
return metric.sweep()
|
metric.sweep()
|
||||||
})
|
})
|
||||||
}, 60000)
|
}, 60000)
|
||||||
|
|
||||||
|
|
18
package-lock.json
generated
18
package-lock.json
generated
|
@ -136,7 +136,7 @@
|
||||||
"@opentelemetry/sdk-node": "^0.28.0",
|
"@opentelemetry/sdk-node": "^0.28.0",
|
||||||
"@opentelemetry/semantic-conventions": "^1.2.0",
|
"@opentelemetry/semantic-conventions": "^1.2.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"prom-client": "^11.1.3",
|
"prom-client": "^14.1.1",
|
||||||
"yn": "^3.1.1"
|
"yn": "^3.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -25779,14 +25779,14 @@
|
||||||
"integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q=="
|
"integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q=="
|
||||||
},
|
},
|
||||||
"node_modules/prom-client": {
|
"node_modules/prom-client": {
|
||||||
"version": "11.5.3",
|
"version": "14.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.1.1.tgz",
|
||||||
"integrity": "sha512-iz22FmTbtkyL2vt0MdDFY+kWof+S9UB/NACxSn2aJcewtw+EERsen0urSkZ2WrHseNdydsvcxCTAnPcSMZZv4Q==",
|
"integrity": "sha512-hFU32q7UZQ59bVJQGUtm3I2PrJ3gWvoCkilX9sF165ks1qflhugVCeK+S1JjJYHvyt3o5kj68+q3bchormjnzw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tdigest": "^0.1.1"
|
"tdigest": "^0.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.1"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/promise": {
|
"node_modules/promise": {
|
||||||
|
@ -43170,7 +43170,7 @@
|
||||||
"chai": "^4.3.6",
|
"chai": "^4.3.6",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"mocha": "^10.2.0",
|
"mocha": "^10.2.0",
|
||||||
"prom-client": "^11.1.3",
|
"prom-client": "^14.1.1",
|
||||||
"sandboxed-module": "^2.0.4",
|
"sandboxed-module": "^2.0.4",
|
||||||
"sinon": "^9.2.4",
|
"sinon": "^9.2.4",
|
||||||
"yn": "^3.1.1"
|
"yn": "^3.1.1"
|
||||||
|
@ -60770,9 +60770,9 @@
|
||||||
"integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q=="
|
"integrity": "sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q=="
|
||||||
},
|
},
|
||||||
"prom-client": {
|
"prom-client": {
|
||||||
"version": "11.5.3",
|
"version": "14.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-11.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.1.1.tgz",
|
||||||
"integrity": "sha512-iz22FmTbtkyL2vt0MdDFY+kWof+S9UB/NACxSn2aJcewtw+EERsen0urSkZ2WrHseNdydsvcxCTAnPcSMZZv4Q==",
|
"integrity": "sha512-hFU32q7UZQ59bVJQGUtm3I2PrJ3gWvoCkilX9sF165ks1qflhugVCeK+S1JjJYHvyt3o5kj68+q3bchormjnzw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"tdigest": "^0.1.1"
|
"tdigest": "^0.1.1"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue