| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- const { promisify } = require('node:util')
- const os = require('node:os')
- const http = require('node:http')
- const { expect } = require('chai')
- const Metrics = require('../..')
- const HOSTNAME = os.hostname()
- const APP_NAME = 'test-app'
- const sleep = promisify(setTimeout)
- describe('Metrics module', function () {
- before(function () {
- process.env.METRICS_APP_NAME = 'test-app'
- require('../../initialize')
- })
- describe('at startup', function () {
- it('increments the process_startup counter', async function () {
- await expectMetricValue('process_startup', 1)
- })
- it('collects default metrics', async function () {
- const metric = await getMetric('process_cpu_user_seconds_total')
- expect(metric).to.exist
- })
- })
- describe('inc()', function () {
- it('increments counts by 1', async function () {
- Metrics.inc('duck_count')
- await expectMetricValue('duck_count', 1)
- Metrics.inc('duck_count')
- Metrics.inc('duck_count')
- await expectMetricValue('duck_count', 3)
- })
- it('escapes special characters in the key', async function () {
- Metrics.inc('show.me the $!!')
- await expectMetricValue('show_me_the____', 1)
- })
- })
- describe('count()', function () {
- it('increments counts by the given count', async function () {
- Metrics.count('rabbit_count', 5)
- await expectMetricValue('rabbit_count', 5)
- Metrics.count('rabbit_count', 6)
- Metrics.count('rabbit_count', 7)
- await expectMetricValue('rabbit_count', 18)
- })
- })
- describe('summary()', function () {
- it('collects observations', async function () {
- Metrics.summary('oven_temp', 200)
- Metrics.summary('oven_temp', 300)
- Metrics.summary('oven_temp', 450)
- const sum = await getSummarySum('oven_temp')
- expect(sum).to.equal(950)
- })
- })
- describe('timing()', function () {
- it('collects timings', async function () {
- Metrics.timing('sprint_100m', 10)
- Metrics.timing('sprint_100m', 20)
- Metrics.timing('sprint_100m', 30)
- const sum = await getSummarySum('timer_sprint_100m')
- expect(sum).to.equal(60)
- })
- })
- 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,
- { label_1: 'a' },
- buckets
- )
- const withOutBuckets = new Metrics.Timer('depth', 1, { label_2: 'b' })
- await sleep(duration)
- withBuckets.done()
- withOutBuckets.done({ label_3: 'c' })
- }
- })
- it('with buckets', async function () {
- await checkHistogramValues('histogram_height', {
- 10: 3,
- 100: 6,
- 1000: 9,
- '+Inf': 9,
- })
- const labelNames = await getMetric('histogram_height').labelNames
- expect(labelNames).to.deep.equal(['label_1'])
- })
- 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,
- })
- const labelNames = await getMetric('timer_depth').labelNames
- expect(labelNames).to.deep.equal(['label_2', 'label_3'])
- })
- })
- describe('gauge()', function () {
- it('records values', async function () {
- Metrics.gauge('water_level', 1.5)
- await expectMetricValue('water_level', 1.5)
- Metrics.gauge('water_level', 4.2)
- await expectMetricValue('water_level', 4.2)
- })
- })
- describe('globalGauge()', function () {
- it('records values without a host label', async function () {
- Metrics.globalGauge('tire_pressure', 99.99)
- const { value, labels } = await getMetricValue('tire_pressure')
- expect(value).to.equal(99.99)
- expect(labels.host).to.equal('global')
- expect(labels.app).to.equal(APP_NAME)
- })
- })
- describe('open_sockets', function () {
- const keyServer1 = 'open_connections_http_127_42_42_1'
- const keyServer2 = 'open_connections_http_127_42_42_2'
- let finish1, finish2, emitResponse1, emitResponse2
- function resetEmitResponse1() {
- emitResponse1 = new Promise(resolve => (finish1 = resolve))
- }
- resetEmitResponse1()
- function resetEmitResponse2() {
- emitResponse2 = new Promise(resolve => (finish2 = resolve))
- }
- resetEmitResponse2()
- let server1, server2
- before(function setupServer1(done) {
- server1 = http.createServer((req, res) => {
- res.write('...')
- emitResponse1.then(() => res.end())
- })
- server1.listen(0, '127.42.42.1', done)
- })
- before(function setupServer2(done) {
- server2 = http.createServer((req, res) => {
- res.write('...')
- emitResponse2.then(() => res.end())
- })
- server2.listen(0, '127.42.42.2', done)
- })
- after(function cleanupPendingRequests() {
- finish1()
- finish2()
- })
- after(function shutdownServer1(done) {
- if (server1) server1.close(done)
- })
- after(function shutdownServer2(done) {
- if (server2) server2.close(done)
- })
- let urlServer1, urlServer2
- before(function setUrls() {
- urlServer1 = `http://127.42.42.1:${server1.address().port}/`
- urlServer2 = `http://127.42.42.2:${server2.address().port}/`
- })
- describe('gaugeOpenSockets()', function () {
- beforeEach(function runGaugeOpenSockets() {
- Metrics.open_sockets.gaugeOpenSockets(true)
- })
- describe('without pending connections', function () {
- it('emits no open_connections', async function () {
- await expectNoMetricValue(keyServer1)
- await expectNoMetricValue(keyServer2)
- })
- })
- describe('with pending connections for server1', function () {
- before(function (done) {
- http.get(urlServer1)
- http.get(urlServer1)
- setTimeout(done, 10)
- })
- it('emits 2 open_connections for server1', async function () {
- await expectMetricValue(keyServer1, 2)
- })
- it('emits no open_connections for server2', async function () {
- await expectNoMetricValue(keyServer2)
- })
- })
- describe('with pending connections for server1 and server2', function () {
- before(function (done) {
- http.get(urlServer2)
- http.get(urlServer2)
- setTimeout(done, 10)
- })
- it('emits 2 open_connections for server1', async function () {
- await expectMetricValue(keyServer1, 2)
- })
- it('emits 2 open_connections for server2', async function () {
- await expectMetricValue(keyServer2, 2)
- })
- })
- describe('when requests finish for server1', function () {
- before(function (done) {
- finish1()
- resetEmitResponse1()
- http.get(urlServer1)
- setTimeout(done, 10)
- })
- it('emits 1 open_connections for server1', async function () {
- await expectMetricValue(keyServer1, 1)
- })
- it('emits 2 open_connections for server2', async function () {
- await expectMetricValue(keyServer2, 2)
- })
- })
- describe('when all requests complete', function () {
- before(function (done) {
- finish1()
- finish2()
- setTimeout(done, 10)
- })
- it('emits no open_connections', async function () {
- await expectNoMetricValue(keyServer1)
- await expectNoMetricValue(keyServer2)
- })
- })
- })
- })
- })
- function getMetric(key) {
- return Metrics.register.getSingleMetric(key)
- }
- async function getSummarySum(key) {
- const metric = getMetric(key)
- const item = await metric.get()
- for (const value of item.values) {
- if (value.metricName === `${key}_sum`) {
- return value.value
- }
- }
- 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)
- return metric.values[0]
- }
- async function expectMetricValue(key, expectedValue) {
- const value = await getMetricValue(key)
- expect(value.value).to.equal(expectedValue)
- expect(value.labels.host).to.equal(HOSTNAME)
- expect(value.labels.app).to.equal(APP_NAME)
- }
- async function expectNoMetricValue(key) {
- const metric = getMetric(key)
- if (!metric) return
- await expectMetricValue(key, 0)
- }
|