mirror of
https://iceshrimp.dev/limepotato/jormungandr-bite.git
synced 2025-03-04 07:18:50 -07:00
135 lines
3.4 KiB
TypeScript
135 lines
3.4 KiB
TypeScript
import Router from "@koa/router";
|
|
import {
|
|
collectDefaultMetrics,
|
|
register,
|
|
Gauge,
|
|
Counter,
|
|
CounterConfiguration,
|
|
} from "prom-client";
|
|
import config from "./config/index.js";
|
|
import { queues } from "./queue/queues.js";
|
|
import cluster from "node:cluster";
|
|
import Xev from "xev";
|
|
|
|
const xev = new Xev();
|
|
const metricsMaster = cluster.worker?.id === 1;
|
|
|
|
if (config.enableMetrics) {
|
|
if (metricsMaster) {
|
|
collectDefaultMetrics();
|
|
|
|
new Gauge({
|
|
name: "iceshrimp_queue_jobs",
|
|
help: "Amount of jobs in the bull queues",
|
|
labelNames: ["queue", "status"] as const,
|
|
async collect() {
|
|
for (const queue of queues) {
|
|
const counts = await queue.getJobCounts();
|
|
this.set({ queue: queue.name, status: "completed" }, counts.completed);
|
|
this.set({ queue: queue.name, status: "waiting" }, counts.waiting);
|
|
this.set({ queue: queue.name, status: "active" }, counts.active);
|
|
this.set({ queue: queue.name, status: "delayed" }, counts.delayed);
|
|
this.set({ queue: queue.name, status: "failed" }, counts.failed);
|
|
}
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
if (metricsMaster) {
|
|
xev.on("registry-request", async () => {
|
|
try {
|
|
const metrics = await register.metrics();
|
|
xev.emit("registry-response", {
|
|
contentType: register.contentType,
|
|
body: metrics
|
|
});
|
|
} catch (error) {
|
|
xev.emit("registry-response", { error });
|
|
}
|
|
});
|
|
}
|
|
|
|
export const handleMetrics: Router.Middleware = async (ctx) => {
|
|
try {
|
|
if (metricsMaster) {
|
|
ctx.set("content-type", register.contentType);
|
|
ctx.body = await register.metrics();
|
|
} else {
|
|
const wait = new Promise<void>((resolve, reject) => {
|
|
const timeout = setTimeout(
|
|
() => reject("Timeout while waiting for cluster master"),
|
|
1000 * 60
|
|
);
|
|
xev.once("registry-response", (response) => {
|
|
clearTimeout(timeout);
|
|
if (response.error) reject(response.error);
|
|
ctx.set("content-type", response.contentType);
|
|
ctx.body = response.body;
|
|
resolve();
|
|
});
|
|
});
|
|
xev.emit("registry-request");
|
|
await wait;
|
|
}
|
|
} catch (err) {
|
|
ctx.res.statusCode = 500;
|
|
ctx.body = err;
|
|
}
|
|
};
|
|
|
|
const counter = (configuration: CounterConfiguration<string>) => {
|
|
if (config.enableMetrics) {
|
|
if (metricsMaster) {
|
|
const counter = new Counter(configuration);
|
|
counter.reset(); // initialize internal hashmap
|
|
xev.on(`metrics-counter-${configuration.name}`, () => counter.inc());
|
|
return () => counter.inc();
|
|
} else {
|
|
return () => xev.emit(`metrics-counter-${configuration.name}`);
|
|
}
|
|
} else {
|
|
return () => { };
|
|
}
|
|
};
|
|
|
|
export const tickOutbox = counter({
|
|
name: "iceshrimp_outbox_total",
|
|
help: "Total AP outbox calls",
|
|
});
|
|
|
|
export const tickInbox = counter({
|
|
name: "iceshrimp_inbox_total",
|
|
help: "Total AP inbox calls",
|
|
});
|
|
|
|
export const tickFetch = counter({
|
|
name: "iceshrimp_fetch_total",
|
|
help: "Total AP fetch calls",
|
|
});
|
|
|
|
export const tickResolve = counter({
|
|
name: "iceshrimp_resolve_total",
|
|
help: "Total AP resolve calls",
|
|
});
|
|
|
|
export const tickObliterate = counter({
|
|
name: "iceshrimp_obliterate_total",
|
|
help: "Total obliterate jobs processed",
|
|
});
|
|
|
|
|
|
export const tickBiteIncoming = counter({
|
|
name: "iceshrimp_bite_remote_incoming_total",
|
|
help: "Total bites received from remote",
|
|
});
|
|
|
|
export const tickBiteOutgoing = counter({
|
|
name: "iceshrimp_bite_remote_outgoing_total",
|
|
help: "Total bites sent to remote",
|
|
});
|
|
|
|
export const tickBiteLocal = counter ({
|
|
name: "iceshrimp_bite_local_total",
|
|
help: "Total local bites"
|
|
});
|