@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.
- package/cache/cache.module.d.ts +0 -6
- package/cache/cache.module.js +7 -7
- package/cache/cache.service.js +12 -0
- package/cache/dependencies/db.dependency.d.ts +0 -13
- package/cache/dependencies/db.dependency.js +0 -16
- package/cache/dependencies/tag.dependency.d.ts +39 -4
- package/cache/dependencies/tag.dependency.js +109 -11
- package/cache/interfaces/cache-options.interface.d.ts +8 -0
- package/cache/providers/memory-cache.provider.d.ts +20 -0
- package/cache/providers/memory-cache.provider.js +40 -0
- package/http-client/config/http-client.config.d.ts +5 -0
- package/http-client/config/http-client.config.js +24 -13
- package/http-client/decorators/http-client.decorators.d.ts +1 -25
- package/http-client/decorators/http-client.decorators.js +97 -90
- package/http-client/entities/http-log.entity.d.ts +0 -20
- package/http-client/entities/http-log.entity.js +0 -12
- package/http-client/examples/advanced-usage.example.d.ts +4 -5
- package/http-client/examples/advanced-usage.example.js +4 -56
- package/http-client/http-client.module.d.ts +35 -2
- package/http-client/http-client.module.js +80 -75
- package/http-client/index.d.ts +1 -1
- package/http-client/interfaces/api-client-config.interface.d.ts +1 -91
- package/http-client/interfaces/http-client-config.interface.d.ts +53 -62
- package/http-client/services/api-client-registry.service.d.ts +5 -23
- package/http-client/services/api-client-registry.service.js +41 -284
- package/http-client/services/circuit-breaker.service.d.ts +69 -2
- package/http-client/services/circuit-breaker.service.js +185 -7
- package/http-client/services/http-client.service.d.ts +58 -23
- package/http-client/services/http-client.service.js +294 -150
- package/http-client/services/http-log-query.service.js +0 -13
- package/http-client/services/index.d.ts +0 -1
- package/http-client/services/index.js +0 -1
- package/http-client/services/logging.service.d.ts +79 -10
- package/http-client/services/logging.service.js +246 -51
- package/http-client/utils/call-stack-extractor.util.d.ts +26 -0
- package/http-client/utils/call-stack-extractor.util.js +35 -0
- package/http-client/utils/security-validator.util.d.ts +118 -0
- package/http-client/utils/security-validator.util.js +352 -0
- package/package.json +1 -1
- package/redis-lock/lock-heartbeat.service.d.ts +2 -0
- package/redis-lock/lock-heartbeat.service.js +12 -2
- package/redis-lock/redis-lock.service.d.ts +4 -0
- package/redis-lock/redis-lock.service.js +61 -8
- package/http-client/services/cache.service.d.ts +0 -76
- 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 {
|
|
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
|
-
|
|
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[],
|
|
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[],
|
|
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(
|
|
35
|
-
this.dataSource = dataSource;
|
|
35
|
+
constructor() {
|
|
36
36
|
this.logger = new common_1.Logger(HttpLoggingService_1.name);
|
|
37
37
|
this.logRepository = null;
|
|
38
|
-
|
|
39
|
-
|
|
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,
|
|
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)
|
|
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
|
|
109
|
-
this.
|
|
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 =
|
|
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: ((
|
|
120
|
-
url: response.config
|
|
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
|
-
|
|
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: (
|
|
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,
|
|
153
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j
|
|
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 =
|
|
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: (
|
|
197
|
-
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((
|
|
201
|
-
params: (
|
|
202
|
-
statusCode: (
|
|
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
|
-
|
|
210
|
-
serviceName: context.appId,
|
|
252
|
+
serviceName: clientName || context.appId,
|
|
211
253
|
operationName: callInfo.operationName,
|
|
212
254
|
clientIp: context.clientIp,
|
|
213
|
-
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.
|
|
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.
|
|
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", [
|
|
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
|
*/
|