Skip to content
This repository was archived by the owner on Jan 8, 2023. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
51 changes: 45 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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),
Expand All @@ -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,
);

/**
Expand All @@ -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();

Expand All @@ -81,24 +102,40 @@ 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));
}
}
}

// observe response length
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));
}
}
}
}
Expand All @@ -110,6 +147,7 @@ module.exports = (userOptions = {}) => {
// used to calculate saturation of the service
Prometheus.collectDefaultMetrics({
prefix: options.prefix,
enableExemplars: options.enableExemplars,
});
}

Expand All @@ -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) {
Expand Down Expand Up @@ -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());
});

Expand Down
15 changes: 10 additions & 5 deletions src/metrics.js
Original file line number Diff line number Diff line change
@@ -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,
});
}
Expand All @@ -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,
});
Expand All @@ -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,
});
Expand All @@ -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,
});
Expand Down