Skip to content

Commit 5ba8245

Browse files
authored
Add Prometheus metrics to export (#29)
1 parent 7688317 commit 5ba8245

File tree

3 files changed

+48
-3
lines changed

3 files changed

+48
-3
lines changed

app.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,26 @@ import express from 'express';
99
import bodyParser from 'body-parser';
1010
import queryString from 'query-string';
1111
import dataTools from '@tidepool/data-tools';
12+
import { Registry, Counter } from 'prom-client';
1213
import logMaker from './log';
1314

1415
const log = logMaker('app.js', { level: process.env.DEBUG_LEVEL || 'info' });
1516

1617
const { createTerminus } = require('@godaddy/terminus');
1718

19+
const client = require('prom-client');
20+
21+
const { collectDefaultMetrics } = client;
22+
const register = new Registry();
23+
24+
collectDefaultMetrics({ register });
25+
26+
const createCounter = (name, help, labelNames) => new Counter({
27+
name, help, labelNames, registers: [register],
28+
});
29+
30+
const statusCount = createCounter('tidepool_export_status_count', 'The number of errors for each status code.', ['status_code', 'export_format']);
31+
1832
function maybeReplaceWithContentsOfFile(obj, field) {
1933
const potentialFile = obj[field];
2034
if (potentialFile != null && fs.existsSync(potentialFile)) {
@@ -41,6 +55,11 @@ log.info(`Export download timeout set to ${config.exportTimeout} ms`);
4155

4256
const app = express();
4357

58+
app.get('/metrics', async (req, res) => {
59+
res.set('Content-Type', register.contentType);
60+
res.end(register.metrics());
61+
});
62+
4463
function buildHeaders(request) {
4564
if (request.headers['x-tidepool-session-token']) {
4665
return {
@@ -82,6 +101,8 @@ app.get('/export/:userid', async (req, res) => {
82101
}
83102
log.info(logString);
84103

104+
const exportFormat = req.query.format;
105+
85106
try {
86107
const cancelRequest = axios.CancelToken.source();
87108

@@ -95,7 +116,7 @@ app.get('/export/:userid', async (req, res) => {
95116

96117
let writeStream = null;
97118

98-
if (req.query.format === 'json') {
119+
if (exportFormat === 'json') {
99120
res.attachment('TidepoolExport.json');
100121
writeStream = dataTools.jsonStreamWriter();
101122

@@ -129,10 +150,11 @@ app.get('/export/:userid', async (req, res) => {
129150
dataResponse.data.on('error', (err) => reject(err));
130151
res.on('error', (err) => reject(err));
131152
res.on('timeout', async () => {
153+
statusCount.inc({ status_code: 408, export_format: exportFormat });
132154
reject(new Error('Data export request took too long to complete. Cancelling the request.'));
133155
});
134156
});
135-
157+
statusCount.inc({ status_code: 200, export_format: exportFormat });
136158
log.debug(`Finished downloading data for User ${req.params.userid}`);
137159
} catch (e) {
138160
log.error(`Error while downloading: ${e}`);
@@ -145,15 +167,18 @@ app.get('/export/:userid', async (req, res) => {
145167
clearTimeout(timer);
146168
} catch (error) {
147169
if (error.response && error.response.status === 403) {
170+
statusCount.inc({ status_code: 403, export_format: exportFormat });
148171
res.status(error.response.status).send('Not authorized to export data for this user.');
149172
log.error(`${error.response.status}: ${error}`);
150173
} else {
174+
statusCount.inc({ status_code: 500, export_format: exportFormat });
151175
res.status(500).send('Server error while processing data. Please contact Tidepool Support.');
152176
log.error(`500: ${error}`);
153177
}
154178
}
155179
});
156180

181+
157182
function beforeShutdown() {
158183
return new Promise((resolve) => {
159184
// Ensure that the export request can time out

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tidepool/export",
3-
"version": "1.5.0",
3+
"version": "1.6.0",
44
"main": "app.js",
55
"repository": {
66
"type": "git",
@@ -24,6 +24,7 @@
2424
"esm": "3.2.25",
2525
"express": "4.17.1",
2626
"lodash": "4.17.15",
27+
"prom-client": "^12.0.0",
2728
"query-string": "6.13.1"
2829
},
2930
"devDependencies": {

yarn.lock

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,11 @@ binary@~0.3.0:
372372
buffers "~0.1.1"
373373
chainsaw "~0.1.0"
374374

375+
376+
version "1.0.1"
377+
resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.1.tgz#0e655c9b9c2435eaab68bf4027226d2b55a34524"
378+
integrity sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=
379+
375380
bl@^3.0.0:
376381
version "3.0.0"
377382
resolved "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88"
@@ -2107,6 +2112,13 @@ progress@^2.0.0:
21072112
resolved "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
21082113
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
21092114

2115+
prom-client@^12.0.0:
2116+
version "12.0.0"
2117+
resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-12.0.0.tgz#9689379b19bd3f6ab88a9866124db9da3d76c6ed"
2118+
integrity sha512-JbzzHnw0VDwCvoqf8y1WDtq4wSBAbthMB1pcVI/0lzdqHGJI3KBJDXle70XK+c7Iv93Gihqo0a5LlOn+g8+DrQ==
2119+
dependencies:
2120+
tdigest "^0.1.1"
2121+
21102122
prop-types@^15.7.2:
21112123
version "15.7.2"
21122124
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
@@ -2629,6 +2641,13 @@ tar-stream@^2.1.0:
26292641
inherits "^2.0.3"
26302642
readable-stream "^3.1.1"
26312643

2644+
tdigest@^0.1.1:
2645+
version "0.1.1"
2646+
resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.1.tgz#2e3cb2c39ea449e55d1e6cd91117accca4588021"
2647+
integrity sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=
2648+
dependencies:
2649+
bintrees "1.0.1"
2650+
26322651
text-table@^0.2.0:
26332652
version "0.2.0"
26342653
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"

0 commit comments

Comments
 (0)