2 Commits

Author SHA1 Message Date
79db79de96 v3.0.3 2026-03-23 14:51:14 +00:00
a01e21663f fix(prometheus): clean up default Prometheus metric collectors on stop 2026-03-23 14:51:14 +00:00
5 changed files with 40 additions and 9 deletions

View File

@@ -1,5 +1,11 @@
# Changelog # Changelog
## 2026-03-23 - 3.0.3 - fix(prometheus)
clean up default Prometheus metric collectors on stop
- Return cleanup handlers from default metric registration for event loop and GC observers.
- Dispose registered Prometheus default metric collectors when stopping Smartmetrics to prevent lingering observers.
## 2026-03-02 - 3.0.2 - fix(pidusage) ## 2026-03-02 - 3.0.2 - fix(pidusage)
prune history entries for PIDs not present in the requested set to avoid stale data and memory growth prune history entries for PIDs not present in the requested set to avoid stale data and memory growth

View File

@@ -1,6 +1,6 @@
{ {
"name": "@push.rocks/smartmetrics", "name": "@push.rocks/smartmetrics",
"version": "3.0.2", "version": "3.0.3",
"private": false, "private": false,
"description": "A package for easy collection and reporting of system and process metrics.", "description": "A package for easy collection and reporting of system and process metrics.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartmetrics', name: '@push.rocks/smartmetrics',
version: '3.0.2', version: '3.0.3',
description: 'A package for easy collection and reporting of system and process metrics.' description: 'A package for easy collection and reporting of system and process metrics.'
} }

View File

@@ -22,10 +22,11 @@ export class SmartMetrics {
// HTTP server for Prometheus endpoint // HTTP server for Prometheus endpoint
private prometheusServer?: plugins.http.Server; private prometheusServer?: plugins.http.Server;
private prometheusPort?: number; private prometheusPort?: number;
private cleanupDefaultMetrics?: () => void;
public setup() { public setup() {
this.registry = new plugins.prom.Registry(); this.registry = new plugins.prom.Registry();
plugins.prom.collectDefaultMetrics(this.registry); this.cleanupDefaultMetrics = plugins.prom.collectDefaultMetrics(this.registry);
// Initialize custom gauges // Initialize custom gauges
this.cpuPercentageGauge = new plugins.prom.Gauge({ this.cpuPercentageGauge = new plugins.prom.Gauge({
@@ -295,5 +296,9 @@ export class SmartMetrics {
public stop() { public stop() {
this.started = false; this.started = false;
this.disablePrometheusEndpoint(); this.disablePrometheusEndpoint();
if (this.cleanupDefaultMetrics) {
this.cleanupDefaultMetrics();
this.cleanupDefaultMetrics = undefined;
}
} }
} }

View File

@@ -288,20 +288,25 @@ export class Histogram implements IMetric {
// ── Default Metrics Collectors ────────────────────────────────────────────── // ── Default Metrics Collectors ──────────────────────────────────────────────
export function collectDefaultMetrics(registry: Registry): void { export function collectDefaultMetrics(registry: Registry): () => void {
registerProcessCpuTotal(registry); registerProcessCpuTotal(registry);
registerProcessStartTime(registry); registerProcessStartTime(registry);
registerProcessMemory(registry); registerProcessMemory(registry);
registerProcessOpenFds(registry); registerProcessOpenFds(registry);
registerProcessMaxFds(registry); registerProcessMaxFds(registry);
registerEventLoopLag(registry); const cleanupEventLoop = registerEventLoopLag(registry);
registerProcessHandles(registry); registerProcessHandles(registry);
registerProcessRequests(registry); registerProcessRequests(registry);
registerProcessResources(registry); registerProcessResources(registry);
registerHeapSizeAndUsed(registry); registerHeapSizeAndUsed(registry);
registerHeapSpaces(registry); registerHeapSpaces(registry);
registerVersion(registry); registerVersion(registry);
registerGc(registry); const cleanupGc = registerGc(registry);
return () => {
cleanupEventLoop();
cleanupGc();
};
} }
function registerProcessCpuTotal(registry: Registry): void { function registerProcessCpuTotal(registry: Registry): void {
@@ -420,7 +425,7 @@ function registerProcessMaxFds(registry: Registry): void {
}); });
} }
function registerEventLoopLag(registry: Registry): void { function registerEventLoopLag(registry: Registry): () => void {
let histogram: ReturnType<typeof monitorEventLoopDelay> | null = null; let histogram: ReturnType<typeof monitorEventLoopDelay> | null = null;
try { try {
histogram = monitorEventLoopDelay({ resolution: 10 }); histogram = monitorEventLoopDelay({ resolution: 10 });
@@ -496,6 +501,13 @@ function registerEventLoopLag(registry: Registry): void {
}, },
}); });
} }
return () => {
if (histogram) {
histogram.disable();
histogram = null;
}
};
} }
function registerProcessHandles(registry: Registry): void { function registerProcessHandles(registry: Registry): void {
@@ -632,7 +644,7 @@ function registerVersion(registry: Registry): void {
}); });
} }
function registerGc(registry: Registry): void { function registerGc(registry: Registry): () => void {
const gcHistogram = new Histogram({ const gcHistogram = new Histogram({
name: 'nodejs_gc_duration_seconds', name: 'nodejs_gc_duration_seconds',
help: 'Garbage collection duration by kind, in seconds.', help: 'Garbage collection duration by kind, in seconds.',
@@ -649,8 +661,9 @@ function registerGc(registry: Registry): void {
15: 'All', 15: 'All',
}; };
let obs: PerformanceObserver | null = null;
try { try {
const obs = new PerformanceObserver((list) => { obs = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) { for (const entry of list.getEntries()) {
const gcEntry = entry as any; const gcEntry = entry as any;
const kind = kindLabels[gcEntry.detail?.kind ?? gcEntry.kind] || 'Unknown'; const kind = kindLabels[gcEntry.detail?.kind ?? gcEntry.kind] || 'Unknown';
@@ -661,6 +674,13 @@ function registerGc(registry: Registry): void {
} catch { } catch {
// GC observation not available // GC observation not available
} }
return () => {
if (obs) {
obs.disconnect();
obs = null;
}
};
} }
// ── Helpers ───────────────────────────────────────────────────────────────── // ── Helpers ─────────────────────────────────────────────────────────────────