Merge pull request #6927 from overleaf/jpa-split-clsi-metrics

[misc] split clsi metrics into multiple categories

GitOrigin-RevId: 964ab57b344b92383a2937beff50139bae273a0e
This commit is contained in:
Jakob Ackermann
2022-03-02 09:02:52 +00:00
committed by Copybot
parent 9aeb753199
commit 4822b131fb
13 changed files with 312 additions and 84 deletions
+16 -2
View File
@@ -131,18 +131,31 @@ function timing(key, timeSpan, sampleRate = 1, opts = {}) {
}
}
function histogram(key, value, buckets, opts = {}) {
key = buildPromKey('histogram_' + key)
promWrapper.metric('histogram', key, buckets).observe(opts, value)
if (process.env.DEBUG_METRICS) {
console.log('doing histogram', key, buckets, opts)
}
}
class Timer {
constructor(key, sampleRate = 1, opts = {}) {
constructor(key, sampleRate = 1, opts = {}, buckets) {
this.start = new Date()
key = buildPromKey(key)
this.key = key
this.sampleRate = sampleRate
this.opts = opts
this.buckets = buckets
}
done() {
const timeSpan = new Date() - this.start
timing(this.key, timeSpan, this.sampleRate, this.opts)
if (this.buckets) {
histogram(this.key, timeSpan, this.buckets, this.opts)
} else {
timing(this.key, timeSpan, this.sampleRate, this.opts)
}
return timeSpan
}
}
@@ -181,6 +194,7 @@ module.exports.inc = inc
module.exports.count = count
module.exports.summary = summary
module.exports.timing = timing
module.exports.histogram = histogram
module.exports.Timer = Timer
module.exports.gauge = gauge
module.exports.globalGauge = globalGauge
+18 -3
View File
@@ -29,6 +29,8 @@ const optsKey = function (opts) {
}
const extendOpts = function (opts, 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] = ''
@@ -49,15 +51,15 @@ const PromWrapper = {
ttlInMinutes: 0,
registry,
metric(type, name) {
return metrics.get(name) || new MetricWrapper(type, name)
metric(type, name, buckets) {
return metrics.get(name) || new MetricWrapper(type, name, buckets)
},
collectDefaultMetrics: prom.collectDefaultMetrics,
}
class MetricWrapper {
constructor(type, name) {
constructor(type, name, buckets) {
metrics.set(name, this)
this.name = name
this.instances = new Map()
@@ -70,6 +72,19 @@ class MetricWrapper {
help: name,
labelNames: ['status', 'method', 'path'],
})
case 'histogram':
return new prom.Histogram({
name,
help: name,
labelNames: [
'path',
'status_code',
'method',
'collection',
'query',
],
buckets,
})
case 'summary':
return new prom.Summary({
name,
@@ -1,3 +1,4 @@
const { promisify } = require('util')
const os = require('os')
const http = require('http')
const { expect } = require('chai')
@@ -5,6 +6,7 @@ const Metrics = require('../..')
const HOSTNAME = os.hostname()
const APP_NAME = 'test-app'
const sleep = promisify(setTimeout)
describe('Metrics module', function () {
before(function () {
@@ -67,6 +69,60 @@ describe('Metrics module', function () {
})
})
describe('histogram()', function () {
it('collects in buckets', async function () {
const buckets = [10, 100, 1000]
Metrics.histogram('distance', 10, buckets)
Metrics.histogram('distance', 20, buckets)
Metrics.histogram('distance', 100, buckets)
Metrics.histogram('distance', 200, buckets)
Metrics.histogram('distance', 1000, buckets)
Metrics.histogram('distance', 2000, buckets)
const sum = await getSummarySum('histogram_distance')
expect(sum).to.equal(3330)
await checkHistogramValues('histogram_distance', {
10: 1,
100: 3,
1000: 5,
'+Inf': 6,
})
})
})
describe('Timer', function () {
beforeEach('collect timings', async function () {
const buckets = [10, 100, 1000]
for (const duration of [1, 1, 1, 15, 15, 15, 105, 105, 105]) {
const withBuckets = new Metrics.Timer('height', 1, {}, buckets)
const withOutBuckets = new Metrics.Timer('depth', 1, {})
await sleep(duration)
withBuckets.done()
withOutBuckets.done()
}
})
it('with buckets', async function () {
await checkHistogramValues('histogram_height', {
10: 3,
100: 6,
1000: 9,
'+Inf': 9,
})
})
it('without buckets', async function () {
await checkSummaryValues('timer_depth', {
0.01: 1,
0.05: 1,
0.5: 15,
0.9: 105,
0.95: 105,
0.99: 105,
0.999: 105,
})
})
})
describe('gauge()', function () {
it('records values', async function () {
Metrics.gauge('water_level', 1.5)
@@ -225,6 +281,38 @@ async function getSummarySum(key) {
return null
}
async function checkHistogramValues(key, values) {
const metric = getMetric(key)
const item = await metric.get()
const found = {}
for (const value of item.values) {
const bucket = value.labels.le
if (!bucket) continue
found[bucket] = value.value
}
expect(found).to.deep.equal(values)
return null
}
async function checkSummaryValues(key, values) {
const metric = getMetric(key)
const item = await metric.get()
const found = {}
for (const value of item.values) {
const quantile = value.labels.quantile
if (!quantile) continue
found[quantile] = value.value
}
for (const quantile of Object.keys(values)) {
expect(found[quantile]).to.be.within(
values[quantile] - 5,
values[quantile] + 5,
`quantile: ${quantile}`
)
}
return null
}
async function getMetricValue(key) {
const metrics = await Metrics.register.getMetricsAsJSON()
const metric = metrics.find(m => m.name === key)