gadget/docs/archive/services/host-monitor.ts

174 lines
4.1 KiB
TypeScript

// src/services/host-monitor.ts
// Copyright (C) 2025 DTP Technologies, LLC
// All Rights Reserved
import env from "../config/env.js";
import os from "node:os";
import v8 from "node:v8";
import { EventEmitter } from "node:events";
import { CronJob } from "cron";
import HostMonitor, { IHostMonitor } from "@/models/host-monitor.js";
import { DtpService } from "../lib/service.js";
export interface IHostMonitorStats {
memoryUtilization: number;
rss: number;
heapTotal: number;
heapUsed: number;
heapExternal: number;
osTotal: number;
osFree: number;
timestamp: Date;
}
class HostMonitorService extends DtpService {
private cronJob: CronJob | undefined;
private stats: IHostMonitor;
private eventEmitter: EventEmitter;
get name(): string {
return "HostMonitorService";
}
get slug(): string {
return "search";
}
constructor() {
super();
this.stats = this.createMonitor();
this.stats.hostname = os.hostname();
this.eventEmitter = new EventEmitter();
}
async start(): Promise<void> {
const { heap_size_limit } = v8.getHeapStatistics();
const limitMB = (heap_size_limit / 1024 / 1024).toFixed(2);
this.log.info("starting host monitor cron job");
this.cronJob = new CronJob(
"*/15 * * * * *",
this.onStoreStats.bind(this),
null,
true,
env.timezone,
);
this.log.info("service started", { heapLimit: limitMB });
}
async stop(): Promise<void> {
if (this.cronJob) {
this.log.info("stopping host monitor cron job");
this.cronJob.stop();
delete this.cronJob;
}
this.log.info("service stopped");
}
subagent(bytes: number): void {
this.stats.memory.ai.subagents.count += 1;
this.stats.memory.ai.subagents.bytes += bytes;
}
fileOperation(bytes: number): void {
this.stats.memory.ai.fileOperations.count += 1;
this.stats.memory.ai.fileOperations.bytes += bytes;
}
toolCall(bytes: number): void {
this.stats.memory.ai.toolCalls.count += 1;
this.stats.memory.ai.toolCalls.bytes += bytes;
}
on(event: string, listener: (...args: any[]) => void): void {
this.eventEmitter.on(event, listener);
}
off(event: string, listener: (...args: any[]) => void): void {
this.eventEmitter.off(event, listener);
}
async onStoreStats(): Promise<void> {
const NOW = new Date();
this.stats.timestamp = NOW;
const usage: NodeJS.MemoryUsage = process.memoryUsage();
this.stats.memory.rss = usage.rss;
this.stats.memory.v8.heapTotal = usage.heapTotal;
this.stats.memory.v8.heapUsed = usage.heapUsed;
this.stats.memory.v8.heapExternal = usage.external ?? 0;
const osTotal = os.totalmem();
const osFree = os.freemem();
this.stats.memory.os.total = osTotal;
this.stats.memory.os.free = osFree;
// store stats to db
await this.stats.save();
// Calculate memory utilization percentage
const memoryUsed = osTotal - osFree;
const memoryUtilization = Math.round((memoryUsed / osTotal) * 100);
// Emit stats event for UI updates
const statsData: IHostMonitorStats = {
memoryUtilization,
rss: usage.rss,
heapTotal: usage.heapTotal,
heapUsed: usage.heapUsed,
heapExternal: usage.external ?? 0,
osTotal,
osFree,
timestamp: NOW,
};
this.eventEmitter.emit("stats", statsData);
this.stats = this.createMonitor();
}
createMonitor(): IHostMonitor {
const monitor = new HostMonitor();
monitor.hostname = os.hostname();
const usage = process.memoryUsage();
monitor.memory = {
rss: usage.rss,
os: {
total: os.totalmem(),
free: os.freemem(),
},
v8: {
heapTotal: usage.heapTotal,
heapUsed: usage.heapUsed,
heapExternal: usage.external ?? 0,
},
logs: {
count: 0,
bytes: 0,
},
ai: {
fileOperations: {
count: 0,
bytes: 0,
},
subagents: {
count: 0,
bytes: 0,
},
toolCalls: {
count: 0,
bytes: 0,
},
},
};
return monitor;
}
}
export default new HostMonitorService();