nestjs-profiler 1.0.25 → 1.0.26
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 +120 -6
- package/collectors/event-collector.d.ts +18 -0
- package/collectors/event-collector.js +254 -0
- package/collectors/event-collector.js.map +1 -0
- package/common/profiler-options.interface.d.ts +8 -0
- package/common/profiler.model.d.ts +25 -0
- package/controllers/profiler.controller.d.ts +13 -1
- package/controllers/profiler.controller.js +103 -17
- package/controllers/profiler.controller.js.map +1 -1
- package/middleware/profiler-auth.middleware.d.ts +10 -0
- package/middleware/profiler-auth.middleware.js +110 -0
- package/middleware/profiler-auth.middleware.js.map +1 -0
- package/package.json +2 -2
- package/profiler.module.js +78 -0
- package/profiler.module.js.map +1 -1
- package/services/entity-explorer.service.d.ts +1 -1
- package/services/entity-explorer.service.js +7 -7
- package/services/entity-explorer.service.js.map +1 -1
- package/services/health.service.js +23 -8
- package/services/health.service.js.map +1 -1
- package/services/profiler.service.d.ts +10 -1
- package/services/profiler.service.js +103 -35
- package/services/profiler.service.js.map +1 -1
- package/services/route-explorer.service.d.ts +23 -1
- package/services/route-explorer.service.js +136 -10
- package/services/route-explorer.service.js.map +1 -1
- package/services/template-builder.service.d.ts +1 -0
- package/services/template-builder.service.js +184 -83
- package/services/template-builder.service.js.map +1 -1
- package/services/view.service.d.ts +1 -1
- package/services/view.service.js +30 -23
- package/services/view.service.js.map +1 -1
- package/views/events.html +763 -0
- package/views/layout.html +31 -9
- package/views/login.html +277 -0
- package/views/routes.html +405 -37
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<img src="https://raw.githubusercontent.com/MohammedRaslan/Nest-JS-Profiler/refs/heads/main/libs/nestjs-profiler/src/assets/logo.png" width="400" alt="NestJS Profiler Logo" />
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
A NestJS module for profiling HTTP requests, database queries, cache operations, outbound HTTP calls, application logs, package health, and
|
|
7
|
+
A NestJS module for profiling HTTP requests, database queries, cache operations, outbound HTTP calls, application logs, package health, code quality, and event tracking. Inspired by Symfony Profiler, it provides a web-based dashboard to inspect everything that happens inside a request — from DB queries to downstream API calls — in real time.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -22,9 +22,11 @@ A NestJS module for profiling HTTP requests, database queries, cache operations,
|
|
|
22
22
|
- **Live Logs Terminal** — Real-time streaming log viewer at `/__profiler/view/logs/live` via Server-Sent Events. Supports level filtering, search, pause/resume, and auto-scroll.
|
|
23
23
|
- **Summary Dashboard** — Aggregate statistics: avg/p95 duration, error rate, cache hit rate, top slow endpoints, top slow queries, and recent errors.
|
|
24
24
|
- **Entity Explorer** — Lists all registered TypeORM/MikroORM entities and their columns.
|
|
25
|
-
- **Route Explorer** — Lists all registered routes with their controllers and
|
|
25
|
+
- **Route Explorer** — Lists all registered routes with their controllers, handlers, HTTP methods, and full path. Each route expands to show path parameters, query parameters, request headers, and body DTOs — including the DTO class name, all decorated properties, and their TypeScript types. Source file paths are shown with a one-click copy button.
|
|
26
|
+
- **Dashboard Authentication** — Optional login wall protecting all `/__profiler` routes. When enabled, an HMAC-signed token is stored in the browser's `localStorage` with a 24-hour TTL. Credentials are defined in `ProfilerModule.forRoot()` — no separate auth server required.
|
|
26
27
|
- **Package Health** — Runs `npm audit` and `npm outdated` to surface known vulnerabilities and stale dependencies. Results are cached for 5 minutes. Works with npm, yarn, and pnpm. If the registry is unreachable, outdated packages are still shown with an inline warning.
|
|
27
28
|
- **Code Quality** — Runs ESLint and TypeScript compiler checks (`tsc --noEmit`) against your source code. Issues are displayed grouped by file or by rule, with direct links to ESLint rule docs and TypeScript error references. Auto-fixable issues are flagged. File paths are copyable with one click.
|
|
29
|
+
- **Event Tracking** — Automatically intercepts every `EventEmitter2` emission and builds a cascading tree showing which service fired each event, which listeners handled it (with individual timings), and any child events emitted from inside a listener. No instrumentation required — works automatically when `@nestjs/event-emitter` is installed.
|
|
28
30
|
- **Web UI** — Built-in dashboard at `/__profiler` with no external dependencies.
|
|
29
31
|
- **Zero Hard Dependencies** — Core functionality works out of the box; database drivers are optional peer dependencies.
|
|
30
32
|
|
|
@@ -103,6 +105,16 @@ import * as pg from 'pg';
|
|
|
103
105
|
// Default: in-memory (last 100 requests).
|
|
104
106
|
// Pass a custom object implementing ProfilerStorage for persistence.
|
|
105
107
|
storage: 'memory',
|
|
108
|
+
|
|
109
|
+
// ── Dashboard Auth ────────────────────────────────────────────
|
|
110
|
+
// Disabled by default. When enabled, all /__profiler routes
|
|
111
|
+
// require login. username + password are both required when
|
|
112
|
+
// enabled — a TypeScript type error is raised if either is missing.
|
|
113
|
+
auth: {
|
|
114
|
+
enabled: true,
|
|
115
|
+
username: 'admin',
|
|
116
|
+
password: 'supersecret',
|
|
117
|
+
},
|
|
106
118
|
}),
|
|
107
119
|
],
|
|
108
120
|
})
|
|
@@ -119,6 +131,8 @@ export class AppModule {}
|
|
|
119
131
|
| `collectQueries` | `true` | Requires `pgDriver` / `mongoDriver` / `mysqlDriver` |
|
|
120
132
|
| `collectCache` | `true` | Requires `@nestjs/cache-manager` |
|
|
121
133
|
| `explain.enabled` | `false` | Opt-in only |
|
|
134
|
+
| Event Tracking | automatic | Active when `@nestjs/event-emitter` is detected |
|
|
135
|
+
| `auth.enabled` | `false` | When `true`, `username` and `password` are required |
|
|
122
136
|
|
|
123
137
|
## Usage
|
|
124
138
|
|
|
@@ -163,9 +177,40 @@ Navigate to `http://localhost:3000/__profiler`.
|
|
|
163
177
|
| `/__profiler/view/routes` | All registered application routes |
|
|
164
178
|
| `/__profiler/view/health` | Package vulnerabilities and outdated dependency report |
|
|
165
179
|
| `/__profiler/view/code-quality` | ESLint and TypeScript static analysis report |
|
|
180
|
+
| `/__profiler/view/events` | Event tracking — cascading tree of all `EventEmitter2` emissions |
|
|
166
181
|
| `/__profiler/:id` | Full detail view for a single request |
|
|
167
182
|
|
|
168
|
-
### 3.
|
|
183
|
+
### 3. Dashboard Authentication
|
|
184
|
+
|
|
185
|
+
By default the profiler dashboard is open to anyone who can reach the server. For shared or remote environments you can enable a login wall:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
ProfilerModule.forRoot({
|
|
189
|
+
auth: {
|
|
190
|
+
enabled: true,
|
|
191
|
+
username: 'admin',
|
|
192
|
+
password: 'supersecret',
|
|
193
|
+
},
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
TypeScript enforces that `username` and `password` are both present when `enabled: true` — setting `enabled: true` without credentials is a compile-time error.
|
|
198
|
+
|
|
199
|
+
**How it works:**
|
|
200
|
+
|
|
201
|
+
When auth is enabled, every `/__profiler` page runs a blocking script before rendering. If no valid token is found in `localStorage`, the browser is immediately redirected to `/__profiler/login`. After a successful login the token is stored in `localStorage.__profiler_auth` and is valid for 24 hours, after which the user is redirected to the login page again.
|
|
202
|
+
|
|
203
|
+
Token generation uses HMAC-SHA256 keyed on the password, so tokens are invalidated automatically if the password changes.
|
|
204
|
+
|
|
205
|
+
| Route | Description |
|
|
206
|
+
|---|---|
|
|
207
|
+
| `GET /__profiler/login` | Login page |
|
|
208
|
+
| `POST /__profiler/api/login` | JSON login endpoint — returns `{ token, expiresAt }` |
|
|
209
|
+
| `GET /__profiler/logout` | Clears the token and redirects to the login page |
|
|
210
|
+
|
|
211
|
+
> **Note:** Auth is enforced client-side via `localStorage`. It is intended to prevent casual access in shared dev/staging environments, not as a security boundary for sensitive data. Do not expose the profiler on a public network regardless of this setting.
|
|
212
|
+
|
|
213
|
+
### 4. Outbound HTTP Tracking
|
|
169
214
|
|
|
170
215
|
Outbound HTTP tracking is **enabled by default**. Any `http.request`, `https.request`, `http.get`, or `https.get` call made during a request is automatically captured — including calls made via axios, node-fetch, and similar libraries.
|
|
171
216
|
|
|
@@ -186,7 +231,7 @@ To disable:
|
|
|
186
231
|
ProfilerModule.forRoot({ collectHttp: false })
|
|
187
232
|
```
|
|
188
233
|
|
|
189
|
-
###
|
|
234
|
+
### 5. Package Health
|
|
190
235
|
|
|
191
236
|
The Health tab runs `npm audit` and `npm outdated` from your project root and displays the results in a searchable UI.
|
|
192
237
|
|
|
@@ -203,7 +248,7 @@ GET /__profiler/api/health # cached result (5 min TTL)
|
|
|
203
248
|
GET /__profiler/api/health?force=true # force fresh scan
|
|
204
249
|
```
|
|
205
250
|
|
|
206
|
-
###
|
|
251
|
+
### 6. Code Quality
|
|
207
252
|
|
|
208
253
|
The Code Quality tab runs static analysis tools against your source code and surfaces every issue without AI or external services.
|
|
209
254
|
|
|
@@ -229,7 +274,76 @@ GET /__profiler/api/code-quality # cached result (5 min TTL)
|
|
|
229
274
|
GET /__profiler/api/code-quality?force=true # force fresh scan
|
|
230
275
|
```
|
|
231
276
|
|
|
232
|
-
###
|
|
277
|
+
### 7. Event Tracking
|
|
278
|
+
|
|
279
|
+
The Event Tracking tab captures every `EventEmitter2` emission across your application and renders it as a cascading tree — no instrumentation required.
|
|
280
|
+
|
|
281
|
+
**What it captures:**
|
|
282
|
+
|
|
283
|
+
- The event name and a JSON preview of the payload
|
|
284
|
+
- Which service or function fired the event (resolved from the call stack)
|
|
285
|
+
- Each listener that handled the event, with individual execution times and pass/fail status
|
|
286
|
+
- Child events emitted from inside a listener, forming a full cascade tree
|
|
287
|
+
- The depth of each event in the chain (root = 0, emitted by a listener = 1, etc.)
|
|
288
|
+
- Whether the event was fired synchronously (`emit`) or asynchronously (`emitAsync`)
|
|
289
|
+
- Any errors thrown by a listener
|
|
290
|
+
|
|
291
|
+
**Example chain visualised:**
|
|
292
|
+
|
|
293
|
+
```
|
|
294
|
+
🔔 order.created (async) ⬆ OrderService.createOrder 145ms
|
|
295
|
+
✓ onOrderCreated [InventoryListener] 45ms
|
|
296
|
+
✓ onOrderCreated [InvoiceListener] 80ms
|
|
297
|
+
✓ onOrderCreated [AuditListener] 5ms
|
|
298
|
+
└── 🔔 inventory.reserved (async) ⬆ InventoryListener.onOrderCreated 23ms
|
|
299
|
+
✓ onInventoryReserved [NotificationListener] 20ms
|
|
300
|
+
✓ onInventoryReserved [AuditListener] 3ms
|
|
301
|
+
└── 🔔 invoice.created (async) ⬆ InvoiceListener.onOrderCreated 4ms
|
|
302
|
+
✓ onInvoiceCreated [AuditListener] 3ms
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Requirements:** `@nestjs/event-emitter` must be installed and `EventEmitterModule.forRoot()` must be imported in your `AppModule`. The profiler detects it automatically — no configuration needed.
|
|
306
|
+
|
|
307
|
+
The page has an **Auto-refresh** toggle that polls every 3 seconds, making it easy to watch events flow in real time as you hit your endpoints.
|
|
308
|
+
|
|
309
|
+
Results are not cached — each refresh fetches the latest events from the in-memory ring buffer (last 500 events).
|
|
310
|
+
|
|
311
|
+
#### JSON endpoint
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
GET /__profiler/api/events # all captured events (flat array, newest first)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### 8. Route Explorer
|
|
318
|
+
|
|
319
|
+
The Route Explorer (`/__profiler/view/routes`) lists every registered route in your application. Routes are grouped by controller and displayed in an expandable accordion. Call `ProfilerModule.initialize(app)` in `main.ts` to populate it (see section 1).
|
|
320
|
+
|
|
321
|
+
**Per-route information:**
|
|
322
|
+
|
|
323
|
+
Each route row shows the HTTP method badge and the full path (including any global prefix). Expanding a row reveals:
|
|
324
|
+
|
|
325
|
+
- **Path parameters** — parameters from the URL pattern (`:id`, `:userId`, etc.), detected either from `@Param('name')` decorators or by scanning the path template directly.
|
|
326
|
+
- **Query parameters** — parameters decorated with `@Query()`, with their TypeScript type.
|
|
327
|
+
- **Headers** — parameters decorated with `@Headers()`, with their TypeScript type.
|
|
328
|
+
- **Request body** — the DTO class name from `@Body()`, with an expanded table of all decorated properties and their types. Properties are discovered via `reflect-metadata` — any class using `class-validator`, `@ApiProperty`, or any other property decorator will have its fields listed automatically.
|
|
329
|
+
- **Source file** — the `.ts` file path of the DTO class, with a one-click copy button, making it easy to jump to the type definition from the profiler.
|
|
330
|
+
|
|
331
|
+
**Example — what gets surfaced:**
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
POST /api/v1/orders/:storeId
|
|
335
|
+
Path params storeId string
|
|
336
|
+
Query params includeVat boolean
|
|
337
|
+
Body (DTO) CreateOrderDto
|
|
338
|
+
items array
|
|
339
|
+
couponCode string
|
|
340
|
+
metadata object
|
|
341
|
+
src/orders/dto/create-order.dto.ts [copy]
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
The explorer performs a best-effort scan using `reflect-metadata`. It works with TypeScript's `emitDecoratorMetadata: true` (standard for NestJS projects) and does not require OpenAPI/Swagger to be installed.
|
|
345
|
+
|
|
346
|
+
### 9. JSON API
|
|
233
347
|
|
|
234
348
|
Retrieve profile data programmatically:
|
|
235
349
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { ProfilerService } from '../services/profiler.service';
|
|
3
|
+
export declare class EventCollector implements OnModuleInit {
|
|
4
|
+
private readonly profilerService;
|
|
5
|
+
private readonly eventEmitter?;
|
|
6
|
+
private resolvedListenerNames;
|
|
7
|
+
private readonly listenerRegistrationCounters;
|
|
8
|
+
constructor(profilerService: ProfilerService, eventEmitter?: any | undefined);
|
|
9
|
+
setListenerNames(map: Map<string, Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
file: string;
|
|
12
|
+
}>>): void;
|
|
13
|
+
onModuleInit(): void;
|
|
14
|
+
private patchEmitter;
|
|
15
|
+
private buildProfile;
|
|
16
|
+
private serializePayload;
|
|
17
|
+
private getCallerLocation;
|
|
18
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
+
};
|
|
44
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
45
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.EventCollector = void 0;
|
|
49
|
+
const common_1 = require("@nestjs/common");
|
|
50
|
+
const async_hooks_1 = require("async_hooks");
|
|
51
|
+
const crypto = __importStar(require("crypto"));
|
|
52
|
+
const profiler_service_1 = require("../services/profiler.service");
|
|
53
|
+
function resolveEE2Token() {
|
|
54
|
+
try {
|
|
55
|
+
const resolved = require.resolve('@nestjs/event-emitter', {
|
|
56
|
+
paths: [process.cwd()],
|
|
57
|
+
});
|
|
58
|
+
return require(resolved).EventEmitter2;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
try {
|
|
62
|
+
return require('@nestjs/event-emitter').EventEmitter2;
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return Symbol('PROFILER_NO_EVENT_EMITTER');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const EE2_TOKEN = resolveEE2Token();
|
|
70
|
+
const eventALS = new async_hooks_1.AsyncLocalStorage();
|
|
71
|
+
let EventCollector = class EventCollector {
|
|
72
|
+
constructor(profilerService, eventEmitter) {
|
|
73
|
+
this.profilerService = profilerService;
|
|
74
|
+
this.eventEmitter = eventEmitter;
|
|
75
|
+
this.resolvedListenerNames = new Map();
|
|
76
|
+
this.listenerRegistrationCounters = new Map();
|
|
77
|
+
}
|
|
78
|
+
setListenerNames(map) {
|
|
79
|
+
this.resolvedListenerNames = map;
|
|
80
|
+
}
|
|
81
|
+
onModuleInit() {
|
|
82
|
+
if (this.eventEmitter) {
|
|
83
|
+
this.patchEmitter(this.eventEmitter);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
patchEmitter(emitter) {
|
|
87
|
+
if (emitter.__profilerPatched)
|
|
88
|
+
return;
|
|
89
|
+
emitter.__profilerPatched = true;
|
|
90
|
+
const self = this;
|
|
91
|
+
const makeWrappedListener = (fn, eventName, listenerIndex) => {
|
|
92
|
+
const eagerName = (fn.name || '')
|
|
93
|
+
.replace(/^bound /, '')
|
|
94
|
+
.replace(/^async /, '')
|
|
95
|
+
.trim();
|
|
96
|
+
return async function profilerListener(...args) {
|
|
97
|
+
const resolvedInfo = self.resolvedListenerNames.get(eventName)?.[listenerIndex];
|
|
98
|
+
const listenerName = resolvedInfo?.name || eagerName || `handler-${listenerIndex + 1}`;
|
|
99
|
+
const listenerFile = resolvedInfo?.file || '';
|
|
100
|
+
const trace = {
|
|
101
|
+
name: listenerName,
|
|
102
|
+
file: listenerFile,
|
|
103
|
+
startTime: Date.now(),
|
|
104
|
+
duration: 0,
|
|
105
|
+
status: 'success',
|
|
106
|
+
};
|
|
107
|
+
try {
|
|
108
|
+
const result = await fn(...args);
|
|
109
|
+
trace.duration = Date.now() - trace.startTime;
|
|
110
|
+
self.profilerService.addListenerTrace(eventALS.getStore()?.eventId, trace);
|
|
111
|
+
return result;
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
trace.duration = Date.now() - trace.startTime;
|
|
115
|
+
trace.status = 'error';
|
|
116
|
+
trace.error = e?.message ?? String(e);
|
|
117
|
+
self.profilerService.addListenerTrace(eventALS.getStore()?.eventId, trace);
|
|
118
|
+
throw e;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
const origOn = emitter.on.bind(emitter);
|
|
123
|
+
const origOnce = emitter.once.bind(emitter);
|
|
124
|
+
const origPrepend = emitter.prependListener
|
|
125
|
+
? emitter.prependListener.bind(emitter)
|
|
126
|
+
: null;
|
|
127
|
+
emitter.on = function (event, fn, options) {
|
|
128
|
+
const idx = self.listenerRegistrationCounters.get(String(event)) ?? 0;
|
|
129
|
+
self.listenerRegistrationCounters.set(String(event), idx + 1);
|
|
130
|
+
return origOn(event, makeWrappedListener(fn, String(event), idx), options);
|
|
131
|
+
};
|
|
132
|
+
emitter.addListener = emitter.on;
|
|
133
|
+
emitter.once = function (event, fn, options) {
|
|
134
|
+
const idx = self.listenerRegistrationCounters.get(String(event)) ?? 0;
|
|
135
|
+
self.listenerRegistrationCounters.set(String(event), idx + 1);
|
|
136
|
+
return origOnce(event, makeWrappedListener(fn, String(event), idx), options);
|
|
137
|
+
};
|
|
138
|
+
if (origPrepend) {
|
|
139
|
+
emitter.prependListener = function (event, fn, options) {
|
|
140
|
+
const idx = self.listenerRegistrationCounters.get(String(event)) ?? 0;
|
|
141
|
+
self.listenerRegistrationCounters.set(String(event), idx + 1);
|
|
142
|
+
return origPrepend(event, makeWrappedListener(fn, String(event), idx), options);
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
const origEmit = emitter.emit.bind(emitter);
|
|
146
|
+
emitter.emit = function (event, ...args) {
|
|
147
|
+
if (typeof event !== 'string')
|
|
148
|
+
return origEmit(event, ...args);
|
|
149
|
+
const profile = self.buildProfile(event, args, false);
|
|
150
|
+
return eventALS.run({ eventId: profile.id, depth: profile.depth }, () => {
|
|
151
|
+
try {
|
|
152
|
+
const result = origEmit(event, ...args);
|
|
153
|
+
self.profilerService.finalizeEvent(profile.id, 'success');
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
self.profilerService.finalizeEvent(profile.id, 'error', e?.message);
|
|
158
|
+
throw e;
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
const origEmitAsync = emitter.emitAsync?.bind(emitter);
|
|
163
|
+
if (origEmitAsync) {
|
|
164
|
+
emitter.emitAsync = async function (event, ...args) {
|
|
165
|
+
if (typeof event !== 'string')
|
|
166
|
+
return origEmitAsync(event, ...args);
|
|
167
|
+
const profile = self.buildProfile(event, args, true);
|
|
168
|
+
return eventALS.run({ eventId: profile.id, depth: profile.depth }, async () => {
|
|
169
|
+
try {
|
|
170
|
+
const result = await origEmitAsync(event, ...args);
|
|
171
|
+
self.profilerService.finalizeEvent(profile.id, 'success');
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
self.profilerService.finalizeEvent(profile.id, 'error', e?.message);
|
|
176
|
+
throw e;
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
buildProfile(eventName, args, isAsync) {
|
|
183
|
+
const parent = eventALS.getStore();
|
|
184
|
+
const depth = parent ? parent.depth + 1 : 0;
|
|
185
|
+
const profile = {
|
|
186
|
+
id: crypto.randomUUID(),
|
|
187
|
+
eventName,
|
|
188
|
+
payloadPreview: this.serializePayload(args[0]),
|
|
189
|
+
emittedAt: Date.now(),
|
|
190
|
+
isAsync,
|
|
191
|
+
...this.getCallerLocation(),
|
|
192
|
+
listeners: [],
|
|
193
|
+
totalDuration: 0,
|
|
194
|
+
status: 'pending',
|
|
195
|
+
parentEventId: parent?.eventId,
|
|
196
|
+
requestId: this.profilerService.getCurrentRequestId(),
|
|
197
|
+
depth,
|
|
198
|
+
childEventIds: [],
|
|
199
|
+
};
|
|
200
|
+
if (parent?.eventId) {
|
|
201
|
+
this.profilerService.linkChildEvent(parent.eventId, profile.id);
|
|
202
|
+
}
|
|
203
|
+
this.profilerService.addEvent(profile);
|
|
204
|
+
return profile;
|
|
205
|
+
}
|
|
206
|
+
serializePayload(payload) {
|
|
207
|
+
try {
|
|
208
|
+
const str = JSON.stringify(payload);
|
|
209
|
+
if (!str)
|
|
210
|
+
return '';
|
|
211
|
+
return str.length > 200 ? str.slice(0, 200) + '…' : str;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return '[unserializable]';
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
getCallerLocation() {
|
|
218
|
+
const lines = (new Error().stack ?? '').split('\n').slice(1);
|
|
219
|
+
const skip = [
|
|
220
|
+
'event-collector',
|
|
221
|
+
'EventCollector',
|
|
222
|
+
'profilerListener',
|
|
223
|
+
'AsyncLocalStorage',
|
|
224
|
+
'node:',
|
|
225
|
+
'eventemitter2',
|
|
226
|
+
'node_modules/@nestjs/event-emitter',
|
|
227
|
+
];
|
|
228
|
+
for (const line of lines) {
|
|
229
|
+
const trimmed = line.trim();
|
|
230
|
+
if (skip.some((s) => trimmed.includes(s)))
|
|
231
|
+
continue;
|
|
232
|
+
const m = trimmed.match(/^at (.+?)\s+\((.+?):(\d+):\d+\)/);
|
|
233
|
+
if (m) {
|
|
234
|
+
const name = m[1];
|
|
235
|
+
if (!name.startsWith('Object.') && name !== 'Function') {
|
|
236
|
+
return { emitterLocation: name, emitterFile: `${m[2]}:${m[3]}` };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const anon = trimmed.match(/^at (.+?):(\d+):\d+$/);
|
|
240
|
+
if (anon && !anon[1].startsWith('node:')) {
|
|
241
|
+
return { emitterLocation: anon[1], emitterFile: `${anon[1]}:${anon[2]}` };
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return { emitterLocation: 'unknown', emitterFile: '' };
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
exports.EventCollector = EventCollector;
|
|
248
|
+
exports.EventCollector = EventCollector = __decorate([
|
|
249
|
+
(0, common_1.Injectable)(),
|
|
250
|
+
__param(1, (0, common_1.Optional)()),
|
|
251
|
+
__param(1, (0, common_1.Inject)(EE2_TOKEN)),
|
|
252
|
+
__metadata("design:paramtypes", [profiler_service_1.ProfilerService, Object])
|
|
253
|
+
], EventCollector);
|
|
254
|
+
//# sourceMappingURL=event-collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-collector.js","sourceRoot":"","sources":["../../../libs/nestjs-profiler/src/collectors/event-collector.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,13 @@ export interface ProfilerExplainOptions {
|
|
|
5
5
|
thresholdMs?: number;
|
|
6
6
|
}
|
|
7
7
|
export type ProfilerStorageType = 'memory' | ProfilerStorage;
|
|
8
|
+
export type ProfilerAuth = {
|
|
9
|
+
enabled: false;
|
|
10
|
+
} | {
|
|
11
|
+
enabled: true;
|
|
12
|
+
username: string;
|
|
13
|
+
password: string;
|
|
14
|
+
};
|
|
8
15
|
export interface ProfilerOptions {
|
|
9
16
|
enabled?: boolean;
|
|
10
17
|
storage?: ProfilerStorageType;
|
|
@@ -18,4 +25,5 @@ export interface ProfilerOptions {
|
|
|
18
25
|
collectMysql?: boolean;
|
|
19
26
|
collectHttp?: boolean;
|
|
20
27
|
explain?: ProfilerExplainOptions;
|
|
28
|
+
auth?: ProfilerAuth;
|
|
21
29
|
}
|
|
@@ -44,6 +44,31 @@ export interface HttpCallProfile {
|
|
|
44
44
|
error?: string;
|
|
45
45
|
protocol: 'http' | 'https';
|
|
46
46
|
}
|
|
47
|
+
export interface EventListenerTrace {
|
|
48
|
+
name: string;
|
|
49
|
+
duration: number;
|
|
50
|
+
startTime: number;
|
|
51
|
+
status: 'success' | 'error';
|
|
52
|
+
error?: string;
|
|
53
|
+
file?: string;
|
|
54
|
+
}
|
|
55
|
+
export interface EventProfile {
|
|
56
|
+
id: string;
|
|
57
|
+
eventName: string;
|
|
58
|
+
payloadPreview: string;
|
|
59
|
+
emittedAt: number;
|
|
60
|
+
isAsync: boolean;
|
|
61
|
+
emitterLocation: string;
|
|
62
|
+
emitterFile: string;
|
|
63
|
+
listeners: EventListenerTrace[];
|
|
64
|
+
totalDuration: number;
|
|
65
|
+
status: 'pending' | 'success' | 'error';
|
|
66
|
+
error?: string;
|
|
67
|
+
parentEventId?: string;
|
|
68
|
+
requestId?: string;
|
|
69
|
+
depth: number;
|
|
70
|
+
childEventIds: string[];
|
|
71
|
+
}
|
|
47
72
|
export interface RequestProfile {
|
|
48
73
|
id: string;
|
|
49
74
|
method: string;
|
|
@@ -6,6 +6,7 @@ import { EntityExplorerService } from '../services/entity-explorer.service';
|
|
|
6
6
|
import { RouteExplorerService } from '../services/route-explorer.service';
|
|
7
7
|
import { HealthService } from '../services/health.service';
|
|
8
8
|
import { CodeQualityService } from '../services/code-quality.service';
|
|
9
|
+
import { ProfilerOptions } from '../common/profiler-options.interface';
|
|
9
10
|
export declare class ProfilerController {
|
|
10
11
|
private readonly profilerService;
|
|
11
12
|
private readonly viewService;
|
|
@@ -14,7 +15,10 @@ export declare class ProfilerController {
|
|
|
14
15
|
private readonly routeExplorer;
|
|
15
16
|
private readonly healthService;
|
|
16
17
|
private readonly codeQualityService;
|
|
17
|
-
|
|
18
|
+
private readonly profilerOptions;
|
|
19
|
+
constructor(profilerService: ProfilerService, viewService: ViewService, templateBuilder: TemplateBuilderService, entityExplorer: EntityExplorerService, routeExplorer: RouteExplorerService, healthService: HealthService, codeQualityService: CodeQualityService, profilerOptions: ProfilerOptions);
|
|
20
|
+
private get authEnabled();
|
|
21
|
+
private renderLayout;
|
|
18
22
|
dashboard(res: Response): Promise<void>;
|
|
19
23
|
requestsList(res: Response): Promise<void>;
|
|
20
24
|
listJson(): Promise<import("..").RequestProfile[]>;
|
|
@@ -27,6 +31,12 @@ export declare class ProfilerController {
|
|
|
27
31
|
message: string;
|
|
28
32
|
tip: string;
|
|
29
33
|
}>;
|
|
34
|
+
loginPage(res: Response, returnTo?: string): Promise<void>;
|
|
35
|
+
apiLogin(res: Response, body: {
|
|
36
|
+
username?: string;
|
|
37
|
+
password?: string;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
logout(res: Response): Promise<void>;
|
|
30
40
|
detail(id: string, res: Response): Promise<void>;
|
|
31
41
|
detailJson(id: string): Promise<import("..").RequestProfile>;
|
|
32
42
|
summary(res: Response): Promise<void>;
|
|
@@ -40,6 +50,8 @@ export declare class ProfilerController {
|
|
|
40
50
|
healthAudit(force?: string): Promise<import("../services/health.service").HealthReport>;
|
|
41
51
|
codeQualityPage(res: Response): Promise<void>;
|
|
42
52
|
codeQualityAudit(force?: string): Promise<import("../services/code-quality.service").CodeQualityReport>;
|
|
53
|
+
eventsPage(res: Response): Promise<void>;
|
|
54
|
+
eventsApi(): Promise<import("..").EventProfile[]>;
|
|
43
55
|
liveLogsPage(res: Response): Promise<void>;
|
|
44
56
|
liveLogsStream(res: Response): void;
|
|
45
57
|
serveAsset(file: string, res: Response): Promise<void>;
|