Path: blob/master/src/packages/hub/servers/app/metrics.ts
1503 views
/*1Express middleware for recording metrics about response time to requests.2*/34import { dirname } from "path";5import { Router } from "express";6import { get, new_histogram } from "@cocalc/server/metrics/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}29// for regular paths, we ignore the file30const path = dirname(req.path).split("/").slice(0, 2).join("/");31resFinished({32path,33method: req.method,34code: res.statusCode,35});36};37next();38}3940export function setupInstrumentation(router: Router) {41router.use(metrics);42}4344async function isEnabled(pool): Promise<boolean> {45const { rows } = await pool.query(46"SELECT value FROM server_settings WHERE name='prometheus_metrics'",47);48const enabled = rows.length > 0 && rows[0].value == "yes";49log.info("isEnabled", enabled);50return enabled;51}5253export function initMetricsEndpoint(router: Router) {54const endpoint = join(basePath, "metrics");55log.info("initMetricsEndpoint at ", endpoint);56// long cache so we can easily check before each response and it is still fast.57const pool = getPool("long");5859router.get(endpoint, async (_req, res) => {60res.header("Content-Type", "text/plain");61res.header("Cache-Control", "no-cache, no-store");62if (!(await isEnabled(pool))) {63res.json({64error:65"Sharing of metrics at /metrics is disabled. Metrics can be enabled in the site administration page.",66});67return;68}69const metricsRecorder = get();70if (metricsRecorder != null) {71res.send(await metricsRecorder.metrics());72} else {73res.json({ error: "Metrics recorder not initialized." });74}75});76}777879