@viplance/nestjs-logger 0.3.8 → 0.4.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 CHANGED
@@ -24,12 +24,13 @@
24
24
  import { LogModule } from '@viplance/nestjs-logger';
25
25
 
26
26
  await LogModule.init(app, {
27
+ ..., // some other properties
27
28
  path: '/logs', // define the public URL for the log list
28
29
  key: 'kjhjmi321lqq7a', // use the key to protect data from unauthorized access
29
30
  });
30
31
  ```
31
32
 
32
- Connect the database to store logs.<br />
33
+ Connect a SQL or NoSQL database to store logs.<br />
33
34
  ```typescript
34
35
  await LogModule.init(app, {
35
36
  ...,
@@ -42,6 +43,18 @@ Connect the database to store logs.<br />
42
43
  });
43
44
  ```
44
45
 
46
+ Enable a WebSocket connection to receive the logs in real time.<br />
47
+ ```typescript
48
+ await LogModule.init(app, {
49
+ ...,
50
+ websocket: {
51
+ secure: true, // enable WSS
52
+ port: 81,
53
+ host: 'your-domain.name',
54
+ }
55
+ });
56
+ ```
57
+
45
58
  4. Use the `LogService` in case of custom logs to debug the application.<br />
46
59
  ```typescript
47
60
  import { LogService } from '@viplance/nestjs-logger';
@@ -19,10 +19,12 @@ const node_querystring_1 = __importDefault(require("node:querystring"));
19
19
  const core_1 = require("@nestjs/core");
20
20
  const node_path_1 = require("node:path");
21
21
  const access_guard_1 = require("./guards/access.guard");
22
+ const ws_service_1 = require("./services/ws.service");
22
23
  let LogModule = class LogModule {
23
24
  static async init(app, options) {
24
25
  app.resolve(log_service_1.LogService);
25
26
  const logService = await app.resolve(log_service_1.LogService);
27
+ const wsService = await app.resolve(ws_service_1.WsService);
26
28
  const logAccessGuard = await app.get(access_guard_1.LogAccessGuard);
27
29
  if (options) {
28
30
  logService.setOptions(options);
@@ -33,6 +35,20 @@ let LogModule = class LogModule {
33
35
  prefix: options.path,
34
36
  });
35
37
  const httpAdapter = app.getHttpAdapter();
38
+ // frontend settings endpoint
39
+ httpAdapter.get((0, node_path_1.join)(options.path, "settings"), async (req, res) => {
40
+ var _a, _b, _c, _d;
41
+ logAccessGuard.canActivate(req);
42
+ const result = {};
43
+ if (options === null || options === void 0 ? void 0 : options.websocket) {
44
+ result.websocket = {
45
+ namespace: (_a = options.websocket) === null || _a === void 0 ? void 0 : _a.namespace,
46
+ port: (_b = options.websocket) === null || _b === void 0 ? void 0 : _b.port,
47
+ host: ((_c = options.websocket) === null || _c === void 0 ? void 0 : _c.host) || ((_d = req.headers) === null || _d === void 0 ? void 0 : _d.host.split(":")[0]),
48
+ };
49
+ }
50
+ res.json(result);
51
+ });
36
52
  // get all logs endpoint
37
53
  httpAdapter.get((0, node_path_1.join)(options.path, "api"), async (req, res) => {
38
54
  logAccessGuard.canActivate(req);
@@ -47,6 +63,10 @@ let LogModule = class LogModule {
47
63
  }
48
64
  res.json(await logService.delete(params.id.toString()));
49
65
  });
66
+ // set up WebSocket connection
67
+ if (options === null || options === void 0 ? void 0 : options.websocket) {
68
+ wsService.setupConnection(options.websocket, options.key);
69
+ }
50
70
  }
51
71
  if (options === null || options === void 0 ? void 0 : options.database) {
52
72
  await logService.connectDb(options);
@@ -58,8 +78,14 @@ exports.LogModule = LogModule = __decorate([
58
78
  (0, common_1.Global)(),
59
79
  (0, common_1.Module)({
60
80
  imports: [typeorm_1.TypeOrmModule],
61
- providers: [core_1.ApplicationConfig, access_guard_1.LogAccessGuard, log_service_1.LogService, memory_db_service_1.MemoryDbService],
62
- exports: [typeorm_1.TypeOrmModule, log_service_1.LogService, memory_db_service_1.MemoryDbService],
81
+ providers: [
82
+ core_1.ApplicationConfig,
83
+ access_guard_1.LogAccessGuard,
84
+ log_service_1.LogService,
85
+ memory_db_service_1.MemoryDbService,
86
+ ws_service_1.WsService,
87
+ ],
88
+ exports: [typeorm_1.TypeOrmModule, log_service_1.LogService, memory_db_service_1.MemoryDbService, ws_service_1.WsService],
63
89
  })
64
90
  ], LogModule);
65
91
  //# sourceMappingURL=log.module.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"log.module.js","sourceRoot":"","sources":["../src/log.module.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,wDAAoD;AACpD,oEAA+D;AAC/D,oEAAgE;AAEhE,6CAAgD;AAChD,wEAA2C;AAC3C,uCAAiD;AACjD,yCAAiC;AACjC,wDAAuD;AAQhD,IAAM,SAAS,GAAf,MAAM,SAAS;IACb,MAAM,CAAC,KAAK,CAAC,IAAI,CACtB,GAAQ,EACR,OAA0B;QAE1B,GAAG,CAAC,OAAO,CAAC,wBAAU,CAAC,CAAC;QAExB,MAAM,UAAU,GAAe,MAAM,GAAG,CAAC,OAAO,CAAC,wBAAU,CAAC,CAAC;QAC7D,MAAM,cAAc,GAAmB,MAAM,GAAG,CAAC,GAAG,CAAC,6BAAc,CAAC,CAAC;QAErE,IAAI,OAAO,EAAE,CAAC;YACZ,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;QAED,GAAG,CAAC,qBAAqB,CAAC,IAAI,gCAAc,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,uBAAuB;QAElF,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,IAAI,EAAE,CAAC;YAClB,GAAG,CAAC,eAAe,CAAC,IAAA,gBAAI,EAAC,SAAS,EAAE,IAAI,EAAE,QAAQ,CAAC,EAAE;gBACnD,MAAM,EAAE,OAAO,CAAC,IAAI;aACrB,CAAC,CAAC;YAEH,MAAM,WAAW,GAAG,GAAG,CAAC,cAAc,EAAE,CAAC;YAEzC,wBAAwB;YACxB,WAAW,CAAC,GAAG,CAAC,IAAA,gBAAI,EAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,KAAK,EAAE,GAAQ,EAAE,GAAQ,EAAE,EAAE;gBACtE,cAAc,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;gBAEhC,GAAG,CAAC,IAAI,CAAC,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;YACtC,CAAC,CAAC,CAAC;YAEH,sBAAsB;YACtB,WAAW,CAAC,MAAM,CAChB,IAAA,gBAAI,EAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,EACzB,KAAK,EAAE,GAAQ,EAAE,GAAQ,EAAE,EAAE;gBAC3B,cAAc,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;gBAEhC,MAAM,MAAM,GAAG,0BAAW,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAExD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;oBACf,MAAM,IAAI,sBAAa,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;gBACjD,CAAC;gBAED,GAAG,CAAC,IAAI,CAAC,MAAM,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YAC1D,CAAC,CACF,CAAC;QACJ,CAAC;QAED,IAAI,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,QAAQ,EAAE,CAAC;YACtB,MAAM,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;CACF,CAAA;AAnDY,8BAAS;oBAAT,SAAS;IANrB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,OAAO,EAAE,CAAC,uBAAa,CAAC;QACxB,SAAS,EAAE,CAAC,wBAAiB,EAAE,6BAAc,EAAE,wBAAU,EAAE,mCAAe,CAAC;QAC3E,OAAO,EAAE,CAAC,uBAAa,EAAE,wBAAU,EAAE,mCAAe,CAAC;KACtD,CAAC;GACW,SAAS,CAmDrB"}
1
+ {"version":3,"file":"log.module.js","sourceRoot":"","sources":["../src/log.module.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,22 @@
1
- import { LoggerService } from "@nestjs/common";
1
+ import { LoggerService, OnApplicationShutdown } from "@nestjs/common";
2
2
  import { MemoryDbService } from "./memory-db.service";
3
3
  import { LogModuleOptions } from "../types";
4
4
  import { DataSource, EntitySchema } from "typeorm";
5
5
  import { ExecutionContextHost } from "@nestjs/core/helpers/execution-context-host";
6
6
  import { setInterval } from "timers";
7
- export declare class LogService implements LoggerService {
7
+ import { WsService } from "./ws.service";
8
+ import { Subscription } from "rxjs";
9
+ export declare class LogService implements LoggerService, OnApplicationShutdown {
8
10
  private readonly memoryDbService;
11
+ private readonly wsService;
9
12
  static connection: DataSource;
10
13
  static options: LogModuleOptions;
11
14
  static Log: EntitySchema;
12
15
  static timer: ReturnType<typeof setInterval>;
16
+ static subscription: Subscription;
13
17
  breadcrumbs: any[];
14
- constructor(memoryDbService: MemoryDbService);
18
+ constructor(memoryDbService: MemoryDbService, wsService: WsService);
19
+ onApplicationShutdown(): void;
15
20
  connectDb(options: LogModuleOptions): Promise<DataSource>;
16
21
  setOptions(options: LogModuleOptions): void;
17
22
  addBreadcrumb(breadcrumb: any): void;
@@ -22,8 +27,9 @@ export declare class LogService implements LoggerService {
22
27
  debug(message: string, context?: ExecutionContextHost): void;
23
28
  verbose(message: string, context?: ExecutionContextHost): void;
24
29
  getAll(): Promise<any[]>;
25
- delete(id: string): Promise<import("typeorm").DeleteResult>;
30
+ delete(_id: string): Promise<import("typeorm").DeleteResult>;
26
31
  private smartInsert;
32
+ private getNewObjectId;
27
33
  private getConnection;
28
34
  private parseContext;
29
35
  private checkRecords;
@@ -19,15 +19,24 @@ const typeorm_1 = require("typeorm");
19
19
  const log_entity_1 = require("../entities/log.entity");
20
20
  const timers_1 = require("timers");
21
21
  const entity2table_1 = require("../utils/entity2table");
22
+ const ws_service_1 = require("./ws.service");
22
23
  let LogService = LogService_1 = class LogService {
23
- constructor(memoryDbService) {
24
+ constructor(memoryDbService, wsService) {
24
25
  this.memoryDbService = memoryDbService;
26
+ this.wsService = wsService;
25
27
  this.breadcrumbs = [];
26
28
  }
29
+ onApplicationShutdown() {
30
+ if (LogService_1.timer) {
31
+ clearInterval(LogService_1.timer);
32
+ }
33
+ }
27
34
  async connectDb(options) {
28
35
  var _a, _b, _c, _d, _e, _f, _g;
29
36
  LogService_1.Log = (0, log_entity_1.createLogEntity)(((_a = options.database) === null || _a === void 0 ? void 0 : _a.collection) || ((_b = options.database) === null || _b === void 0 ? void 0 : _b.table) || defaults_1.defaultTable, ((_c = options.database) === null || _c === void 0 ? void 0 : _c.type) || "mongodb");
30
- this.setOptions(options);
37
+ if (!LogService_1.options) {
38
+ this.setOptions(options);
39
+ }
31
40
  const dataSourceOptions = {
32
41
  type: (_d = options.database) === null || _d === void 0 ? void 0 : _d.type,
33
42
  database: (_e = options.database) === null || _e === void 0 ? void 0 : _e.database,
@@ -38,7 +47,6 @@ let LogService = LogService_1 = class LogService {
38
47
  LogService_1.connection = new typeorm_1.DataSource(dataSourceOptions);
39
48
  await LogService_1.connection.initialize();
40
49
  if (dataSourceOptions.type !== "mongodb") {
41
- // LogService.idName = "id";
42
50
  const queryRunner = LogService_1.connection.createQueryRunner();
43
51
  try {
44
52
  await queryRunner.connect();
@@ -57,6 +65,21 @@ let LogService = LogService_1 = class LogService {
57
65
  }
58
66
  setOptions(options) {
59
67
  LogService_1.options = options;
68
+ if (options.websocket && !LogService_1.subscription) {
69
+ LogService_1.subscription = this.wsService.onMessage.subscribe(async (message) => {
70
+ switch (message.action) {
71
+ case "getLogs":
72
+ this.wsService.sendMessage({
73
+ action: "list",
74
+ data: await this.getAll(),
75
+ });
76
+ break;
77
+ case "delete":
78
+ this.delete(message.data._id);
79
+ break;
80
+ }
81
+ });
82
+ }
60
83
  }
61
84
  addBreadcrumb(breadcrumb) {
62
85
  this.breadcrumbs.push(breadcrumb);
@@ -116,8 +139,12 @@ let LogService = LogService_1 = class LogService {
116
139
  order: { updatedAt: "DESC" },
117
140
  });
118
141
  }
119
- async delete(id) {
120
- return this.getConnection().delete(LogService_1.Log, id);
142
+ async delete(_id) {
143
+ this.wsService.sendMessage({
144
+ action: "delete",
145
+ data: { _id },
146
+ });
147
+ return this.getConnection().delete(LogService_1.Log, _id);
121
148
  }
122
149
  async smartInsert(data) {
123
150
  const currentDate = new Date();
@@ -131,15 +158,27 @@ let LogService = LogService_1 = class LogService {
131
158
  });
132
159
  const context = data.context ? this.parseContext(data.context) : undefined;
133
160
  if (log) {
134
- return await connection.update(LogService_1.Log, log["_id"], {
161
+ const updatedLog = {
135
162
  context,
136
163
  trace: data.trace,
137
164
  breadcrumbs: this.breadcrumbs,
138
165
  count: log.count + 1,
139
166
  updatedAt: currentDate,
167
+ };
168
+ this.wsService.sendMessage({
169
+ action: "update",
170
+ data: { ...log, ...updatedLog },
140
171
  });
172
+ await connection.update(LogService_1.Log, log["_id"], {
173
+ context,
174
+ trace: data.trace,
175
+ breadcrumbs: this.breadcrumbs,
176
+ count: log.count + 1,
177
+ updatedAt: currentDate,
178
+ });
179
+ return { ...log, ...updatedLog };
141
180
  }
142
- return await connection.insert(LogService_1.Log, {
181
+ const insertedLog = {
143
182
  type: data.type,
144
183
  message: data.message,
145
184
  context,
@@ -148,7 +187,21 @@ let LogService = LogService_1 = class LogService {
148
187
  count: 1,
149
188
  createdAt: currentDate,
150
189
  updatedAt: currentDate,
190
+ };
191
+ const res = await connection.insert(LogService_1.Log, insertedLog);
192
+ const _id = this.getNewObjectId(res);
193
+ this.wsService.sendMessage({
194
+ action: "insert",
195
+ data: { _id, ...insertedLog },
151
196
  });
197
+ return { _id, ...insertedLog };
198
+ }
199
+ getNewObjectId(result) {
200
+ if (result.identifiers) {
201
+ return result.identifiers[0]._id;
202
+ }
203
+ console.log(result);
204
+ return result._id;
152
205
  }
153
206
  getConnection() {
154
207
  var _a;
@@ -203,6 +256,7 @@ exports.LogService = LogService;
203
256
  LogService.Log = (0, log_entity_1.createLogEntity)(defaults_1.defaultTable, "memory");
204
257
  exports.LogService = LogService = LogService_1 = __decorate([
205
258
  (0, common_1.Injectable)({ scope: common_1.Scope.TRANSIENT }),
206
- __metadata("design:paramtypes", [memory_db_service_1.MemoryDbService])
259
+ __metadata("design:paramtypes", [memory_db_service_1.MemoryDbService,
260
+ ws_service_1.WsService])
207
261
  ], LogService);
208
262
  //# sourceMappingURL=log.service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"log.service.js","sourceRoot":"","sources":["../../src/services/log.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"log.service.js","sourceRoot":"","sources":["../../src/services/log.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { LogModuleOptions } from "../types";
2
+ import { Subject } from "rxjs";
3
+ export declare class WsService {
4
+ onMessage: Subject<any>;
5
+ private ws;
6
+ private connected;
7
+ private connectionTimeout;
8
+ private options;
9
+ private key;
10
+ setupConnection(options: LogModuleOptions["websocket"], key?: string): void;
11
+ sendMessage(message: any): void;
12
+ private handleError;
13
+ private closeConnection;
14
+ private ping;
15
+ private handleMessage;
16
+ private getServerUrl;
17
+ private handleOpenConnection;
18
+ }
@@ -0,0 +1,106 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.WsService = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const ws_1 = require("ws");
12
+ const rxjs_1 = require("rxjs");
13
+ let WsService = class WsService {
14
+ constructor() {
15
+ this.onMessage = new rxjs_1.Subject();
16
+ this.ws = null;
17
+ this.connected = false;
18
+ this.connectionTimeout = 500;
19
+ this.options = {
20
+ port: 8080,
21
+ host: "localhost",
22
+ };
23
+ this.key = "";
24
+ this.handleError = () => {
25
+ const serverUrl = this.getServerUrl();
26
+ console.error(`Server ${serverUrl} is not available.`);
27
+ setTimeout(this.setupConnection, this.connectionTimeout);
28
+ };
29
+ this.closeConnection = (connection) => {
30
+ clearTimeout(connection.pingTimeout);
31
+ if (this.connected) {
32
+ console.log("Connection has been closed by server.");
33
+ this.connected = false;
34
+ this.handleError();
35
+ }
36
+ };
37
+ this.ping = (connection) => {
38
+ console.log("Ping remote server.");
39
+ clearTimeout(connection.pingTimeout);
40
+ connection.pingTimeout = setTimeout(() => {
41
+ connection.terminate();
42
+ }, 30000 + this.connectionTimeout);
43
+ };
44
+ this.handleMessage = (message) => {
45
+ try {
46
+ const data = JSON.parse((message.data || message).toString());
47
+ if (this.key !== "" && data.key !== this.key) {
48
+ throw new Error("WebSocket unauthorized");
49
+ }
50
+ if (this.options)
51
+ if (data.action) {
52
+ this.onMessage.next(data);
53
+ }
54
+ }
55
+ catch (err) {
56
+ console.error(err);
57
+ }
58
+ };
59
+ this.getServerUrl = () => {
60
+ var _a, _b, _c;
61
+ return `${((_a = this.options) === null || _a === void 0 ? void 0 : _a.secure) ? "wss" : "ws"}://${(_b = this.options) === null || _b === void 0 ? void 0 : _b.host}:${(_c = this.options) === null || _c === void 0 ? void 0 : _c.port}`;
62
+ };
63
+ this.handleOpenConnection = async () => {
64
+ this.connected = true;
65
+ const serverUrl = this.getServerUrl();
66
+ console.log(`${serverUrl} has been connected.`);
67
+ };
68
+ }
69
+ setupConnection(options, key = "") {
70
+ var _a;
71
+ this.options = {
72
+ ...this.options,
73
+ ...options,
74
+ };
75
+ this.key = key;
76
+ // Set up Web Socket server
77
+ if (this.ws) {
78
+ return;
79
+ }
80
+ const wsServer = new ws_1.WebSocketServer({
81
+ retryCount: 1,
82
+ reconnectInterval: 1,
83
+ handshakeTimeout: this.connectionTimeout,
84
+ port: (_a = this.options) === null || _a === void 0 ? void 0 : _a.port,
85
+ });
86
+ console.log(`Logs WebSocket server is listening on port ${this.options.port}`);
87
+ wsServer.on("error", this.handleError);
88
+ wsServer.on("open", () => this.handleOpenConnection());
89
+ wsServer.on("ping", () => this.ping(this.ws));
90
+ wsServer.on("close", () => this.closeConnection(this.ws));
91
+ wsServer.on("message", this.handleMessage);
92
+ wsServer.on("connection", (connection) => {
93
+ this.ws = connection;
94
+ connection.onmessage = this.handleMessage;
95
+ });
96
+ }
97
+ sendMessage(message) {
98
+ var _a;
99
+ (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(message));
100
+ }
101
+ };
102
+ exports.WsService = WsService;
103
+ exports.WsService = WsService = __decorate([
104
+ (0, common_1.Injectable)()
105
+ ], WsService);
106
+ //# sourceMappingURL=ws.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws.service.js","sourceRoot":"","sources":["../../src/services/ws.service.ts"],"names":[],"mappings":"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"}
@@ -11,4 +11,10 @@ export type LogModuleOptions = {
11
11
  table?: string;
12
12
  collection?: string;
13
13
  };
14
+ websocket?: {
15
+ port?: number;
16
+ namespace?: string;
17
+ host?: string;
18
+ secure?: boolean;
19
+ };
14
20
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viplance/nestjs-logger",
3
- "version": "0.3.8",
3
+ "version": "0.4.0",
4
4
  "description": "NestJS internal logging system",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -26,12 +26,14 @@
26
26
  "author": "Dzmitry Sharko",
27
27
  "license": "ISC",
28
28
  "peerDependencies": {
29
- "@nestjs/common": "^10.0.0",
30
- "@nestjs/core": "^10.0.0",
29
+ "@nestjs/common": "^11.0.0",
30
+ "@nestjs/core": "^11.0.0",
31
31
  "@nestjs/typeorm": "^11.0.0",
32
+ "@nestjs/websockets": "^11.1.7",
32
33
  "reflect-metadata": "^0.2.2",
33
34
  "rxjs": "^7.8.2",
34
- "typeorm": "^0.3.27"
35
+ "typeorm": "^0.3.27",
36
+ "ws": "^8.18.3"
35
37
  },
36
38
  "devDependencies": {
37
39
  "@types/node": "^24.5.2",
package/public/index.html CHANGED
@@ -8,6 +8,11 @@
8
8
  <link rel="stylesheet" href="styles/reset.css" />
9
9
  <link rel="stylesheet" href="styles/colors.css" />
10
10
  <link rel="stylesheet" href="styles/index.css" />
11
+ <script
12
+ defer="defer"
13
+ src="scripts/ws.js
14
+ "
15
+ ></script>
11
16
  <script
12
17
  defer="defer"
13
18
  src="scripts/json-viewer.js
@@ -42,12 +47,12 @@
42
47
  </ul>
43
48
  </nav>
44
49
  <input id="search" onkeyup="search(event)" type="text" placeholder="Search" />
45
- <div onclick="getLogs()"><button class="white">Refresh</button></div>
50
+ <div id="refresh" onclick="getLogs()"><button class="white">Refresh</button></div>
51
+ <div id="freeze" onclick="toggleFreeze()"><button class="white">Freeze</button></div>
46
52
  </div>
47
53
  <div class="container table-header">
48
54
  <div class="col">Type</div>
49
55
  <div class="col">Info</div>
50
- <div class="col">Trace</div>
51
56
  <div class="col">Count</div>
52
57
  </div>
53
58
  </header>
@@ -12,9 +12,7 @@ const logTypes = Object.keys(selectedLogTypes).filter((key) => key !== `all`);
12
12
  let logs = [];
13
13
  let text = "";
14
14
 
15
- window.addEventListener("load", async () => {
16
- getLogs();
17
- });
15
+ connectWebSocket();
18
16
 
19
17
  document.addEventListener(`click`, (e) => {
20
18
  const target = e.target;
@@ -140,10 +138,10 @@ function getLogHtmlElement(log) {
140
138
  </div>`;
141
139
  }
142
140
 
143
- function renderLogs() {
141
+ function renderLogs(logList = logs) {
144
142
  let html = "";
145
143
 
146
- logs
144
+ logList
147
145
  .filter((log) => {
148
146
  return selectedLogTypes["all"] || selectedLogTypes[log.type];
149
147
  })
@@ -165,50 +163,83 @@ function renderLogs() {
165
163
  document.getElementById("logs").innerHTML = html;
166
164
  }
167
165
 
166
+ async function checkElementsVisibility(logList = logs) {
167
+ if (logList.length === 0) {
168
+ document.getElementById("no-logs").style.display = "block";
169
+ document.getElementById("search").style.display = "none";
170
+ document.querySelector(".table-header").style.display = "none";
171
+ document.querySelector("nav").style.display = "none";
172
+ } else {
173
+ document.getElementById("no-logs").style.display = "none";
174
+ document.getElementById("search").style.display = "inline-block";
175
+ document.querySelector(".table-header").style.display = "flex";
176
+ document.querySelector("nav").style.display = "flex";
177
+ }
178
+ }
179
+
168
180
  async function getLogs() {
169
181
  const { origin, pathname, search } = window.location;
182
+ const searchParams = new URLSearchParams(search);
183
+ const key = searchParams.get("key");
184
+
185
+ if (!!socket) {
186
+ socket.send(
187
+ JSON.stringify({
188
+ action: "getLogs",
189
+ key,
190
+ })
191
+ );
192
+ } else {
193
+ const res = await fetch(`${origin}${pathname}api${search}`);
170
194
 
171
- const res = await fetch(`${origin}${pathname}api${search}`);
195
+ if (res.ok) {
196
+ logs = await res.json();
172
197
 
173
- if (res.ok) {
174
- logs = await res.json();
198
+ checkElementsVisibility();
175
199
 
176
- if (logs.length === 0) {
177
- document.getElementById("no-logs").style.display = "block";
178
- document.querySelector(".table-header").style.display = "none";
179
- document.querySelector("nav").style.display = "none";
200
+ renderLogs();
180
201
  } else {
181
- document.getElementById("no-logs").style.display = "none";
182
- document.querySelector(".table-header").style.display = "flex";
183
- document.querySelector("nav").style.display = "flex";
202
+ alert("An error occurred while fetching logs.");
184
203
  }
185
-
186
- renderLogs();
187
- } else {
188
- alert("An error occurred while fetching logs.");
189
204
  }
190
205
  }
191
206
 
192
- async function deleteLog(id) {
207
+ async function deleteLog(_id) {
193
208
  if (!confirm("Are you sure? It can't be undone.")) return;
194
209
 
195
210
  const { origin, pathname, search: searchParams } = window.location;
196
211
 
197
212
  const searchParamsWithId = new URLSearchParams(searchParams);
198
- searchParamsWithId.set("id", id);
199
-
200
- const res = await fetch(
201
- `${origin}${pathname}api?${searchParamsWithId.toString()}`,
202
- {
203
- method: "DELETE",
204
- }
205
- );
206
-
207
- if (res.ok) {
213
+ const key = searchParamsWithId.get("key");
214
+
215
+ if (!!socket) {
216
+ socket.send(
217
+ JSON.stringify({
218
+ action: "delete",
219
+ key,
220
+ data: {
221
+ _id,
222
+ },
223
+ })
224
+ );
208
225
  closePopup();
209
226
  getLogs();
210
227
  } else {
211
- alert("An error occurred while deleting log.");
228
+ searchParamsWithId.set("id", _id);
229
+
230
+ const res = await fetch(
231
+ `${origin}${pathname}api?${searchParamsWithId.toString()}`,
232
+ {
233
+ method: "DELETE",
234
+ }
235
+ );
236
+
237
+ if (res.ok) {
238
+ closePopup();
239
+ getLogs();
240
+ } else {
241
+ alert("An error occurred while deleting log.");
242
+ }
212
243
  }
213
244
  }
214
245
 
@@ -3,11 +3,20 @@ function showLogDetails(log) {
3
3
  const context = getObject(log.context);
4
4
  const breadcrumbs = getObject(log.breadcrumbs);
5
5
 
6
+ const timeInfo =
7
+ log.updatedAt === log.createdAt
8
+ ? getDate(log.updatedAt)
9
+ : `Updated: ${getDate(log.updatedAt)}.&nbsp;&nbsp;&nbsp;First seen: ${getDate(
10
+ log.createdAt
11
+ )}`;
12
+
6
13
  popup.innerHTML = `
7
14
  <div class="content center">
8
15
  <div class="container">
9
- <h2 class="popup-title ${log.type}">${log.type}: ${log.message}</h2>
10
- <div class="mt-05">${getDate(log.updatedAt)}</div>
16
+ <h2 class="popup-title ${log.type}">${log.type}: ${log.message} (${
17
+ log.count
18
+ })</h2>
19
+ <div class="mt-05">${timeInfo}</div>
11
20
  ${
12
21
  log.trace
13
22
  ? `
@@ -0,0 +1,83 @@
1
+ // WebSocket connection
2
+ let socket;
3
+ let frozen = false;
4
+
5
+ async function connectWebSocket() {
6
+ const { hostname, origin, pathname, search } = window.location;
7
+
8
+ const res = await fetch(`${origin}${pathname}settings${search}`);
9
+
10
+ if (!res.ok) {
11
+ alert('An error occurred while fetching settings.');
12
+ return;
13
+ }
14
+
15
+ const settings = await res.json();
16
+
17
+ if (!settings.websocket) {
18
+ getLogs();
19
+ document.getElementById('refresh').style.display = 'block';
20
+ }
21
+
22
+ document.getElementById('freeze').style.display = 'block';
23
+
24
+ if (!settings.websocket?.port) {
25
+ alert('WebSocket port is not configured.');
26
+ return;
27
+ }
28
+
29
+ const socketUrl = `ws://${hostname}:${settings.websocket.port}`;
30
+ socket = new WebSocket(socketUrl, 'json');
31
+
32
+ socket.onerror = (error) => {
33
+ console.error(error);
34
+ };
35
+
36
+ socket.onopen = (event) => {
37
+ getLogs();
38
+ };
39
+
40
+ socket.onclose = (event) => {
41
+ console.log(event);
42
+ setTimeout(connectWebSocket, 5000);
43
+ };
44
+
45
+ socket.onmessage = (event) => {
46
+ const data = JSON.parse(event.data.toString());
47
+
48
+ if (data['action'] && !frozen) {
49
+ switch (data['action']) {
50
+ case 'list':
51
+ logs = data['data'];
52
+ checkElementsVisibility(logs);
53
+ renderLogs(logs);
54
+ break;
55
+ case 'insert':
56
+ getLogs();
57
+ break;
58
+ case 'update':
59
+ getLogs();
60
+ return;
61
+ case 'delete':
62
+ return;
63
+ }
64
+ }
65
+ };
66
+ }
67
+ function sendMessage(message) {
68
+ socket.send(JSON.stringify(message));
69
+ }
70
+
71
+ function toggleFreeze() {
72
+ frozen = !frozen;
73
+ const button = document.querySelector('#freeze button');
74
+ button.innerHTML = frozen ? 'Frozen' : 'Freeze';
75
+
76
+ if (frozen) {
77
+ button.classList.remove('white');
78
+ button.classList.add('light');
79
+ } else {
80
+ button.classList.remove('light');
81
+ button.classList.add('white');
82
+ }
83
+ }
@@ -190,6 +190,11 @@ nav ul li:hover {
190
190
  font-size: 1rem;
191
191
  }
192
192
 
193
+ #refresh,
194
+ #freeze {
195
+ display: none;
196
+ }
197
+
193
198
  /* table */
194
199
  .table-header,
195
200
  .row {
package/src/log.module.ts CHANGED
@@ -8,12 +8,19 @@ import querystring from "node:querystring";
8
8
  import { ApplicationConfig } from "@nestjs/core";
9
9
  import { join } from "node:path";
10
10
  import { LogAccessGuard } from "./guards/access.guard";
11
+ import { WsService } from "./services/ws.service";
11
12
 
12
13
  @Global()
13
14
  @Module({
14
15
  imports: [TypeOrmModule],
15
- providers: [ApplicationConfig, LogAccessGuard, LogService, MemoryDbService],
16
- exports: [TypeOrmModule, LogService, MemoryDbService],
16
+ providers: [
17
+ ApplicationConfig,
18
+ LogAccessGuard,
19
+ LogService,
20
+ MemoryDbService,
21
+ WsService,
22
+ ],
23
+ exports: [TypeOrmModule, LogService, MemoryDbService, WsService],
17
24
  })
18
25
  export class LogModule {
19
26
  public static async init(
@@ -23,6 +30,7 @@ export class LogModule {
23
30
  app.resolve(LogService);
24
31
 
25
32
  const logService: LogService = await app.resolve(LogService);
33
+ const wsService: WsService = await app.resolve(WsService);
26
34
  const logAccessGuard: LogAccessGuard = await app.get(LogAccessGuard);
27
35
 
28
36
  if (options) {
@@ -38,6 +46,26 @@ export class LogModule {
38
46
 
39
47
  const httpAdapter = app.getHttpAdapter();
40
48
 
49
+ // frontend settings endpoint
50
+ httpAdapter.get(
51
+ join(options.path, "settings"),
52
+ async (req: any, res: any) => {
53
+ logAccessGuard.canActivate(req);
54
+
55
+ const result: { [key: string]: any } = {};
56
+
57
+ if (options?.websocket) {
58
+ result.websocket = {
59
+ namespace: options.websocket?.namespace,
60
+ port: options.websocket?.port,
61
+ host: options.websocket?.host || req.headers?.host.split(":")[0],
62
+ };
63
+ }
64
+
65
+ res.json(result);
66
+ }
67
+ );
68
+
41
69
  // get all logs endpoint
42
70
  httpAdapter.get(join(options.path, "api"), async (req: any, res: any) => {
43
71
  logAccessGuard.canActivate(req);
@@ -60,6 +88,11 @@ export class LogModule {
60
88
  res.json(await logService.delete(params.id.toString()));
61
89
  }
62
90
  );
91
+
92
+ // set up WebSocket connection
93
+ if (options?.websocket) {
94
+ wsService.setupConnection(options.websocket, options.key);
95
+ }
63
96
  }
64
97
 
65
98
  if (options?.database) {
@@ -1,4 +1,9 @@
1
- import { Injectable, LoggerService, Scope } from "@nestjs/common";
1
+ import {
2
+ Injectable,
3
+ LoggerService,
4
+ OnApplicationShutdown,
5
+ Scope,
6
+ } from "@nestjs/common";
2
7
  import { MemoryDbService } from "./memory-db.service";
3
8
  import { defaultTable } from "../defaults";
4
9
  import { Context, LogModuleOptions, LogType } from "../types";
@@ -12,17 +17,29 @@ import { createLogEntity } from "../entities/log.entity";
12
17
  import { ExecutionContextHost } from "@nestjs/core/helpers/execution-context-host";
13
18
  import { setInterval } from "timers";
14
19
  import { entity2table } from "../utils/entity2table";
20
+ import { WsService } from "./ws.service";
21
+ import { Subscription } from "rxjs";
15
22
 
16
23
  @Injectable({ scope: Scope.TRANSIENT })
17
- export class LogService implements LoggerService {
24
+ export class LogService implements LoggerService, OnApplicationShutdown {
18
25
  static connection: DataSource;
19
26
  static options: LogModuleOptions;
20
27
  static Log: EntitySchema = createLogEntity(defaultTable, "memory");
21
28
  static timer: ReturnType<typeof setInterval>;
29
+ static subscription: Subscription;
22
30
 
23
31
  breadcrumbs: any[] = [];
24
32
 
25
- constructor(private readonly memoryDbService: MemoryDbService) {}
33
+ constructor(
34
+ private readonly memoryDbService: MemoryDbService,
35
+ private readonly wsService: WsService
36
+ ) {}
37
+
38
+ onApplicationShutdown() {
39
+ if (LogService.timer) {
40
+ clearInterval(LogService.timer);
41
+ }
42
+ }
26
43
 
27
44
  async connectDb(options: LogModuleOptions): Promise<DataSource> {
28
45
  LogService.Log = createLogEntity(
@@ -30,7 +47,9 @@ export class LogService implements LoggerService {
30
47
  options.database?.type || "mongodb"
31
48
  );
32
49
 
33
- this.setOptions(options);
50
+ if (!LogService.options) {
51
+ this.setOptions(options);
52
+ }
34
53
 
35
54
  const dataSourceOptions = {
36
55
  type: options.database?.type,
@@ -44,8 +63,6 @@ export class LogService implements LoggerService {
44
63
  await LogService.connection.initialize();
45
64
 
46
65
  if (dataSourceOptions.type !== "mongodb") {
47
- // LogService.idName = "id";
48
-
49
66
  const queryRunner = LogService.connection.createQueryRunner();
50
67
 
51
68
  try {
@@ -70,6 +87,24 @@ export class LogService implements LoggerService {
70
87
 
71
88
  setOptions(options: LogModuleOptions) {
72
89
  LogService.options = options;
90
+
91
+ if (options.websocket && !LogService.subscription) {
92
+ LogService.subscription = this.wsService.onMessage.subscribe(
93
+ async (message) => {
94
+ switch (message.action) {
95
+ case "getLogs":
96
+ this.wsService.sendMessage({
97
+ action: "list",
98
+ data: await this.getAll(),
99
+ });
100
+ break;
101
+ case "delete":
102
+ this.delete(message.data._id);
103
+ break;
104
+ }
105
+ }
106
+ );
107
+ }
73
108
  }
74
109
 
75
110
  addBreadcrumb(breadcrumb: any) {
@@ -138,8 +173,12 @@ export class LogService implements LoggerService {
138
173
  });
139
174
  }
140
175
 
141
- async delete(id: string) {
142
- return this.getConnection().delete(LogService.Log, id);
176
+ async delete(_id: string) {
177
+ this.wsService.sendMessage({
178
+ action: "delete",
179
+ data: { _id },
180
+ });
181
+ return this.getConnection().delete(LogService.Log, _id);
143
182
  }
144
183
 
145
184
  private async smartInsert(data: {
@@ -163,16 +202,31 @@ export class LogService implements LoggerService {
163
202
  const context = data.context ? this.parseContext(data.context) : undefined;
164
203
 
165
204
  if (log) {
166
- return await connection.update(LogService.Log, log["_id"], {
205
+ const updatedLog = {
167
206
  context,
168
207
  trace: data.trace,
169
208
  breadcrumbs: this.breadcrumbs,
170
209
  count: log.count + 1,
171
210
  updatedAt: currentDate,
211
+ };
212
+
213
+ this.wsService.sendMessage({
214
+ action: "update",
215
+ data: { ...log, ...updatedLog },
172
216
  });
217
+
218
+ await connection.update(LogService.Log, log["_id"], {
219
+ context,
220
+ trace: data.trace,
221
+ breadcrumbs: this.breadcrumbs,
222
+ count: log.count + 1,
223
+ updatedAt: currentDate,
224
+ });
225
+
226
+ return { ...log, ...updatedLog };
173
227
  }
174
228
 
175
- return await connection.insert(LogService.Log, {
229
+ const insertedLog = {
176
230
  type: data.type,
177
231
  message: data.message,
178
232
  context,
@@ -181,7 +235,27 @@ export class LogService implements LoggerService {
181
235
  count: 1,
182
236
  createdAt: currentDate,
183
237
  updatedAt: currentDate,
238
+ };
239
+
240
+ const res = await connection.insert(LogService.Log, insertedLog);
241
+ const _id = this.getNewObjectId(res);
242
+
243
+ this.wsService.sendMessage({
244
+ action: "insert",
245
+ data: { _id, ...insertedLog },
184
246
  });
247
+
248
+ return { _id, ...insertedLog };
249
+ }
250
+
251
+ private getNewObjectId(result: any): string | number {
252
+ if (result.identifiers) {
253
+ return result.identifiers[0]._id;
254
+ }
255
+
256
+ console.log(result);
257
+
258
+ return result._id;
185
259
  }
186
260
 
187
261
  private getConnection(): EntityManager {
@@ -0,0 +1 @@
1
+ declare module "ws";
@@ -0,0 +1,110 @@
1
+ import { Injectable } from "@nestjs/common";
2
+ import WebSocket, { WebSocketServer } from "ws";
3
+ import { LogModuleOptions } from "../types";
4
+ import { Subject } from "rxjs";
5
+
6
+ @Injectable()
7
+ export class WsService {
8
+ public onMessage: Subject<any> = new Subject();
9
+ private ws: WebSocket | null = null;
10
+ private connected: boolean = false;
11
+ private connectionTimeout: number = 500;
12
+ private options: LogModuleOptions["websocket"] = {
13
+ port: 8080,
14
+ host: "localhost",
15
+ };
16
+ private key: string = "";
17
+
18
+ setupConnection(options: LogModuleOptions["websocket"], key = "") {
19
+ this.options = {
20
+ ...this.options,
21
+ ...options,
22
+ };
23
+ this.key = key;
24
+
25
+ // Set up Web Socket server
26
+ if (this.ws) {
27
+ return;
28
+ }
29
+
30
+ const wsServer = new WebSocketServer({
31
+ retryCount: 1,
32
+ reconnectInterval: 1,
33
+ handshakeTimeout: this.connectionTimeout,
34
+ port: this.options?.port,
35
+ });
36
+
37
+ console.log(
38
+ `Logs WebSocket server is listening on port ${this.options.port}`
39
+ );
40
+
41
+ wsServer.on("error", this.handleError);
42
+ wsServer.on("open", () => this.handleOpenConnection());
43
+ wsServer.on("ping", () => this.ping(this.ws));
44
+ wsServer.on("close", () => this.closeConnection(this.ws));
45
+ wsServer.on("message", this.handleMessage);
46
+ wsServer.on("connection", (connection: WebSocket) => {
47
+ this.ws = connection;
48
+ connection.onmessage = this.handleMessage;
49
+ });
50
+ }
51
+
52
+ sendMessage(message: any) {
53
+ this.ws?.send(JSON.stringify(message));
54
+ }
55
+
56
+ private handleError = () => {
57
+ const serverUrl = this.getServerUrl();
58
+ console.error(`Server ${serverUrl} is not available.`);
59
+
60
+ setTimeout(this.setupConnection, this.connectionTimeout);
61
+ };
62
+
63
+ private closeConnection = (connection: any) => {
64
+ clearTimeout(connection.pingTimeout);
65
+
66
+ if (this.connected) {
67
+ console.log("Connection has been closed by server.");
68
+ this.connected = false;
69
+ this.handleError();
70
+ }
71
+ };
72
+
73
+ private ping = (connection: any) => {
74
+ console.log("Ping remote server.");
75
+ clearTimeout(connection.pingTimeout);
76
+
77
+ connection.pingTimeout = setTimeout(() => {
78
+ connection.terminate();
79
+ }, 30000 + this.connectionTimeout);
80
+ };
81
+
82
+ private handleMessage = (message: any) => {
83
+ try {
84
+ const data = JSON.parse((message.data || message).toString());
85
+
86
+ if (this.key !== "" && data.key !== this.key) {
87
+ throw new Error("WebSocket unauthorized");
88
+ }
89
+
90
+ if (this.options)
91
+ if (data.action) {
92
+ this.onMessage.next(data);
93
+ }
94
+ } catch (err) {
95
+ console.error(err);
96
+ }
97
+ };
98
+
99
+ private getServerUrl = (): string => {
100
+ return `${this.options?.secure ? "wss" : "ws"}://${this.options?.host}:${
101
+ this.options?.port
102
+ }`;
103
+ };
104
+
105
+ private handleOpenConnection = async () => {
106
+ this.connected = true;
107
+ const serverUrl = this.getServerUrl();
108
+ console.log(`${serverUrl} has been connected.`);
109
+ };
110
+ }
@@ -12,4 +12,10 @@ export type LogModuleOptions = {
12
12
  table?: string;
13
13
  collection?: string;
14
14
  };
15
+ websocket?: {
16
+ port?: number;
17
+ namespace?: string;
18
+ host?: string;
19
+ secure?: boolean;
20
+ };
15
21
  };