nestjs-profiler 1.0.15 → 1.0.17
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 +25 -20
- package/package.json +1 -1
- package/views/entities.html +1 -36
- package/views/layout.html +35 -1
package/README.md
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
# nestjs-profiler
|
|
2
2
|
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/MohammedRaslan/Nest-JS-Profiler/refs/heads/main/libs/nestjs-profiler/src/assets/logo.png" width="400" alt="Dashboard Preview">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
A NestJS module for profiling HTTP requests, database queries, and cache operations. Inspired by Symfony Profiler, it provides a web-based dashboard to inspect request duration, executed queries, log messages, and explain plans for slow queries.
|
|
4
8
|
|
|
5
9
|
## Features
|
|
6
10
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
11
|
+
- **HTTP Request Tracing**: Tracks method, URL, controller handler, duration, and status code.
|
|
12
|
+
- **Database Profiling**:
|
|
13
|
+
- **PostgreSQL**: Captures queries executed via `pg` (compatible with TypeORM, MikroORM, raw pg). Supports **Auto-Explain** to running `EXPLAIN` or `EXPLAIN ANALYZE` on slow queries.
|
|
14
|
+
- **MongoDB**: Profiles MongoDB commands and queries.
|
|
15
|
+
- **MySQL**: Profiles MySQL queries.
|
|
16
|
+
- **Cache Profiling**: Tracks cache operations (get, set, del) when using `@nestjs/cache-manager`.
|
|
17
|
+
- **Logger Profiling**: Captures application logs associated with the request context.
|
|
18
|
+
- **Web UI**: Built-in lightweight dashboard at `/__profiler` to view traces.
|
|
19
|
+
- **Zero Hard Dependencies**: Core functionality works out of the box; database drivers are optional peer dependencies.
|
|
16
20
|
|
|
17
21
|
## Installation
|
|
18
22
|
|
|
@@ -56,27 +60,27 @@ import { ProfilerModule } from 'nestjs-profiler';
|
|
|
56
60
|
collectQueries: true,
|
|
57
61
|
explain: {
|
|
58
62
|
enabled: true,
|
|
59
|
-
thresholdMs: 50,
|
|
60
|
-
analyze: false,
|
|
63
|
+
thresholdMs: 50, // Only explain queries taking > 50ms
|
|
64
|
+
analyze: false, // If true, runs EXPLAIN ANALYZE (execution!)
|
|
61
65
|
},
|
|
62
66
|
// Add pgDriver
|
|
63
67
|
pgDriver: pg,
|
|
64
|
-
|
|
68
|
+
|
|
65
69
|
// MongoDB Profiling (default: true)
|
|
66
70
|
collectMongo: true,
|
|
67
|
-
|
|
71
|
+
|
|
68
72
|
// MySQL Profiling (default: true)
|
|
69
73
|
collectMysql: true,
|
|
70
|
-
|
|
74
|
+
|
|
71
75
|
// Cache Profiling (default: true)
|
|
72
76
|
collectCache: true,
|
|
73
|
-
|
|
77
|
+
|
|
74
78
|
// Log Profiling (default: true)
|
|
75
79
|
collectLogs: true,
|
|
76
80
|
|
|
77
81
|
// Storage backend (default: InMemory)
|
|
78
82
|
// You can implement custom storage by passing an object implementing ProfilerStorage
|
|
79
|
-
storage: 'memory',
|
|
83
|
+
storage: 'memory',
|
|
80
84
|
}),
|
|
81
85
|
],
|
|
82
86
|
})
|
|
@@ -97,10 +101,10 @@ import { ProfilerModule } from 'nestjs-profiler';
|
|
|
97
101
|
|
|
98
102
|
async function bootstrap() {
|
|
99
103
|
const app = await NestFactory.create(AppModule);
|
|
100
|
-
|
|
104
|
+
|
|
101
105
|
// Initialize Profiler Explorers
|
|
102
106
|
ProfilerModule.initialize(app);
|
|
103
|
-
|
|
107
|
+
|
|
104
108
|
await app.listen(3000);
|
|
105
109
|
}
|
|
106
110
|
bootstrap();
|
|
@@ -115,8 +119,9 @@ Navigate to `http://localhost:3000/__profiler` (or your app's port).
|
|
|
115
119
|
### 3. JSON API
|
|
116
120
|
|
|
117
121
|
You can also retrieve profile data programmatically:
|
|
118
|
-
|
|
119
|
-
-
|
|
122
|
+
|
|
123
|
+
- `GET /__profiler/json` - List recent requests
|
|
124
|
+
- `GET /__profiler/:id/json` - Get details for a specific request
|
|
120
125
|
|
|
121
126
|
## License
|
|
122
127
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nestjs-profiler",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "A NestJS module for profiling HTTP requests, database queries, and cache operations. Inspired by Symfony Profiler, it provides a web-based dashboard to inspect request duration, executed queries, log messages, and explain plans for slow queries.",
|
|
5
5
|
"author": "Mohamed Raslan",
|
|
6
6
|
"main": "./index.js",
|
package/views/entities.html
CHANGED
|
@@ -38,39 +38,4 @@
|
|
|
38
38
|
</div>
|
|
39
39
|
|
|
40
40
|
{{{ emptyState }}}
|
|
41
|
-
</div>
|
|
42
|
-
|
|
43
|
-
<script>
|
|
44
|
-
function toggleEntityRow(id) {
|
|
45
|
-
const row = document.getElementById(id);
|
|
46
|
-
const icon = document.getElementById('icon-' + id);
|
|
47
|
-
const contentDiv = document.getElementById('content-' + id);
|
|
48
|
-
|
|
49
|
-
if (!row || !icon) return;
|
|
50
|
-
|
|
51
|
-
// Toggle visibility
|
|
52
|
-
if (row.classList.contains('hidden')) {
|
|
53
|
-
row.classList.remove('hidden');
|
|
54
|
-
icon.classList.add('rotate-90');
|
|
55
|
-
|
|
56
|
-
// Populate if empty (lazy render)
|
|
57
|
-
if (contentDiv && contentDiv.children.length === 0) {
|
|
58
|
-
try {
|
|
59
|
-
const columns = JSON.parse(row.dataset.columns || '[]');
|
|
60
|
-
if (columns.length === 0) {
|
|
61
|
-
contentDiv.innerHTML = '<span class="text-gray-400 italic">No columns defined</span>';
|
|
62
|
-
} else {
|
|
63
|
-
contentDiv.innerHTML = columns.map(col =>
|
|
64
|
-
`<div class="bg-white px-2 py-1 rounded border border-gray-200 truncate" title="${col}">${col}</div>`
|
|
65
|
-
).join('');
|
|
66
|
-
}
|
|
67
|
-
} catch (e) {
|
|
68
|
-
console.error('Error parsing columns', e);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
} else {
|
|
72
|
-
row.classList.add('hidden');
|
|
73
|
-
icon.classList.remove('rotate-90');
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
</script>
|
|
41
|
+
</div>
|
package/views/layout.html
CHANGED
|
@@ -44,6 +44,40 @@
|
|
|
44
44
|
background: #94a3b8;
|
|
45
45
|
}
|
|
46
46
|
</style>
|
|
47
|
+
<script>
|
|
48
|
+
function toggleEntityRow(id) {
|
|
49
|
+
const row = document.getElementById(id);
|
|
50
|
+
const icon = document.getElementById('icon-' + id);
|
|
51
|
+
const contentDiv = document.getElementById('content-' + id);
|
|
52
|
+
|
|
53
|
+
if (!row || !icon) return;
|
|
54
|
+
|
|
55
|
+
// Toggle visibility
|
|
56
|
+
if (row.classList.contains('hidden')) {
|
|
57
|
+
row.classList.remove('hidden');
|
|
58
|
+
icon.classList.add('rotate-90');
|
|
59
|
+
|
|
60
|
+
// Populate if empty (lazy render)
|
|
61
|
+
if (contentDiv && contentDiv.children.length === 0) {
|
|
62
|
+
try {
|
|
63
|
+
const columns = JSON.parse(row.dataset.columns || '[]');
|
|
64
|
+
if (columns.length === 0) {
|
|
65
|
+
contentDiv.innerHTML = '<span class="text-gray-400 italic">No columns defined</span>';
|
|
66
|
+
} else {
|
|
67
|
+
contentDiv.innerHTML = columns.map(col =>
|
|
68
|
+
`<div class="bg-white px-2 py-1 rounded border border-gray-200 truncate" title="${col}">${col}</div>`
|
|
69
|
+
).join('');
|
|
70
|
+
}
|
|
71
|
+
} catch (e) {
|
|
72
|
+
console.error('Error parsing columns', e);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
row.classList.add('hidden');
|
|
77
|
+
icon.classList.remove('rotate-90');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
</script>
|
|
47
81
|
</head>
|
|
48
82
|
|
|
49
83
|
<body class="h-full">
|
|
@@ -249,4 +283,4 @@
|
|
|
249
283
|
</div>
|
|
250
284
|
</body>
|
|
251
285
|
|
|
252
|
-
</html>
|
|
286
|
+
</html>
|