Memory Watch 🚨
Advanced Node.js Memory Monitoring with Stack Trace Analysis
A powerful Node.js library that not only monitors memory usage but also identifies exactly which functions and files are causing memory issues. Unlike basic memory monitors, Memory Watch provides detailed stack traces, user code detection, and actionable insights for fixing memory leaks.
🚀 Key Features
- 📊 Real-time memory monitoring with customizable thresholds
- 🎯 User code detection - identifies YOUR functions causing memory issues (not just Node.js internals)
- 🔍 Advanced stack trace analysis - shows exact file paths and line numbers
- � Automatic memory leak detection with smart recommendations
- 📈 Comprehensive diagnostics - heap breakdown, active handles, CPU usage
- 💾 Manual context capture for better tracking with
captureContext()
- 🎬 Multiple action callbacks for alerts, logging, notifications
- 📋 Detailed diagnostic reports with actionable insights
What Makes It Different
❌ Basic memory monitors tell you:
- "Memory usage is high: 85%"
✅ Memory Watch tells you:
- "Memory spike in
processUserData() function"
- "File:
src/services/userService.js:123"
- "Cause: Creating too many objects in loop"
- "Recommendation: Implement data streaming"
Installation
npm install memory-watch
Quick Start
Basic Memory Monitoring
const { MemoryWatch } = require("memory-watch");
const watch = new MemoryWatch({
threshold: 0.8,
interval: 30000,
actions: [
(data) => {
console.log("🚨 Memory threshold reached!");
console.log(`Usage: ${(data.percentage * 100).toFixed(2)}%`);
if (data.context?.stackTrace?.[0]) {
const trace = data.context.stackTrace[0];
console.log(`Problem in: ${trace.functionName}`);
console.log(`File: ${trace.fileName}:${trace.lineNumber}`);
}
},
],
});
watch.start();
Advanced User Code Tracking
const { MemoryWatch } = require("memory-watch");
const watch = new MemoryWatch({
threshold: 0.7,
interval: 10000,
actions: [
(data) => {
const report = generateDiagnosticReport(data);
console.log(report);
sendSlackAlert({
message: `Memory leak detected in ${data.context?.stackTrace?.[0]?.functionName}`,
file: data.context?.stackTrace?.[0]?.fileName,
line: data.context?.stackTrace?.[0]?.lineNumber,
});
},
],
});
function processLargeDataset() {
watch.captureContext("processLargeDataset", __filename, 45);
}
watch.start();
API Reference
Constructor Options
interface MemoryWatchOptions {
threshold: number;
interval: number;
actions: Array<(data: MemoryData) => void | Promise<void>>;
continuous?: boolean;
customMemoryCheck?: () => { used: number; total: number };
}
MemoryData Object
interface MemoryData {
used: number;
total: number;
percentage: number;
usedBytes: number;
totalBytes: number;
timestamp: Date;
breakdown: {
rss: number;
heapUsed: number;
heapTotal: number;
external: number;
arrayBuffers: number;
};
context?: {
triggerSource?: string;
pid: number;
nodeVersion: string;
platform: string;
stackTrace?: Array<{
functionName: string;
fileName?: string;
lineNumber?: number;
columnNumber?: number;
}>;
activeHandles?: number;
activeRequests?: number;
uptime: number;
cpuUsage?: {
user: number;
system: number;
};
};
}
Diagnostic Utilities
import {
generateDiagnosticReport,
getMemoryLeakIndicators,
} from "memory-watch";
const data = watch.getCurrentMemory();
const report = generateDiagnosticReport(data);
console.log(report);
const leakIndicators = getMemoryLeakIndicators(data);
if (leakIndicators.length > 0) {
console.log("Potential memory leaks detected:", leakIndicators);
}
Methods
start() - Start monitoring
stop() - Stop monitoring
getCurrentMemory() - Get current memory status
isRunning() - Check if monitoring is active
captureContext(functionName, fileName?, lineNumber?) - NEW! Manually capture user context for better tracking
MemoryWatch.checkOnce(threshold) - One-time memory check (static method)
Usage Examples
1. Basic Memory Monitoring with User Code Detection
const { MemoryWatch, generateDiagnosticReport } = require("memory-watch");
const watch = new MemoryWatch({
threshold: 0.8,
interval: 30000,
actions: [
(data) => {
if (data.context?.stackTrace?.[0]) {
const trace = data.context.stackTrace[0];
console.log(`🎯 Problem in function: ${trace.functionName}`);
console.log(`📁 File: ${trace.fileName}:${trace.lineNumber}`);
}
sendSlackNotification({
message: `Memory threshold reached: ${(data.percentage * 100).toFixed(
1
)}%`,
function: data.context?.stackTrace?.[0]?.functionName,
file: data.context?.stackTrace?.[0]?.fileName,
line: data.context?.stackTrace?.[0]?.lineNumber,
});
},
],
});
watch.start();
2. Manual Context Tracking for Better Analysis
const { MemoryWatch } = require("memory-watch");
const watch = new MemoryWatch({
threshold: 0.7,
interval: 15000,
actions: [
(data) => {
const report = generateDiagnosticReport(data);
console.log(report);
const leakIndicators = getMemoryLeakIndicators(data);
if (leakIndicators.length > 0) {
console.log('🔴 Memory leak detected:', leakIndicators);
}
}
]
});
function processLargeDataset(data) {
watch.captureContext('processLargeDataset', __filename, 25);
const result = data.map(item => );
return result;
}
function handleAPIRequest(req, res) {
watch.captureContext('handleAPIRequest', __filename, 35);
}
watch.start();
Advanced Diagnostic Monitoring
const {
MemoryWatch,
generateDiagnosticReport,
getMemoryLeakIndicators,
} = require("memory-watch");
const watch = new MemoryWatch({
threshold: 0.7,
interval: 10000,
actions: [
(data) => {
const report = generateDiagnosticReport(data);
console.log(report);
const leakIndicators = getMemoryLeakIndicators(data);
if (leakIndicators.length > 0) {
console.log("🔴 Memory leak indicators:", leakIndicators);
sendAlert({
type: "memory_leak",
indicators: leakIndicators,
stackTrace: data.context?.stackTrace,
file: data.context?.stackTrace?.[0]?.fileName,
function: data.context?.stackTrace?.[0]?.functionName,
});
}
if (data.context?.stackTrace) {
const sourceFiles = data.context.stackTrace
.filter((trace) => trace.fileName)
.map((trace) => `${trace.fileName}:${trace.lineNumber}`)
.slice(0, 3);
console.log("🎯 Check these files for memory issues:", sourceFiles);
}
},
],
});
Production Server Monitoring
const watch = new MemoryWatch({
threshold: 0.85,
interval: 60000,
actions: [
async (data) => {
await sendToDatadog({
metric: "memory.usage.high",
value: data.percentage,
tags: [
`pid:${data.context?.pid}`,
`platform:${data.context?.platform}`,
`trigger:${data.context?.triggerSource}`,
`active_handles:${data.context?.activeHandles}`,
`active_requests:${data.context?.activeRequests}`,
],
stackTrace: data.context?.stackTrace,
});
console.log("Memory breakdown:", {
heap: `${(data.breakdown.heapUsed / 1024 / 1024).toFixed(2)}MB`,
rss: `${(data.breakdown.rss / 1024 / 1024).toFixed(2)}MB`,
external: `${(data.breakdown.external / 1024 / 1024).toFixed(2)}MB`,
activeHandles: data.context?.activeHandles,
topFunction: data.context?.stackTrace?.[0]?.functionName,
});
},
],
});
One-time Memory Check
const result = await MemoryWatch.checkOnce(0.5);
if (result) {
console.log("Memory usage is high:", result);
}
Real-world Use Cases
Real-world Use Cases
1. API Memory Leak Detection
Identify which API endpoints are causing memory leaks:
const watch = new MemoryWatch({
threshold: 0.8,
interval: 30000,
actions: [
(data) => {
const apiEndpoint = data.context?.stackTrace?.find(
(trace) =>
trace.fileName?.includes("routes") ||
trace.fileName?.includes("controllers")
);
if (apiEndpoint) {
console.log(
`🚨 Memory issue in API: ${apiEndpoint.fileName}:${apiEndpoint.lineNumber}`
);
console.log(` Function: ${apiEndpoint.functionName}`);
console.log(` Memory: ${(data.percentage * 100).toFixed(1)}%`);
}
},
],
});
2. Database Connection Monitoring
Monitor for unclosed database connections:
const watch = new MemoryWatch({
threshold: 0.7,
interval: 15000,
actions: [
(data) => {
if (data.context?.activeHandles > 50) {
console.log(`⚠️ High active handles: ${data.context.activeHandles}`);
console.log("Possible unclosed database connections or timers");
const dbTrace = data.context?.stackTrace?.find(
(trace) =>
trace.functionName?.includes("query") ||
trace.functionName?.includes("connection") ||
trace.fileName?.includes("database")
);
if (dbTrace) {
console.log(
`🔍 Check database code: ${dbTrace.fileName}:${dbTrace.lineNumber}`
);
}
}
},
],
});
3. Development Memory Profiling
Use during development to catch memory issues early:
const watch = new MemoryWatch({
threshold: 0.6,
interval: 5000,
continuous: false,
actions: [
(data) => {
const report = generateDiagnosticReport(data);
console.log(report);
require("fs").writeFileSync(`memory-report-${Date.now()}.txt`, report);
console.log(
"💡 Tip: Check the stack trace above for the problematic code"
);
},
],
});
What Makes This Different
Unlike basic memory monitoring tools, Memory Watch provides:
- 🎯 Exact source identification: Tells you which file and function is causing memory issues
- 📊 Detailed breakdown: Shows heap, RSS, external memory separately
- 🔍 Root cause analysis: Identifies patterns like unclosed handles or large buffers
- 🚨 Smart leak detection: Automatically detects common memory leak patterns
- 📈 Process insights: Tracks active handles, requests, and CPU usage
- 💡 Actionable recommendations: Provides specific suggestions for fixing issues
Examples Output
When memory threshold is reached, you'll see detailed reports like:
🔍 MEMORY DIAGNOSTIC REPORT
================================
📊 Overall Usage: 78.5% (245MB / 312MB)
⏰ Timestamp: 2025-09-10T11:30:15.123Z
📈 MEMORY BREAKDOWN:
• Heap Used: 178MB
• Heap Total: 245MB
• RSS (Physical): 312MB
• External: 45MB
• Array Buffers: 12MB
🖥️ PROCESS INFO:
• PID: 12345
• Node.js: v18.20.4
• Platform: linux
• Uptime: 2h 15m 30s
• Active Handles: 15
• Active Requests: 3
🎯 POTENTIAL SOURCES (Stack Trace):
1. processLargeDataset (/app/src/data-processor.js:45:12)
2. handleApiRequest (/app/src/routes/api.js:123:8)
3. middleware (/app/src/middleware/auth.js:67:15)
💡 RECOMMENDATIONS:
⚠️ Heap usage is very high - possible memory leak
⚠️ Check the stack trace above for problematic functions
Available Scripts
Test the library with included examples:
npm run example
npm run example-basic
npm run example-advanced
npm run example-tracking
npm run build
npm run dev
Development & Testing
To test the library locally:
- Clone the repository
- Install dependencies:
npm install
- Build the project:
npm run build
- Run examples:
npm run example-tracking
NPM Package Information
- Package Name:
memory-watch
- Version: 1.0.0
- Node.js Support: >=14.0.0
- TypeScript: Full TypeScript support with type definitions
- License: MIT
- Bundle Size: Lightweight (~50KB)
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Development Setup
git clone https://github.com/muhcen/memory-watch.git
cd memory-watch
npm install
npm run build
npm run example-tracking
License
MIT
Copyright (c) 2025 Mohsen Moradi
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.