diff --git a/packages/master/src/Master.js b/packages/master/src/Master.js index 2bb3ccb7..3e57f984 100644 --- a/packages/master/src/Master.js +++ b/packages/master/src/Master.js @@ -6,7 +6,7 @@ const fs = require("fs-extra"); const http = require("http"); const https = require("https"); const path = require("path"); - +const stream = require("stream"); const libConfig = require("@clusterio/lib/config"); const libData = require("@clusterio/lib/data"); @@ -26,9 +26,10 @@ const UserManager = require("./UserManager"); const WsServer = require("./WsServer"); -const hitCounter = new libPrometheus.Counter( - "clusterio_master_http_hits_total", - "How many HTTP requests in total have been received" +const endpointDurationSummary = new libPrometheus.Summary( + "clusterio_master_http_endpoint_duration_seconds", + "Time it took to respond to a an HTTP request", + { labels: ["route"] } ); /** @@ -487,7 +488,15 @@ class Master { static addAppRoutes(app, pluginInfos) { app.use((req, res, next) => { - hitCounter.inc(); + let start = process.hrtime.bigint(); + stream.finished(res, () => { + let routePath = "static"; + if (req.route && req.route.path) { + routePath = req.route.path; + } + let end = process.hrtime.bigint(); + endpointDurationSummary.labels(routePath).observe(Number(end - start) / 1e9); + }); next(); }); app.use(compression()); diff --git a/packages/master/src/metrics.js b/packages/master/src/metrics.js index eb753828..ad400eed 100644 --- a/packages/master/src/metrics.js +++ b/packages/master/src/metrics.js @@ -1,9 +1,14 @@ // Shared metrics "use strict"; const libPrometheus = require("@clusterio/lib/prometheus"); +const util = require("util"); exports.endpointHitCounter = new libPrometheus.Counter( "clusterio_master_http_endpoint_hits_total", "How many requests a particular HTTP endpoint has gotten", - { labels: ["route"] } + { labels: ["route"], register: false } +); + +exports.endpointHitCounter.labels = util.deprecate( + exports.endpointHitCounter.labels, "incrementing endpointHitCounter is no longer needed and has no effect" ); diff --git a/packages/master/src/routes.js b/packages/master/src/routes.js index 4e48f1f5..e9af7ff3 100644 --- a/packages/master/src/routes.js +++ b/packages/master/src/routes.js @@ -19,8 +19,6 @@ const libPlugin = require("@clusterio/lib/plugin"); const libPrometheus = require("@clusterio/lib/prometheus"); const { logger } = require("@clusterio/lib/logging"); -const { endpointHitCounter } = require("./metrics"); - const finished = util.promisify(nodeStream.finished); @@ -41,8 +39,6 @@ function mergeSamples(destinationResult, sourceResult) { // Prometheus polling endpoint async function getMetrics(req, res, next) { - endpointHitCounter.labels(req.route.path).inc(); - let results = []; let pluginResults = await libPlugin.invokeHook(req.app.locals.master.plugins, "onMetrics"); for (let metricIterator of pluginResults) { @@ -181,7 +177,6 @@ function validateUserToken(req, res, next) { // Handle an uploaded export package. async function uploadExport(req, res) { - endpointHitCounter.labels(req.route.path).inc(); if (req.get("Content-Type") !== "application/zip") { res.sendStatus(415); return; @@ -340,7 +335,6 @@ async function uploadSave(req, res) { res.status(403).json({ request_errors: [err.message] }); return; } - endpointHitCounter.labels(req.route.path).inc(); let contentType = req.get("Content-Type"); let match = contentTypeRegExp.exec(contentType); @@ -479,7 +473,6 @@ async function uploadMod(req, res) { res.status(403).json({ request_errors: [err.message] }); return; } - endpointHitCounter.labels(req.route.path).inc(); let contentType = req.get("Content-Type"); let match = contentTypeRegExp.exec(contentType); diff --git a/plugins/player_auth/master.js b/plugins/player_auth/master.js index 143b2c28..f6b0dcc6 100644 --- a/plugins/player_auth/master.js +++ b/plugins/player_auth/master.js @@ -44,8 +44,6 @@ class MasterPlugin extends libPlugin.BaseMasterPlugin { }, 60e3).unref(); this.master.app.get("/api/player_auth/servers", (req, res) => { - this.metrics.endpointHitCounter.labels(req.route.path).inc(); - let servers = []; for (let instance of this.master.instances.values()) { if (instance.status === "running" && instance.config.get("player_auth.load_plugin")) { @@ -56,12 +54,10 @@ class MasterPlugin extends libPlugin.BaseMasterPlugin { }); this.master.app.post("/api/player_auth/player_code", express.json(), (req, res, next) => { - this.metrics.endpointHitCounter.labels(req.route.path).inc(); this.handlePlayerCode(req, res).catch(next); }); this.master.app.post("/api/player_auth/verify", express.json(), (req, res, next) => { - this.metrics.endpointHitCounter.labels(req.route.path).inc(); this.handleVerify(req, res).catch(next); }); } diff --git a/plugins/subspace_storage/master.js b/plugins/subspace_storage/master.js index dc8e32d7..9e8a4ffc 100644 --- a/plugins/subspace_storage/master.js +++ b/plugins/subspace_storage/master.js @@ -86,7 +86,7 @@ class MasterPlugin extends libPlugin.BaseMasterPlugin { this.subscribedControlLinks = new Set(); - routes.addApiRoutes(this.master.app, this.items, this.metrics.endpointHitCounter); + routes.addApiRoutes(this.master.app, this.items); } updateStorage() { diff --git a/plugins/subspace_storage/routes.js b/plugins/subspace_storage/routes.js index 16223b9a..4ce93818 100644 --- a/plugins/subspace_storage/routes.js +++ b/plugins/subspace_storage/routes.js @@ -1,5 +1,5 @@ "use strict"; -function addApiRoutes(app, items, endpointHitCounter) { +function addApiRoutes(app, items) { /** * GET endpoint to read the masters current inventory of items. @@ -10,7 +10,6 @@ function addApiRoutes(app, items, endpointHitCounter) { * @returns {object[]} JSON [{name:"iron-plate", count:100},{name:"copper-plate",count:5}] */ app.get("/api/inventory", (req, res) => { - endpointHitCounter.labels(req.route.path).inc(); res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); // Check it and send it @@ -31,7 +30,6 @@ function addApiRoutes(app, items, endpointHitCounter) { * @returns {object} JSON {"iron-plate":100, "copper-plate":5} */ app.get("/api/inventoryAsObject", (req, res) => { - endpointHitCounter.labels(req.route.path).inc(); res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); // Check it and send it diff --git a/test/mock.js b/test/mock.js index 00d46ceb..a346d408 100644 --- a/test/mock.js +++ b/test/mock.js @@ -219,9 +219,7 @@ class MockMaster { async function createMasterPlugin(MasterPluginClass, info) { let master = new MockMaster(); - let metrics = { - endpointHitCounter: new libPrometheus.Counter("hit_counter", "Hit Counter", { labels: ["route"] }), - }; + let metrics = {}; let logger = new MockLogger(); let plugin = new MasterPluginClass(info, master, metrics, logger); await plugin.init();