139 lines
4.4 KiB
JavaScript
139 lines
4.4 KiB
JavaScript
import { collectDefaultMetrics, register, Gauge, Counter } 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();
|
|
if (config.metrics?.enable) {
|
|
if (cluster.isPrimary) {
|
|
collectDefaultMetrics();
|
|
new Gauge({
|
|
name: "iceshrimp_queue_jobs",
|
|
help: "Amount of jobs in the bull queues",
|
|
labelNames: [
|
|
"queue",
|
|
"status"
|
|
],
|
|
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 (cluster.isPrimary) {
|
|
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 = async (ctx)=>{
|
|
if (config.metrics?.token !== undefined) {
|
|
if (ctx.query.token === undefined) {
|
|
ctx.res.statusCode = 401;
|
|
ctx.body = "Missing token parameter";
|
|
return;
|
|
}
|
|
const correct = config.metrics.token === ctx.query.token;
|
|
if (!correct) {
|
|
ctx.res.statusCode = 403;
|
|
ctx.body = "Incorrect token";
|
|
return;
|
|
}
|
|
}
|
|
try {
|
|
if (cluster.isPrimary) {
|
|
ctx.set("content-type", register.contentType);
|
|
ctx.body = await register.metrics();
|
|
} else {
|
|
const wait = new Promise((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)=>{
|
|
if (config.metrics?.enable) {
|
|
if (cluster.isPrimary) {
|
|
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 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"
|
|
});
|