@nest-omni/core 4.1.3-19 → 4.1.3-20

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.
Files changed (45) hide show
  1. package/cache/cache.module.d.ts +0 -6
  2. package/cache/cache.module.js +7 -7
  3. package/cache/cache.service.js +12 -0
  4. package/cache/dependencies/db.dependency.d.ts +0 -13
  5. package/cache/dependencies/db.dependency.js +0 -16
  6. package/cache/dependencies/tag.dependency.d.ts +39 -4
  7. package/cache/dependencies/tag.dependency.js +109 -11
  8. package/cache/interfaces/cache-options.interface.d.ts +8 -0
  9. package/cache/providers/memory-cache.provider.d.ts +20 -0
  10. package/cache/providers/memory-cache.provider.js +40 -0
  11. package/http-client/config/http-client.config.d.ts +5 -0
  12. package/http-client/config/http-client.config.js +24 -13
  13. package/http-client/decorators/http-client.decorators.d.ts +1 -25
  14. package/http-client/decorators/http-client.decorators.js +97 -90
  15. package/http-client/entities/http-log.entity.d.ts +0 -20
  16. package/http-client/entities/http-log.entity.js +0 -12
  17. package/http-client/examples/advanced-usage.example.d.ts +4 -5
  18. package/http-client/examples/advanced-usage.example.js +4 -56
  19. package/http-client/http-client.module.d.ts +35 -2
  20. package/http-client/http-client.module.js +80 -75
  21. package/http-client/index.d.ts +1 -1
  22. package/http-client/interfaces/api-client-config.interface.d.ts +1 -91
  23. package/http-client/interfaces/http-client-config.interface.d.ts +53 -62
  24. package/http-client/services/api-client-registry.service.d.ts +5 -23
  25. package/http-client/services/api-client-registry.service.js +41 -284
  26. package/http-client/services/circuit-breaker.service.d.ts +69 -2
  27. package/http-client/services/circuit-breaker.service.js +185 -7
  28. package/http-client/services/http-client.service.d.ts +58 -23
  29. package/http-client/services/http-client.service.js +294 -150
  30. package/http-client/services/http-log-query.service.js +0 -13
  31. package/http-client/services/index.d.ts +0 -1
  32. package/http-client/services/index.js +0 -1
  33. package/http-client/services/logging.service.d.ts +79 -10
  34. package/http-client/services/logging.service.js +246 -51
  35. package/http-client/utils/call-stack-extractor.util.d.ts +26 -0
  36. package/http-client/utils/call-stack-extractor.util.js +35 -0
  37. package/http-client/utils/security-validator.util.d.ts +118 -0
  38. package/http-client/utils/security-validator.util.js +352 -0
  39. package/package.json +1 -1
  40. package/redis-lock/lock-heartbeat.service.d.ts +2 -0
  41. package/redis-lock/lock-heartbeat.service.js +12 -2
  42. package/redis-lock/redis-lock.service.d.ts +4 -0
  43. package/redis-lock/redis-lock.service.js +61 -8
  44. package/http-client/services/cache.service.d.ts +0 -76
  45. package/http-client/services/cache.service.js +0 -333
@@ -225,12 +225,6 @@ let HttpLogQueryService = HttpLogQueryService_1 = class HttpLogQueryService {
225
225
  .addSelect('SUM(log.attemptCount - 1)', 'totalRetries')
226
226
  .addSelect('COUNT(*)', 'totalRequests')
227
227
  .getRawOne();
228
- // 缓存统计
229
- const cacheStats = yield baseQuery
230
- .select('SUM(CASE WHEN log.cacheHit = true THEN 1 ELSE 0 END)', 'cacheHits')
231
- .addSelect('SUM(CASE WHEN log.cacheHit = false OR log.cacheHit IS NULL THEN 1 ELSE 0 END)', 'cacheMisses')
232
- .addSelect('COUNT(*)', 'total')
233
- .getRawOne();
234
228
  // 百分位数计算(简化版)
235
229
  const percentiles = yield this.calculatePercentiles(baseQuery);
236
230
  return {
@@ -259,13 +253,6 @@ let HttpLogQueryService = HttpLogQueryService_1 = class HttpLogQueryService {
259
253
  : 0,
260
254
  retryRate: total > 0 ? (((retryStats === null || retryStats === void 0 ? void 0 : retryStats.totalRetries) || 0) / total) * 100 : 0,
261
255
  },
262
- cacheStats: {
263
- cacheHits: (cacheStats === null || cacheStats === void 0 ? void 0 : cacheStats.cacheHits) || 0,
264
- cacheMisses: (cacheStats === null || cacheStats === void 0 ? void 0 : cacheStats.cacheMisses) || 0,
265
- cacheHitRate: (cacheStats === null || cacheStats === void 0 ? void 0 : cacheStats.total) > 0
266
- ? (((cacheStats === null || cacheStats === void 0 ? void 0 : cacheStats.cacheHits) || 0) / cacheStats.total) * 100
267
- : 0,
268
- },
269
256
  };
270
257
  });
271
258
  }
@@ -1,7 +1,6 @@
1
1
  export * from './http-client.service';
2
2
  export * from './circuit-breaker.service';
3
3
  export * from './logging.service';
4
- export * from './cache.service';
5
4
  export * from './http-log-query.service';
6
5
  export * from './log-cleanup.service';
7
6
  export * from './api-client-registry.service';
@@ -17,7 +17,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./http-client.service"), exports);
18
18
  __exportStar(require("./circuit-breaker.service"), exports);
19
19
  __exportStar(require("./logging.service"), exports);
20
- __exportStar(require("./cache.service"), exports);
21
20
  __exportStar(require("./http-log-query.service"), exports);
22
21
  __exportStar(require("./log-cleanup.service"), exports);
23
22
  __exportStar(require("./api-client-registry.service"), exports);
@@ -1,15 +1,39 @@
1
- import { DataSource } from 'typeorm';
1
+ import { OnModuleDestroy } from '@nestjs/common';
2
+ import { Repository } from 'typeorm';
2
3
  import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios';
3
4
  import { HttpLogEntity } from '../entities/http-log.entity';
5
+ /**
6
+ * 异步日志配置
7
+ */
8
+ interface AsyncLogConfig {
9
+ /** 批量大小 */
10
+ batchSize: number;
11
+ /** 刷新间隔(毫秒) */
12
+ flushIntervalMs: number;
13
+ /** 最大重试次数 */
14
+ maxRetries: number;
15
+ /** 是否启用异步日志 */
16
+ enabled: boolean;
17
+ }
4
18
  /**
5
19
  * HTTP日志服务
6
20
  * 基于Spring Boot的请求日志记录机制,集成现有的ContextProvider
21
+ * 支持异步批量日志写入以提升性能
7
22
  */
8
- export declare class HttpLoggingService {
9
- private readonly dataSource?;
23
+ export declare class HttpLoggingService implements OnModuleDestroy {
10
24
  private readonly logger;
11
25
  private logRepository;
12
- constructor(dataSource?: DataSource);
26
+ /** 日志队列 */
27
+ private logQueue;
28
+ /** 定时器 */
29
+ private flushTimer?;
30
+ /** 默认异步日志配置 */
31
+ private readonly asyncConfig;
32
+ /** 处理中标志 */
33
+ private isProcessing;
34
+ constructor();
35
+ onModuleDestroy(): void;
36
+ initRepository(dataSource: string, tableName: string): Repository<HttpLogEntity>;
13
37
  /**
14
38
  * 记录请求开始
15
39
  */
@@ -17,11 +41,19 @@ export declare class HttpLoggingService {
17
41
  /**
18
42
  * 记录请求成功响应
19
43
  */
20
- logRequestSuccess(response: AxiosResponse, startTime: number, requestId: string, loggingOptions: any, databaseLogging?: boolean, retryRecords?: any[], cacheHit?: boolean, circuitBreakerState?: string, decoratorContext?: any): Promise<void>;
44
+ logRequestSuccess(response: AxiosResponse, startTime: number, requestId: string, loggingOptions: any, databaseLogging?: boolean, retryRecords?: any[], circuitBreakerState?: string, decoratorContext?: any, clientName?: string, callingContext?: {
45
+ serviceClass?: string;
46
+ methodName?: string;
47
+ operationName?: string;
48
+ }): Promise<void>;
21
49
  /**
22
50
  * 记录请求错误
23
51
  */
24
- logRequestError(error: AxiosError, startTime: number, requestId: string, attemptCount: number, loggingOptions: any, databaseLogging?: boolean, retryRecords?: any[], cacheHit?: boolean, circuitBreakerState?: string, decoratorContext?: any): void;
52
+ logRequestError(error: AxiosError, startTime: number, requestId: string, attemptCount: number, loggingOptions: any, databaseLogging?: boolean, retryRecords?: any[], circuitBreakerState?: string, decoratorContext?: any, clientName?: string, callingContext?: {
53
+ serviceClass?: string;
54
+ methodName?: string;
55
+ operationName?: string;
56
+ }): void;
25
57
  /**
26
58
  * 查询请求日志
27
59
  */
@@ -45,18 +77,48 @@ export declare class HttpLoggingService {
45
77
  endDate?: Date;
46
78
  userId?: string;
47
79
  }): Promise<any>;
48
- /**
49
- * 初始化数据库日志记录
50
- */
51
- private initializeDatabaseLogging;
52
80
  /**
53
81
  * 提取调用信息
54
82
  */
55
83
  private extractCallInfo;
56
84
  /**
57
85
  * 保存到数据库
86
+ * 如果启用异步日志,将日志项添加到队列中
87
+ * 否则同步保存到数据库
58
88
  */
59
89
  private saveToDatabase;
90
+ /**
91
+ * 启动定期刷新任务
92
+ */
93
+ private startFlushTask;
94
+ /**
95
+ * 刷新日志队列到数据库
96
+ */
97
+ private flush;
98
+ /**
99
+ * 处理单个日志项
100
+ */
101
+ private processLogItem;
102
+ /**
103
+ * 同步保存日志到数据库(用于异步模式内部)
104
+ */
105
+ private saveLogToDatabase;
106
+ /**
107
+ * 获取日志队列统计信息
108
+ */
109
+ getQueueStats(): {
110
+ queueSize: number;
111
+ isProcessing: boolean;
112
+ config: AsyncLogConfig;
113
+ };
114
+ /**
115
+ * 手动刷新日志队列
116
+ */
117
+ manualFlush(): Promise<void>;
118
+ /**
119
+ * 设置异步日志配置
120
+ */
121
+ setAsyncConfig(config: Partial<AsyncLogConfig>): void;
60
122
  /**
61
123
  * 转换请求体为字符串
62
124
  */
@@ -73,4 +135,11 @@ export declare class HttpLoggingService {
73
135
  * 递归清理对象中的敏感字段
74
136
  */
75
137
  private sanitizeObject;
138
+ /**
139
+ * 获取完整URL
140
+ * @param config Axios请求配置
141
+ * @returns 完整的URL字符串
142
+ */
143
+ private getFullUrl;
76
144
  }
145
+ export {};
@@ -21,22 +21,53 @@ var HttpLoggingService_1;
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.HttpLoggingService = void 0;
23
23
  const common_1 = require("@nestjs/common");
24
- const typeorm_1 = require("typeorm");
25
24
  const http_log_entity_1 = require("../entities/http-log.entity");
26
25
  const request_id_util_1 = require("../utils/request-id.util");
27
26
  const context_extractor_util_1 = require("../utils/context-extractor.util");
28
27
  const call_stack_extractor_util_1 = require("../utils/call-stack-extractor.util");
28
+ const transaction_1 = require("@nest-omni/transaction");
29
29
  /**
30
30
  * HTTP日志服务
31
31
  * 基于Spring Boot的请求日志记录机制,集成现有的ContextProvider
32
+ * 支持异步批量日志写入以提升性能
32
33
  */
33
34
  let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
34
- constructor(dataSource) {
35
- this.dataSource = dataSource;
35
+ constructor() {
36
36
  this.logger = new common_1.Logger(HttpLoggingService_1.name);
37
37
  this.logRepository = null;
38
- if (dataSource) {
39
- this.initializeDatabaseLogging();
38
+ /** 日志队列 */
39
+ this.logQueue = [];
40
+ /** 默认异步日志配置 */
41
+ this.asyncConfig = {
42
+ batchSize: 50, // 批量写入50条日志
43
+ flushIntervalMs: 5000, // 每5秒刷新一次
44
+ maxRetries: 3, // 失败后最多重试3次
45
+ enabled: true, // 默认启用异步日志
46
+ };
47
+ /** 处理中标志 */
48
+ this.isProcessing = false;
49
+ // 启动定期刷新任务
50
+ if (this.asyncConfig.enabled) {
51
+ this.startFlushTask();
52
+ }
53
+ }
54
+ onModuleDestroy() {
55
+ // 清理定时器
56
+ if (this.flushTimer) {
57
+ clearInterval(this.flushTimer);
58
+ }
59
+ // 刷新剩余日志
60
+ this.flush();
61
+ }
62
+ initRepository(dataSource, tableName) {
63
+ try {
64
+ this.logRepository = (0, transaction_1.getDataSource)(dataSource).getRepository(http_log_entity_1.HttpLogEntity);
65
+ this.logger.log('Database logging initialized');
66
+ return this.logRepository;
67
+ }
68
+ catch (error) {
69
+ this.logger.error('Failed to initialize database logging', error);
70
+ throw error;
40
71
  }
41
72
  }
42
73
  /**
@@ -75,8 +106,8 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
75
106
  * 记录请求成功响应
76
107
  */
77
108
  logRequestSuccess(response_1, startTime_1, requestId_1, loggingOptions_1) {
78
- return __awaiter(this, arguments, void 0, function* (response, startTime, requestId, loggingOptions, databaseLogging = false, retryRecords, cacheHit, circuitBreakerState, decoratorContext) {
79
- var _a, _b;
109
+ return __awaiter(this, arguments, void 0, function* (response, startTime, requestId, loggingOptions, databaseLogging = false, retryRecords, circuitBreakerState, decoratorContext, clientName, callingContext) {
110
+ var _a, _b, _c, _d;
80
111
  const { logHeaders = true, logBody = true, sanitizeHeaders = [], } = loggingOptions || {};
81
112
  const responseTime = Date.now() - startTime;
82
113
  // 获取当前上下文信息
@@ -91,56 +122,63 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
91
122
  : undefined,
92
123
  body: logBody ? this.sanitizeBody(response.data) : undefined,
93
124
  responseSize: JSON.stringify(response.data).length,
94
- cacheHit,
95
125
  circuitBreakerState,
96
126
  };
97
- const logMessage = `HTTP Response [${requestId}]: ${response.status} (${responseTime}ms)${cacheHit ? ' [CACHE HIT]' : ''}`;
127
+ const logMessage = `HTTP Response [${requestId}]: ${response.status} (${responseTime}ms)`;
98
128
  switch ((loggingOptions || {}).logLevel) {
99
129
  case 'debug':
100
130
  this.logger.debug(logMessage, logData);
101
131
  break;
102
- ``;
103
132
  case 'info':
104
133
  default:
105
134
  this.logger.log(logMessage, logData);
106
135
  break;
107
136
  }
108
- if (!this.logRepository && this.dataSource) {
109
- this.logRepository = this.dataSource.getRepository(loggingOptions.databaseLogging.tableName);
137
+ if (!this.logRepository) {
138
+ this.logger.debug('Initializing log repository', {
139
+ dataSource: (_a = loggingOptions.databaseLogging) === null || _a === void 0 ? void 0 : _a.dataSource,
140
+ tableName: (_b = loggingOptions.databaseLogging) === null || _b === void 0 ? void 0 : _b.tableName,
141
+ });
142
+ this.initRepository(loggingOptions.databaseLogging.dataSource, loggingOptions.databaseLogging.tableName);
110
143
  }
111
144
  // 数据库日志记录
145
+ if (databaseLogging && this.logRepository) {
146
+ this.logger.log('Database logging conditions met, saving to database');
147
+ }
148
+ else {
149
+ this.logger.warn(`Database logging conditions NOT met: databaseLogging=${databaseLogging}, hasRepository=${!!this.logRepository}, type=${typeof this.logRepository}`);
150
+ }
112
151
  if (databaseLogging && this.logRepository) {
113
152
  // 提取调用信息
114
- const callInfo = this.extractCallInfo(decoratorContext, response.config);
153
+ const callInfo = callingContext && callingContext.operationName
154
+ ? callingContext
155
+ : this.extractCallInfo(decoratorContext, response.config);
115
156
  this.saveToDatabase({
116
157
  id: requestId,
117
158
  requestId,
118
159
  userId: context.userId,
119
- method: ((_a = response.config.method) === null || _a === void 0 ? void 0 : _a.toUpperCase()) || 'UNKNOWN',
120
- url: response.config.url || '',
160
+ method: ((_c = response.config.method) === null || _c === void 0 ? void 0 : _c.toUpperCase()) || 'UNKNOWN',
161
+ url: this.getFullUrl(response.config) || '',
121
162
  headers: this.sanitizeHeaders(response.config.headers, sanitizeHeaders),
122
163
  body: this.sanitizeBodyAsString(response.config.data),
123
164
  params: response.config.params,
124
165
  statusCode: response.status,
125
166
  responseTime,
126
167
  attemptCount: (retryRecords === null || retryRecords === void 0 ? void 0 : retryRecords.length)
127
- ? Math.max(...retryRecords.map((r) => r.attempt))
168
+ ? Math.max(...retryRecords.map((r) => r.attempt)) + 1
128
169
  : 1,
129
170
  success: true,
130
171
  responseHeaders: this.sanitizeHeaders(response.headers, sanitizeHeaders),
131
172
  responseBody: logBody
132
173
  ? this.sanitizeBodyAsString(response.data)
133
174
  : undefined,
134
- responseSize: JSON.stringify(response.data).length,
135
- requestSize: JSON.stringify(response.config.data).length,
136
- serviceName: context.appId,
175
+ serviceName: clientName || context.appId,
137
176
  operationName: callInfo.operationName,
138
177
  clientIp: context.clientIp,
139
- source: (_b = context.metadata) === null || _b === void 0 ? void 0 : _b.source,
178
+ source: (_d = context.metadata) === null || _d === void 0 ? void 0 : _d.source,
140
179
  tags: context.tags,
141
180
  metadata: logData,
142
181
  retryRecords,
143
- cacheHit,
144
182
  circuitBreakerState,
145
183
  });
146
184
  }
@@ -149,8 +187,8 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
149
187
  /**
150
188
  * 记录请求错误
151
189
  */
152
- logRequestError(error, startTime, requestId, attemptCount, loggingOptions, databaseLogging = false, retryRecords, cacheHit, circuitBreakerState, decoratorContext) {
153
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
190
+ logRequestError(error, startTime, requestId, attemptCount, loggingOptions, databaseLogging = false, retryRecords, circuitBreakerState, decoratorContext, clientName, callingContext) {
191
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
154
192
  const { logHeaders = true, sanitizeHeaders = [] } = loggingOptions;
155
193
  const responseTime = Date.now() - startTime;
156
194
  // 获取当前上下文信息
@@ -184,37 +222,40 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
184
222
  this.logger.error(logMessage, logData);
185
223
  break;
186
224
  }
225
+ if (!this.logRepository) {
226
+ this.initRepository(loggingOptions.databaseLogging.dataSource, loggingOptions.databaseLogging.tableName);
227
+ }
187
228
  // 数据库日志记录
188
229
  if (databaseLogging && this.logRepository) {
189
230
  // 提取调用信息
190
- const callInfo = this.extractCallInfo(decoratorContext, error.config);
231
+ const callInfo = callingContext && callingContext.operationName
232
+ ? callingContext
233
+ : this.extractCallInfo(decoratorContext, error.config);
191
234
  this.saveToDatabase({
192
235
  id: requestId,
193
236
  requestId,
194
237
  userId: context.userId,
195
238
  method: ((_d = (_c = error.config) === null || _c === void 0 ? void 0 : _c.method) === null || _d === void 0 ? void 0 : _d.toUpperCase()) || 'UNKNOWN',
196
- url: ((_e = error.config) === null || _e === void 0 ? void 0 : _e.url) || '',
197
- headers: ((_f = error.config) === null || _f === void 0 ? void 0 : _f.headers)
239
+ url: this.getFullUrl(error.config) || '',
240
+ headers: ((_e = error.config) === null || _e === void 0 ? void 0 : _e.headers)
198
241
  ? this.sanitizeHeaders(error.config.headers, sanitizeHeaders)
199
242
  : {},
200
- body: this.sanitizeBodyAsString((_g = error.config) === null || _g === void 0 ? void 0 : _g.data),
201
- params: (_h = error.config) === null || _h === void 0 ? void 0 : _h.params,
202
- statusCode: (_j = error.response) === null || _j === void 0 ? void 0 : _j.status,
243
+ body: this.sanitizeBodyAsString((_f = error.config) === null || _f === void 0 ? void 0 : _f.data),
244
+ params: (_g = error.config) === null || _g === void 0 ? void 0 : _g.params,
245
+ statusCode: (_h = error.response) === null || _h === void 0 ? void 0 : _h.status,
203
246
  responseTime,
204
247
  attemptCount,
205
248
  success: false,
206
249
  errorMessage: error.message,
207
250
  errorStack: error.stack,
208
251
  errorCode: error.code,
209
- requestSize: JSON.stringify(((_k = error.config) === null || _k === void 0 ? void 0 : _k.data) || {}).length,
210
- serviceName: context.appId,
252
+ serviceName: clientName || context.appId,
211
253
  operationName: callInfo.operationName,
212
254
  clientIp: context.clientIp,
213
- source: (_l = context.metadata) === null || _l === void 0 ? void 0 : _l.source,
255
+ source: (_j = context.metadata) === null || _j === void 0 ? void 0 : _j.source,
214
256
  tags: context.tags,
215
257
  metadata: logData,
216
258
  retryRecords,
217
- cacheHit,
218
259
  circuitBreakerState,
219
260
  });
220
261
  }
@@ -335,20 +376,6 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
335
376
  };
336
377
  });
337
378
  }
338
- /**
339
- * 初始化数据库日志记录
340
- */
341
- initializeDatabaseLogging() {
342
- return __awaiter(this, void 0, void 0, function* () {
343
- try {
344
- this.logRepository = this.dataSource.getRepository(http_log_entity_1.HttpLogEntity);
345
- this.logger.log('Database logging initialized');
346
- }
347
- catch (error) {
348
- this.logger.error('Failed to initialize database logging', error);
349
- }
350
- });
351
- }
352
379
  /**
353
380
  * 提取调用信息
354
381
  */
@@ -392,20 +419,117 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
392
419
  }
393
420
  /**
394
421
  * 保存到数据库
422
+ * 如果启用异步日志,将日志项添加到队列中
423
+ * 否则同步保存到数据库
395
424
  */
396
425
  saveToDatabase(logEntity) {
397
426
  return __awaiter(this, void 0, void 0, function* () {
427
+ if (!this.logRepository) {
428
+ this.logger.warn('Log repository not initialized, skipping database logging');
429
+ return;
430
+ }
431
+ if (this.asyncConfig.enabled) {
432
+ // 异步模式:添加到队列
433
+ this.logQueue.push({
434
+ logEntity,
435
+ attempt: 0,
436
+ });
437
+ // 如果队列达到批量大小,立即刷新
438
+ if (this.logQueue.length >= this.asyncConfig.batchSize) {
439
+ setImmediate(() => this.flush());
440
+ }
441
+ }
442
+ else {
443
+ // 同步模式:直接保存
444
+ yield this.saveLogToDatabase(logEntity);
445
+ }
446
+ });
447
+ }
448
+ /**
449
+ * 启动定期刷新任务
450
+ */
451
+ startFlushTask() {
452
+ this.flushTimer = setInterval(() => {
453
+ this.flush();
454
+ }, this.asyncConfig.flushIntervalMs);
455
+ this.logger.log(`Async log flushing started (interval: ${this.asyncConfig.flushIntervalMs}ms, batchSize: ${this.asyncConfig.batchSize})`);
456
+ }
457
+ /**
458
+ * 刷新日志队列到数据库
459
+ */
460
+ flush() {
461
+ if (this.isProcessing || this.logQueue.length === 0) {
462
+ return;
463
+ }
464
+ this.isProcessing = true;
465
+ // 取出当前批次
466
+ const batch = this.logQueue.splice(0, this.asyncConfig.batchSize);
467
+ // 异步处理
468
+ setImmediate(() => __awaiter(this, void 0, void 0, function* () {
469
+ try {
470
+ // 批量保存
471
+ yield Promise.all(batch.map((item) => this.processLogItem(item)));
472
+ this.logger.debug(`Flushed ${batch.length} log entries to database`);
473
+ // 如果还有剩余日志,继续处理
474
+ if (this.logQueue.length > 0) {
475
+ setImmediate(() => this.flush());
476
+ }
477
+ }
478
+ catch (error) {
479
+ this.logger.error('Error flushing log queue', error);
480
+ // 将失败的日志重新放回队列(如果还有重试机会)
481
+ batch.forEach((item) => {
482
+ if (item.attempt < this.asyncConfig.maxRetries) {
483
+ item.attempt++;
484
+ this.logQueue.unshift(item); // 放回队列头部
485
+ }
486
+ else {
487
+ this.logger.error(`Log entry exceeded max retries, discarding: ${item.logEntity.requestId}`);
488
+ }
489
+ });
490
+ }
491
+ finally {
492
+ this.isProcessing = false;
493
+ }
494
+ }));
495
+ }
496
+ /**
497
+ * 处理单个日志项
498
+ */
499
+ processLogItem(item) {
500
+ return __awaiter(this, void 0, void 0, function* () {
501
+ if (!this.logRepository) {
502
+ return;
503
+ }
504
+ try {
505
+ yield this.logRepository.save(item.logEntity, { reload: false });
506
+ }
507
+ catch (error) {
508
+ this.logger.error(`Failed to save log entry: ${item.logEntity.requestId}`, error);
509
+ throw error; // 重新抛出错误以便重试
510
+ }
511
+ });
512
+ }
513
+ /**
514
+ * 同步保存日志到数据库(用于异步模式内部)
515
+ */
516
+ saveLogToDatabase(logEntity) {
517
+ return __awaiter(this, void 0, void 0, function* () {
518
+ var _a;
398
519
  if (!this.logRepository) {
399
520
  this.logger.warn('Log repository not initialized, skipping database logging');
400
521
  return;
401
522
  }
402
523
  try {
403
- this.logger.debug('Saving HTTP log to database', {
524
+ this.logger.log('Saving HTTP log to database', {
404
525
  requestId: logEntity.requestId,
526
+ circuitBreakerState: logEntity.circuitBreakerState,
527
+ retryRecordsCount: ((_a = logEntity.retryRecords) === null || _a === void 0 ? void 0 : _a.length) || 0,
405
528
  });
406
- yield this.logRepository.save(logEntity);
407
- this.logger.debug('Successfully saved HTTP log to database', {
529
+ yield this.logRepository.save(logEntity, { reload: false });
530
+ this.logger.log('Successfully saved HTTP log to database', {
408
531
  requestId: logEntity.requestId,
532
+ circuitBreakerState: logEntity.circuitBreakerState,
409
533
  });
410
534
  }
411
535
  catch (error) {
@@ -414,6 +538,50 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
414
538
  }
415
539
  });
416
540
  }
541
+ /**
542
+ * 获取日志队列统计信息
543
+ */
544
+ getQueueStats() {
545
+ return {
546
+ queueSize: this.logQueue.length,
547
+ isProcessing: this.isProcessing,
548
+ config: Object.assign({}, this.asyncConfig),
549
+ };
550
+ }
551
+ /**
552
+ * 手动刷新日志队列
553
+ */
554
+ manualFlush() {
555
+ return __awaiter(this, void 0, void 0, function* () {
556
+ return new Promise((resolve) => {
557
+ const checkProcessing = () => {
558
+ if (!this.isProcessing) {
559
+ this.flush();
560
+ // 等待刷新完成
561
+ setTimeout(() => resolve(), 100);
562
+ }
563
+ else {
564
+ setTimeout(checkProcessing, 50);
565
+ }
566
+ };
567
+ checkProcessing();
568
+ });
569
+ });
570
+ }
571
+ /**
572
+ * 设置异步日志配置
573
+ */
574
+ setAsyncConfig(config) {
575
+ Object.assign(this.asyncConfig, config);
576
+ // 重启刷新任务
577
+ if (this.flushTimer) {
578
+ clearInterval(this.flushTimer);
579
+ }
580
+ if (this.asyncConfig.enabled) {
581
+ this.startFlushTask();
582
+ }
583
+ this.logger.log(`Async log config updated: ${JSON.stringify(this.asyncConfig)}`);
584
+ }
417
585
  /**
418
586
  * 转换请求体为字符串
419
587
  */
@@ -505,9 +673,36 @@ let HttpLoggingService = HttpLoggingService_1 = class HttpLoggingService {
505
673
  }
506
674
  }
507
675
  }
676
+ /**
677
+ * 获取完整URL
678
+ * @param config Axios请求配置
679
+ * @returns 完整的URL字符串
680
+ */
681
+ getFullUrl(config) {
682
+ if (!config)
683
+ return '';
684
+ // 如果已经有完整的URL,直接返回
685
+ if (config.url && config.url.startsWith('http')) {
686
+ return config.url;
687
+ }
688
+ // 获取baseURL
689
+ const baseURL = config.baseURL || '';
690
+ // 获取URL路径
691
+ const urlPath = config.url || '';
692
+ // 如果baseURL为空,直接返回URL路径
693
+ if (!baseURL) {
694
+ return urlPath;
695
+ }
696
+ // 确保baseURL以/结尾,urlPath不以/开头(除非它是绝对路径)
697
+ const normalizedBaseURL = baseURL.endsWith('/') ? baseURL : baseURL + '/';
698
+ const normalizedUrlPath = urlPath.startsWith('/')
699
+ ? urlPath.substring(1)
700
+ : urlPath;
701
+ return normalizedBaseURL + normalizedUrlPath;
702
+ }
508
703
  };
509
704
  exports.HttpLoggingService = HttpLoggingService;
510
705
  exports.HttpLoggingService = HttpLoggingService = HttpLoggingService_1 = __decorate([
511
706
  (0, common_1.Injectable)(),
512
- __metadata("design:paramtypes", [typeorm_1.DataSource])
707
+ __metadata("design:paramtypes", [])
513
708
  ], HttpLoggingService);
@@ -3,6 +3,32 @@
3
3
  * 用于自动提取HTTP请求的调用服务和方法信息
4
4
  */
5
5
  export declare class CallStackExtractor {
6
+ /**
7
+ * 存储装饰器上下文(用于传递装饰器信息到HTTP客户端)
8
+ */
9
+ private static decoratorContext;
10
+ /**
11
+ * 装饰器上下文引用计数,用于处理多个装饰器嵌套的情况
12
+ */
13
+ private static decoratorContextDepth;
14
+ /**
15
+ * 设置装饰器上下文
16
+ */
17
+ static setDecoratorContext(context: {
18
+ target: any;
19
+ propertyKey: string;
20
+ }): void;
21
+ /**
22
+ * 获取装饰器上下文
23
+ */
24
+ static getDecoratorContext(): {
25
+ target: any;
26
+ propertyKey: string;
27
+ } | null;
28
+ /**
29
+ * 清除装饰器上下文
30
+ */
31
+ static clearDecoratorContext(): void;
6
32
  /**
7
33
  * 从调用栈中提取服务和方法信息
8
34
  */