Files
shields/core/server/prometheus-metrics.js
Marcin Mielnicki e66cfa3c21 📈 PaaS-friendly metrics (#4874)
* prom-client JSON to InfluxDB line protocol converter

* Converts a metric with separate names

* prom-client JSON to InfluxDB line protocol (version 2) converter

* Server has instance id

* Read the instance id from an environment variable

* More unit tests for instance-metadata

* Log instance id

* Push influx metrics

* INSTANCE_ID with dyno metadata

* Prepare influx metrics in one place

* Influx metrics endpoint should return metrics

* More readable tests

* Env added to instance metadata

* hostname as an instance label value

* HEROKU_DYNO_ID as an instance id for heroku

* Instance env can be set by env variable

* HEROKU_APP_NAME as an instance env

* Log instance metadata as a JSON

* Typo fix

* Code refactoring in tests

* wait-for-expect dev dependency added

* Test for pushing metrics

* Test for pushing metrics

* Use basic authentication for pushing metrics

* intervalSeconds=2 for development env

* Using existing methods

* TODOs removed

* Schema for influx credentials

* Influx config removed from config files

* Require username and password when influx metrics are enabled

* Unused args removed

* pushing component should log errors

* Speed up tests

* should log error responses

* InstanceMetadata class replaces by simple object

* Influx metrics can be configuredd by env variables

* Use application label name instead of service

* Unused code removed

* Integration test for prom-client and converter

* metrics.influx.enabled configuration option added

* Improved influx configuration schema

* instanceMetadata validation

* Typo fix

* Default value for env

* metrics.infux.hostnameAsAInstanceId added

* should add hostname as an instance label when hostnameAsAInstanceId is enabled

* Default values for influx configuration

* flatMap is not available in Node.js 9.4

* Env vars removed from Procfile

* Better instance metadata values in tests

* Typo fix

* lodash.groupby added to prod dependencies

* Allow other keys in private config

* Missing test - should allow other private keys when influx metrics are enabled

* Missing test - should require private metrics config when influx configuration is enabled

* log.error instead of console.log

* metrics.influx.uri -> metrics.influx.url

* Unused arguments removed

* async removed

* promisify sendMetrics

* Allow to disable prometheus metrics

* Create test server with custom config

* 'metrics-influx' resource removed

* 'metrics-influx' resource removed

* Private config schema flattened out

* Extra code removed in Prometheus tests

* promisify moved outside of the class

* Do not throw errors from got in a specific test

* hostnameAliases added

* instanceIdFrom added

* instanceIdEnvVarName added

* envLabel added to schema

* instanceMetadata is not used by InfluxMetrics

* Instance metadata removed

* hostnameAsAnInstanceId removed

* A comment added

* waitForExpect removed

* Unused code removed
2020-04-19 20:03:00 +02:00

122 lines
3.1 KiB
JavaScript

'use strict'
const decamelize = require('decamelize')
const prometheus = require('prom-client')
module.exports = class PrometheusMetrics {
constructor({ register } = {}) {
this.register = register || new prometheus.Registry()
this.counters = {
numRequests: new prometheus.Counter({
name: 'service_requests_total',
help: 'Total service requests',
labelNames: ['category', 'family', 'service'],
registers: [this.register],
}),
responseTime: new prometheus.Histogram({
name: 'service_response_millis',
help: 'Service response time in milliseconds',
// 250 ms increments up to 2 seconds, then 500 ms increments up to 8
// seconds, then 1 second increments up to 15 seconds.
buckets: [
250,
500,
750,
1000,
1250,
1500,
1750,
2000,
2250,
2500,
2750,
3000,
3250,
3500,
3750,
4000,
4500,
5000,
5500,
6000,
6500,
7000,
7500,
8000,
9000,
10000,
11000,
12000,
13000,
14000,
15000,
],
registers: [this.register],
}),
rateLimitExceeded: new prometheus.Counter({
name: 'rate_limit_exceeded_total',
help: 'Count of rate limit exceeded by type',
labelNames: ['rate_limit_type'],
registers: [this.register],
}),
serviceResponseSize: new prometheus.Histogram({
name: 'service_response_bytes',
help: 'Service response size in bytes',
labelNames: ['category', 'family', 'service'],
// buckets: 64KiB, 128KiB, 256KiB, 512KiB, 1MiB, 2MiB, 4MiB, 8MiB
buckets: prometheus.exponentialBuckets(64 * 1024, 2, 8),
registers: [this.register],
}),
}
this.interval = prometheus.collectDefaultMetrics({
register: this.register,
})
}
async registerMetricsEndpoint(server) {
const { register } = this
server.route(/^\/metrics$/, (data, match, end, ask) => {
ask.res.setHeader('Content-Type', register.contentType)
ask.res.end(register.metrics())
})
}
stop() {
this.register.clear()
if (this.interval) {
clearInterval(this.interval)
this.interval = undefined
}
}
metrics() {
return this.register.getMetricsAsJSON()
}
/**
* @returns {object} `{ inc() {} }`.
*/
createNumRequestCounter({ category, serviceFamily, name }) {
const service = decamelize(name)
return this.counters.numRequests.labels(category, serviceFamily, service)
}
noteResponseTime(responseTime) {
return this.counters.responseTime.observe(responseTime)
}
noteRateLimitExceeded(rateLimitType) {
return this.counters.rateLimitExceeded.labels(rateLimitType).inc()
}
createServiceResponseSizeHistogram({ category, serviceFamily, name }) {
const service = decamelize(name)
return this.counters.serviceResponseSize.labels(
category,
serviceFamily,
service
)
}
}