Skip to content
This repository was archived by the owner on Jan 8, 2023. It is now read-only.

Commit 49fa9f5

Browse files
Andrei Dobrevoltbit
authored andcommitted
feat: add suport for registry types and exemplars
- Added support for selecting the registry type - Added support for OpenMetrics format - Added support for out of the box integration with OpenTelemetry so exemplars of the default metrics are populated with trace data
1 parent da31acf commit 49fa9f5

File tree

3 files changed

+57
-12
lines changed

3 files changed

+57
-12
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
},
3535
"dependencies": {
3636
"response-time": "^2.3.2",
37-
"url-value-parser": "^2.0.0"
37+
"url-value-parser": "^2.0.0",
38+
"@opentelemetry/api": "^1.0.2"
3839
},
3940
"peerDependencies": {
4041
"express": "4.x",

src/index.js

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const express = require('express');
2-
const Prometheus = require('prom-client');
2+
const OtelApi = require('@opentelemetry/api');
3+
const Prometheus = require('/home/andrei/dev/prom-client');
34
const ResponseTime = require('response-time');
45

56
const {
@@ -20,6 +21,8 @@ const defaultOptions = {
2021
authenticate: null,
2122
collectDefaultMetrics: true,
2223
collectGCMetrics: false,
24+
enableExemplars: false,
25+
contentType: Prometheus.Registry.contentType,
2326
// buckets for response time from 0.05s to 2.5s
2427
// these are arbitrary values since i dont know any better ¯\_(ツ)_/¯
2528
requestDurationBuckets: Prometheus.exponentialBuckets(0.05, 1.75, 8),
@@ -41,24 +44,30 @@ module.exports = (userOptions = {}) => {
4144
const app = express();
4245
app.disable('x-powered-by');
4346

47+
Prometheus.register.setContentType(options.contentType);
48+
4449
const requestDuration = requestDurationGenerator(
4550
options.customLabels,
4651
options.requestDurationBuckets,
4752
options.prefix,
53+
options.enableExemplars,
4854
);
4955
const requestCount = requestCountGenerator(
5056
options.customLabels,
5157
options.prefix,
58+
options.enableExemplars,
5259
);
5360
const requestLength = requestLengthGenerator(
5461
options.customLabels,
5562
options.requestLengthBuckets,
5663
options.prefix,
64+
options.enableExemplars,
5765
);
5866
const responseLength = responseLengthGenerator(
5967
options.customLabels,
6068
options.responseLengthBuckets,
6169
options.prefix,
70+
options.enableExemplars,
6271
);
6372

6473
/**
@@ -73,6 +82,18 @@ module.exports = (userOptions = {}) => {
7382
const route = normalizePath(originalUrl, options.extraMasks);
7483

7584
if (route !== metricsPath) {
85+
let exemplarLabels = null;
86+
if(options.enableExemplars) {
87+
exemplarLabels = {};
88+
let current_span = OtelApi.trace.getSpan(OtelApi.context.active());
89+
if(current_span) {
90+
exemplarLabels = {
91+
'traceId': current_span.spanContext().traceId,
92+
'spanId': current_span.spanContext().spanId
93+
};
94+
}
95+
}
96+
7697
const status = normalizeStatus
7798
? normalizeStatusCode(res.statusCode) : res.statusCode.toString();
7899

@@ -81,24 +102,40 @@ module.exports = (userOptions = {}) => {
81102
if (typeof options.transformLabels === 'function') {
82103
options.transformLabels(labels, req, res);
83104
}
84-
requestCount.inc(labels);
105+
if (exemplarLabels != null) {
106+
requestCount.inc({ labels: labels, exemplarLabels: exemplarLabels });
107+
} else {
108+
requestCount.inc(labels, exemplarLabels);
109+
}
85110

86111
// observe normalizing to seconds
87-
requestDuration.observe(labels, time / 1000);
112+
if (exemplarLabels != null) {
113+
requestDuration.observe({ labels: labels, value: (time/1000), exemplarLabels: exemplarLabels });
114+
} else {
115+
requestDuration.observe(labels, time / 1000);
116+
}
88117

89118
// observe request length
90119
if (options.requestLengthBuckets.length) {
91120
const reqLength = req.get('Content-Length');
92121
if (reqLength) {
93-
requestLength.observe(labels, Number(reqLength));
122+
if (exemplarLabels != null) {
123+
requestLength.observe({ labels: labels, value: Number(reqLength), exemplarLabels: exemplarLabels });
124+
} else {
125+
requestLength.observe(labels, Number(reqLength));
126+
}
94127
}
95128
}
96129

97130
// observe response length
98131
if (options.responseLengthBuckets.length) {
99132
const resLength = res.get('Content-Length');
100133
if (resLength) {
101-
responseLength.observe(labels, Number(resLength));
134+
if (exemplarLabels != null) {
135+
responseLength.observe({ labels: labels, value: Number(resLength), exemplarLabels: exemplarLabels });
136+
} else {
137+
responseLength.observe(labels, Number(resLength));
138+
}
102139
}
103140
}
104141
}
@@ -110,6 +147,7 @@ module.exports = (userOptions = {}) => {
110147
// used to calculate saturation of the service
111148
Prometheus.collectDefaultMetrics({
112149
prefix: options.prefix,
150+
enableExemplars: options.enableExemplars,
113151
});
114152
}
115153

@@ -125,6 +163,7 @@ module.exports = (userOptions = {}) => {
125163
/* eslint-enable global-require */
126164
const startGcStats = gcStats(Prometheus.register, {
127165
prefix: options.prefix,
166+
enableExemplars: options.enableExemplars
128167
});
129168
startGcStats();
130169
} catch (err) {
@@ -154,7 +193,7 @@ module.exports = (userOptions = {}) => {
154193
}
155194
}
156195

157-
res.set('Content-Type', Prometheus.register.contentType);
196+
res.set('Content-Type', options.contentType);
158197
return res.end(await Prometheus.register.metrics());
159198
});
160199

src/metrics.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
const Prometheus = require('prom-client');
1+
// const Prometheus = require('prom-client');
2+
const Prometheus = require('/home/andrei/dev/prom-client');
23

34
/**
45
* @param prefix - metrics name prefix
56
* request counter
67
*/
7-
function requestCountGenerator(labelNames, prefix = '') {
8+
function requestCountGenerator(labelNames, prefix = '', enableExemplars = false) {
89
return new Prometheus.Counter({
910
name: `${prefix}http_requests_total`,
1011
help: 'Counter for total requests received',
12+
enableExemplars: enableExemplars,
1113
labelNames,
1214
});
1315
}
@@ -17,10 +19,11 @@ function requestCountGenerator(labelNames, prefix = '') {
1719
* @param prefix - metrics name prefix
1820
* request duration
1921
*/
20-
function requestDurationGenerator(labelNames, buckets, prefix = '') {
22+
function requestDurationGenerator(labelNames, buckets, prefix = '', enableExemplars = false) {
2123
return new Prometheus.Histogram({
2224
name: `${prefix}http_request_duration_seconds`,
2325
help: 'Duration of HTTP requests in seconds',
26+
enableExemplars: enableExemplars,
2427
labelNames,
2528
buckets,
2629
});
@@ -31,10 +34,11 @@ function requestDurationGenerator(labelNames, buckets, prefix = '') {
3134
* @param prefix - metrics name prefix
3235
* request length
3336
*/
34-
function requestLengthGenerator(labelNames, buckets, prefix = '') {
37+
function requestLengthGenerator(labelNames, buckets, prefix = '', enableExemplars = false) {
3538
return new Prometheus.Histogram({
3639
name: `${prefix}http_request_length_bytes`,
3740
help: 'Content-Length of HTTP request',
41+
enableExemplars: enableExemplars,
3842
labelNames,
3943
buckets,
4044
});
@@ -45,10 +49,11 @@ function requestLengthGenerator(labelNames, buckets, prefix = '') {
4549
* @param prefix - metrics name prefix
4650
* response length
4751
*/
48-
function responseLengthGenerator(labelNames, buckets, prefix = '') {
52+
function responseLengthGenerator(labelNames, buckets, prefix = '', enableExemplars = false) {
4953
return new Prometheus.Histogram({
5054
name: `${prefix}http_response_length_bytes`,
5155
help: 'Content-Length of HTTP response',
56+
enableExemplars: enableExemplars,
5257
labelNames,
5358
buckets,
5459
});

0 commit comments

Comments
 (0)