@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, _b, _c, _d, _e;
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
- if (statusCode === 500) {
30
- console.error({
31
- message: exception.message,
32
- stack: exception.stack,
33
- connection: {
34
- localAddress: (_a = request.socket) === null || _a === void 0 ? void 0 : _a.localAddress,
35
- remoteAddress: (_b = request.socket) === null || _b === void 0 ? void 0 : _b.remoteAddress,
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 = ((_e = firstError.target) === null || _e === void 0 ? void 0 : _e.constructor) || firstError.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 logRequestResponse;
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
- const { method, url, headers, body } = request;
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
- this.logRequestResponse(requestId, request, response, duration);
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().pipe((0, operators_1.catchError)((error) => {
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
- logRequestResponse(requestId, request, response, duration) {
50
+ logRequest(requestId, request, response, duration) {
55
51
  const statusCode = response.statusCode;
56
- const logData = {
57
- requestId,
58
- timestamp: new Date().toISOString(),
59
- env: this.configService.nodeEnv,
60
- appName: this.configService.getString('NAME'),
61
- http: {
62
- method: request.method,
63
- url: request.url,
64
- query: request.query,
65
- statusCode,
66
- duration: `${duration}ms`,
67
- req: {
68
- headers: this.sanitizeHeaders(request.headers),
69
- body: this.sanitizeBody(request.body),
70
- },
71
- res: {
72
- headers: this.sanitizeHeaders(response.getHeaders()),
73
- },
74
- },
75
- user: this.extractUser(request.headers),
76
- };
77
- const message = `HTTP ${request.method} ${request.url} ${statusCode} (${duration}ms)`;
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 = ['authorization', 'apikey', 'x-api-key', 'token', 'cookie'];
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nest-omni/core",
3
- "version": "4.1.3-27",
3
+ "version": "4.1.3-28",
4
4
  "description": "A comprehensive NestJS framework for building enterprise-grade applications with best practices",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",