@nest-omni/core 4.1.3-27 → 4.1.3-28
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.
|
@@ -13,7 +13,7 @@ const nestjs_i18n_1 = require("nestjs-i18n");
|
|
|
13
13
|
const validation_metadata_helper_1 = require("../common/helpers/validation-metadata-helper");
|
|
14
14
|
let HttpExceptionFilter = class HttpExceptionFilter {
|
|
15
15
|
catch(exception, host) {
|
|
16
|
-
var _a
|
|
16
|
+
var _a;
|
|
17
17
|
const i18n = nestjs_i18n_1.I18nContext.current(host);
|
|
18
18
|
const ctx = host.switchToHttp();
|
|
19
19
|
const response = ctx.getResponse();
|
|
@@ -26,19 +26,13 @@ let HttpExceptionFilter = class HttpExceptionFilter {
|
|
|
26
26
|
statusCode = 404;
|
|
27
27
|
error = 'Entity not found';
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
bytesRead: (_c = request.socket) === null || _c === void 0 ? void 0 : _c.bytesRead,
|
|
37
|
-
bytesWritten: (_d = request.socket) === null || _d === void 0 ? void 0 : _d.bytesWritten,
|
|
38
|
-
},
|
|
39
|
-
headers: request.headers,
|
|
40
|
-
});
|
|
41
|
-
}
|
|
29
|
+
// 将异常信息存储到 request 对象上,供 HttpLoggingInterceptor 使用
|
|
30
|
+
request._exception = {
|
|
31
|
+
message: exception.message,
|
|
32
|
+
stack: exception.stack,
|
|
33
|
+
code: exception.code,
|
|
34
|
+
statusCode,
|
|
35
|
+
};
|
|
42
36
|
const getFirstError = (error, property = '') => {
|
|
43
37
|
var _a;
|
|
44
38
|
if (((_a = error === null || error === void 0 ? void 0 : error.children) === null || _a === void 0 ? void 0 : _a.length) > 0) {
|
|
@@ -57,7 +51,7 @@ let HttpExceptionFilter = class HttpExceptionFilter {
|
|
|
57
51
|
const lang = i18n.lang || 'zh';
|
|
58
52
|
// 在 stringify 之前获取 target
|
|
59
53
|
// firstError.target 可能是实例而不是构造函数,所以需要获取 constructor
|
|
60
|
-
let target = ((
|
|
54
|
+
let target = ((_a = firstError.target) === null || _a === void 0 ? void 0 : _a.constructor) || firstError.target;
|
|
61
55
|
if (!target && (firstError === null || firstError === void 0 ? void 0 : firstError.object)) {
|
|
62
56
|
target = firstError.object.constructor;
|
|
63
57
|
}
|
|
@@ -4,7 +4,7 @@ import { ApiConfigService } from '../shared/services/api-config.service';
|
|
|
4
4
|
/**
|
|
5
5
|
* HTTP 日志拦截器
|
|
6
6
|
* 参考 Tomcat AccessLog 的实现,每个请求只记录一条日志
|
|
7
|
-
*
|
|
7
|
+
* 只在 finish 事件中记录,因为异常过滤器会处理所有错误并返回响应
|
|
8
8
|
*/
|
|
9
9
|
export declare class HttpLoggingInterceptor implements NestInterceptor {
|
|
10
10
|
private readonly configService;
|
|
@@ -18,11 +18,7 @@ export declare class HttpLoggingInterceptor implements NestInterceptor {
|
|
|
18
18
|
/**
|
|
19
19
|
* 记录请求和响应(一条日志)
|
|
20
20
|
*/
|
|
21
|
-
private
|
|
22
|
-
/**
|
|
23
|
-
* 记录请求和错误(一条日志)
|
|
24
|
-
*/
|
|
25
|
-
private logRequestError;
|
|
21
|
+
private logRequest;
|
|
26
22
|
/**
|
|
27
23
|
* 清理敏感的 header 信息
|
|
28
24
|
*/
|
|
@@ -12,12 +12,11 @@ var HttpLoggingInterceptor_1;
|
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
13
|
exports.HttpLoggingInterceptor = void 0;
|
|
14
14
|
const common_1 = require("@nestjs/common");
|
|
15
|
-
const operators_1 = require("rxjs/operators");
|
|
16
15
|
const api_config_service_1 = require("../shared/services/api-config.service");
|
|
17
16
|
/**
|
|
18
17
|
* HTTP 日志拦截器
|
|
19
18
|
* 参考 Tomcat AccessLog 的实现,每个请求只记录一条日志
|
|
20
|
-
*
|
|
19
|
+
* 只在 finish 事件中记录,因为异常过滤器会处理所有错误并返回响应
|
|
21
20
|
*/
|
|
22
21
|
let HttpLoggingInterceptor = HttpLoggingInterceptor_1 = class HttpLoggingInterceptor {
|
|
23
22
|
constructor(configService) {
|
|
@@ -29,18 +28,15 @@ let HttpLoggingInterceptor = HttpLoggingInterceptor_1 = class HttpLoggingInterce
|
|
|
29
28
|
const request = ctx.getRequest();
|
|
30
29
|
const response = ctx.getResponse();
|
|
31
30
|
const startTime = Date.now();
|
|
32
|
-
|
|
33
|
-
const requestId = String(request.id || headers['x-request-id'] || this.generateRequestId());
|
|
34
|
-
// 监听响应完成事件,一条日志同时记录请求和响应
|
|
31
|
+
// 只在 finish 事件中记录日志(此时响应已完成,状态码已确定)
|
|
35
32
|
response.on('finish', () => {
|
|
36
33
|
const duration = Date.now() - startTime;
|
|
37
|
-
|
|
34
|
+
const requestId = String(request.id ||
|
|
35
|
+
request.headers['x-request-id'] ||
|
|
36
|
+
this.generateRequestId());
|
|
37
|
+
this.logRequest(requestId, request, response, duration);
|
|
38
38
|
});
|
|
39
|
-
return next.handle()
|
|
40
|
-
const duration = Date.now() - startTime;
|
|
41
|
-
this.logRequestError(requestId, request, error, duration);
|
|
42
|
-
throw error;
|
|
43
|
-
}));
|
|
39
|
+
return next.handle();
|
|
44
40
|
}
|
|
45
41
|
/**
|
|
46
42
|
* 生成请求 ID
|
|
@@ -51,31 +47,30 @@ let HttpLoggingInterceptor = HttpLoggingInterceptor_1 = class HttpLoggingInterce
|
|
|
51
47
|
/**
|
|
52
48
|
* 记录请求和响应(一条日志)
|
|
53
49
|
*/
|
|
54
|
-
|
|
50
|
+
logRequest(requestId, request, response, duration) {
|
|
55
51
|
const statusCode = response.statusCode;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (statusCode >= 500) {
|
|
52
|
+
// 从 request 对象中读取异常信息(由 HttpExceptionFilter 存储)
|
|
53
|
+
const exception = request._exception;
|
|
54
|
+
// 将异常信息放在 message 中,这样一定会被输出
|
|
55
|
+
let errorMessage = '';
|
|
56
|
+
if (exception) {
|
|
57
|
+
errorMessage = `\n Error: ${exception.message}\n Stack: ${exception.stack}`;
|
|
58
|
+
}
|
|
59
|
+
const logData = Object.assign(Object.assign({ requestId, timestamp: new Date().toISOString(), env: this.configService.nodeEnv, appName: this.configService.getString('NAME'), httpMethod: request.method, httpUrl: request.url, query: request.query, statusCode, duration: `${duration}ms`, requestHeaders: this.sanitizeHeaders(request.headers), requestBody: this.sanitizeBody(request.body), responseHeaders: this.sanitizeHeaders(response.getHeaders()) }, (exception && {
|
|
60
|
+
errorMessage: exception.message,
|
|
61
|
+
errorStack: exception.stack,
|
|
62
|
+
errorCode: exception.code,
|
|
63
|
+
originalStatusCode: exception.statusCode,
|
|
64
|
+
})), { user: this.extractUser(request.headers) });
|
|
65
|
+
const message = `HTTP ${request.method} ${request.url} ${statusCode} (${duration}ms)${errorMessage}`;
|
|
66
|
+
// 根据是否有异常来决定日志级别
|
|
67
|
+
if ((exception === null || exception === void 0 ? void 0 : exception.statusCode) >= 500) {
|
|
68
|
+
this.logger.error(message, logData);
|
|
69
|
+
}
|
|
70
|
+
else if ((exception === null || exception === void 0 ? void 0 : exception.statusCode) >= 400) {
|
|
71
|
+
this.logger.warn(message, logData);
|
|
72
|
+
}
|
|
73
|
+
else if (statusCode >= 500) {
|
|
79
74
|
this.logger.error(message, logData);
|
|
80
75
|
}
|
|
81
76
|
else if (statusCode >= 400) {
|
|
@@ -85,35 +80,6 @@ let HttpLoggingInterceptor = HttpLoggingInterceptor_1 = class HttpLoggingInterce
|
|
|
85
80
|
this.logger.log(message, logData);
|
|
86
81
|
}
|
|
87
82
|
}
|
|
88
|
-
/**
|
|
89
|
-
* 记录请求和错误(一条日志)
|
|
90
|
-
*/
|
|
91
|
-
logRequestError(requestId, request, error, duration) {
|
|
92
|
-
const logData = {
|
|
93
|
-
requestId,
|
|
94
|
-
timestamp: new Date().toISOString(),
|
|
95
|
-
env: this.configService.nodeEnv,
|
|
96
|
-
appName: this.configService.getString('NAME'),
|
|
97
|
-
http: {
|
|
98
|
-
method: request.method,
|
|
99
|
-
url: request.url,
|
|
100
|
-
query: request.query,
|
|
101
|
-
statusCode: (error === null || error === void 0 ? void 0 : error.status) || 500,
|
|
102
|
-
duration: `${duration}ms`,
|
|
103
|
-
req: {
|
|
104
|
-
headers: this.sanitizeHeaders(request.headers),
|
|
105
|
-
body: this.sanitizeBody(request.body),
|
|
106
|
-
},
|
|
107
|
-
},
|
|
108
|
-
error: {
|
|
109
|
-
message: error.message,
|
|
110
|
-
stack: error.stack,
|
|
111
|
-
code: error.code,
|
|
112
|
-
},
|
|
113
|
-
user: this.extractUser(request.headers),
|
|
114
|
-
};
|
|
115
|
-
this.logger.error(`HTTP ${request.method} ${request.url} ${(error === null || error === void 0 ? void 0 : error.status) || 500} (${duration}ms) - ${error.message}`, logData);
|
|
116
|
-
}
|
|
117
83
|
/**
|
|
118
84
|
* 清理敏感的 header 信息
|
|
119
85
|
*/
|
|
@@ -121,7 +87,13 @@ let HttpLoggingInterceptor = HttpLoggingInterceptor_1 = class HttpLoggingInterce
|
|
|
121
87
|
if (!headers)
|
|
122
88
|
return {};
|
|
123
89
|
const sanitized = Object.assign({}, headers);
|
|
124
|
-
const sensitiveKeys = [
|
|
90
|
+
const sensitiveKeys = [
|
|
91
|
+
'authorization',
|
|
92
|
+
'apikey',
|
|
93
|
+
'x-api-key',
|
|
94
|
+
'token',
|
|
95
|
+
'cookie',
|
|
96
|
+
];
|
|
125
97
|
for (const key of Object.keys(sanitized)) {
|
|
126
98
|
if (sensitiveKeys.includes(key.toLowerCase())) {
|
|
127
99
|
sanitized[key] = '[REDACTED]';
|
|
@@ -135,7 +107,6 @@ let HttpLoggingInterceptor = HttpLoggingInterceptor_1 = class HttpLoggingInterce
|
|
|
135
107
|
sanitizeBody(body) {
|
|
136
108
|
if (!body)
|
|
137
109
|
return body;
|
|
138
|
-
// 如果 body 太大,只记录部分信息
|
|
139
110
|
const bodyStr = JSON.stringify(body);
|
|
140
111
|
if (bodyStr.length > 1000) {
|
|
141
112
|
return { _large: `${bodyStr.length} bytes` };
|
package/package.json
CHANGED