feat: add prometheus metrics

This commit is contained in:
Naomi Carrigan 2024-08-13 08:13:03 -07:00
parent 188480052b
commit 23fba3d95d
Signed by: naomi
SSH Key Fingerprint: SHA256:rca1iUI2OhAM6n4FIUaFcZcicmri0jgocqKiTTAfrt8
9 changed files with 78 additions and 0 deletions

View File

@ -44,6 +44,7 @@
"express": "4.19.2",
"node-html-to-image": "4.0.0",
"node-schedule": "2.1.1",
"prom-client": "15.1.3",
"winston": "3.13.0"
}
}

30
pnpm-lock.yaml generated
View File

@ -29,6 +29,9 @@ importers:
node-schedule:
specifier: 2.1.1
version: 2.1.1
prom-client:
specifier: 15.1.3
version: 15.1.3
winston:
specifier: 3.13.0
version: 3.13.0
@ -252,6 +255,10 @@ packages:
'@octokit/types@13.5.0':
resolution: {integrity: sha512-HdqWTf5Z3qwDVlzCrP8UJquMwunpDiMPt5er+QjGzL4hqr/vBVY/MauQgS1xWxCDT1oMx1EULyqxncdCY/NVSQ==}
'@opentelemetry/api@1.9.0':
resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==}
engines: {node: '>=8.0.0'}
'@pkgr/core@0.1.1':
resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
@ -547,6 +554,9 @@ packages:
before-after-hook@2.2.3:
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
bintrees@1.0.2:
resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==}
body-parser@1.20.2:
resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
@ -1716,6 +1726,10 @@ packages:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
engines: {node: '>=0.4.0'}
prom-client@15.1.3:
resolution: {integrity: sha512-6ZiOBfCywsD4k1BN9IX0uZhF+tJkV8q8llP64G5Hajs4JOeVLPCwpPVcpXy3BwYiUGgyJzsJJQeOIv7+hDSq8g==}
engines: {node: ^16 || ^18 || >=20}
proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
@ -1991,6 +2005,9 @@ packages:
tar-stream@3.1.7:
resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
tdigest@0.1.2:
resolution: {integrity: sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==}
text-hex@1.0.0:
resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==}
@ -2476,6 +2493,8 @@ snapshots:
dependencies:
'@octokit/openapi-types': 22.2.0
'@opentelemetry/api@1.9.0': {}
'@pkgr/core@0.1.1': {}
'@prisma/client@5.13.0(prisma@5.13.0)':
@ -2816,6 +2835,8 @@ snapshots:
before-after-hook@2.2.3: {}
bintrees@1.0.2: {}
body-parser@1.20.2:
dependencies:
bytes: 3.1.2
@ -4168,6 +4189,11 @@ snapshots:
progress@2.0.3: {}
prom-client@15.1.3:
dependencies:
'@opentelemetry/api': 1.9.0
tdigest: 0.1.2
proxy-addr@2.0.7:
dependencies:
forwarded: 0.2.0
@ -4512,6 +4538,10 @@ snapshots:
fast-fifo: 1.3.2
streamx: 2.16.1
tdigest@0.1.2:
dependencies:
bintrees: 1.0.2
text-hex@1.0.0: {}
text-table@0.2.0: {}

View File

@ -3,6 +3,7 @@ import { scheduleJob } from "node-schedule";
import { ExtendedClient } from "../../interfaces/ExtendedClient";
import { maintainSecurity } from "../../modules/maintainSecurity";
import { postBirthdays } from "../../modules/postBirthdays";
import { Prometheus } from "../../modules/prometheus";
import { registerCommands } from "../../utils/registerCommands";
import { sendDebugMessage } from "../../utils/sendDebugMessage";
@ -14,6 +15,7 @@ import { sendDebugMessage } from "../../utils/sendDebugMessage";
export const onReady = async (bot: ExtendedClient) => {
await sendDebugMessage(bot, `Logged in as ${bot.user?.tag}`);
await registerCommands(bot);
bot.analytics = new Prometheus(bot);
// Daily at 9am PST
scheduleJob("birthdays", "0 9 * * *", async () => await postBirthdays(bot));

View File

@ -15,4 +15,5 @@ export const onGuildCreate = async function (
await bot.env.debugHook.send({
content: `JOINED GUILD: ${guild.name} (${guild.id}) - owned by ${owner?.displayName} (${owner.id})`
});
bot.analytics.updateGuilds(bot);
};

View File

@ -36,6 +36,7 @@ export const onGuildDelete = async function (
await bot.db.security
.deleteMany({ where: { serverId: guild.id } })
.catch(() => null);
bot.analytics.updateGuilds(bot);
} catch (err) {
await errorHandler(bot, "on guild delete", err);
}

View File

@ -35,6 +35,7 @@ export const onInteraction = async (
);
return;
}
bot.analytics.commandUsed();
if (interaction.isChatInputCommand()) {
handleChatInputCommand(bot, interaction);
}

View File

@ -4,6 +4,8 @@ import { Client, WebhookClient } from "discord.js";
import { Command } from "./Command";
import { Context } from "./Context";
import type { Prometheus } from "../modules/prometheus.js";
export interface ExtendedClient extends Client {
env: {
token: string;
@ -12,6 +14,7 @@ export interface ExtendedClient extends Client {
devMode: boolean;
};
db: PrismaClient;
analytics: Prometheus;
commands: Command[];
contexts: Context[];
configs: { [serverId: string]: Omit<configs, "id"> };

29
src/modules/prometheus.ts Normal file
View File

@ -0,0 +1,29 @@
import client, { Counter, Gauge } from "prom-client";
import type { ExtendedClient } from "../interfaces/ExtendedClient.js";
export class Prometheus {
private client = client;
private guilds: Gauge;
private commands: Counter;
constructor(bot: ExtendedClient) {
this.guilds = new Gauge({
name: "guilds",
help: "The number of guilds the bot is in."
});
this.guilds.set(bot.guilds.cache.size);
this.commands = new Counter({
name: "commands",
help: "The number of commands that have been used since last boot."
});
this.client.collectDefaultMetrics();
}
public commandUsed() {
this.commands.inc();
}
public updateGuilds(bot: ExtendedClient) {
this.guilds.set(bot.guilds.cache.size);
}
}

View File

@ -6,6 +6,7 @@ import https from "https";
import { Octokit } from "@octokit/rest";
import { GuildTextBasedChannel } from "discord.js";
import express from "express";
import { register } from "prom-client";
import { IgnoredActors, ThankYou } from "../config/Github";
import { ExtendedClient } from "../interfaces/ExtendedClient";
@ -47,6 +48,15 @@ export const serve = async (bot: ExtendedClient) => {
res.send("bot online!");
});
app.get("/metrics", async (_req, res) => {
try {
res.set("Content-Type", register.contentType);
res.end(await register.metrics());
} catch (err) {
res.status(500).end(err);
}
});
app.post("/kofi", async (req, res) => {
const payload = JSON.parse(req.body.data);
const {