fix(prometheus): clean up default Prometheus metric collectors on stop
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
# 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)
|
||||
prune history entries for PIDs not present in the requested set to avoid stale data and memory growth
|
||||
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
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.'
|
||||
}
|
||||
|
||||
@@ -22,10 +22,11 @@ export class SmartMetrics {
|
||||
// HTTP server for Prometheus endpoint
|
||||
private prometheusServer?: plugins.http.Server;
|
||||
private prometheusPort?: number;
|
||||
private cleanupDefaultMetrics?: () => void;
|
||||
|
||||
public setup() {
|
||||
this.registry = new plugins.prom.Registry();
|
||||
plugins.prom.collectDefaultMetrics(this.registry);
|
||||
this.cleanupDefaultMetrics = plugins.prom.collectDefaultMetrics(this.registry);
|
||||
|
||||
// Initialize custom gauges
|
||||
this.cpuPercentageGauge = new plugins.prom.Gauge({
|
||||
@@ -295,5 +296,9 @@ export class SmartMetrics {
|
||||
public stop() {
|
||||
this.started = false;
|
||||
this.disablePrometheusEndpoint();
|
||||
if (this.cleanupDefaultMetrics) {
|
||||
this.cleanupDefaultMetrics();
|
||||
this.cleanupDefaultMetrics = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,20 +288,25 @@ export class Histogram implements IMetric {
|
||||
|
||||
// ── Default Metrics Collectors ──────────────────────────────────────────────
|
||||
|
||||
export function collectDefaultMetrics(registry: Registry): void {
|
||||
export function collectDefaultMetrics(registry: Registry): () => void {
|
||||
registerProcessCpuTotal(registry);
|
||||
registerProcessStartTime(registry);
|
||||
registerProcessMemory(registry);
|
||||
registerProcessOpenFds(registry);
|
||||
registerProcessMaxFds(registry);
|
||||
registerEventLoopLag(registry);
|
||||
const cleanupEventLoop = registerEventLoopLag(registry);
|
||||
registerProcessHandles(registry);
|
||||
registerProcessRequests(registry);
|
||||
registerProcessResources(registry);
|
||||
registerHeapSizeAndUsed(registry);
|
||||
registerHeapSpaces(registry);
|
||||
registerVersion(registry);
|
||||
registerGc(registry);
|
||||
const cleanupGc = registerGc(registry);
|
||||
|
||||
return () => {
|
||||
cleanupEventLoop();
|
||||
cleanupGc();
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
try {
|
||||
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 {
|
||||
@@ -632,7 +644,7 @@ function registerVersion(registry: Registry): void {
|
||||
});
|
||||
}
|
||||
|
||||
function registerGc(registry: Registry): void {
|
||||
function registerGc(registry: Registry): () => void {
|
||||
const gcHistogram = new Histogram({
|
||||
name: 'nodejs_gc_duration_seconds',
|
||||
help: 'Garbage collection duration by kind, in seconds.',
|
||||
@@ -649,8 +661,9 @@ function registerGc(registry: Registry): void {
|
||||
15: 'All',
|
||||
};
|
||||
|
||||
let obs: PerformanceObserver | null = null;
|
||||
try {
|
||||
const obs = new PerformanceObserver((list) => {
|
||||
obs = new PerformanceObserver((list) => {
|
||||
for (const entry of list.getEntries()) {
|
||||
const gcEntry = entry as any;
|
||||
const kind = kindLabels[gcEntry.detail?.kind ?? gcEntry.kind] || 'Unknown';
|
||||
@@ -661,6 +674,13 @@ function registerGc(registry: Registry): void {
|
||||
} catch {
|
||||
// GC observation not available
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (obs) {
|
||||
obs.disconnect();
|
||||
obs = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user