@ngn-net/nestjs-telescope 0.3.1 → 0.3.3
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 +20 -35
- package/dist/enums/entry-type.enum.d.ts +2 -1
- package/dist/enums/entry-type.enum.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/telescope.module.js +13 -0
- package/dist/ui/manifest.json +2 -2
- package/dist/watchers/exception.watcher.js +5 -4
- package/dist/watchers/gate.watcher.js +6 -2
- package/dist/watchers/log.watcher.js +1 -1
- package/dist/watchers/rabbitmq.watcher.d.ts +11 -0
- package/dist/watchers/rabbitmq.watcher.js +231 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
A full-featured developer assistant and application monitoring suite for NestJS, inspired by Laravel Telescope. It monitors incoming HTTP requests, database queries, cache operations, background queue jobs, application events, outgoing mail, logs, unhandled exceptions, cron job schedules, Redis commands, CLI commands, ORM model lifecycle events, notifications, and authorization gate checks.
|
|
6
6
|
|
|
7
|
-
All telemetry records are persisted to **MongoDB** (via Mongoose)
|
|
7
|
+
All telemetry records are persisted to a **dedicated MongoDB** (via Mongoose) and accessible via a modern, high-fidelity dark-themed single-page application dashboard. Telescope uses its own MongoDB connection — completely separate from your project's database.
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -41,18 +41,18 @@ Telescope stores its telemetry data in a **dedicated MongoDB collection** (`tele
|
|
|
41
41
|
|
|
42
42
|
### MongoDB Connection
|
|
43
43
|
|
|
44
|
-
Telescope connects to MongoDB using the `
|
|
44
|
+
Telescope connects to its own dedicated MongoDB using the `TELESCOPE_MONGO_URI` environment variable, or falls back to `mongodb://localhost:27017/telescope`.
|
|
45
45
|
|
|
46
46
|
```bash
|
|
47
47
|
# .env
|
|
48
|
-
|
|
48
|
+
TELESCOPE_MONGO_URI=mongodb://localhost:27017/telescope
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Or pass the URI directly in configuration:
|
|
52
52
|
|
|
53
53
|
```typescript
|
|
54
54
|
TelescopeModule.forRoot({
|
|
55
|
-
mongoUri: 'mongodb://
|
|
55
|
+
mongoUri: 'mongodb://your-mongo-host:27017/telescope',
|
|
56
56
|
// ... other options
|
|
57
57
|
})
|
|
58
58
|
```
|
|
@@ -64,10 +64,10 @@ TelescopeModule.forRoot({
|
|
|
64
64
|
Install the package:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
-
npm install @ngn-net/nestjs-telescope @nestjs/
|
|
67
|
+
npm install @ngn-net/nestjs-telescope @nestjs/jwt @nestjs/passport passport-jwt
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
No need to install `mongoose` or configure `MongooseModule` — Telescope handles its own MongoDB connection internally.
|
|
71
71
|
|
|
72
72
|
---
|
|
73
73
|
|
|
@@ -75,25 +75,23 @@ Ensure you have `MongooseModule` initialized in your NestJS application.
|
|
|
75
75
|
|
|
76
76
|
```typescript
|
|
77
77
|
import { Module } from '@nestjs/common';
|
|
78
|
-
import { MongooseModule } from '@nestjs/mongoose';
|
|
79
78
|
import { TelescopeModule } from '@ngn-net/nestjs-telescope';
|
|
80
79
|
|
|
81
80
|
@Module({
|
|
82
81
|
imports: [
|
|
83
|
-
MongooseModule.forRoot(process.env.MONGO_URI || 'mongodb://localhost:27017/telescope'),
|
|
84
82
|
TelescopeModule.forRoot({
|
|
85
83
|
path: 'telescope',
|
|
86
84
|
jwtSecret: 'your-secret-key',
|
|
87
85
|
password: 'your-password',
|
|
86
|
+
// Telescope connects to its own MongoDB automatically
|
|
87
|
+
// Default: mongodb://localhost:27017/telescope
|
|
88
88
|
}),
|
|
89
89
|
],
|
|
90
90
|
})
|
|
91
91
|
export class AppModule {}
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
## Optional & Peer Dependencies
|
|
94
|
+
Your app's database (TypeORM/Prisma/Sequelize/etc.) stays completely separate — Telescope never touches it.
|
|
97
95
|
|
|
98
96
|
All watchers are dynamically activated. If your application does not use certain modules, Telescope will safely boot without them. Simply install the peer dependencies for the watchers you want to enable:
|
|
99
97
|
|
|
@@ -147,35 +145,22 @@ You can pass configuration options into `TelescopeModule.forRoot(options)`:
|
|
|
147
145
|
|
|
148
146
|
```typescript
|
|
149
147
|
import { Module } from '@nestjs/common';
|
|
150
|
-
import {
|
|
148
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
151
149
|
import { TelescopeModule, EntryType } from '@ngn-net/nestjs-telescope';
|
|
152
150
|
|
|
153
151
|
@Module({
|
|
154
152
|
imports: [
|
|
155
|
-
|
|
153
|
+
// Your project database — completely separate from Telescope
|
|
154
|
+
TypeOrmModule.forRoot({
|
|
155
|
+
type: 'postgres',
|
|
156
|
+
database: 'myapp_db',
|
|
157
|
+
}),
|
|
156
158
|
TelescopeModule.forRoot({
|
|
157
159
|
path: 'telescope',
|
|
158
160
|
password: 'my-secure-password',
|
|
159
161
|
jwtSecret: 'my-jwt-secret',
|
|
160
162
|
maxEntries: 500,
|
|
161
|
-
|
|
162
|
-
enabledEntryTypes: [
|
|
163
|
-
EntryType.REQUEST,
|
|
164
|
-
EntryType.HTTP_CLIENT,
|
|
165
|
-
EntryType.QUERY,
|
|
166
|
-
EntryType.CACHE,
|
|
167
|
-
EntryType.JOB,
|
|
168
|
-
EntryType.EVENT,
|
|
169
|
-
EntryType.LOG,
|
|
170
|
-
EntryType.EXCEPTION,
|
|
171
|
-
EntryType.SCHEDULED_TASK,
|
|
172
|
-
EntryType.REDIS,
|
|
173
|
-
EntryType.MAIL,
|
|
174
|
-
EntryType.GATE,
|
|
175
|
-
EntryType.COMMAND,
|
|
176
|
-
EntryType.MODEL,
|
|
177
|
-
EntryType.NOTIFICATION,
|
|
178
|
-
],
|
|
163
|
+
// Telescope uses its own MongoDB: mongodb://localhost:27017/telescope
|
|
179
164
|
}),
|
|
180
165
|
],
|
|
181
166
|
})
|
|
@@ -277,14 +262,14 @@ npm run test
|
|
|
277
262
|
|
|
278
263
|
If upgrading from v0.2.x (which used TypeORM/SQL):
|
|
279
264
|
|
|
280
|
-
1.
|
|
265
|
+
1. Update the package:
|
|
281
266
|
```bash
|
|
282
|
-
npm install @ngn-net/nestjs-telescope
|
|
267
|
+
npm install @ngn-net/nestjs-telescope@latest
|
|
283
268
|
```
|
|
284
269
|
|
|
285
|
-
2.
|
|
270
|
+
2. Ensure MongoDB is available at `mongodb://localhost:27017/telescope` (or configure `mongoUri`).
|
|
286
271
|
|
|
287
|
-
3. Remove the old
|
|
272
|
+
3. Remove the old `TypeOrmModule` import if it was only added for Telescope.
|
|
288
273
|
|
|
289
274
|
4. The old `telescope_entries` SQL table can be dropped after migration.
|
|
290
275
|
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -29,3 +29,4 @@ __exportStar(require("./watchers/model.watcher"), exports);
|
|
|
29
29
|
__exportStar(require("./watchers/notification.watcher"), exports);
|
|
30
30
|
__exportStar(require("./watchers/gate.watcher"), exports);
|
|
31
31
|
__exportStar(require("./watchers/http-client.watcher"), exports);
|
|
32
|
+
__exportStar(require("./watchers/rabbitmq.watcher"), exports);
|
package/dist/telescope.module.js
CHANGED
|
@@ -42,6 +42,7 @@ const command_watcher_1 = require("./watchers/command.watcher");
|
|
|
42
42
|
const model_watcher_1 = require("./watchers/model.watcher");
|
|
43
43
|
const notification_watcher_1 = require("./watchers/notification.watcher");
|
|
44
44
|
const gate_watcher_1 = require("./watchers/gate.watcher");
|
|
45
|
+
const rabbitmq_watcher_1 = require("./watchers/rabbitmq.watcher");
|
|
45
46
|
const constants_1 = require("./constants");
|
|
46
47
|
const entry_type_enum_1 = require("./enums/entry-type.enum");
|
|
47
48
|
let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
|
|
@@ -295,6 +296,13 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
|
|
|
295
296
|
}
|
|
296
297
|
}
|
|
297
298
|
catch (e) { }
|
|
299
|
+
try {
|
|
300
|
+
require('@golevelup/nestjs-rabbitmq');
|
|
301
|
+
if (!options.enabledEntryTypes || options.enabledEntryTypes.includes(entry_type_enum_1.EntryType.RABBITMQ)) {
|
|
302
|
+
providers.push(rabbitmq_watcher_1.RabbitMQWatcher);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
catch (e) { }
|
|
298
306
|
}
|
|
299
307
|
static registerWatchersAndModulesAsync(providers, imports) {
|
|
300
308
|
providers.push({ provide: core_1.APP_INTERCEPTOR, useClass: http_request_watcher_1.HttpRequestWatcher });
|
|
@@ -344,6 +352,11 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
|
|
|
344
352
|
providers.push(mail_watcher_1.MailWatcher);
|
|
345
353
|
}
|
|
346
354
|
catch (e) { }
|
|
355
|
+
try {
|
|
356
|
+
require('@golevelup/nestjs-rabbitmq');
|
|
357
|
+
providers.push(rabbitmq_watcher_1.RabbitMQWatcher);
|
|
358
|
+
}
|
|
359
|
+
catch (e) { }
|
|
347
360
|
}
|
|
348
361
|
};
|
|
349
362
|
exports.TelescopeModule = TelescopeModule;
|
package/dist/ui/manifest.json
CHANGED
|
@@ -10,7 +10,6 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.ExceptionWatcher = void 0;
|
|
13
|
-
// src/watchers/exception.watcher.ts
|
|
14
13
|
const common_1 = require("@nestjs/common");
|
|
15
14
|
const rxjs_1 = require("rxjs");
|
|
16
15
|
const operators_1 = require("rxjs/operators");
|
|
@@ -22,11 +21,14 @@ let ExceptionWatcher = class ExceptionWatcher {
|
|
|
22
21
|
this.telescope = telescope;
|
|
23
22
|
}
|
|
24
23
|
intercept(context, next) {
|
|
25
|
-
// Skip non-HTTP execution contexts (e.g. RPC/RabbitMQ or WebSockets)
|
|
26
24
|
if (context.getType() !== 'http') {
|
|
27
25
|
return next.handle();
|
|
28
26
|
}
|
|
29
27
|
const request = context.switchToHttp().getRequest();
|
|
28
|
+
const url = request.originalUrl || request.url || '';
|
|
29
|
+
if (this.telescope.shouldIgnorePath(url)) {
|
|
30
|
+
return next.handle();
|
|
31
|
+
}
|
|
30
32
|
return next.handle().pipe((0, operators_1.catchError)((err) => {
|
|
31
33
|
this.telescope.record({
|
|
32
34
|
type: entry_type_enum_1.EntryType.EXCEPTION,
|
|
@@ -35,10 +37,9 @@ let ExceptionWatcher = class ExceptionWatcher {
|
|
|
35
37
|
message: err.message || String(err),
|
|
36
38
|
stack: err.stack || null,
|
|
37
39
|
status: err.status || err.statusCode || 500,
|
|
38
|
-
// Include request context for easier debugging
|
|
39
40
|
request: {
|
|
40
41
|
method: request?.method,
|
|
41
|
-
url
|
|
42
|
+
url,
|
|
42
43
|
ip: request?.ip,
|
|
43
44
|
},
|
|
44
45
|
},
|
|
@@ -24,6 +24,10 @@ let GateWatcher = class GateWatcher {
|
|
|
24
24
|
return next.handle();
|
|
25
25
|
}
|
|
26
26
|
const request = context.switchToHttp().getRequest();
|
|
27
|
+
const url = request.originalUrl || request.url || '';
|
|
28
|
+
if (this.telescope.shouldIgnorePath(url)) {
|
|
29
|
+
return next.handle();
|
|
30
|
+
}
|
|
27
31
|
const user = request.user || request.auth || null;
|
|
28
32
|
return next.handle().pipe((0, rxjs_1.tap)({
|
|
29
33
|
next: () => {
|
|
@@ -34,7 +38,7 @@ let GateWatcher = class GateWatcher {
|
|
|
34
38
|
allowed: true,
|
|
35
39
|
user: user ? { id: user.sub || user.id || user.username } : null,
|
|
36
40
|
method: request.method,
|
|
37
|
-
url
|
|
41
|
+
url,
|
|
38
42
|
},
|
|
39
43
|
}).catch(() => { });
|
|
40
44
|
},
|
|
@@ -50,7 +54,7 @@ let GateWatcher = class GateWatcher {
|
|
|
50
54
|
reason: err.message || err.constructor?.name || 'Access denied',
|
|
51
55
|
user: user ? { id: user.sub || user.id || user.username } : null,
|
|
52
56
|
method: request.method,
|
|
53
|
-
url
|
|
57
|
+
url,
|
|
54
58
|
},
|
|
55
59
|
}).catch(() => { });
|
|
56
60
|
}
|
|
@@ -41,7 +41,7 @@ let LogWatcher = class LogWatcher {
|
|
|
41
41
|
rawMessage = chunk.toString(encoding || 'utf8');
|
|
42
42
|
}
|
|
43
43
|
// Skip telescope internal queries or module logs
|
|
44
|
-
if (rawMessage.includes('telescope_entries') || rawMessage.includes('TelescopeModule')) {
|
|
44
|
+
if (rawMessage.includes('telescope_entries') || rawMessage.includes('TelescopeModule') || rawMessage.includes('Telescope')) {
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
47
|
// Parse the log message
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { TelescopeService } from '../telescope.service';
|
|
3
|
+
export declare class RabbitMQWatcher implements OnModuleInit {
|
|
4
|
+
private readonly telescope;
|
|
5
|
+
private readonly logger;
|
|
6
|
+
constructor(telescope: TelescopeService);
|
|
7
|
+
onModuleInit(): void;
|
|
8
|
+
private patchAmqpConnection;
|
|
9
|
+
private patchRabbitSubscribe;
|
|
10
|
+
private sanitizeMessage;
|
|
11
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var RabbitMQWatcher_1;
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.RabbitMQWatcher = void 0;
|
|
14
|
+
const common_1 = require("@nestjs/common");
|
|
15
|
+
const telescope_service_1 = require("../telescope.service");
|
|
16
|
+
const entry_type_enum_1 = require("../enums/entry-type.enum");
|
|
17
|
+
let RabbitMQWatcher = RabbitMQWatcher_1 = class RabbitMQWatcher {
|
|
18
|
+
telescope;
|
|
19
|
+
logger = new common_1.Logger(RabbitMQWatcher_1.name);
|
|
20
|
+
constructor(telescope) {
|
|
21
|
+
this.telescope = telescope;
|
|
22
|
+
}
|
|
23
|
+
onModuleInit() {
|
|
24
|
+
this.patchAmqpConnection();
|
|
25
|
+
this.patchRabbitSubscribe();
|
|
26
|
+
}
|
|
27
|
+
patchAmqpConnection() {
|
|
28
|
+
try {
|
|
29
|
+
const { AmqpConnection } = require('@golevelup/nestjs-rabbitmq');
|
|
30
|
+
if (!AmqpConnection || !AmqpConnection.prototype)
|
|
31
|
+
return;
|
|
32
|
+
const originalPublish = AmqpConnection.prototype.publish;
|
|
33
|
+
if (!originalPublish)
|
|
34
|
+
return;
|
|
35
|
+
const self = this;
|
|
36
|
+
AmqpConnection.prototype.publish = async function (exchange, routingKey, msg, ...args) {
|
|
37
|
+
const startTime = Date.now();
|
|
38
|
+
const result = await originalPublish.call(this, exchange, routingKey, msg, ...args);
|
|
39
|
+
const duration = Date.now() - startTime;
|
|
40
|
+
const sanitizedMsg = self.sanitizeMessage(msg);
|
|
41
|
+
self.telescope.record({
|
|
42
|
+
type: entry_type_enum_1.EntryType.RABBITMQ,
|
|
43
|
+
content: {
|
|
44
|
+
direction: 'outgoing',
|
|
45
|
+
exchange,
|
|
46
|
+
routingKey,
|
|
47
|
+
message: sanitizedMsg,
|
|
48
|
+
duration,
|
|
49
|
+
status: 'sent',
|
|
50
|
+
},
|
|
51
|
+
}).catch(() => { });
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch { }
|
|
56
|
+
}
|
|
57
|
+
patchRabbitSubscribe() {
|
|
58
|
+
try {
|
|
59
|
+
const rabbitmq = require('@golevelup/nestjs-rabbitmq');
|
|
60
|
+
if (!rabbitmq)
|
|
61
|
+
return;
|
|
62
|
+
const self = this;
|
|
63
|
+
const RabbitSubscribeHandler = rabbitmq.RabbitSubscribe;
|
|
64
|
+
if (RabbitSubscribeHandler) {
|
|
65
|
+
const originalHandler = RabbitSubscribeHandler;
|
|
66
|
+
rabbitmq.RabbitSubscribe = function (options) {
|
|
67
|
+
const decorator = originalHandler(options);
|
|
68
|
+
return function (target, propertyKey, descriptor) {
|
|
69
|
+
const originalMethod = descriptor.value;
|
|
70
|
+
const exchange = options?.exchange || '';
|
|
71
|
+
const routingKey = options?.routingKey || '';
|
|
72
|
+
const queue = options?.queue || '';
|
|
73
|
+
descriptor.value = async function (...args) {
|
|
74
|
+
const startTime = Date.now();
|
|
75
|
+
const incomingMsg = args[0];
|
|
76
|
+
self.telescope.record({
|
|
77
|
+
type: entry_type_enum_1.EntryType.RABBITMQ,
|
|
78
|
+
content: {
|
|
79
|
+
direction: 'incoming',
|
|
80
|
+
exchange,
|
|
81
|
+
routingKey,
|
|
82
|
+
queue,
|
|
83
|
+
message: self.sanitizeMessage(incomingMsg),
|
|
84
|
+
handler: `${target.constructor?.name || 'Unknown'}.${propertyKey}`,
|
|
85
|
+
timestamp: new Date().toISOString(),
|
|
86
|
+
},
|
|
87
|
+
}).catch(() => { });
|
|
88
|
+
try {
|
|
89
|
+
const result = await originalMethod.apply(this, args);
|
|
90
|
+
const duration = Date.now() - startTime;
|
|
91
|
+
self.telescope.record({
|
|
92
|
+
type: entry_type_enum_1.EntryType.RABBITMQ,
|
|
93
|
+
content: {
|
|
94
|
+
direction: 'incoming_response',
|
|
95
|
+
exchange,
|
|
96
|
+
routingKey,
|
|
97
|
+
queue,
|
|
98
|
+
handler: `${target.constructor?.name || 'Unknown'}.${propertyKey}`,
|
|
99
|
+
duration,
|
|
100
|
+
status: 'completed',
|
|
101
|
+
},
|
|
102
|
+
}).catch(() => { });
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const duration = Date.now() - startTime;
|
|
107
|
+
self.telescope.record({
|
|
108
|
+
type: entry_type_enum_1.EntryType.RABBITMQ,
|
|
109
|
+
content: {
|
|
110
|
+
direction: 'incoming_response',
|
|
111
|
+
exchange,
|
|
112
|
+
routingKey,
|
|
113
|
+
queue,
|
|
114
|
+
handler: `${target.constructor?.name || 'Unknown'}.${propertyKey}`,
|
|
115
|
+
duration,
|
|
116
|
+
status: 'failed',
|
|
117
|
+
error: err.message || String(err),
|
|
118
|
+
},
|
|
119
|
+
}).catch(() => { });
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
return descriptor;
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const RabbitRPCHandler = rabbitmq.RabbitRPC;
|
|
128
|
+
if (RabbitRPCHandler) {
|
|
129
|
+
const originalRPCHandler = RabbitRPCHandler;
|
|
130
|
+
rabbitmq.RabbitRPC = function (options) {
|
|
131
|
+
const decorator = originalRPCHandler(options);
|
|
132
|
+
return function (target, propertyKey, descriptor) {
|
|
133
|
+
const originalMethod = descriptor.value;
|
|
134
|
+
const exchange = options?.exchange || '';
|
|
135
|
+
const routingKey = options?.routingKey || '';
|
|
136
|
+
const queue = options?.queue || '';
|
|
137
|
+
descriptor.value = async function (...args) {
|
|
138
|
+
const startTime = Date.now();
|
|
139
|
+
const incomingMsg = args[0];
|
|
140
|
+
self.telescope.record({
|
|
141
|
+
type: entry_type_enum_1.EntryType.RABBITMQ,
|
|
142
|
+
content: {
|
|
143
|
+
direction: 'incoming_rpc',
|
|
144
|
+
exchange,
|
|
145
|
+
routingKey,
|
|
146
|
+
queue,
|
|
147
|
+
message: self.sanitizeMessage(incomingMsg),
|
|
148
|
+
handler: `${target.constructor?.name || 'Unknown'}.${propertyKey}`,
|
|
149
|
+
timestamp: new Date().toISOString(),
|
|
150
|
+
},
|
|
151
|
+
}).catch(() => { });
|
|
152
|
+
try {
|
|
153
|
+
const result = await originalMethod.apply(this, args);
|
|
154
|
+
const duration = Date.now() - startTime;
|
|
155
|
+
self.telescope.record({
|
|
156
|
+
type: entry_type_enum_1.EntryType.RABBITMQ,
|
|
157
|
+
content: {
|
|
158
|
+
direction: 'incoming_rpc_response',
|
|
159
|
+
exchange,
|
|
160
|
+
routingKey,
|
|
161
|
+
queue,
|
|
162
|
+
handler: `${target.constructor?.name || 'Unknown'}.${propertyKey}`,
|
|
163
|
+
duration,
|
|
164
|
+
status: 'completed',
|
|
165
|
+
},
|
|
166
|
+
}).catch(() => { });
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
const duration = Date.now() - startTime;
|
|
171
|
+
self.telescope.record({
|
|
172
|
+
type: entry_type_enum_1.EntryType.RABBITMQ,
|
|
173
|
+
content: {
|
|
174
|
+
direction: 'incoming_rpc_response',
|
|
175
|
+
exchange,
|
|
176
|
+
routingKey,
|
|
177
|
+
queue,
|
|
178
|
+
handler: `${target.constructor?.name || 'Unknown'}.${propertyKey}`,
|
|
179
|
+
duration,
|
|
180
|
+
status: 'failed',
|
|
181
|
+
error: err.message || String(err),
|
|
182
|
+
},
|
|
183
|
+
}).catch(() => { });
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
return descriptor;
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
catch { }
|
|
193
|
+
}
|
|
194
|
+
sanitizeMessage(msg) {
|
|
195
|
+
if (!msg)
|
|
196
|
+
return null;
|
|
197
|
+
if (typeof msg === 'string') {
|
|
198
|
+
try {
|
|
199
|
+
const parsed = JSON.parse(msg);
|
|
200
|
+
return this.sanitizeMessage(parsed);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return msg.length > 1000 ? msg.substring(0, 1000) + '...[truncated]' : msg;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (Buffer.isBuffer(msg)) {
|
|
207
|
+
return '[Buffer]';
|
|
208
|
+
}
|
|
209
|
+
if (typeof msg === 'object') {
|
|
210
|
+
const sanitized = {};
|
|
211
|
+
for (const [key, value] of Object.entries(msg)) {
|
|
212
|
+
if (key.toLowerCase().includes('password') || key.toLowerCase().includes('secret') || key.toLowerCase().includes('token')) {
|
|
213
|
+
sanitized[key] = '[REDACTED]';
|
|
214
|
+
}
|
|
215
|
+
else if (typeof value === 'string' && value.length > 500) {
|
|
216
|
+
sanitized[key] = value.substring(0, 500) + '...[truncated]';
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
sanitized[key] = value;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return sanitized;
|
|
223
|
+
}
|
|
224
|
+
return msg;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
exports.RabbitMQWatcher = RabbitMQWatcher;
|
|
228
|
+
exports.RabbitMQWatcher = RabbitMQWatcher = RabbitMQWatcher_1 = __decorate([
|
|
229
|
+
(0, common_1.Injectable)(),
|
|
230
|
+
__metadata("design:paramtypes", [telescope_service_1.TelescopeService])
|
|
231
|
+
], RabbitMQWatcher);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ngn-net/nestjs-telescope",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"reflect-metadata": "^0.2"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
+
"@golevelup/nestjs-rabbitmq": "^9",
|
|
33
34
|
"@nestjs/bullmq": "^11",
|
|
34
35
|
"@nestjs/cache-manager": "^3",
|
|
35
36
|
"@nestjs/common": "^11",
|
|
@@ -45,6 +46,9 @@
|
|
|
45
46
|
"typeorm": "^0.3"
|
|
46
47
|
},
|
|
47
48
|
"peerDependenciesMeta": {
|
|
49
|
+
"@golevelup/nestjs-rabbitmq": {
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
48
52
|
"@nestjs/bullmq": {
|
|
49
53
|
"optional": true
|
|
50
54
|
},
|