From 8a6bf05198dba86fe942b8b81701056112c4cc0a Mon Sep 17 00:00:00 2001 From: cosmix Date: Wed, 28 Mar 2018 01:13:03 +0200 Subject: [PATCH] Improve health endpoint. Add average response middleware and hook to metrics endpoint. (#6) * WiP. Add average response middleware and hook to health endpoint. * Add standard health formatting, average response time for last 50 requests. * Replace homebrew metrics with prometheus to placate the Vrachnis/Panagiotou duo * Add unit suffix on prometheus metric * Remove dangling sprintf-js dependency and require statement. --- package.json | 3 ++- src/avgresp.js | 42 ++++++++++++++++++++++++++++++++++++++++++ src/server.js | 30 +++++++++++++++++++++++++++--- 3 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 src/avgresp.js diff --git a/package.json b/package.json index eb6f22c..3d07070 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "proj4": "2.4.4", "request": "2.83.0", "sharp": "^0.20.0", - "tileserver-gl-styles": "1.2.0" + "tileserver-gl-styles": "1.2.0", + "prom-client": "11.0.0" }, "devDependencies": { "should": "^11.2.0", diff --git a/src/avgresp.js b/src/avgresp.js new file mode 100644 index 0000000..7ba796d --- /dev/null +++ b/src/avgresp.js @@ -0,0 +1,42 @@ +#!/usr/bin/env node +'use strict'; + +var prometheus = require('prom-client'); + +module.exports = { + avgresp: avgresp +}; + + +var history; +var samples; +var fullhistory = false; + +/** + * Create middleware to record and output average response times + * @param {Object} options + * @return {function} + */ +function avgresp(options) { + + var opts = options || {}; + + history = 50; + samples = new Array(history); + var currentIndex = 0; + + const respSummary = new prometheus.Summary({ + name: "tileserver_static_latency_seconds", + help: "The tileserver response time in seconds" + }); + + return function avgresp(req, res, next) { + var end = respSummary.startTimer(); + + res.on('finish', function() { + end(); + }) + + next(); + } +} diff --git a/src/server.js b/src/server.js index db67be4..64cf3dc 100644 --- a/src/server.js +++ b/src/server.js @@ -21,7 +21,11 @@ var packageJson = require('../package'), serve_rendered = null, serve_style = require('./serve_style'), serve_data = require('./serve_data'), - utils = require('./utils'); + utils = require('./utils'), + avgresp = require('./avgresp'), + prometheus = require('prom-client'); + +prometheus.collectDefaultMetrics({ timeout: 5000 }); var isLight = packageJson.name.slice(-6) == '-light'; if (!isLight) { @@ -52,6 +56,8 @@ function start(opts) { })); } + app.use(/\/styles\/.*\/static\/.*$/, avgresp.avgresp()); + var config = opts.config || null; var configPath = null; if (opts.configPath) { @@ -391,12 +397,30 @@ function start(opts) { console.log('Startup complete'); startupComplete = true; }); + app.get('/health', function(req, res, next) { + + var healthTemplate = { + "service": "tileserver", + "status": "200", + "message": "OK" + }; + + var statusCode = 200; + if (startupComplete) { - return res.status(200).send('OK'); + statusCode = 200; + healthTemplate.message = "OK"; } else { - return res.status(503).send('Starting'); + statusCode = 503; + healthTemplate.message = "Starting"; } + healthTemplate.status = statusCode; + return res.status(statusCode).send(healthTemplate); + }); + + app.get('/metrics', function(req, res, next){ + res.end(prometheus.register.metrics()); }); var server = app.listen(process.env.PORT || opts.port, process.env.BIND || opts.bind, function() {