mirror of
https://gitlab.com/soapbox-pub/ditto.git
synced 2025-12-06 03:19:46 +00:00
Add Prometheus metrics
This commit is contained in:
parent
766290bd45
commit
31a5533fd7
10 changed files with 105 additions and 2 deletions
|
|
@ -59,6 +59,7 @@
|
|||
"nostr-relaypool": "npm:nostr-relaypool2@0.6.34",
|
||||
"nostr-tools": "npm:nostr-tools@2.5.1",
|
||||
"nostr-wasm": "npm:nostr-wasm@^0.1.0",
|
||||
"prom-client": "npm:prom-client@^15.1.2",
|
||||
"question-deno": "https://raw.githubusercontent.com/ocpu/question-deno/10022b8e52555335aa510adb08b0a300df3cf904/mod.ts",
|
||||
"tldts": "npm:tldts@^6.0.14",
|
||||
"tseep": "npm:tseep@^1.2.1",
|
||||
|
|
|
|||
|
|
@ -50,6 +50,12 @@ server {
|
|||
root /opt/ditto/public;
|
||||
}
|
||||
|
||||
location /metrics {
|
||||
allow 127.0.0.1;
|
||||
deny all;
|
||||
proxy_pass http://ditto;
|
||||
}
|
||||
|
||||
location ~ ^/(instance|sw\.js$|sw\.js\.map$) {
|
||||
root /opt/ditto/public;
|
||||
try_files $uri =404;
|
||||
|
|
|
|||
14
src/controllers/metrics.ts
Normal file
14
src/controllers/metrics.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
import { register } from 'prom-client';
|
||||
|
||||
import { AppController } from '@/app.ts';
|
||||
|
||||
/** Prometheus/OpenMetrics controller. */
|
||||
export const metricsController: AppController = async (c) => {
|
||||
const metrics = await register.metrics();
|
||||
|
||||
const headers: HeadersInit = {
|
||||
'Content-Type': register.contentType,
|
||||
};
|
||||
|
||||
return c.text(metrics, 200, headers);
|
||||
};
|
||||
|
|
@ -10,6 +10,7 @@ import {
|
|||
|
||||
import { AppController } from '@/app.ts';
|
||||
import { relayInfoController } from '@/controllers/nostr/relay-info.ts';
|
||||
import { relayEventCounter, relayMessageCounter } from '@/metrics.ts';
|
||||
import * as pipeline from '@/pipeline.ts';
|
||||
import { RelayError } from '@/RelayError.ts';
|
||||
import { Storages } from '@/storages.ts';
|
||||
|
|
@ -22,6 +23,7 @@ function connectStream(socket: WebSocket) {
|
|||
const controllers = new Map<string, AbortController>();
|
||||
|
||||
socket.onmessage = (e) => {
|
||||
relayMessageCounter.inc();
|
||||
const result = n.json().pipe(n.clientMsg()).safeParse(e.data);
|
||||
if (result.success) {
|
||||
handleMsg(result.data);
|
||||
|
|
@ -40,15 +42,18 @@ function connectStream(socket: WebSocket) {
|
|||
function handleMsg(msg: NostrClientMsg) {
|
||||
switch (msg[0]) {
|
||||
case 'REQ':
|
||||
relayEventCounter.inc();
|
||||
handleReq(msg);
|
||||
return;
|
||||
case 'EVENT':
|
||||
relayEventCounter.inc({ kind: msg[1].kind.toString() });
|
||||
handleEvent(msg);
|
||||
return;
|
||||
case 'CLOSE':
|
||||
handleClose(msg);
|
||||
return;
|
||||
case 'COUNT':
|
||||
relayEventCounter.inc();
|
||||
handleCount(msg);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import { Stickynotes } from '@soapbox/stickynotes';
|
||||
|
||||
import { firehoseEventCounter } from '@/metrics.ts';
|
||||
import { Storages } from '@/storages.ts';
|
||||
import { nostrNow } from '@/utils.ts';
|
||||
|
||||
|
|
@ -12,13 +13,14 @@ const console = new Stickynotes('ditto:firehose');
|
|||
* side-effects based on them, such as trending hashtag tracking
|
||||
* and storing events for notifications and the home feed.
|
||||
*/
|
||||
export async function startFirehose() {
|
||||
export async function startFirehose(): Promise<void> {
|
||||
const store = await Storages.client();
|
||||
|
||||
for await (const msg of store.req([{ kinds: [0, 1, 3, 5, 6, 7, 9735, 10002], limit: 0, since: nostrNow() }])) {
|
||||
if (msg[0] === 'EVENT') {
|
||||
const event = msg[2];
|
||||
console.debug(`NostrEvent<${event.kind}> ${event.id}`);
|
||||
firehoseEventCounter.inc({ kind: event.kind });
|
||||
|
||||
pipeline
|
||||
.handleEvent(event, AbortSignal.timeout(5000))
|
||||
|
|
|
|||
58
src/metrics.ts
Normal file
58
src/metrics.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { Counter } from 'prom-client';
|
||||
|
||||
export const httpRequestCounter = new Counter({
|
||||
name: 'http_requests_total',
|
||||
help: 'Total number of HTTP requests',
|
||||
labelNames: ['method', 'path'],
|
||||
});
|
||||
|
||||
export const fetchCounter = new Counter({
|
||||
name: 'fetch_total',
|
||||
help: 'Total number of fetch requests',
|
||||
labelNames: ['method', 'path'],
|
||||
});
|
||||
|
||||
export const firehoseEventCounter = new Counter({
|
||||
name: 'firehose_events_total',
|
||||
help: 'Total number of Nostr events processed by the firehose',
|
||||
labelNames: ['kind'],
|
||||
});
|
||||
|
||||
export const pipelineEventCounter = new Counter({
|
||||
name: 'pipeline_events_total',
|
||||
help: 'Total number of Nostr events processed by the pipeline',
|
||||
labelNames: ['kind'],
|
||||
});
|
||||
|
||||
export const relayReqCounter = new Counter({
|
||||
name: 'relay_reqs_total',
|
||||
help: 'Total number of REQ messages processed by the relay',
|
||||
});
|
||||
|
||||
export const relayEventCounter = new Counter({
|
||||
name: 'relay_events_total',
|
||||
help: 'Total number of EVENT messages processed by the relay',
|
||||
labelNames: ['kind'],
|
||||
});
|
||||
|
||||
export const relayCountCounter = new Counter({
|
||||
name: 'relay_counts_total',
|
||||
help: 'Total number of COUNT messages processed by the relay',
|
||||
});
|
||||
|
||||
export const relayMessageCounter = new Counter({
|
||||
name: 'relay_messages_total',
|
||||
help: 'Total number of Nostr messages processed by the relay',
|
||||
});
|
||||
|
||||
export const dbQueryCounter = new Counter({
|
||||
name: 'db_query_total',
|
||||
help: 'Total number of database queries',
|
||||
labelNames: ['kind'],
|
||||
});
|
||||
|
||||
export const dbEventCounter = new Counter({
|
||||
name: 'db_events_total',
|
||||
help: 'Total number of database inserts',
|
||||
labelNames: ['kind'],
|
||||
});
|
||||
10
src/middleware/metricsMiddleware.ts
Normal file
10
src/middleware/metricsMiddleware.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { MiddlewareHandler } from '@hono/hono';
|
||||
|
||||
import { httpRequestCounter } from '@/metrics.ts';
|
||||
|
||||
export const metricsMiddleware: MiddlewareHandler = async (c, next) => {
|
||||
const { method, path } = c.req;
|
||||
httpRequestCounter.inc({ method, path });
|
||||
|
||||
await next();
|
||||
};
|
||||
|
|
@ -7,6 +7,7 @@ import { Conf } from '@/config.ts';
|
|||
import { DittoDB } from '@/db/DittoDB.ts';
|
||||
import { deleteAttachedMedia } from '@/db/unattached-media.ts';
|
||||
import { DittoEvent } from '@/interfaces/DittoEvent.ts';
|
||||
import { pipelineEventCounter } from '@/metrics.ts';
|
||||
import { RelayError } from '@/RelayError.ts';
|
||||
import { AdminSigner } from '@/signers/AdminSigner.ts';
|
||||
import { hydrateEvents } from '@/storages/hydrate.ts';
|
||||
|
|
@ -36,6 +37,7 @@ async function handleEvent(event: DittoEvent, signal: AbortSignal): Promise<void
|
|||
if (encounterEvent(event)) return;
|
||||
if (await existsInDB(event)) return;
|
||||
debug(`NostrEvent<${event.kind}> ${event.id}`);
|
||||
pipelineEventCounter.inc({ kind: event.kind });
|
||||
|
||||
if (event.kind !== 24133) {
|
||||
await policyFilter(event);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { nip27 } from 'nostr-tools';
|
|||
|
||||
import { Conf } from '@/config.ts';
|
||||
import { DittoTables } from '@/db/DittoTables.ts';
|
||||
import { dbEventCounter, dbQueryCounter } from '@/metrics.ts';
|
||||
import { RelayError } from '@/RelayError.ts';
|
||||
import { purifyEvent } from '@/storages/hydrate.ts';
|
||||
import { isNostrId, isURL } from '@/utils.ts';
|
||||
|
|
@ -53,6 +54,7 @@ class EventsDB implements NStore {
|
|||
async event(event: NostrEvent, _opts?: { signal?: AbortSignal }): Promise<void> {
|
||||
event = purifyEvent(event);
|
||||
this.console.debug('EVENT', JSON.stringify(event));
|
||||
dbEventCounter.inc({ kind: event.kind });
|
||||
|
||||
if (await this.isDeletedAdmin(event)) {
|
||||
throw new RelayError('blocked', 'event deleted by admin');
|
||||
|
|
@ -137,6 +139,7 @@ class EventsDB implements NStore {
|
|||
/** Get events for filters from the database. */
|
||||
async query(filters: NostrFilter[], opts: { signal?: AbortSignal; limit?: number } = {}): Promise<NostrEvent[]> {
|
||||
filters = await this.expandFilters(filters);
|
||||
dbQueryCounter.inc();
|
||||
|
||||
for (const filter of filters) {
|
||||
if (filter.since && filter.since >= 2_147_483_647) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import * as Comlink from 'comlink';
|
||||
|
||||
import { FetchWorker } from './fetch.worker.ts';
|
||||
import './handlers/abortsignal.ts';
|
||||
|
||||
import type { FetchWorker } from './fetch.worker.ts';
|
||||
import { fetchCounter } from '@/metrics.ts';
|
||||
|
||||
const worker = new Worker(new URL('./fetch.worker.ts', import.meta.url), { type: 'module' });
|
||||
const client = Comlink.wrap<typeof FetchWorker>(worker);
|
||||
|
|
@ -24,6 +25,7 @@ const fetchWorker: typeof fetch = async (...args) => {
|
|||
await ready;
|
||||
const [url, init] = serializeFetchArgs(args);
|
||||
const { body, signal, ...rest } = init;
|
||||
fetchCounter.inc({ method: init.method, path: new URL(url).pathname });
|
||||
const result = await client.fetch(url, { ...rest, body: await prepareBodyForWorker(body) }, signal);
|
||||
return new Response(...result);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue