From 566eabdc31e9d4ab2427f7037cc7fe9c584f11ab Mon Sep 17 00:00:00 2001 From: Ajay Date: Wed, 2 Oct 2024 20:06:57 -0400 Subject: [PATCH] Add metrics endpoint --- src/app.ts | 2 + src/routes/getMetrics.ts | 107 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 src/routes/getMetrics.ts diff --git a/src/app.ts b/src/app.ts index a8f38a3f..6e85b673 100644 --- a/src/app.ts +++ b/src/app.ts @@ -57,6 +57,7 @@ import { getBrandingStats } from "./routes/getBrandingStats"; import { getTopBrandingUsers } from "./routes/getTopBrandingUsers"; import { getFeatureFlag } from "./routes/getFeatureFlag"; import { getReady } from "./routes/getReady"; +import { getMetrics } from "./routes/getMetrics"; export function createServer(callback: () => void): Server { // Create a service (the app object is just a callback). @@ -205,6 +206,7 @@ function setupRoutes(router: Router, server: Server) { // get status router.get("/api/status/:value", (req, res) => getStatus(req, res, server)); router.get("/api/status", (req, res) => getStatus(req, res, server)); + router.get("/metrics", (req, res) => getMetrics(req, res, server)); router.get("/api/ready", (req, res) => getReady(req, res, server)); diff --git a/src/routes/getMetrics.ts b/src/routes/getMetrics.ts new file mode 100644 index 00000000..92f1037e --- /dev/null +++ b/src/routes/getMetrics.ts @@ -0,0 +1,107 @@ +import { db, privateDB } from "../databases/databases"; +import { Request, Response } from "express"; +import os from "os"; +import redis from "../utils/redis"; +import { Postgres } from "../databases/Postgres"; +import { Server } from "http"; + +export async function getMetrics(req: Request, res: Response, server: Server): Promise { + return res.type("text").send([ + `# HELP sb_uptime Uptime of this instance`, + `# TYPE sb_uptime counter`, + `sb_uptime ${process.uptime()}`, + `# HELP sb_commit The commit hash of the running version`, + `# TYPE sb_commit gauge`, + `sb_commit ${(global as any).HEADCOMMIT ?? "unknown"}`, + `# HELP sb_db_version The version of the database`, + `# TYPE sb_db_version counter`, + `sb_db_version ${await db.prepare("get", "SELECT key, value FROM config where key = ?", ["version"]).then(e => e.value).catch(() => -1)}`, + `# HELP sb_start_time The time this instance was started`, + `# TYPE sb_start_time gauge`, + `sb_start_time ${Date.now()}`, + `# HELP sb_loadavg_5 The 5 minute load average of the system`, + `# TYPE sb_loadavg_5 gauge`, + `sb_loadavg_5 ${os.loadavg()[0]}`, + `# HELP sb_loadavg_15 The 15 minute load average of the system`, + `# TYPE sb_loadavg_15 gauge`, + `sb_loadavg_15 ${os.loadavg()[1]}`, + `# HELP sb_connections The number of connections to this instance`, + `# TYPE sb_connections gauge`, + `sb_connections ${await new Promise((resolve) => server.getConnections((_, count) => resolve(count)) as any)}`, + `# HELP sb_status_requests The number of status requests made to this instance`, + `# TYPE sb_status_requests gauge`, + `sb_status_requests ${await redis.increment("statusRequest").then(e => e[0]).catch(() => -1)}`, + `# HELP sb_postgres_active_requests The number of active requests to the postgres database`, + `# TYPE sb_postgres_active_requests gauge`, + `sb_postgres_active_requests ${(db as Postgres)?.getStats?.()?.activeRequests ?? -1}`, + `# HELP sb_postgres_avg_read_time The average read time of the postgres database`, + `# TYPE sb_postgres_avg_read_time gauge`, + `sb_postgres_avg_read_time ${(db as Postgres)?.getStats?.()?.avgReadTime ?? -1}`, + `# HELP sb_postgres_avg_write_time The average write time of the postgres database`, + `# TYPE sb_postgres_avg_write_time gauge`, + `sb_postgres_avg_write_time ${(db as Postgres)?.getStats?.()?.avgWriteTime ?? -1}`, + `# HELP sb_postgres_avg_failed_time The average failed time of the postgres database`, + `# TYPE sb_postgres_avg_failed_time gauge`, + `sb_postgres_avg_failed_time ${(db as Postgres)?.getStats?.()?.avgFailedTime ?? -1}`, + `# HELP sb_postgres_pool_total The total number of connections in the postgres pool`, + `# TYPE sb_postgres_pool_total gauge`, + `sb_postgres_pool_total ${(db as Postgres)?.getStats?.()?.pool?.total ?? -1}`, + `# HELP sb_postgres_pool_idle The number of idle connections in the postgres pool`, + `# TYPE sb_postgres_pool_idle gauge`, + `sb_postgres_pool_idle ${(db as Postgres)?.getStats?.()?.pool?.idle ?? -1}`, + `# HELP sb_postgres_pool_waiting The number of connections waiting in the postgres pool`, + `# TYPE sb_postgres_pool_waiting gauge`, + `sb_postgres_pool_waiting ${(db as Postgres)?.getStats?.()?.pool?.waiting ?? -1}`, + `# HELP sb_postgres_private_active_requests The number of active requests to the private postgres database`, + `# TYPE sb_postgres_private_active_requests gauge`, + `sb_postgres_private_active_requests ${(privateDB as Postgres)?.getStats?.()?.activeRequests ?? -1}`, + `# HELP sb_postgres_private_avg_read_time The average read time of the private postgres database`, + `# TYPE sb_postgres_private_avg_read_time gauge`, + `sb_postgres_private_avg_read_time ${(privateDB as Postgres)?.getStats?.()?.avgReadTime ?? -1}`, + `# HELP sb_postgres_private_avg_write_time The average write time of the private postgres database`, + `# TYPE sb_postgres_private_avg_write_time gauge`, + `sb_postgres_private_avg_write_time ${(privateDB as Postgres)?.getStats?.()?.avgWriteTime ?? -1}`, + `# HELP sb_postgres_private_avg_failed_time The average failed time of the private postgres database`, + `# TYPE sb_postgres_private_avg_failed_time gauge`, + `sb_postgres_private_avg_failed_time ${(privateDB as Postgres)?.getStats?.()?.avgFailedTime ?? -1}`, + `# HELP sb_postgres_private_pool_total The total number of connections in the private postgres pool`, + `# TYPE sb_postgres_private_pool_total gauge`, + `sb_postgres_private_pool_total ${(privateDB as Postgres)?.getStats?.()?.pool?.total ?? -1}`, + `# HELP sb_postgres_private_pool_idle The number of idle connections in the private postgres pool`, + `# TYPE sb_postgres_private_pool_idle gauge`, + `sb_postgres_private_pool_idle ${(privateDB as Postgres)?.getStats?.()?.pool?.idle ?? -1}`, + `# HELP sb_postgres_private_pool_waiting The number of connections waiting in the private postgres pool`, + `# TYPE sb_postgres_private_pool_waiting gauge`, + `sb_postgres_private_pool_waiting ${(privateDB as Postgres)?.getStats?.()?.pool?.waiting ?? -1}`, + `# HELP sb_redis_active_requests The number of active requests to redis`, + `# TYPE sb_redis_active_requests gauge`, + `sb_redis_active_requests ${(redis as any)?.getStats?.()?.activeRequests ?? -1}`, + `# HELP sb_redis_write_requests The number of write requests to redis`, + `# TYPE sb_redis_write_requests gauge`, + `sb_redis_write_requests ${(redis as any)?.getStats?.()?.writeRequests ?? -1}`, + `# HELP sb_redis_avg_read_time The average read time of redis`, + `# TYPE sb_redis_avg_read_time gauge`, + `sb_redis_avg_read_time ${(redis as any)?.getStats?.()?.avgReadTime ?? -1}`, + `# HELP sb_redis_avg_write_time The average write time of redis`, + `# TYPE sb_redis_avg_write_time gauge`, + `sb_redis_avg_write_time ${(redis as any)?.getStats?.()?.avgWriteTime ?? -1}`, + `# HELP sb_redis_memory_cache_hits The cache hit ratio in redis`, + `# TYPE sb_redis_memory_cache_hits gauge`, + `sb_redis_memory_cache_hits ${(redis as any)?.getStats?.()?.memoryCacheHits ?? -1}`, + `# HELP sb_redis_memory_cache_total_hits The cache hit ratio in redis including uncached items`, + `# TYPE sb_redis_memory_cache_total_hits gauge`, + `sb_redis_memory_cache_total_hits ${(redis as any)?.getStats?.()?.memoryCacheTotalHits ?? -1}`, + `# HELP sb_redis_memory_cache_length The length of the memory cache in redis`, + `# TYPE sb_redis_memory_cache_length gauge`, + `sb_redis_memory_cache_length ${(redis as any)?.getStats?.()?.memoryCacheLength ?? -1}`, + `# HELP sb_redis_memory_cache_size The size of the memory cache in redis`, + `# TYPE sb_redis_memory_cache_size gauge`, + `sb_redis_memory_cache_size ${(redis as any)?.getStats?.()?.memoryCacheSize ?? -1}`, + `# HELP sb_redis_last_invalidation The time of the last successful invalidation in redis`, + `# TYPE sb_redis_last_invalidation gauge`, + `sb_redis_last_invalidation ${(redis as any)?.getStats?.()?.lastInvalidation ?? -1}`, + `# HELP sb_redis_last_invalidation_message The time of the last invalidation message in redis`, + `# TYPE sb_redis_last_invalidation_message gauge`, + `sb_redis_last_invalidation_message ${(redis as any)?.getStats?.()?.lastInvalidationMessage ?? -1}`, + ].join("\n")); +} \ No newline at end of file