├── .npmignore ├── .gitignore ├── src ├── eventLoopStats.js ├── eventLoopStats.d.ts └── eventLoopStats.cc ├── .editorconfig ├── binding.gyp ├── test ├── types.ts └── eventLoopStats.js ├── CHANGELOG.md ├── example.js ├── .github └── workflows │ └── verify.yml ├── package.json ├── README.md └── LICENSE /.npmignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | node_modules 3 | 4 | -------------------------------------------------------------------------------- /src/eventLoopStats.js: -------------------------------------------------------------------------------- 1 | var eventLoopStats = require('../build/Release/eventLoopStats'); 2 | exports.sense = eventLoopStats.sense; 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | indent_style = space 7 | indent_size = 2 8 | trim_trailing_whitespace = true 9 | charset = utf-8 10 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [{ 3 | "target_name" : "eventLoopStats", 4 | "sources" : [ "src/eventLoopStats.cc" ], 5 | "include_dirs" : [ 6 | "src", 7 | "", 12 | "license": "MIT", 13 | "keywords": [ 14 | "libuv", 15 | "stats", 16 | "monitoring", 17 | "loop" 18 | ], 19 | "scripts": { 20 | "install": "node-gyp rebuild", 21 | "rebuild": "node-gyp rebuild", 22 | "test": "mocha && tsc --noEmit --strict --esModuleInterop --module commonjs test/types.ts" 23 | }, 24 | "engines": { 25 | "node": ">=4.0.0" 26 | }, 27 | "dependencies": { 28 | "nan": "^2.14.0" 29 | }, 30 | "gypfile": true, 31 | "devDependencies": { 32 | "@types/node": "^17.0.10", 33 | "chai": "4.1.2", 34 | "mocha": "10.2.0", 35 | "typescript": "^4.5.4" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
Exposes stats about the libuv default loop
3 | 4 | ## Installation 5 | 6 | ``` 7 | npm install --save event-loop-stats 8 | ``` 9 | 10 | ## Usage 11 | ```javascript 12 | var eventLoopStats = require('event-loop-stats'); 13 | console.log('Stats', eventLoopStats.sense()); 14 | ``` 15 | 16 | This will print the following information: 17 | 18 | ``` 19 | Stats { 20 | max: 5, 21 | min: 0, 22 | sum: 10, 23 | num: 5 24 | } 25 | ``` 26 | 27 | ## Property insights 28 | - `max`: Maximum number of milliseconds spent in a single loop since last `sense call`. 29 | - `min`: Minimum number of milliseconds spent in a single loop since last `sense call`. 30 | - `sum`: Total number of milliseconds spent in the loop since last `sense call`. 31 | - `num`: Total number of loops since last `sense call`. 32 | 33 | ## Node version compatibility 34 | `event-loop-stats` depends on C++ extensions which are compiled when the `event-loop-stats` module is installed. Compatibility information can be inspected via the [Travis-CI build jobs](https://travis-ci.org/bripkens/event-loop-stats). 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2022 Ben Blackmore and contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/eventLoopStats.js: -------------------------------------------------------------------------------- 1 | var eventLoopStats = require('..'); 2 | var expect = require('chai').expect; 3 | 4 | describe('eventLoopStats', function() { 5 | it('should expose a sense function', function() { 6 | expect(eventLoopStats.sense).to.be.a('function'); 7 | }); 8 | 9 | it('should return stats via the sense function', function(done) { 10 | setTimeout(function() { 11 | // provoke execution on the default loop 12 | }, 500); 13 | 14 | setTimeout(function() { 15 | var stats = eventLoopStats.sense(); 16 | expect(stats.num).to.be.a('number'); 17 | expect(stats.num).to.be.gt(0); 18 | expect(stats.sum).to.be.a('number'); 19 | expect(stats.max).to.be.a('number'); 20 | expect(stats.min).to.be.a('number'); 21 | done(); 22 | }, 1000); 23 | }); 24 | 25 | // This pattern will usually allow for at least _two_ on_check calls between 26 | // the two sense calls. Check the next test for a slightly different pattern. 27 | it('should detect a blocked event loop', function(done) { 28 | // Call sense to reset max. 29 | eventLoopStats.sense(); 30 | // On the next tick, block for 500ms. 31 | setTimeout(function() { 32 | var waitUntill = new Date(Date.now() + 500); 33 | // Block event loop with busy waiting. 34 | while (waitUntill > new Date()) {} 35 | 36 | // On the next tick, detect stats again - the 500 ms block should have 37 | // been noticed. 38 | setTimeout(function() { 39 | var stats = eventLoopStats.sense(); 40 | expect(stats.max).to.be.gte(490); 41 | expect(stats.max).to.be.lt(2000); 42 | expect(stats.sum).to.be.gte(490); 43 | expect(stats.sum).to.be.lt(2000); 44 | 45 | // At least two on_check calls should have happened (older Node.js versions will call it more often). 46 | expect(stats.num).to.be.gte(2); 47 | 48 | // Since there are at least two on_check calls, min and max should be different. 49 | expect(stats.min).to.be.gte(0); 50 | expect(stats.min).to.be.lt(stats.max); 51 | 52 | done(); 53 | }, 0); 54 | }, 0); 55 | }); 56 | 57 | // This pattern will typically allow for only _one_ on_check call between the 58 | // two sense calls (at least on more recent Node.js versions). Check the 59 | // previous test for a slightly different pattern. 60 | it('one on_check call should suffice to report correct max duration ', function(done) { 61 | // Call sense to reset max. 62 | eventLoopStats.sense(); 63 | var now = Date.now(); 64 | var end = now + 50; 65 | setTimeout(function() { 66 | // After 100ms, call sense again - the 50ms block (see below) should have been noticed. 67 | var stats = eventLoopStats.sense(); 68 | expect(stats.max).to.be.gte(40); 69 | expect(stats.max).to.be.lt(1000); 70 | expect(stats.sum).to.be.gte(40); 71 | expect(stats.sum).to.be.lt(1000); 72 | 73 | // Only one on_check call might have happened. 74 | expect(stats.num).to.gte(1); 75 | 76 | done(); 77 | }, 100); 78 | 79 | // Directly block for 50ms right in this tick, synchronously. 80 | while (Date.now() < end) {} 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /src/eventLoopStats.cc: -------------------------------------------------------------------------------- 1 | #include