@cocalc/hub
Version:
CoCalc: Backend webserver component
85 lines • 3.24 kB
JavaScript
;
/*
Express middleware for recording metrics about response time to requests.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.initMetricsEndpoint = exports.setupInstrumentation = void 0;
const path_1 = require("path");
const { get, new_histogram } = require("@cocalc/hub/metrics-recorder");
const path_2 = require("path");
const base_path_1 = __importDefault(require("@cocalc/backend/base-path"));
const pool_1 = __importDefault(require("@cocalc/database/pool"));
const logger_1 = require("@cocalc/hub/logger");
const log = (0, logger_1.getLogger)("metrics");
// initialize metrics
const responseTimeHistogram = new_histogram("http_histogram", "http server", {
buckets: [0.01, 0.1, 1, 2, 5, 10, 20],
labels: ["path", "method", "code"],
});
// response time metrics
function metrics(req, res, next) {
const resFinished = responseTimeHistogram.startTimer();
const originalEnd = res.end;
res.end = (...args) => {
originalEnd.apply(res, args);
if (!req.path) {
return;
}
const pathSplit = req.path.split("/");
// for API paths, we want to have data for each endpoint
const path_tail = pathSplit.slice(pathSplit.length - 3);
const is_api = path_tail[0] === "api" && path_tail[1] === "v1";
let path;
if (is_api) {
path = path_tail.join("/");
}
else {
// for regular paths, we ignore the file
path = (0, path_1.dirname)(req.path).split("/").slice(0, 2).join("/");
}
resFinished({
path,
method: req.method,
code: res.statusCode,
});
};
next();
}
function setupInstrumentation(router) {
router.use(metrics);
}
exports.setupInstrumentation = setupInstrumentation;
async function isEnabled(pool) {
const { rows } = await pool.query("SELECT value FROM server_settings WHERE name='prometheus_metrics'");
const enabled = rows.length > 0 && rows[0].value == "yes";
log.info("isEnabled", enabled);
return enabled;
}
function initMetricsEndpoint(router) {
const endpoint = (0, path_2.join)(base_path_1.default, "metrics");
log.info("initMetricsEndpoint at ", endpoint);
// long cache so we can easily check before each response and it is still fast.
const pool = (0, pool_1.default)("long");
router.get(endpoint, async (_req, res) => {
res.header("Content-Type", "text/plain");
res.header("Cache-Control", "no-cache, no-store");
if (!(await isEnabled(pool))) {
res.json({
error: "Sharing of metrics at /metrics is disabled. Metrics can be enabled in the site administration page.",
});
return;
}
const metricsRecorder = get();
if (metricsRecorder != null) {
res.send(await metricsRecorder.metrics());
}
else {
res.json({ error: "Metrics recorder not initialized." });
}
});
}
exports.initMetricsEndpoint = initMetricsEndpoint;
//# sourceMappingURL=metrics.js.map