diff --git a/Dockerfile.socket b/Dockerfile.socket index 444c42d..53540d0 100644 --- a/Dockerfile.socket +++ b/Dockerfile.socket @@ -10,5 +10,6 @@ RUN npm install -g --unsafe-perm prisma RUN npx prisma generate EXPOSE 4000 +EXPOSE 4001 CMD [ "npm", "run", "socket" ] diff --git a/docker-compose.yml b/docker-compose.yml index b9f7dc5..013457f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -73,6 +73,7 @@ services: SOCKET_PORT: '${SOCKET_PORT}' ports: - '127.0.0.1:${SOCKET_PORT}:4000' + - '127.0.0.1:${SOCKET_METRICS_PORT}:4001' networks: - back diff --git a/package-lock.json b/package-lock.json index 892dd86..8e0e2a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "node-fetch": "^2.6.7", "node-pushnotifications": "^2.0.3", "nodemailer": "^6.8.0", + "prom-client": "^14.1.0", "socket.io": "^4.5.1", "webhook-discord": "^3.7.8" }, @@ -1382,6 +1383,11 @@ "node": ">=8" } }, + "node_modules/bintrees": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", + "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" + }, "node_modules/bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -4043,6 +4049,17 @@ "node": ">=0.4.0" } }, + "node_modules/prom-client": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.1.0.tgz", + "integrity": "sha512-iFWCchQmi4170omLpFXbzz62SQTmPhtBL35v0qGEVRHKcqIeiexaoYeP0vfZTujxEq3tA87iqOdRbC9svS1B9A==", + "dependencies": { + "tdigest": "^0.1.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -4803,6 +4820,14 @@ "node": ">= 10" } }, + "node_modules/tdigest": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", + "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", + "dependencies": { + "bintrees": "1.0.2" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -6292,6 +6317,11 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bintrees": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.2.tgz", + "integrity": "sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==" + }, "bn.js": { "version": "4.12.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", @@ -8327,6 +8357,14 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, + "prom-client": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.1.0.tgz", + "integrity": "sha512-iFWCchQmi4170omLpFXbzz62SQTmPhtBL35v0qGEVRHKcqIeiexaoYeP0vfZTujxEq3tA87iqOdRbC9svS1B9A==", + "requires": { + "tdigest": "^0.1.1" + } + }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -8888,6 +8926,14 @@ "yallist": "^4.0.0" } }, + "tdigest": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.2.tgz", + "integrity": "sha512-+G0LLgjjo9BZX2MfdvPfH+MKLCrxlXSYec5DaPYP1fe6Iyhf0/fSmJ0bFiZ1F8BT6cGXl2LpltQptzjXKWEkKA==", + "requires": { + "bintrees": "1.0.2" + } + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", diff --git a/package.json b/package.json index 9aaeb07..9f5125d 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "node-fetch": "^2.6.7", "node-pushnotifications": "^2.0.3", "nodemailer": "^6.8.0", + "prom-client": "^14.1.0", "socket.io": "^4.5.1", "webhook-discord": "^3.7.8" }, diff --git a/src/services/socket/listeners/chat.socket.listeners.ts b/src/services/socket/listeners/chat.socket.listeners.ts index e683238..b203ed4 100644 --- a/src/services/socket/listeners/chat.socket.listeners.ts +++ b/src/services/socket/listeners/chat.socket.listeners.ts @@ -4,9 +4,10 @@ import { AuthenticatedSocket } from '@interfaces/auth.interface'; import { throwIfNotFunction, throwIfNotNumber, throwIfNotString } from '@utils/controller.utils'; import { Logger } from '@utils/logs.utils'; -import { handleSocketRawError, getUsersSockets } from '@utils/socket.utils'; +import { handleSocketRawError } from '@utils/socket.utils'; import * as chatSocket from '../emitters/chat.socket.emitters'; +import { totalUsersOnSocket } from '@services/socket/metrics/metrics.socket'; export function startSocket() { chatNamespace.on('connection', async (socket: AuthenticatedSocket) => { @@ -16,6 +17,7 @@ export function startSocket() { handleSocketRawError(null, error); } const logger = new Logger('Chat Socket', socket.user); + totalUsersOnSocket.inc(); logger.log('Connected'); socket.on('join_conversation', async (data, callback) => { @@ -106,6 +108,7 @@ export function startSocket() { socket.on('disconnecting', async () => { try { await chatSocket.sendConnectionStatus(socket, false); + totalUsersOnSocket.dec(); logger.log('Disconnected'); } catch (error) { console.error('Disconnection handling error', error); diff --git a/src/services/socket/listeners/dropy.socket.listeners.ts b/src/services/socket/listeners/dropy.socket.listeners.ts index 6b64a6e..bc53c80 100644 --- a/src/services/socket/listeners/dropy.socket.listeners.ts +++ b/src/services/socket/listeners/dropy.socket.listeners.ts @@ -14,7 +14,6 @@ export function startSocket() { dropyNamespace.on('connection', async (socket: AuthenticatedSocket) => { const logger = new Logger('Dropy Socket', socket.user); socket.user = await resetUserBadgeNotification(socket.user); - logger.log('Connected'); socket.on('zones_update', async (data: any, callback) => { diff --git a/src/services/socket/metrics/metrics.socket.ts b/src/services/socket/metrics/metrics.socket.ts new file mode 100644 index 0000000..8ba9ae5 --- /dev/null +++ b/src/services/socket/metrics/metrics.socket.ts @@ -0,0 +1,14 @@ +import Prometheus, { Counter, Gauge } from 'prom-client'; +import { Express } from 'express'; + +export const totalUsersOnSocket = new Gauge({ + name: 'total_users_on_socket', + help: 'Number of users connected to the socket', +}); + +export const addMetricsRoute = (server: Express) => { + server.get('/metrics', async (req, res) => { + res.set('Content-Type', Prometheus.register.contentType); + res.end(await Prometheus.register.metrics()); + }); +}; diff --git a/src/services/socket/socket.ts b/src/services/socket/socket.ts index 9fcd770..06333da 100644 --- a/src/services/socket/socket.ts +++ b/src/services/socket/socket.ts @@ -1,5 +1,6 @@ import { Server } from 'socket.io'; -import { NextFunction } from 'express'; +import express, { NextFunction } from 'express'; +import { addMetricsRoute } from './metrics/metrics.socket'; import { logStartedService } from '@/utils/logs.utils'; @@ -11,6 +12,7 @@ import * as dropySocket from './listeners/dropy.socket.listeners'; import * as chatSocket from './listeners/chat.socket.listeners'; const socketPort = 4000; +const metricsPort = 4001; const io = new Server(socketPort, { maxHttpBufferSize: 1e8, @@ -38,4 +40,11 @@ chatSocket.startSocket(); logStartedService('Dropy Socket', socketPort); +const metrics_server = express(); +addMetricsRoute(metrics_server); + +metrics_server.listen(metricsPort, () => { + logStartedService('Socket Metrics', metricsPort); +}); + export default io;