add mongo stats to diagnostic service

This commit is contained in:
Alex Holliday committed 2026-05-26 13:31:13 -07:00
1 parent 2764468e90
commit 2d432e0a3d
4 files changed
+127 -49

No files matched your search

+66 -45
View File
@@ -121,7 +121,7 @@ import {
IMaintenanceWindowsRepository,
} from "@/repositories/index.js";
import { ILogger } from "@/utils/logger.js";
import { AppError } from "@/utils/AppError.js";
// import { AppError } from "@/utils/AppError.js";
export type InitializedServices = {
settingsService: ISettingsService;
@@ -173,55 +173,76 @@ export const initializeServices = async ({
}): Promise<InitializedServices> => {
// Create DB
const dbType = envSettings.dbType;
let db: IDb | null = null;
if (dbType === "mongodb") {
db = new MongoDB(logger, envSettings);
}
if (!db) {
throw new AppError({ message: "Unsupported database type", status: 500 });
}
db = new MongoDB(logger, envSettings);
await db.connect();
let monitorsRepository: IMonitorsRepository;
let checksRepository: IChecksRepository;
let geoChecksRepository: IGeoChecksRepository;
let monitorStatsRepository: IMonitorStatsRepository;
let statusPagesRepository: IStatusPagesRepository;
let usersRepository: IUsersRepository;
let invitesRepository: IInvitesRepository;
let recoveryTokensRepository: IRecoveryTokensRepository;
let settingsRepository: ISettingsRepository;
let notificationsRepository: INotificationsRepository;
let tagsRepository: ITagsRepository;
let incidentsRepository: IIncidentsRepository;
let teamsRepository: ITeamsRepository;
let maintenanceWindowsRepository: IMaintenanceWindowsRepository;
// ** NOTE **
// const dbType = envSettings.dbType;
// DB type has been fixed to MongoDB for now
// if (dbType === "mongodb") {
// db = new MongoDB(logger, envSettings);
// }
// if (!db) {
// throw new AppError({ message: "Unsupported database type", status: 500 });
// }
// await db.connect();
// Repositories
if (dbType === "mongodb") {
monitorsRepository = new MongoMonitorsRepository();
checksRepository = new MongoChecksRepository(logger);
geoChecksRepository = new MongoGeoChecksRepository(logger);
monitorStatsRepository = new MongoMonitorStatsRepository();
statusPagesRepository = new MongoStatusPagesRepository();
usersRepository = new MongoUsersRepository();
invitesRepository = new MongoInvitesRepository();
recoveryTokensRepository = new MongoRecoveryTokensRepository();
settingsRepository = new MongoSettingsRepository();
notificationsRepository = new MongoNotificationsRepository();
tagsRepository = new MongoTagsRepository();
incidentsRepository = new MongoIncidentsRepository();
teamsRepository = new MongoTeamsRepository();
maintenanceWindowsRepository = new MongoMaintenanceWindowsRepository();
} else {
throw new AppError({ message: "Unsupported database type", status: 500 });
}
// ** NOTE **
// DB Type fixed to MongoDB for now
// let monitorsRepository: IMonitorsRepository;
// let checksRepository: IChecksRepository;
// let geoChecksRepository: IGeoChecksRepository;
// let monitorStatsRepository: IMonitorStatsRepository;
// let statusPagesRepository: IStatusPagesRepository;
// let usersRepository: IUsersRepository;
// let invitesRepository: IInvitesRepository;
// let recoveryTokensRepository: IRecoveryTokensRepository;
// let settingsRepository: ISettingsRepository;
// let notificationsRepository: INotificationsRepository;
// let tagsRepository: ITagsRepository;
// let incidentsRepository: IIncidentsRepository;
// let teamsRepository: ITeamsRepository;
// let maintenanceWindowsRepository: IMaintenanceWindowsRepository;
// if (dbType === "mongodb") {
// monitorsRepository = new MongoMonitorsRepository();
// checksRepository = new MongoChecksRepository(logger);
// geoChecksRepository = new MongoGeoChecksRepository(logger);
// monitorStatsRepository = new MongoMonitorStatsRepository();
// statusPagesRepository = new MongoStatusPagesRepository();
// usersRepository = new MongoUsersRepository();
// invitesRepository = new MongoInvitesRepository();
// recoveryTokensRepository = new MongoRecoveryTokensRepository();
// settingsRepository = new MongoSettingsRepository();
// notificationsRepository = new MongoNotificationsRepository();
// tagsRepository = new MongoTagsRepository();
// incidentsRepository = new MongoIncidentsRepository();
// teamsRepository = new MongoTeamsRepository();
// maintenanceWindowsRepository = new MongoMaintenanceWindowsRepository();
// } else {
// throw new AppError({ message: "Unsupported database type", status: 500 });
// }
const monitorsRepository = new MongoMonitorsRepository();
const checksRepository = new MongoChecksRepository(logger);
const geoChecksRepository = new MongoGeoChecksRepository(logger);
const monitorStatsRepository = new MongoMonitorStatsRepository();
const statusPagesRepository = new MongoStatusPagesRepository();
const usersRepository = new MongoUsersRepository();
const invitesRepository = new MongoInvitesRepository();
const recoveryTokensRepository = new MongoRecoveryTokensRepository();
const settingsRepository = new MongoSettingsRepository();
const notificationsRepository = new MongoNotificationsRepository();
const tagsRepository = new MongoTagsRepository();
const incidentsRepository = new MongoIncidentsRepository();
const teamsRepository = new MongoTeamsRepository();
const maintenanceWindowsRepository = new MongoMaintenanceWindowsRepository();
// Inject settings repository into settings service (now that DB is connected)
(settingsService as SettingsService).setRepository(settingsRepository);
@@ -340,7 +361,7 @@ export const initializeServices = async ({
teamsRepository,
});
const diagnosticService = new DiagnosticService();
const diagnosticService = new DiagnosticService(db);
const inviteService = new InviteService({
invitesRepository,
settingsService,
+2 -1
View File
@@ -1,4 +1,5 @@
export interface IDb {
export interface IDb<TConnection = unknown> {
connect(): Promise<void>;
disconnect(): Promise<void>;
getConnection(): Promise<TConnection>;
}
+13 -2
View File
@@ -1,3 +1,4 @@
import { Mongoose } from "mongoose";
import mongoose from "mongoose";
import AppSettings from "@/db/models/AppSettings.js";
import { runMigrations } from "@/db/migration/index.js";
@@ -5,16 +6,19 @@ import { ILogger } from "@/utils/logger.js";
import { EnvConfig } from "@/service/system/settingsService.js";
const SERVICE_NAME = "MongoDB";
import { IDb } from "@/db/IDb.js";
import { AppError } from "@/utils/AppError.js";
class MongoDB implements IDb {
class MongoDB implements IDb<Mongoose> {
static SERVICE_NAME = SERVICE_NAME;
private logger: ILogger;
private envSettings: EnvConfig;
private db: Mongoose | null;
constructor(logger: ILogger, envSettings: EnvConfig) {
this.logger = logger;
this.envSettings = envSettings;
this.db = null;
}
get serviceName() {
@@ -24,7 +28,7 @@ class MongoDB implements IDb {
connect = async () => {
try {
const connectionString = this.envSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db";
await mongoose.connect(connectionString);
this.db = await mongoose.connect(connectionString);
// If there are no AppSettings, create one // TODO why is this here?
await AppSettings.findOneAndUpdate(
{}, // empty filter to match any document
@@ -63,6 +67,13 @@ class MongoDB implements IDb {
}
};
getConnection = async (): Promise<Mongoose> => {
if (this.db === null) {
throw new AppError({ message: "Database not connected", service: SERVICE_NAME, method: "getConnection" });
}
return this.db;
};
disconnect = async () => {
try {
this.logger.info({ message: "Disconnecting from MongoDB", service: SERVICE_NAME, method: "disconnect" });
@@ -1,5 +1,8 @@
import v8 from "v8";
import os from "os";
import { IDb } from "@/db/IDb.js";
import { Mongoose } from "mongoose";
import { AppError } from "@/utils/AppError.js";
const SERVICE_NAME = "diagnosticService";
@@ -18,7 +21,7 @@ export interface IDiagnosticService {
export class DiagnosticService implements IDiagnosticService {
static SERVICE_NAME = SERVICE_NAME;
constructor() {
constructor(private db: IDb<Mongoose>) {
/**
* Performance Observer for monitoring system performance metrics.
* Clears performance marks after each measurement to prevent memory leaks.
@@ -47,6 +50,44 @@ export class DiagnosticService implements IDiagnosticService {
return cpuUsage;
};
private getMongoDBStats = async () => {
const mongo = await this.db.getConnection();
const db = mongo.connection.db;
if (!db) {
throw new AppError({ message: "Database connection is not available", service: SERVICE_NAME, method: "getMongoDBStats" });
}
const readyState = mongo.connection.readyState;
const host = mongo.connection.host;
const port = mongo.connection.port;
const dbName = mongo.connection.name;
const serverStatus = await db.admin().serverStatus();
const serverInfo = await db.admin().serverInfo();
const stats = await db.stats();
const collections = await db.collections();
const collectionStats = await Promise.all(
(collections || []).map(async (collection) => {
const stats = await db.command({ collStats: collection.collectionName });
const documentCount = await db.collection(collection.collectionName).countDocuments({});
return { name: collection.collectionName, documentCount, ...stats };
})
);
return {
readyState,
host,
port,
dbName,
serverStatus,
serverInfo,
stats,
collectionStats,
};
};
getSystemStats = async () => {
// Memory Usage
const totalMemory = os.totalmem();
@@ -93,6 +134,9 @@ export class DiagnosticService implements IDiagnosticService {
// Uptime
const uptimeMs = process.uptime() * 1000; // ms
// Mongo
const mongoStats = await this.getMongoDBStats();
// Combine Metrics
const diagnostics = {
osStats,
@@ -101,6 +145,7 @@ export class DiagnosticService implements IDiagnosticService {
v8HeapStats: v8Metrics,
eventLoopDelayMs: eventLoopDelay,
uptimeMs,
mongoStats,
};
return diagnostics;