@@ -9,12 +9,26 @@ import express from 'express';
99import bodyParser from 'body-parser' ;
1010import queryString from 'query-string' ;
1111import dataTools from '@tidepool/data-tools' ;
12+ import { Registry , Counter } from 'prom-client' ;
1213import logMaker from './log' ;
1314
1415const log = logMaker ( 'app.js' , { level : process . env . DEBUG_LEVEL || 'info' } ) ;
1516
1617const { 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+
1832function 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
4256const app = express ( ) ;
4357
58+ app . get ( '/metrics' , async ( req , res ) => {
59+ res . set ( 'Content-Type' , register . contentType ) ;
60+ res . end ( register . metrics ( ) ) ;
61+ } ) ;
62+
4463function 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+
157182function beforeShutdown ( ) {
158183 return new Promise ( ( resolve ) => {
159184 // Ensure that the export request can time out
0 commit comments