UNPKG

lag-monitor

Version:

A lightweight utility for monitoring JavaScript event loop lag in real-time

283 lines (208 loc) โ€ข 8.16 kB
# Browser Event Loop Lag Monitor A lightweight, TypeScript-first utility for monitoring JavaScript event loop lag in real-time. Perfect for performance monitoring, debugging, and alerting on event loop blocking in both browser and Node.js environments. [![npm version](https://badge.fury.io/js/lag-monitor.svg)](https://badge.fury.io/js/lag-monitor) [![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) ## Features - ๐Ÿš€ **Lightweight**: Minimal overhead with efficient circular buffer implementation - ๐Ÿ“Š **Real-time Monitoring**: Track event loop lag as it happens - ๐ŸŽฏ **Multiple Metrics**: Built-in support for latest, average, min, max, and custom metrics - ๐Ÿ”ง **Flexible Configuration**: Customizable sample rates, buffer sizes, and measurement methods - ๐ŸŒ **Universal**: Works in browsers and Node.js environments - ๐Ÿ“ **TypeScript First**: Full type safety with comprehensive TypeScript definitions - ๐Ÿงช **Well Tested**: Comprehensive test suite with high coverage - ๐Ÿ“ˆ **Custom Metrics**: Define your own metrics for specialized monitoring needs ## Installation ```bash npm install lag-monitor ``` ## Quick Start ```typescript import LagMonitor from 'lag-monitor'; // Basic usage with default settings const monitor = new LagMonitor(); // Get current lag metrics console.log(monitor.snapshot()); // Output: { latest: 2.1, average: 1.8, min: 0.5, max: 5.2, samples: 10 } // Stop monitoring when done monitor.stop(); ``` ## API Reference ### Constructor ```typescript new LagMonitor(options?: LagMonitorOptions) ``` #### Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `lagType` | `'setTimeout' \| 'setInterval' \| 'requestAnimationFrame'` | `'setTimeout'` | Method used for scheduling measurements | | `sampleRate` | `number` | `100` | Interval between measurements in milliseconds | | `sampleCount` | `number` | `10` | Number of samples to keep for calculations | | `callback` | `LagCallback` | `undefined` | Optional callback invoked on each measurement | | `autoStart` | `boolean` | `true` | Whether to start monitoring immediately | | `userMetrics` | `Record<string, (samples: number[]) => number>` | `{}` | Custom metrics to compute | ### Methods #### `start(): void` Start lag monitoring. #### `stop(): void` Stop lag monitoring. #### `restart(): void` Reset and restart monitoring. #### `reset(): void` Clear all collected samples and stop monitoring. #### `isRunning(): boolean` Check if monitoring is currently active. #### `snapshot(metric?: string): LagData | { [key: string]: number }` Get current lag statistics. If `metric` is specified, returns only that metric. #### `getAvailableMetrics(): string[]` Get list of all available metrics (built-in + user-defined). ### Properties #### `latest: number` Most recent lag measurement in milliseconds. #### `average: number` Average lag over the current sample window. #### `min: number` Minimum lag in the current sample window. #### `max: number` Maximum lag in the current sample window. ## Usage Examples ### Basic Monitoring ```typescript import LagMonitor from 'lag-monitor'; const monitor = new LagMonitor({ sampleRate: 50, // Check every 50ms sampleCount: 20 // Keep last 20 samples }); // Check lag periodically setInterval(() => { const lag = monitor.latest; if (lag > 16) { console.warn(`High lag detected: ${lag.toFixed(2)}ms`); } }, 1000); ``` ### With Callback ```typescript const monitor = new LagMonitor({ callback: (currentLag, lagData) => { if (currentLag > 16) { console.warn(`Frame drop detected: ${currentLag.toFixed(2)}ms`); console.log('Current stats:', lagData); } } }); ``` ### Custom Metrics ```typescript const monitor = new LagMonitor({ userMetrics: { median: (samples) => { const sorted = [...samples].sort((a, b) => a - b); const mid = Math.floor(sorted.length / 2); return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid]; }, p95: (samples) => { const sorted = [...samples].sort((a, b) => a - b); const index = Math.ceil(sorted.length * 0.95) - 1; return sorted[index]; } } }); console.log(monitor.snapshot()); // Output includes: { latest: 2.1, average: 1.8, median: 1.9, p95: 4.2, ... } ``` ### Manual Control ```typescript const monitor = new LagMonitor({ autoStart: false }); // Start monitoring when needed monitor.start(); // Get specific metrics console.log('Current lag:', monitor.latest); console.log('Average lag:', monitor.average); // Get all metrics at once (atomic snapshot) const stats = monitor.snapshot(); console.log('All metrics:', stats); // Stop when done monitor.stop(); ``` ### Performance Dashboard ```typescript class PerformanceDashboard { private monitor: LagMonitor; private alertThreshold = 16; // 60fps threshold constructor() { this.monitor = new LagMonitor({ sampleRate: 16, // Check every frame at 60fps callback: this.onLagMeasurement.bind(this) }); } private onLagMeasurement(lag: number, data: any) { this.updateUI(data); if (lag > this.alertThreshold) { this.triggerAlert(lag); } } private updateUI(data: any) { // Update your dashboard UI document.getElementById('current-lag').textContent = `${data.latest.toFixed(1)}ms`; document.getElementById('avg-lag').textContent = `${data.average.toFixed(1)}ms`; document.getElementById('max-lag').textContent = `${data.max.toFixed(1)}ms`; } private triggerAlert(lag: number) { console.warn(`Performance alert: ${lag.toFixed(2)}ms lag detected`); // Send to monitoring service, show notification, etc. } } ``` ## Browser vs Node.js The library works in both environments with some considerations: ### Browser - All lag types supported: `setTimeout`, `setInterval`, `requestAnimationFrame` - Uses `performance.now()` for high-precision timing - Ideal for monitoring UI responsiveness ### Node.js - Supports `setTimeout` and `setInterval` - `requestAnimationFrame` will throw an error (not available) - Uses `performance.now()` for timing - Great for monitoring server-side event loop health ```typescript // Node.js specific configuration const monitor = new LagMonitor({ lagType: 'setTimeout', // Avoid requestAnimationFrame sampleRate: 100 }); ``` ## Performance Considerations - **Minimal Overhead**: The monitor itself adds minimal overhead (~0.1ms per measurement) - **Efficient Storage**: Uses circular buffer to maintain constant memory usage - **Configurable Impact**: Adjust `sampleRate` to balance accuracy vs. performance - **Smart Scheduling**: Different lag types for different use cases: - `setTimeout`: General purpose, works everywhere - `setInterval`: More consistent timing, good for steady monitoring - `requestAnimationFrame`: Browser only, synced with display refresh ## TypeScript Support Full TypeScript support with comprehensive type definitions: ```typescript import LagMonitor, { LagMonitorOptions, LagData, LagCallback } from 'lag-monitor'; const options: LagMonitorOptions = { sampleRate: 100, callback: (lag: number, data: LagData) => { console.log(`Lag: ${lag}ms`, data); } }; const monitor = new LagMonitor(options); ``` ## Contributing Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests. ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## Changelog See [CHANGELOG.md](CHANGELOG.md) for a detailed history of changes. ## Support - ๐Ÿ“– [Documentation](https://github.com/yourusername/lag-monitor#readme) - ๐Ÿ› [Issue Tracker](https://github.com/yourusername/lag-monitor/issues) - ๐Ÿ’ฌ [Discussions](https://github.com/yourusername/lag-monitor/discussions) --- Made with โค๏ธ for the JavaScript performance monitoring community.