@globalart/nestjs-logger 1.0.8 → 1.0.10

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.
@@ -14,10 +14,15 @@ export declare class HttpLoggerInterceptor implements NestInterceptor {
14
14
  private readonly pid;
15
15
  constructor(logger: LoggerService, dataSanitizer: IDataSanitizer, requestIdGenerator: IRequestIdGenerator, config: LoggerConfiguration, reflector: Reflector);
16
16
  intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
17
+ private handleGraphQLRequest;
17
18
  private createHttpLogEntry;
18
19
  private createLogEntry;
19
20
  private getClientIp;
20
21
  private sanitizeHeaders;
21
22
  private getRequestMethod;
22
23
  private shouldExcludeRequest;
24
+ private sanitizeGraphQLArgs;
25
+ private getGraphQLResultSize;
26
+ private extractErrorMessage;
27
+ private extractErrorTrace;
23
28
  }
@@ -16,6 +16,14 @@ exports.HttpLoggerInterceptor = void 0;
16
16
  const common_1 = require("@nestjs/common");
17
17
  const operators_1 = require("rxjs/operators");
18
18
  const core_1 = require("@nestjs/core");
19
+ // Опциональный импорт для GraphQL
20
+ let GqlExecutionContext;
21
+ try {
22
+ GqlExecutionContext = require("@nestjs/graphql").GqlExecutionContext;
23
+ }
24
+ catch {
25
+ // GraphQL модуль не установлен
26
+ }
19
27
  const logger_service_1 = require("./logger.service");
20
28
  const constants_1 = require("../constants");
21
29
  const os_1 = require("os");
@@ -30,8 +38,15 @@ let HttpLoggerInterceptor = class HttpLoggerInterceptor {
30
38
  this.pid = process.pid;
31
39
  }
32
40
  intercept(context, next) {
41
+ const contextType = context.getType();
42
+ if (contextType === "graphql") {
43
+ return this.handleGraphQLRequest(context, next);
44
+ }
33
45
  const request = context.switchToHttp().getRequest();
34
46
  const response = context.switchToHttp().getResponse();
47
+ if (!request || !request.method) {
48
+ return next.handle();
49
+ }
35
50
  const requestMethod = this.getRequestMethod(request.method);
36
51
  if (this.shouldExcludeRequest(requestMethod, request.url)) {
37
52
  return next.handle();
@@ -52,17 +67,91 @@ let HttpLoggerInterceptor = class HttpLoggerInterceptor {
52
67
  this.logger.log(entry);
53
68
  }
54
69
  }), (0, operators_1.catchError)((error) => {
70
+ const errorMessage = this.extractErrorMessage(error);
71
+ const errorTrace = this.extractErrorTrace(error);
55
72
  if (this.config.format === "pino") {
56
- const entry = this.createHttpLogEntry(request, response, requestId, startTime, 50, "request failed");
73
+ const entry = this.createHttpLogEntry(request, response, requestId, startTime, 50, errorMessage || "request failed");
57
74
  this.logger.logHttpRequest(entry);
75
+ const baseEntry = this.createLogEntry(request, response, requestId, startTime);
76
+ this.logger.error({
77
+ ...baseEntry,
78
+ message: errorMessage || baseEntry.message,
79
+ trace: errorTrace,
80
+ });
58
81
  }
59
82
  else {
60
- const entry = this.createLogEntry(request, response, requestId, startTime);
61
- this.logger.error(entry);
83
+ const baseEntry = this.createLogEntry(request, response, requestId, startTime);
84
+ this.logger.error({
85
+ ...baseEntry,
86
+ message: errorMessage || baseEntry.message,
87
+ trace: errorTrace,
88
+ });
62
89
  }
63
90
  throw error;
64
91
  }));
65
92
  }
93
+ handleGraphQLRequest(context, next) {
94
+ if (!GqlExecutionContext) {
95
+ return next.handle();
96
+ }
97
+ try {
98
+ const gqlContext = GqlExecutionContext.create(context);
99
+ const info = gqlContext.getInfo();
100
+ const args = gqlContext.getArgs();
101
+ const startTime = Date.now();
102
+ const requestId = this.requestIdGenerator.generate();
103
+ const operationType = info.operation.operation;
104
+ const operationName = info.operation.name?.value || "Anonymous";
105
+ const fieldName = info.fieldName;
106
+ this.logger.log({
107
+ message: `GraphQL ${operationType}: ${operationName}.${fieldName}`,
108
+ context: "GraphQL",
109
+ metadata: {
110
+ requestId,
111
+ operationType,
112
+ operationName,
113
+ fieldName,
114
+ args: this.sanitizeGraphQLArgs(args),
115
+ },
116
+ });
117
+ return next.handle().pipe((0, operators_1.tap)((result) => {
118
+ const responseTime = Date.now() - startTime;
119
+ this.logger.log({
120
+ message: `GraphQL ${operationType} completed: ${operationName}.${fieldName} (${responseTime}ms)`,
121
+ context: "GraphQL",
122
+ metadata: {
123
+ requestId,
124
+ operationType,
125
+ operationName,
126
+ fieldName,
127
+ responseTime,
128
+ resultSize: this.getGraphQLResultSize(result),
129
+ },
130
+ });
131
+ }), (0, operators_1.catchError)((error) => {
132
+ const responseTime = Date.now() - startTime;
133
+ const errorMessage = this.extractErrorMessage(error);
134
+ const errorTrace = this.extractErrorTrace(error);
135
+ this.logger.error({
136
+ message: errorMessage ||
137
+ `GraphQL ${operationType} failed: ${operationName}.${fieldName} (${responseTime}ms)`,
138
+ context: "GraphQL",
139
+ metadata: {
140
+ requestId,
141
+ operationType,
142
+ operationName,
143
+ fieldName,
144
+ responseTime,
145
+ },
146
+ trace: errorTrace,
147
+ });
148
+ throw error;
149
+ }));
150
+ }
151
+ catch (gqlError) {
152
+ return next.handle();
153
+ }
154
+ }
66
155
  createHttpLogEntry(request, response, requestId, startTime, level, message) {
67
156
  const responseTime = Date.now() - startTime;
68
157
  const ip = this.getClientIp(request);
@@ -164,6 +253,63 @@ let HttpLoggerInterceptor = class HttpLoggerInterceptor {
164
253
  return path === excludeOption.path;
165
254
  });
166
255
  }
256
+ sanitizeGraphQLArgs(args) {
257
+ if (!args || typeof args !== "object") {
258
+ return args;
259
+ }
260
+ const sanitized = { ...args };
261
+ if (sanitized.input && sanitized.input.password) {
262
+ sanitized.input = { ...sanitized.input, password: "[HIDDEN]" };
263
+ }
264
+ return this.dataSanitizer.sanitize(sanitized);
265
+ }
266
+ getGraphQLResultSize(result) {
267
+ if (Array.isArray(result)) {
268
+ return `${result.length} items`;
269
+ }
270
+ if (result && typeof result === "object") {
271
+ return "1 object";
272
+ }
273
+ return "primitive";
274
+ }
275
+ extractErrorMessage(error) {
276
+ if (!error)
277
+ return undefined;
278
+ // Nest HttpException
279
+ if (typeof error.getResponse === "function") {
280
+ const resp = error.getResponse();
281
+ if (typeof resp === "string")
282
+ return resp;
283
+ if (resp && typeof resp === "object") {
284
+ return (resp.message || resp.error || error.message);
285
+ }
286
+ }
287
+ // GraphQL/Apollo ошибки
288
+ if (error.extensions && error.extensions.exception) {
289
+ const ex = error.extensions.exception;
290
+ return ex.message || error.message;
291
+ }
292
+ return error.message;
293
+ }
294
+ extractErrorTrace(error) {
295
+ if (!error)
296
+ return undefined;
297
+ // Nest HttpException
298
+ if (typeof error.getResponse === "function") {
299
+ const ex = error;
300
+ return ex.stack || ex.trace || undefined;
301
+ }
302
+ // GraphQL/Apollo ошибки
303
+ if (error.extensions && error.extensions.exception) {
304
+ const ex = error.extensions.exception;
305
+ return ex.stacktrace
306
+ ? Array.isArray(ex.stacktrace)
307
+ ? ex.stacktrace.join("\n")
308
+ : String(ex.stacktrace)
309
+ : ex.stack || undefined;
310
+ }
311
+ return error.stack;
312
+ }
167
313
  };
168
314
  exports.HttpLoggerInterceptor = HttpLoggerInterceptor;
169
315
  exports.HttpLoggerInterceptor = HttpLoggerInterceptor = __decorate([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@globalart/nestjs-logger",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "A advanced logger for NestJS",
5
5
  "author": {
6
6
  "name": "GlobalArt, Inc"