@epic-js/express-profiler 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/cli.js +8 -0
- package/examples/express-app.js +45 -0
- package/index.js +21 -0
- package/package.json +27 -0
- package/profiler.js +11 -0
- package/wrappers.js +16 -0
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# express-profiler
|
|
2
|
+
|
|
3
|
+
Lightweight Express API profiler with route, middleware, DB, and external API timing.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
```bash
|
|
7
|
+
npm install express-profiler
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
```js
|
|
12
|
+
const express = require('express');
|
|
13
|
+
const expressProfiler = require('express-profiler');
|
|
14
|
+
const { wrapAsync } = require('express-profiler/wrappers');
|
|
15
|
+
|
|
16
|
+
const app = express();
|
|
17
|
+
app.use(expressProfiler());
|
|
18
|
+
|
|
19
|
+
app.get('/users', async (req, res) => {
|
|
20
|
+
const users = await wrapAsync(async () => [{ id: 1, name: 'Alice' }], 'DB', req)();
|
|
21
|
+
res.send(users);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
app.listen(3000);
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## CLI Usage
|
|
28
|
+
```
|
|
29
|
+
express-profiler-cli logs.json
|
|
30
|
+
```
|
package/cli.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
|
|
4
|
+
const logs = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
|
|
5
|
+
logs.forEach(log => {
|
|
6
|
+
console.log(`${log.route} ${log.status} ${log.total.toFixed(1)}ms`);
|
|
7
|
+
Object.entries(log.timings).forEach(([k, v]) => console.log(` ${k}: ${v.toFixed(1)}ms`));
|
|
8
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const express = require('express');
|
|
2
|
+
const expressProfiler = require('../index');
|
|
3
|
+
const { wrapAsync } = require('../wrappers');
|
|
4
|
+
|
|
5
|
+
const app = express();
|
|
6
|
+
|
|
7
|
+
// ----- Middleware example -----
|
|
8
|
+
app.use((req, res, next) => {
|
|
9
|
+
const start = Date.now();
|
|
10
|
+
// Simulate middleware work
|
|
11
|
+
setTimeout(() => {
|
|
12
|
+
if (req._profiler) req._profiler.timings['customMiddleware'] = Date.now() - start;
|
|
13
|
+
next();
|
|
14
|
+
}, 50);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Attach profiler
|
|
18
|
+
app.use(expressProfiler());
|
|
19
|
+
|
|
20
|
+
// ----- Fast route -----
|
|
21
|
+
app.get('/users', async (req, res) => {
|
|
22
|
+
const users = await wrapAsync(async () => [{ id: 1, name: 'Alice' }], 'DB', req)();
|
|
23
|
+
res.send(users);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// ----- Slow route with multiple async tasks -----
|
|
27
|
+
app.get('/slow', async (req, res) => {
|
|
28
|
+
const dbTask = wrapAsync(() => new Promise(r => setTimeout(r, 300)), 'DB', req);
|
|
29
|
+
const apiTask = wrapAsync(() => new Promise(r => setTimeout(r, 200)), 'External API', req);
|
|
30
|
+
|
|
31
|
+
await dbTask();
|
|
32
|
+
await apiTask();
|
|
33
|
+
res.send('Slow route done');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// ----- Route with nested async -----
|
|
37
|
+
app.get('/nested', async (req, res) => {
|
|
38
|
+
await wrapAsync(async () => {
|
|
39
|
+
await wrapAsync(() => new Promise(r => setTimeout(r, 100)), 'Inner DB', req)();
|
|
40
|
+
}, 'Outer wrapper', req)();
|
|
41
|
+
res.send('Nested async done');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// ----- Start server -----
|
|
45
|
+
app.listen(3000, () => console.log('Server running on http://localhost:3000'));
|
package/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const profiler = require('./profiler');
|
|
2
|
+
|
|
3
|
+
module.exports = function expressProfiler(options = {}) {
|
|
4
|
+
return function (req, res, next) {
|
|
5
|
+
const start = process.hrtime.bigint();
|
|
6
|
+
|
|
7
|
+
req._profiler = { timings: {} };
|
|
8
|
+
|
|
9
|
+
res.on('finish', () => {
|
|
10
|
+
const end = process.hrtime.bigint();
|
|
11
|
+
const totalMs = Number(end - start) / 1e6;
|
|
12
|
+
req._profiler.totalMs = totalMs;
|
|
13
|
+
|
|
14
|
+
if (options.log !== false) {
|
|
15
|
+
console.log(profiler.format(req));
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
next();
|
|
20
|
+
};
|
|
21
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@epic-js/express-profiler",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Lightweight Express API profiler with route/middleware/async timing",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"express-profiler-cli": "./cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node examples/express-app.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"express",
|
|
14
|
+
"profiler",
|
|
15
|
+
"api",
|
|
16
|
+
"middleware",
|
|
17
|
+
"performance",
|
|
18
|
+
"express-profiler",
|
|
19
|
+
"epicprofiler",
|
|
20
|
+
"epic profiler"
|
|
21
|
+
],
|
|
22
|
+
"author": "Rajgowthaman Rajendran",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"express": "^5.2.1"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/profiler.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
function format(req) {
|
|
2
|
+
const route = req.method + ' ' + req.originalUrl;
|
|
3
|
+
const total = req._profiler.totalMs.toFixed(1);
|
|
4
|
+
const details = Object.entries(req._profiler.timings)
|
|
5
|
+
.map(([k, v]) => ` ${k}: ${v.toFixed(1)}ms`)
|
|
6
|
+
.join('\n');
|
|
7
|
+
|
|
8
|
+
return `${route} ${req.res.statusCode || 200} ${total}ms\n${details}`;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
module.exports = { format };
|
package/wrappers.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
function wrapAsync(fn, label, req) {
|
|
2
|
+
return async (...args) => {
|
|
3
|
+
const start = process.hrtime.bigint();
|
|
4
|
+
const result = await fn(...args);
|
|
5
|
+
const end = process.hrtime.bigint();
|
|
6
|
+
|
|
7
|
+
if (req && req._profiler) {
|
|
8
|
+
const ms = Number(end - start) / 1e6;
|
|
9
|
+
req._profiler.timings[label] = ms;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return result;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { wrapAsync };
|