@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
|
|
61
|
-
this.logger.error(
|
|
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([
|