UNPKG

memory-watch

Version:

Advanced Node.js memory monitoring with stack trace analysis, user code detection, and memory leak identification

549 lines (439 loc) 15.3 kB
# 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 ```bash npm install memory-watch ``` ## Quick Start ### Basic Memory Monitoring ```javascript const { MemoryWatch } = require("memory-watch"); const watch = new MemoryWatch({ threshold: 0.8, // 80% of max heap interval: 30000, // Check every 30 seconds actions: [ (data) => { console.log("🚨 Memory threshold reached!"); console.log(`Usage: ${(data.percentage * 100).toFixed(2)}%`); // See which function caused the issue 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 ```javascript const { MemoryWatch } = require("memory-watch"); const watch = new MemoryWatch({ threshold: 0.7, interval: 10000, actions: [ (data) => { // Get detailed diagnostic report const report = generateDiagnosticReport(data); console.log(report); // Send alert with specific function details sendSlackAlert({ message: `Memory leak detected in ${data.context?.stackTrace?.[0]?.functionName}`, file: data.context?.stackTrace?.[0]?.fileName, line: data.context?.stackTrace?.[0]?.lineNumber, }); }, ], }); // Manually track important functions function processLargeDataset() { watch.captureContext("processLargeDataset", __filename, 45); // Your memory-intensive code here } watch.start(); ``` ## API Reference ### Constructor Options ```typescript interface MemoryWatchOptions { threshold: number; // Memory threshold (0-1) interval: number; // Check interval in milliseconds actions: Array<(data: MemoryData) => void | Promise<void>>; continuous?: boolean; // Continue monitoring after threshold (default: true) customMemoryCheck?: () => { used: number; total: number }; } ``` ### MemoryData Object ```typescript interface MemoryData { used: number; // Current memory usage total: number; // Total available memory percentage: number; // Usage percentage (0-1) usedBytes: number; // Memory usage in bytes totalBytes: number; // Total memory in bytes timestamp: Date; // Measurement timestamp breakdown: { rss: number; // Resident Set Size (physical memory) heapUsed: number; // Heap memory used heapTotal: number; // Total heap allocated external: number; // External memory (C++ objects) arrayBuffers: number; // ArrayBuffer memory }; context?: { triggerSource?: string; // What triggered the measurement pid: number; // Process ID nodeVersion: string; // Node.js version platform: string; // Platform information stackTrace?: Array<{ functionName: string; fileName?: string; lineNumber?: number; columnNumber?: number; }>; activeHandles?: number; // Active timers, servers, etc. activeRequests?: number; // Active HTTP requests uptime: number; // Process uptime in seconds cpuUsage?: { user: number; // CPU user time system: number; // CPU system time }; }; } ``` ### Diagnostic Utilities ```typescript // Generate detailed diagnostic report import { generateDiagnosticReport, getMemoryLeakIndicators, } from "memory-watch"; const data = watch.getCurrentMemory(); const report = generateDiagnosticReport(data); console.log(report); // Check for memory leak indicators 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 ```javascript const { MemoryWatch, generateDiagnosticReport } = require("memory-watch"); const watch = new MemoryWatch({ threshold: 0.8, interval: 30000, actions: [ (data) => { // Show which user function caused the issue 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}`); } // Send detailed alert 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 ```javascript const { MemoryWatch } = require("memory-watch"); const watch = new MemoryWatch({ threshold: 0.7, interval: 15000, actions: [ (data) => { const report = generateDiagnosticReport(data); console.log(report); // Get memory leak indicators const leakIndicators = getMemoryLeakIndicators(data); if (leakIndicators.length > 0) { console.log('🔴 Memory leak detected:', leakIndicators); } } ] }); // In your application functions, add manual tracking: function processLargeDataset(data) { // Track this function for better memory analysis watch.captureContext('processLargeDataset', __filename, 25); // Your processing logic here... const result = data.map(item => /* heavy processing */); return result; } function handleAPIRequest(req, res) { watch.captureContext('handleAPIRequest', __filename, 35); // Your API logic here... } watch.start(); ``` ### Advanced Diagnostic Monitoring ```javascript const { MemoryWatch, generateDiagnosticReport, getMemoryLeakIndicators, } = require("memory-watch"); const watch = new MemoryWatch({ threshold: 0.7, interval: 10000, actions: [ (data) => { // Generate comprehensive diagnostic report const report = generateDiagnosticReport(data); console.log(report); // Check for memory leak indicators const leakIndicators = getMemoryLeakIndicators(data); if (leakIndicators.length > 0) { console.log("🔴 Memory leak indicators:", leakIndicators); // Send alert with specific details sendAlert({ type: "memory_leak", indicators: leakIndicators, stackTrace: data.context?.stackTrace, file: data.context?.stackTrace?.[0]?.fileName, function: data.context?.stackTrace?.[0]?.functionName, }); } // Log problematic source files 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 ```javascript const watch = new MemoryWatch({ threshold: 0.85, interval: 60000, // Check every minute actions: [ async (data) => { // Send to monitoring service with detailed context 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, }); // Log detailed breakdown for ops team 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 ```javascript // Check if memory usage is above 50% 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: ```javascript 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: ```javascript 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"); // Check stack trace for database-related functions 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: ```javascript const watch = new MemoryWatch({ threshold: 0.6, interval: 5000, continuous: false, // Stop after first alert actions: [ (data) => { const report = generateDiagnosticReport(data); console.log(report); // Save detailed report to file for analysis 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: ```bash # Simple memory monitoring npm run example # Basic usage with multiple actions npm run example-basic # Advanced diagnostics with detailed analysis npm run example-advanced # User code tracking demonstration npm run example-tracking # Build the project npm run build # Development mode with watch npm run dev ``` ## Development & Testing To test the library locally: 1. Clone the repository 2. Install dependencies: `npm install` 3. Build the project: `npm run build` 4. 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 ```bash 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.