| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- /*
- * 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 registry = require('prom-client').register
- const metrics = new Map()
- const optsKey = function (opts) {
- let keys = Object.keys(opts)
- if (keys.length === 0) {
- return ''
- }
- keys = keys.sort()
- let hash = ''
- for (const key of Array.from(keys)) {
- if (hash.length) {
- hash += ','
- }
- hash += `${key}:${opts[key]}`
- }
- return hash
- }
- 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] = ''
- }
- }
- return opts
- }
- const optsAsArgs = function (opts, labelNames) {
- const args = []
- for (const label of Array.from(labelNames)) {
- args.push(opts[label] || '')
- }
- return args
- }
- const PromWrapper = {
- ttlInMinutes: 0,
- registry,
- metric(type, name, buckets) {
- return metrics.get(name) || new MetricWrapper(type, name, buckets)
- },
- collectDefaultMetrics: prom.collectDefaultMetrics,
- }
- class MetricWrapper {
- constructor(type, name, buckets) {
- metrics.set(name, this)
- this.name = name
- this.instances = new Map()
- this.lastAccess = new Date()
- this.metric = (() => {
- switch (type) {
- case 'counter':
- return new prom.Counter({
- name,
- 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,
- help: name,
- maxAgeSeconds: 60,
- ageBuckets: 10,
- labelNames: [
- 'path',
- 'status_code',
- 'method',
- 'collection',
- 'query',
- ],
- })
- case 'gauge':
- return new prom.Gauge({
- name,
- help: name,
- labelNames: ['host', 'status'],
- })
- }
- })()
- }
- inc(opts, value) {
- return this._execMethod('inc', opts, value)
- }
- observe(opts, value) {
- return this._execMethod('observe', opts, value)
- }
- set(opts, value) {
- return this._execMethod('set', opts, value)
- }
- sweep() {
- const thresh = new Date(Date.now() - 1000 * 60 * PromWrapper.ttlInMinutes)
- this.instances.forEach((instance, key) => {
- if (thresh > instance.time) {
- if (process.env.DEBUG_METRICS) {
- // eslint-disable-next-line no-console
- console.log(
- 'Sweeping stale metric instance',
- this.name,
- { opts: instance.opts },
- key
- )
- }
- return this.metric.remove(
- ...Array.from(optsAsArgs(instance.opts, this.metric.labelNames) || [])
- )
- }
- })
- if (thresh > this.lastAccess) {
- if (process.env.DEBUG_METRICS) {
- // eslint-disable-next-line no-console
- console.log('Sweeping stale metric', this.name, thresh, this.lastAccess)
- }
- metrics.delete(this.name)
- return registry.removeSingleMetric(this.name)
- }
- }
- _execMethod(method, opts, value) {
- opts = extendOpts(opts, this.metric.labelNames)
- const key = optsKey(opts)
- if (key !== '') {
- this.instances.set(key, { time: new Date(), opts })
- }
- this.lastAccess = new Date()
- return this.metric[method](opts, value)
- }
- }
- let sweepingInterval
- PromWrapper.setupSweeping = function () {
- if (sweepingInterval) {
- clearInterval(sweepingInterval)
- }
- if (!PromWrapper.ttlInMinutes) {
- if (process.env.DEBUG_METRICS) {
- // eslint-disable-next-line no-console
- console.log('Not registering sweep method -- empty ttl')
- }
- return
- }
- if (process.env.DEBUG_METRICS) {
- // eslint-disable-next-line no-console
- console.log('Registering sweep method')
- }
- sweepingInterval = setInterval(function () {
- if (process.env.DEBUG_METRICS) {
- // eslint-disable-next-line no-console
- console.log('Sweeping metrics')
- }
- return metrics.forEach((metric, key) => {
- return metric.sweep()
- })
- }, 60000)
- const Metrics = require('./index')
- Metrics.registerDestructor(() => clearInterval(sweepingInterval))
- }
- module.exports = PromWrapper
|