Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/hub/servers/app/metrics.ts
Views: 687
/*1Express middleware for recording metrics about response time to requests.2*/34import { dirname } from "path";5import { Router } from "express";6const { get, new_histogram } = require("@cocalc/hub/metrics-recorder");7import { join } from "path";8import basePath from "@cocalc/backend/base-path";9import getPool from "@cocalc/database/pool";10import { getLogger } from "@cocalc/hub/logger";1112const log = getLogger("metrics");1314// initialize metrics15const responseTimeHistogram = new_histogram("http_histogram", "http server", {16buckets: [0.01, 0.1, 1, 2, 5, 10, 20],17labels: ["path", "method", "code"],18});1920// response time metrics21function metrics(req, res, next) {22const resFinished = responseTimeHistogram.startTimer();23const originalEnd = res.end;24res.end = (...args) => {25originalEnd.apply(res, args);26if (!req.path) {27return;28}29const pathSplit = req.path.split("/");30// for API paths, we want to have data for each endpoint31const path_tail = pathSplit.slice(pathSplit.length - 3);32const is_api = path_tail[0] === "api" && path_tail[1] === "v1";33let path;34if (is_api) {35path = path_tail.join("/");36} else {37// for regular paths, we ignore the file38path = dirname(req.path).split("/").slice(0, 2).join("/");39}40resFinished({41path,42method: req.method,43code: res.statusCode,44});45};46next();47}4849export function setupInstrumentation(router: Router) {50router.use(metrics);51}5253async function isEnabled(pool): Promise<boolean> {54const {rows} = await pool.query(55"SELECT value FROM server_settings WHERE name='prometheus_metrics'"56);57const enabled = rows.length > 0 && rows[0].value == "yes";58log.info("isEnabled", enabled);59return enabled;60}6162export function initMetricsEndpoint(router: Router) {63const endpoint = join(basePath, "metrics");64log.info("initMetricsEndpoint at ", endpoint);65// long cache so we can easily check before each response and it is still fast.66const pool = getPool("long");6768router.get(endpoint, async (_req, res) => {69res.header("Content-Type", "text/plain");70res.header("Cache-Control", "no-cache, no-store");71if (!(await isEnabled(pool))) {72res.json({73error:74"Sharing of metrics at /metrics is disabled. Metrics can be enabled in the site administration page.",75});76return;77}78const metricsRecorder = get();79if (metricsRecorder != null) {80res.send(await metricsRecorder.metrics());81} else {82res.json({ error: "Metrics recorder not initialized." });83}84});85}868788