From 1486ccf2f5bfce7a9a0e80799007aea4f32f5b02 Mon Sep 17 00:00:00 2001 From: zkldi Date: Wed, 10 Apr 2024 02:31:47 +0100 Subject: [PATCH] feat: prometheus metrics (#1033) --- package.json | 1 + pnpm-lock.yaml | 47 ++++++++++++++++++++++++++++++++++ server/package.json | 1 + server/src/lib/setup/config.ts | 3 +++ server/src/server/server.ts | 5 ++++ 5 files changed, 57 insertions(+) diff --git a/package.json b/package.json index 041357140..51a828cbf 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "@types/bson": "1.0.11", "@types/node": "18.11.18", "@types/express-serve-static-core": "4.17.29", + "@types/express": "4.17.13", "ts-node": "10.9.1", "mongodb": "3.6.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 851d10f6c..ff10ec33c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,6 +9,7 @@ overrides: '@types/bson': 1.0.11 '@types/node': 18.11.18 '@types/express-serve-static-core': 4.17.29 + '@types/express': 4.17.13 ts-node: 10.9.1 mongodb: 3.6.3 @@ -576,6 +577,9 @@ importers: express-async-errors: specifier: 3.1.1 version: 3.1.1(express@4.18.2) + express-prom-bundle: + specifier: ^7.0.0 + version: 7.0.0(prom-client@15.1.1) express-rate-limit: specifier: 5.5.1 version: 5.5.1 @@ -3127,6 +3131,11 @@ packages: aggregate-error: 3.1.0 dev: false + /@opentelemetry/api@1.8.0: + resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} + engines: {node: '>=8.0.0'} + dev: false + /@popperjs/core@2.11.6: resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==} dev: false @@ -4156,6 +4165,10 @@ packages: file-uri-to-path: 1.0.0 dev: false + /bintrees@1.0.2: + resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==} + dev: false + /bl@2.2.1: resolution: {integrity: sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==} dependencies: @@ -5959,6 +5972,21 @@ packages: express: 4.18.2 dev: false + /express-prom-bundle@7.0.0(prom-client@15.1.1): + resolution: {integrity: sha512-VwVaCyGBGHkHdecpTqRdW1Jm2fXK8weCUKjGjNWorc9g4M+cZ3xoj+N9uQzfRWfIPXJG5QOaiAziOIalQzMwgA==} + engines: {node: '>=18'} + peerDependencies: + prom-client: '>=15.0.0' + dependencies: + '@types/express': 4.17.13 + express: 4.18.2 + on-finished: 2.4.1 + prom-client: 15.1.1 + url-value-parser: 2.2.0 + transitivePeerDependencies: + - supports-color + dev: false + /express-rate-limit@5.5.1: resolution: {integrity: sha512-MTjE2eIbHv5DyfuFz4zLYWxpqVhEhkTiwFGuB74Q9CSou2WHO52nlE5y3Zlg6SIsiYUIPj6ifFxnkPz6O3sIUg==} dev: false @@ -8771,6 +8799,14 @@ packages: fromentries: 1.3.2 dev: true + /prom-client@15.1.1: + resolution: {integrity: sha512-GVA2H96QCg2q71rjc3VYvSrVG7OpnJxyryC7dMzvfJfpJJHzQVwF3TJLfHzKORcwJpElWs1TwXLthlJAFJxq2A==} + engines: {node: ^16 || ^18 || >=20} + dependencies: + '@opentelemetry/api': 1.8.0 + tdigest: 0.1.2 + dev: false + /promise@7.3.1: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} dependencies: @@ -10264,6 +10300,12 @@ packages: diff: 4.0.2 dev: true + /tdigest@0.1.2: + resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==} + dependencies: + bintrees: 1.0.2 + dev: false + /terser@5.16.1: resolution: {integrity: sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==} engines: {node: '>=10'} @@ -10675,6 +10717,11 @@ packages: resolution: {integrity: sha512-mYFmBHCapZjtcNHW0MDq9967t+z4Dmg5CJ0KqysK3+ZbyoNOWQHksGCTWwDhxGXllkWlOc10Xfko6v4a3ucM6A==} dev: false + /url-value-parser@2.2.0: + resolution: {integrity: sha512-yIQdxJpgkPamPPAPuGdS7Q548rLhny42tg8d4vyTNzFqvOnwqrgHXvgehT09U7fwrzxi3RxCiXjoNUNnNOlQ8A==} + engines: {node: '>=6.0.0'} + dev: false + /use-isomorphic-layout-effect@1.1.2(@types/react@17.0.13)(react@17.0.2): resolution: {integrity: sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==} peerDependencies: diff --git a/server/package.json b/server/package.json index 35c6b4ec9..f37f27f64 100644 --- a/server/package.json +++ b/server/package.json @@ -72,6 +72,7 @@ "dotenv": "16.0.0", "express": "4.18.2", "express-async-errors": "3.1.1", + "express-prom-bundle": "^7.0.0", "express-rate-limit": "5.5.1", "express-session": "1.17.2", "fast-json-stable-hash": "1.0.3", diff --git a/server/src/lib/setup/config.ts b/server/src/lib/setup/config.ts index 6cc5e8615..2d2f19126 100644 --- a/server/src/lib/setup/config.ts +++ b/server/src/lib/setup/config.ts @@ -83,6 +83,7 @@ export interface TachiServerConfig { OPTIONS_ALWAYS_SUCCEEDS?: boolean; USE_EXTERNAL_SCORE_IMPORT_WORKER: boolean; EXTERNAL_SCORE_IMPORT_WORKER_CONCURRENCY?: integer; + ENABLE_METRICS: boolean; SEEDS_CONFIG?: { REPO_URL: string; USER_NAME: string | null; @@ -176,6 +177,7 @@ const err = p(config, { USE_EXTERNAL_SCORE_IMPORT_WORKER: "*boolean", EXTERNAL_SCORE_IMPORT_WORKER_CONCURRENCY: p.optional(p.isPositiveInteger), ALLOW_RUNNING_OFFLINE: "*boolean", + ENABLE_METRICS: "*boolean", EMAIL_CONFIG: p.optional({ FROM: "string", DKIM: "*object", @@ -273,6 +275,7 @@ tachiServerConfig.MAX_RIVALS ??= 5; tachiServerConfig.MAX_FOLLOWING_AMOUNT ??= 1_000; tachiServerConfig.USE_EXTERNAL_SCORE_IMPORT_WORKER ??= false; tachiServerConfig.TACHI_CONFIG.SIGNUPS_ENABLED ??= true; +tachiServerConfig.ENABLE_METRICS ??= false; export const TachiConfig = tachiServerConfig.TACHI_CONFIG; export const ServerConfig = tachiServerConfig; diff --git a/server/src/server/server.ts b/server/src/server/server.ts index e789c7c2d..2b3f5f0e6 100644 --- a/server/src/server/server.ts +++ b/server/src/server/server.ts @@ -7,6 +7,7 @@ import "express-async-errors"; import { RequestLoggerMiddleware } from "./middleware/request-logger"; import mainRouter from "./router/router"; import connectRedis from "connect-redis"; +import ExpressPromBundle from "express-prom-bundle"; import expressSession from "express-session"; import { RedisClient } from "external/redis/redis"; import helmet from "helmet"; @@ -84,6 +85,10 @@ if (Environment.nodeEnv !== "production" && IsNonEmptyString(ServerConfig.CLIENT next(); }); + if (ServerConfig.ENABLE_METRICS) { + app.use(ExpressPromBundle({ includeMethod: true, includePath: true })); + } + // hack to allow all OPTIONS requests. Remember that this setting should not be on in production! if (ServerConfig.OPTIONS_ALWAYS_SUCCEEDS === true) { app.options("*", (req, res) => res.send());