diff --git a/package.json b/package.json index 02b1236..91ddafa 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ }, "dependencies": { "response-time": "^2.3.2", - "url-value-parser": "^2.0.0" + "url-value-parser": "^2.0.0", + "@opentelemetry/api": "^1.0.2" }, "peerDependencies": { "express": "4.x", diff --git a/src/index.js b/src/index.js index eb61e62..11a1ce7 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ const express = require('express'); -const Prometheus = require('prom-client'); +const OtelApi = require('@opentelemetry/api'); +const Prometheus = require('/home/andrei/dev/prom-client'); const ResponseTime = require('response-time'); const { @@ -20,6 +21,8 @@ const defaultOptions = { authenticate: null, collectDefaultMetrics: true, collectGCMetrics: false, + enableExemplars: false, + contentType: Prometheus.Registry.contentType, // buckets for response time from 0.05s to 2.5s // these are arbitrary values since i dont know any better ¯\_(ツ)_/¯ requestDurationBuckets: Prometheus.exponentialBuckets(0.05, 1.75, 8), @@ -41,24 +44,30 @@ module.exports = (userOptions = {}) => { const app = express(); app.disable('x-powered-by'); + Prometheus.register.setContentType(options.contentType); + const requestDuration = requestDurationGenerator( options.customLabels, options.requestDurationBuckets, options.prefix, + options.enableExemplars, ); const requestCount = requestCountGenerator( options.customLabels, options.prefix, + options.enableExemplars, ); const requestLength = requestLengthGenerator( options.customLabels, options.requestLengthBuckets, options.prefix, + options.enableExemplars, ); const responseLength = responseLengthGenerator( options.customLabels, options.responseLengthBuckets, options.prefix, + options.enableExemplars, ); /** @@ -73,6 +82,18 @@ module.exports = (userOptions = {}) => { const route = normalizePath(originalUrl, options.extraMasks); if (route !== metricsPath) { + let exemplarLabels = null; + if(options.enableExemplars) { + exemplarLabels = {}; + let current_span = OtelApi.trace.getSpan(OtelApi.context.active()); + if(current_span) { + exemplarLabels = { + 'traceId': current_span.spanContext().traceId, + 'spanId': current_span.spanContext().spanId + }; + } + } + const status = normalizeStatus ? normalizeStatusCode(res.statusCode) : res.statusCode.toString(); @@ -81,16 +102,28 @@ module.exports = (userOptions = {}) => { if (typeof options.transformLabels === 'function') { options.transformLabels(labels, req, res); } - requestCount.inc(labels); + if (exemplarLabels != null) { + requestCount.inc({ labels: labels, exemplarLabels: exemplarLabels }); + } else { + requestCount.inc(labels, exemplarLabels); + } // observe normalizing to seconds - requestDuration.observe(labels, time / 1000); + if (exemplarLabels != null) { + requestDuration.observe({ labels: labels, value: (time/1000), exemplarLabels: exemplarLabels }); + } else { + requestDuration.observe(labels, time / 1000); + } // observe request length if (options.requestLengthBuckets.length) { const reqLength = req.get('Content-Length'); if (reqLength) { - requestLength.observe(labels, Number(reqLength)); + if (exemplarLabels != null) { + requestLength.observe({ labels: labels, value: Number(reqLength), exemplarLabels: exemplarLabels }); + } else { + requestLength.observe(labels, Number(reqLength)); + } } } @@ -98,7 +131,11 @@ module.exports = (userOptions = {}) => { if (options.responseLengthBuckets.length) { const resLength = res.get('Content-Length'); if (resLength) { - responseLength.observe(labels, Number(resLength)); + if (exemplarLabels != null) { + responseLength.observe({ labels: labels, value: Number(resLength), exemplarLabels: exemplarLabels }); + } else { + responseLength.observe(labels, Number(resLength)); + } } } } @@ -110,6 +147,7 @@ module.exports = (userOptions = {}) => { // used to calculate saturation of the service Prometheus.collectDefaultMetrics({ prefix: options.prefix, + enableExemplars: options.enableExemplars, }); } @@ -125,6 +163,7 @@ module.exports = (userOptions = {}) => { /* eslint-enable global-require */ const startGcStats = gcStats(Prometheus.register, { prefix: options.prefix, + enableExemplars: options.enableExemplars }); startGcStats(); } catch (err) { @@ -154,7 +193,7 @@ module.exports = (userOptions = {}) => { } } - res.set('Content-Type', Prometheus.register.contentType); + res.set('Content-Type', options.contentType); return res.end(await Prometheus.register.metrics()); }); diff --git a/src/metrics.js b/src/metrics.js index d745859..8c246cd 100644 --- a/src/metrics.js +++ b/src/metrics.js @@ -1,13 +1,15 @@ -const Prometheus = require('prom-client'); +// const Prometheus = require('prom-client'); +const Prometheus = require('/home/andrei/dev/prom-client'); /** * @param prefix - metrics name prefix * request counter */ -function requestCountGenerator(labelNames, prefix = '') { +function requestCountGenerator(labelNames, prefix = '', enableExemplars = false) { return new Prometheus.Counter({ name: `${prefix}http_requests_total`, help: 'Counter for total requests received', + enableExemplars: enableExemplars, labelNames, }); } @@ -17,10 +19,11 @@ function requestCountGenerator(labelNames, prefix = '') { * @param prefix - metrics name prefix * request duration */ -function requestDurationGenerator(labelNames, buckets, prefix = '') { +function requestDurationGenerator(labelNames, buckets, prefix = '', enableExemplars = false) { return new Prometheus.Histogram({ name: `${prefix}http_request_duration_seconds`, help: 'Duration of HTTP requests in seconds', + enableExemplars: enableExemplars, labelNames, buckets, }); @@ -31,10 +34,11 @@ function requestDurationGenerator(labelNames, buckets, prefix = '') { * @param prefix - metrics name prefix * request length */ -function requestLengthGenerator(labelNames, buckets, prefix = '') { +function requestLengthGenerator(labelNames, buckets, prefix = '', enableExemplars = false) { return new Prometheus.Histogram({ name: `${prefix}http_request_length_bytes`, help: 'Content-Length of HTTP request', + enableExemplars: enableExemplars, labelNames, buckets, }); @@ -45,10 +49,11 @@ function requestLengthGenerator(labelNames, buckets, prefix = '') { * @param prefix - metrics name prefix * response length */ -function responseLengthGenerator(labelNames, buckets, prefix = '') { +function responseLengthGenerator(labelNames, buckets, prefix = '', enableExemplars = false) { return new Prometheus.Histogram({ name: `${prefix}http_response_length_bytes`, help: 'Content-Length of HTTP response', + enableExemplars: enableExemplars, labelNames, buckets, });