@nest-omni/core 4.1.3-1 → 4.1.3-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.
- package/audit/audit.module.d.ts +10 -0
- package/audit/audit.module.js +15 -0
- package/audit/controllers/audit.controller.d.ts +24 -0
- package/audit/controllers/audit.controller.js +24 -0
- package/audit/decorators/audit-controller.decorator.d.ts +8 -0
- package/audit/decorators/audit-controller.decorator.js +9 -0
- package/audit/decorators/audit-operation.decorator.d.ts +45 -0
- package/audit/decorators/audit-operation.decorator.js +49 -0
- package/audit/decorators/entity-audit.decorator.d.ts +8 -0
- package/audit/decorators/entity-audit.decorator.js +9 -0
- package/audit/dto/audit-log-query.dto.d.ts +3 -0
- package/audit/dto/audit-log-query.dto.js +3 -0
- package/audit/dto/begin-transaction.dto.d.ts +3 -0
- package/audit/dto/begin-transaction.dto.js +3 -0
- package/audit/dto/compare-entities.dto.d.ts +3 -0
- package/audit/dto/compare-entities.dto.js +3 -0
- package/audit/dto/pre-check-restore.dto.d.ts +3 -0
- package/audit/dto/pre-check-restore.dto.js +3 -0
- package/audit/dto/restore-entity.dto.d.ts +3 -0
- package/audit/dto/restore-entity.dto.js +3 -0
- package/audit/entities/entity-audit-log.entity.d.ts +3 -0
- package/audit/entities/entity-audit-log.entity.js +3 -0
- package/audit/entities/entity-transaction.entity.d.ts +3 -0
- package/audit/entities/entity-transaction.entity.js +3 -0
- package/audit/entities/manual-operation-log.entity.d.ts +4 -0
- package/audit/entities/manual-operation-log.entity.js +4 -0
- package/audit/entities/operation-template.entity.d.ts +4 -0
- package/audit/entities/operation-template.entity.js +4 -0
- package/audit/enums/audit.enums.d.ts +17 -2
- package/audit/enums/audit.enums.js +15 -0
- package/audit/index.js +10 -0
- package/audit/interceptors/audit.interceptor.d.ts +15 -0
- package/audit/interceptors/audit.interceptor.js +23 -1
- package/audit/interfaces/audit.interfaces.d.ts +42 -0
- package/audit/services/audit-context.service.d.ts +15 -0
- package/audit/services/audit-context.service.js +15 -0
- package/audit/services/audit-strategy.service.d.ts +6 -0
- package/audit/services/audit-strategy.service.js +13 -0
- package/audit/services/entity-audit.service.d.ts +57 -0
- package/audit/services/entity-audit.service.js +91 -0
- package/audit/services/manual-audit-log.service.d.ts +124 -0
- package/audit/services/manual-audit-log.service.js +138 -0
- package/audit/services/multi-database.service.d.ts +12 -0
- package/audit/services/multi-database.service.js +12 -0
- package/audit/services/operation-description.service.d.ts +59 -0
- package/audit/services/operation-description.service.js +76 -2
- package/audit/services/transaction-audit.service.d.ts +30 -0
- package/audit/services/transaction-audit.service.js +47 -0
- package/audit/subscribers/entity-audit.subscriber.d.ts +15 -0
- package/audit/subscribers/entity-audit.subscriber.js +29 -1
- package/cache/cache-metrics.service.d.ts +67 -0
- package/cache/cache-metrics.service.js +68 -4
- package/cache/cache-serialization.service.d.ts +31 -0
- package/cache/cache-serialization.service.js +25 -0
- package/cache/cache.constants.d.ts +9 -0
- package/cache/cache.constants.js +9 -0
- package/cache/cache.health.d.ts +26 -0
- package/cache/cache.health.js +30 -0
- package/cache/cache.module.d.ts +86 -0
- package/cache/cache.module.js +71 -0
- package/cache/cache.service.d.ts +140 -0
- package/cache/cache.service.js +157 -0
- package/cache/cache.warmup.service.d.ts +39 -0
- package/cache/cache.warmup.service.js +32 -0
- package/cache/decorators/cache-evict.decorator.d.ts +47 -0
- package/cache/decorators/cache-evict.decorator.js +56 -0
- package/cache/decorators/cache-put.decorator.d.ts +34 -0
- package/cache/decorators/cache-put.decorator.js +39 -0
- package/cache/decorators/cacheable.decorator.d.ts +40 -0
- package/cache/decorators/cacheable.decorator.js +55 -0
- package/cache/dependencies/callback.dependency.d.ts +33 -0
- package/cache/dependencies/callback.dependency.js +39 -1
- package/cache/dependencies/chain.dependency.d.ts +28 -0
- package/cache/dependencies/chain.dependency.js +34 -0
- package/cache/dependencies/db.dependency.d.ts +45 -0
- package/cache/dependencies/db.dependency.js +48 -1
- package/cache/dependencies/file.dependency.d.ts +32 -0
- package/cache/dependencies/file.dependency.js +34 -0
- package/cache/dependencies/tag.dependency.d.ts +36 -0
- package/cache/dependencies/tag.dependency.js +36 -0
- package/cache/dependencies/time.dependency.d.ts +43 -0
- package/cache/dependencies/time.dependency.js +43 -0
- package/cache/examples/basic-usage.d.ts +15 -0
- package/cache/examples/basic-usage.js +62 -8
- package/cache/index.js +9 -0
- package/cache/interfaces/cache-dependency.interface.d.ts +53 -0
- package/cache/interfaces/cache-options.interface.d.ts +81 -0
- package/cache/interfaces/cache-options.interface.js +6 -0
- package/cache/interfaces/cache-provider.interface.d.ts +78 -0
- package/cache/providers/base-cache.provider.d.ts +14 -0
- package/cache/providers/base-cache.provider.js +16 -0
- package/cache/providers/cls-cache.provider.d.ts +20 -0
- package/cache/providers/cls-cache.provider.js +28 -0
- package/cache/providers/memory-cache.provider.d.ts +23 -0
- package/cache/providers/memory-cache.provider.js +26 -0
- package/cache/providers/redis-cache.provider.d.ts +26 -0
- package/cache/providers/redis-cache.provider.js +29 -0
- package/cache/utils/dependency-manager.util.d.ts +52 -0
- package/cache/utils/dependency-manager.util.js +59 -0
- package/cache/utils/key-generator.util.d.ts +42 -0
- package/cache/utils/key-generator.util.js +53 -1
- package/common/abstract.entity.d.ts +14 -0
- package/common/abstract.entity.js +14 -0
- package/common/boilerplate.polyfill.d.ts +142 -4
- package/common/boilerplate.polyfill.js +24 -100
- package/common/dto/dto-container.d.ts +16 -0
- package/common/dto/dto-container.js +20 -0
- package/common/dto/dto-decorators.d.ts +18 -0
- package/common/dto/dto-decorators.js +14 -0
- package/common/dto/dto-extensions.d.ts +11 -0
- package/common/dto/dto-extensions.js +9 -0
- package/common/dto/dto-service-accessor.d.ts +17 -0
- package/common/dto/dto-service-accessor.js +18 -0
- package/common/dto/dto-transformer.d.ts +12 -0
- package/common/dto/dto-transformer.js +9 -0
- package/common/dto/index.js +2 -0
- package/common/examples/paginate-and-map.example.d.ts +6 -0
- package/common/examples/paginate-and-map.example.js +26 -0
- package/common/utils.d.ts +15 -0
- package/common/utils.js +15 -0
- package/constants/language-code.js +1 -0
- package/decorators/field.decorators.js +8 -1
- package/decorators/property.decorators.js +1 -0
- package/decorators/public-route.decorator.js +1 -0
- package/decorators/transform.decorators.d.ts +27 -0
- package/decorators/transform.decorators.js +29 -0
- package/decorators/translate.decorator.js +1 -0
- package/decorators/user.decorator.js +1 -0
- package/decorators/validator.decorators.d.ts +8 -18
- package/decorators/validator.decorators.js +22 -190
- package/filters/constraint-errors.js +1 -0
- package/helpers/common.helper.d.ts +13 -0
- package/helpers/common.helper.js +13 -0
- package/http-client/config/http-client.config.d.ts +15 -0
- package/http-client/config/http-client.config.js +25 -9
- package/http-client/decorators/http-client.decorators.d.ts +63 -0
- package/http-client/decorators/http-client.decorators.js +71 -3
- package/http-client/entities/http-log.entity.d.ts +229 -0
- package/http-client/entities/http-log.entity.js +6 -1
- package/http-client/errors/http-client.errors.d.ts +57 -0
- package/http-client/errors/http-client.errors.js +58 -0
- package/http-client/examples/advanced-usage.example.d.ts +41 -0
- package/http-client/examples/advanced-usage.example.js +68 -24
- package/http-client/examples/auth-with-waiting-lock.example.d.ts +31 -0
- package/http-client/examples/auth-with-waiting-lock.example.js +52 -5
- package/http-client/examples/basic-usage.example.d.ts +60 -0
- package/http-client/examples/basic-usage.example.js +60 -0
- package/http-client/examples/multi-api-configuration.example.d.ts +60 -0
- package/http-client/examples/multi-api-configuration.example.js +76 -5
- package/http-client/http-client.module.d.ts +13 -0
- package/http-client/http-client.module.js +19 -0
- package/http-client/index.js +8 -0
- package/http-client/interfaces/api-client-config.interface.d.ts +125 -0
- package/http-client/interfaces/api-client-config.interface.js +3 -0
- package/http-client/interfaces/http-client-config.interface.d.ts +60 -0
- package/http-client/services/api-client-registry.service.d.ts +57 -0
- package/http-client/services/api-client-registry.service.js +84 -1
- package/http-client/services/cache.service.d.ts +52 -0
- package/http-client/services/cache.service.js +72 -3
- package/http-client/services/circuit-breaker.service.d.ts +46 -0
- package/http-client/services/circuit-breaker.service.js +52 -0
- package/http-client/services/http-client.service.d.ts +67 -0
- package/http-client/services/http-client.service.js +105 -4
- package/http-client/services/http-log-query.service.d.ts +83 -0
- package/http-client/services/http-log-query.service.js +122 -1
- package/http-client/services/http-replay.service.d.ts +101 -0
- package/http-client/services/http-replay.service.js +86 -0
- package/http-client/services/log-cleanup.service.d.ts +63 -0
- package/http-client/services/log-cleanup.service.js +54 -2
- package/http-client/services/logging.service.d.ts +40 -0
- package/http-client/services/logging.service.js +53 -0
- package/http-client/utils/call-stack-extractor.util.d.ts +37 -0
- package/http-client/utils/call-stack-extractor.util.js +48 -0
- package/http-client/utils/context-extractor.util.d.ts +49 -0
- package/http-client/utils/context-extractor.util.js +52 -0
- package/http-client/utils/curl-generator.util.d.ts +21 -0
- package/http-client/utils/curl-generator.util.js +44 -3
- package/http-client/utils/request-id.util.d.ts +18 -0
- package/http-client/utils/request-id.util.js +20 -0
- package/http-client/utils/retry-recorder.util.d.ts +42 -0
- package/http-client/utils/retry-recorder.util.js +44 -0
- package/i18n/en_US/validation.json +2 -1
- package/i18n/zh_CN/validation.json +2 -1
- package/index.js +8 -0
- package/interceptors/translation-interceptor.service.js +5 -0
- package/package.json +1 -1
- package/providers/context.provider.js +2 -0
- package/providers/generator.provider.d.ts +4 -0
- package/providers/generator.provider.js +4 -0
- package/redis-lock/comprehensive-lock-cleanup.service.d.ts +94 -0
- package/redis-lock/comprehensive-lock-cleanup.service.js +253 -0
- package/redis-lock/examples/lock-strategy.examples.d.ts +89 -0
- package/redis-lock/examples/lock-strategy.examples.js +130 -15
- package/redis-lock/index.d.ts +2 -0
- package/redis-lock/index.js +8 -1
- package/redis-lock/lock-heartbeat.service.d.ts +78 -0
- package/redis-lock/lock-heartbeat.service.js +222 -0
- package/redis-lock/redis-lock.decorator.d.ts +101 -0
- package/redis-lock/redis-lock.decorator.js +120 -0
- package/redis-lock/redis-lock.module.d.ts +66 -0
- package/redis-lock/redis-lock.module.js +175 -70
- package/redis-lock/redis-lock.service.d.ts +260 -0
- package/redis-lock/redis-lock.service.js +244 -4
- package/setup/bootstrap.setup.js +20 -0
- package/setup/mode.setup.d.ts +44 -0
- package/setup/mode.setup.js +44 -0
- package/setup/schedule.decorator.d.ts +227 -0
- package/setup/schedule.decorator.js +219 -6
- package/setup/worker.decorator.d.ts +86 -0
- package/setup/worker.decorator.js +88 -0
- package/shared/serviceRegistryModule.js +9 -1
- package/shared/services/api-config.service.d.ts +3 -0
- package/shared/services/api-config.service.js +20 -9
- package/validator-json/decorators.d.ts +17 -0
- package/validator-json/decorators.js +17 -2
- package/validator-json/default.d.ts +6 -0
- package/validator-json/default.js +30 -2
- package/validator-json/defaultConverters.js +1 -0
- package/validator-json/options.d.ts +23 -0
- package/validators/common-validators.d.ts +143 -0
- package/validators/common-validators.js +249 -0
- package/validators/custom-validate.examples.d.ts +96 -0
- package/validators/custom-validate.examples.js +400 -0
- package/validators/custom-validate.validator.d.ts +134 -0
- package/validators/custom-validate.validator.js +214 -0
- package/validators/index.d.ts +2 -0
- package/validators/index.js +2 -0
- package/validators/is-exists.validator.d.ts +18 -4
- package/validators/is-exists.validator.js +67 -6
- package/validators/is-unique.validator.d.ts +32 -5
- package/validators/is-unique.validator.js +99 -17
- package/validators/skip-empty.validator.d.ts +5 -0
- package/validators/skip-empty.validator.js +5 -0
- package/vault/interfaces/vault-options.interface.d.ts +9 -0
- package/vault/vault-config.loader.d.ts +30 -0
- package/vault/vault-config.loader.js +48 -1
- package/vault/vault-config.service.d.ts +53 -0
- package/vault/vault-config.service.js +57 -0
- package/vault/vault.module.d.ts +4 -0
- package/vault/vault.module.js +4 -0
- package/decorators/examples/validation-decorators.example.d.ts +0 -69
- package/decorators/examples/validation-decorators.example.js +0 -331
|
@@ -30,6 +30,9 @@ const enums_1 = require("../enums");
|
|
|
30
30
|
const audit_context_service_1 = require("./audit-context.service");
|
|
31
31
|
const entity_audit_service_1 = require("./entity-audit.service");
|
|
32
32
|
const multi_database_service_1 = require("./multi-database.service");
|
|
33
|
+
/**
|
|
34
|
+
* 事务审计服务
|
|
35
|
+
*/
|
|
33
36
|
let TransactionAuditService = class TransactionAuditService {
|
|
34
37
|
constructor(transactionRepository, contextService, auditService, multiDbService) {
|
|
35
38
|
this.transactionRepository = transactionRepository;
|
|
@@ -37,6 +40,9 @@ let TransactionAuditService = class TransactionAuditService {
|
|
|
37
40
|
this.auditService = auditService;
|
|
38
41
|
this.multiDbService = multiDbService;
|
|
39
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* 开始事务
|
|
45
|
+
*/
|
|
40
46
|
beginTransaction(description) {
|
|
41
47
|
return __awaiter(this, void 0, void 0, function* () {
|
|
42
48
|
const context = yield this.contextService.getCurrentContext();
|
|
@@ -48,12 +54,16 @@ let TransactionAuditService = class TransactionAuditService {
|
|
|
48
54
|
username: context.username,
|
|
49
55
|
});
|
|
50
56
|
const savedTransaction = yield this.transactionRepository.save(transaction);
|
|
57
|
+
// 设置事务ID到上下文
|
|
51
58
|
this.contextService.setContext({
|
|
52
59
|
transactionId: savedTransaction.id,
|
|
53
60
|
});
|
|
54
61
|
return savedTransaction.id;
|
|
55
62
|
});
|
|
56
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* 提交事务
|
|
66
|
+
*/
|
|
57
67
|
commitTransaction(transactionId) {
|
|
58
68
|
return __awaiter(this, void 0, void 0, function* () {
|
|
59
69
|
const transaction = yield this.transactionRepository.findOne({
|
|
@@ -66,6 +76,9 @@ let TransactionAuditService = class TransactionAuditService {
|
|
|
66
76
|
yield this.transactionRepository.save(transaction);
|
|
67
77
|
});
|
|
68
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* 回滚事务(支持多数据库)
|
|
81
|
+
*/
|
|
69
82
|
rollbackTransaction(transactionId) {
|
|
70
83
|
return __awaiter(this, void 0, void 0, function* () {
|
|
71
84
|
const transaction = yield this.transactionRepository.findOne({
|
|
@@ -74,11 +87,15 @@ let TransactionAuditService = class TransactionAuditService {
|
|
|
74
87
|
if (!transaction) {
|
|
75
88
|
throw new Error(`Transaction not found: ${transactionId}`);
|
|
76
89
|
}
|
|
90
|
+
// 执行回滚操作
|
|
77
91
|
yield this.performRollback(transaction);
|
|
78
92
|
transaction.status = enums_1.TransactionStatus.ROLLED_BACK;
|
|
79
93
|
yield this.transactionRepository.save(transaction);
|
|
80
94
|
});
|
|
81
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* 添加实体到事务
|
|
98
|
+
*/
|
|
82
99
|
addEntityToTransaction(transactionId, entityType, entityId, operation) {
|
|
83
100
|
return __awaiter(this, void 0, void 0, function* () {
|
|
84
101
|
const transaction = yield this.transactionRepository.findOne({
|
|
@@ -95,19 +112,30 @@ let TransactionAuditService = class TransactionAuditService {
|
|
|
95
112
|
yield this.transactionRepository.save(transaction);
|
|
96
113
|
});
|
|
97
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* 执行回滚(支持多数据库)
|
|
117
|
+
*/
|
|
98
118
|
performRollback(transaction) {
|
|
99
119
|
return __awaiter(this, void 0, void 0, function* () {
|
|
120
|
+
// 按相反顺序回滚实体
|
|
100
121
|
const reversedEntities = [...transaction.entities].reverse();
|
|
122
|
+
// 按数据库连接分组
|
|
101
123
|
const entitiesByConnection = yield this.groupEntitiesByConnection(reversedEntities);
|
|
124
|
+
// 对每个数据库连接执行回滚
|
|
102
125
|
for (const [connectionName, entities] of Object.entries(entitiesByConnection)) {
|
|
103
126
|
yield this.rollbackEntitiesInConnection(connectionName, entities);
|
|
104
127
|
}
|
|
105
128
|
});
|
|
106
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* 按数据库连接分组实体
|
|
132
|
+
*/
|
|
107
133
|
groupEntitiesByConnection(entities) {
|
|
108
134
|
return __awaiter(this, void 0, void 0, function* () {
|
|
109
135
|
const grouped = {};
|
|
136
|
+
// 获取所有监听的连接
|
|
110
137
|
const monitoredConnections = this.multiDbService.getMonitoredConnections();
|
|
138
|
+
// 尝试确定每个实体属于哪个连接
|
|
111
139
|
for (const entity of entities) {
|
|
112
140
|
let connectionFound = false;
|
|
113
141
|
for (const connectionName of monitoredConnections) {
|
|
@@ -124,9 +152,11 @@ let TransactionAuditService = class TransactionAuditService {
|
|
|
124
152
|
}
|
|
125
153
|
}
|
|
126
154
|
catch (error) {
|
|
155
|
+
// 继续尝试下一个连接
|
|
127
156
|
continue;
|
|
128
157
|
}
|
|
129
158
|
}
|
|
159
|
+
// 如果没有找到连接,使用默认连接
|
|
130
160
|
if (!connectionFound) {
|
|
131
161
|
const defaultConnection = 'default';
|
|
132
162
|
if (!grouped[defaultConnection]) {
|
|
@@ -138,9 +168,13 @@ let TransactionAuditService = class TransactionAuditService {
|
|
|
138
168
|
return grouped;
|
|
139
169
|
});
|
|
140
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* 在指定连接中回滚实体
|
|
173
|
+
*/
|
|
141
174
|
rollbackEntitiesInConnection(connectionName, entities) {
|
|
142
175
|
return __awaiter(this, void 0, void 0, function* () {
|
|
143
176
|
const dataSource = yield this.multiDbService.getDataSource(connectionName);
|
|
177
|
+
// 使用事务执行回滚
|
|
144
178
|
yield dataSource.transaction((manager) => __awaiter(this, void 0, void 0, function* () {
|
|
145
179
|
for (const entity of entities) {
|
|
146
180
|
try {
|
|
@@ -154,26 +188,34 @@ let TransactionAuditService = class TransactionAuditService {
|
|
|
154
188
|
}));
|
|
155
189
|
});
|
|
156
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* 回滚单个实体
|
|
193
|
+
*/
|
|
157
194
|
rollbackSingleEntity(manager, entity, connectionName) {
|
|
158
195
|
return __awaiter(this, void 0, void 0, function* () {
|
|
159
196
|
const repository = manager.getRepository(entity.entityType);
|
|
160
197
|
switch (entity.operation) {
|
|
161
198
|
case enums_1.AuditOperation.CREATE:
|
|
199
|
+
// 创建操作回滚:删除实体
|
|
162
200
|
yield repository.delete({ id: entity.entityId });
|
|
163
201
|
break;
|
|
164
202
|
case enums_1.AuditOperation.UPDATE:
|
|
203
|
+
// 更新操作回滚:恢复到之前的值
|
|
204
|
+
// 需要从审计日志中获取之前的值
|
|
165
205
|
const auditLog = yield this.getLatestAuditLog(entity.entityType, entity.entityId);
|
|
166
206
|
if (auditLog && auditLog.oldValue) {
|
|
167
207
|
yield repository.update({ id: entity.entityId }, auditLog.oldValue);
|
|
168
208
|
}
|
|
169
209
|
break;
|
|
170
210
|
case enums_1.AuditOperation.DELETE:
|
|
211
|
+
// 删除操作回滚:恢复实体
|
|
171
212
|
const deletedAuditLog = yield this.getLatestAuditLog(entity.entityType, entity.entityId);
|
|
172
213
|
if (deletedAuditLog && deletedAuditLog.oldValue) {
|
|
173
214
|
yield repository.save(deletedAuditLog.oldValue);
|
|
174
215
|
}
|
|
175
216
|
break;
|
|
176
217
|
case enums_1.AuditOperation.RESTORE:
|
|
218
|
+
// 恢复操作回滚:撤销恢复
|
|
177
219
|
const restoreAuditLog = yield this.getLatestAuditLog(entity.entityType, entity.entityId);
|
|
178
220
|
if (restoreAuditLog && restoreAuditLog.oldValue) {
|
|
179
221
|
yield repository.update({ id: entity.entityId }, restoreAuditLog.oldValue);
|
|
@@ -184,8 +226,13 @@ let TransactionAuditService = class TransactionAuditService {
|
|
|
184
226
|
}
|
|
185
227
|
});
|
|
186
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* 获取最新的审计日志
|
|
231
|
+
*/
|
|
187
232
|
getLatestAuditLog(entityType, entityId) {
|
|
188
233
|
return __awaiter(this, void 0, void 0, function* () {
|
|
234
|
+
// 这里需要从审计服务获取
|
|
235
|
+
// 由于循环依赖,这里简化实现
|
|
189
236
|
return null;
|
|
190
237
|
});
|
|
191
238
|
}
|
|
@@ -2,13 +2,28 @@ import { EntitySubscriberInterface, InsertEvent, UpdateEvent, RemoveEvent, DataS
|
|
|
2
2
|
import { EntityAuditService } from '../services/entity-audit.service';
|
|
3
3
|
import { AuditContextService } from '../services/audit-context.service';
|
|
4
4
|
import { DefaultAuditStrategy } from '../services/audit-strategy.service';
|
|
5
|
+
/**
|
|
6
|
+
* 实体审计订阅者
|
|
7
|
+
*/
|
|
5
8
|
export declare class EntityAuditSubscriber implements EntitySubscriberInterface<any> {
|
|
6
9
|
private readonly entityAuditService;
|
|
7
10
|
private readonly contextService;
|
|
8
11
|
private readonly auditStrategy;
|
|
9
12
|
constructor(entityAuditService: EntityAuditService, contextService: AuditContextService, auditStrategy: DefaultAuditStrategy, dataSource: DataSource);
|
|
13
|
+
/**
|
|
14
|
+
* 在实体插入后记录审计日志
|
|
15
|
+
*/
|
|
10
16
|
afterInsert(event: InsertEvent<any>): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* 在实体更新后记录审计日志
|
|
19
|
+
*/
|
|
11
20
|
afterUpdate(event: UpdateEvent<any>): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* 在实体删除前记录审计日志
|
|
23
|
+
*/
|
|
12
24
|
beforeRemove(event: RemoveEvent<any>): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* 检查是否启用了审计
|
|
27
|
+
*/
|
|
13
28
|
private isAuditEnabled;
|
|
14
29
|
}
|
|
@@ -26,6 +26,9 @@ const audit_context_service_1 = require("../services/audit-context.service");
|
|
|
26
26
|
const audit_strategy_service_1 = require("../services/audit-strategy.service");
|
|
27
27
|
const enums_1 = require("../enums");
|
|
28
28
|
const decorators_1 = require("../decorators");
|
|
29
|
+
/**
|
|
30
|
+
* 实体审计订阅者
|
|
31
|
+
*/
|
|
29
32
|
let EntityAuditSubscriber = class EntityAuditSubscriber {
|
|
30
33
|
constructor(entityAuditService, contextService, auditStrategy, dataSource) {
|
|
31
34
|
this.entityAuditService = entityAuditService;
|
|
@@ -33,6 +36,9 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
|
|
|
33
36
|
this.auditStrategy = auditStrategy;
|
|
34
37
|
dataSource.subscribers.push(this);
|
|
35
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* 在实体插入后记录审计日志
|
|
41
|
+
*/
|
|
36
42
|
afterInsert(event) {
|
|
37
43
|
return __awaiter(this, void 0, void 0, function* () {
|
|
38
44
|
const entity = event.entity;
|
|
@@ -40,14 +46,18 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
|
|
|
40
46
|
return;
|
|
41
47
|
const entityType = entity.constructor.name;
|
|
42
48
|
const entityId = entity.id;
|
|
49
|
+
// 检查是否启用了审计
|
|
43
50
|
if (!this.isAuditEnabled(entity)) {
|
|
44
51
|
return;
|
|
45
52
|
}
|
|
53
|
+
// 检查策略
|
|
46
54
|
if (!this.auditStrategy.shouldRecord(entityType, enums_1.AuditOperation.CREATE)) {
|
|
47
55
|
return;
|
|
48
56
|
}
|
|
49
57
|
try {
|
|
58
|
+
// 获取上下文信息
|
|
50
59
|
const context = yield this.contextService.getCurrentContext();
|
|
60
|
+
// 记录审计日志
|
|
51
61
|
yield this.entityAuditService.logEntityChange(entityType, entityId, enums_1.AuditOperation.CREATE, {}, entity, {
|
|
52
62
|
requestId: context.requestId,
|
|
53
63
|
requestIp: context.requestIp,
|
|
@@ -59,6 +69,9 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
|
|
|
59
69
|
}
|
|
60
70
|
});
|
|
61
71
|
}
|
|
72
|
+
/**
|
|
73
|
+
* 在实体更新后记录审计日志
|
|
74
|
+
*/
|
|
62
75
|
afterUpdate(event) {
|
|
63
76
|
return __awaiter(this, void 0, void 0, function* () {
|
|
64
77
|
const entity = event.entity;
|
|
@@ -67,14 +80,18 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
|
|
|
67
80
|
return;
|
|
68
81
|
const entityType = entity.constructor.name;
|
|
69
82
|
const entityId = entity.id;
|
|
83
|
+
// 检查是否启用了审计
|
|
70
84
|
if (!this.isAuditEnabled(entity)) {
|
|
71
85
|
return;
|
|
72
86
|
}
|
|
87
|
+
// 检查策略
|
|
73
88
|
if (!this.auditStrategy.shouldRecord(entityType, enums_1.AuditOperation.UPDATE)) {
|
|
74
89
|
return;
|
|
75
90
|
}
|
|
76
91
|
try {
|
|
92
|
+
// 获取上下文信息
|
|
77
93
|
const context = yield this.contextService.getCurrentContext();
|
|
94
|
+
// 记录审计日志
|
|
78
95
|
yield this.entityAuditService.logEntityChange(entityType, entityId, enums_1.AuditOperation.UPDATE, databaseEntity || {}, entity, {
|
|
79
96
|
requestId: context.requestId,
|
|
80
97
|
requestIp: context.requestIp,
|
|
@@ -86,6 +103,9 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
|
|
|
86
103
|
}
|
|
87
104
|
});
|
|
88
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* 在实体删除前记录审计日志
|
|
108
|
+
*/
|
|
89
109
|
beforeRemove(event) {
|
|
90
110
|
return __awaiter(this, void 0, void 0, function* () {
|
|
91
111
|
const entity = event.entity;
|
|
@@ -93,14 +113,18 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
|
|
|
93
113
|
return;
|
|
94
114
|
const entityType = entity.constructor.name;
|
|
95
115
|
const entityId = entity.id;
|
|
116
|
+
// 检查是否启用了审计
|
|
96
117
|
if (!this.isAuditEnabled(entity)) {
|
|
97
118
|
return;
|
|
98
119
|
}
|
|
120
|
+
// 检查策略
|
|
99
121
|
if (!this.auditStrategy.shouldRecord(entityType, enums_1.AuditOperation.DELETE)) {
|
|
100
122
|
return;
|
|
101
123
|
}
|
|
102
124
|
try {
|
|
125
|
+
// 获取上下文信息
|
|
103
126
|
const context = yield this.contextService.getCurrentContext();
|
|
127
|
+
// 记录审计日志
|
|
104
128
|
yield this.entityAuditService.logEntityChange(entityType, entityId, enums_1.AuditOperation.DELETE, entity, {}, {
|
|
105
129
|
requestId: context.requestId,
|
|
106
130
|
requestIp: context.requestIp,
|
|
@@ -112,8 +136,12 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
|
|
|
112
136
|
}
|
|
113
137
|
});
|
|
114
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* 检查是否启用了审计
|
|
141
|
+
*/
|
|
115
142
|
isAuditEnabled(entity) {
|
|
116
143
|
try {
|
|
144
|
+
// 检查装饰器
|
|
117
145
|
const auditOptions = Reflect.getMetadata(decorators_1.ENTITY_AUDIT_OPTIONS, entity.constructor);
|
|
118
146
|
if (auditOptions && auditOptions.enabled === false) {
|
|
119
147
|
return false;
|
|
@@ -121,7 +149,7 @@ let EntityAuditSubscriber = class EntityAuditSubscriber {
|
|
|
121
149
|
return true;
|
|
122
150
|
}
|
|
123
151
|
catch (error) {
|
|
124
|
-
return true;
|
|
152
|
+
return true; // 默认启用
|
|
125
153
|
}
|
|
126
154
|
}
|
|
127
155
|
};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
+
/**
|
|
3
|
+
* Individual cache operation record
|
|
4
|
+
*/
|
|
2
5
|
export interface CacheOperationRecord {
|
|
3
6
|
type: 'get' | 'set' | 'delete' | 'clear';
|
|
4
7
|
layer: string;
|
|
@@ -8,24 +11,34 @@ export interface CacheOperationRecord {
|
|
|
8
11
|
key?: string;
|
|
9
12
|
timestamp: Date;
|
|
10
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* Cache operation metrics
|
|
16
|
+
*/
|
|
11
17
|
export interface CacheMetrics {
|
|
18
|
+
/** Total number of operations */
|
|
12
19
|
totalOperations: number;
|
|
20
|
+
/** Hit/miss statistics */
|
|
13
21
|
hits: number;
|
|
14
22
|
misses: number;
|
|
15
23
|
hitRate: number;
|
|
24
|
+
/** Latency metrics (ms) */
|
|
16
25
|
avgLatency: number;
|
|
17
26
|
minLatency: number;
|
|
18
27
|
maxLatency: number;
|
|
19
28
|
p95Latency: number;
|
|
20
29
|
p99Latency: number;
|
|
30
|
+
/** Operation counts by type */
|
|
21
31
|
gets: number;
|
|
22
32
|
sets: number;
|
|
23
33
|
deletes: number;
|
|
24
34
|
clears: number;
|
|
35
|
+
/** Size metrics */
|
|
25
36
|
totalKeys: number;
|
|
26
37
|
totalSize: number;
|
|
38
|
+
/** Error metrics */
|
|
27
39
|
errors: number;
|
|
28
40
|
errorRate: number;
|
|
41
|
+
/** Layer-specific metrics */
|
|
29
42
|
layerMetrics: {
|
|
30
43
|
cls: {
|
|
31
44
|
hits: number;
|
|
@@ -44,40 +57,94 @@ export interface CacheMetrics {
|
|
|
44
57
|
avgLatency: number;
|
|
45
58
|
};
|
|
46
59
|
};
|
|
60
|
+
/** Time window for these metrics */
|
|
47
61
|
timeWindow: {
|
|
48
62
|
start: Date;
|
|
49
63
|
end: Date;
|
|
50
64
|
duration: number;
|
|
51
65
|
};
|
|
52
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Cache metrics service
|
|
69
|
+
*
|
|
70
|
+
* Collects and analyzes cache performance metrics
|
|
71
|
+
*/
|
|
53
72
|
export declare class CacheMetricsService extends EventEmitter {
|
|
54
73
|
private readonly logger;
|
|
55
74
|
private readonly operations;
|
|
56
75
|
private readonly maxOperations;
|
|
57
76
|
private readonly maxAge;
|
|
58
77
|
constructor();
|
|
78
|
+
/**
|
|
79
|
+
* Record a cache operation
|
|
80
|
+
*/
|
|
59
81
|
recordOperation(type: 'get' | 'set' | 'delete' | 'clear', layer: string, hit: boolean, latency: number, error?: string, key?: string): void;
|
|
82
|
+
/**
|
|
83
|
+
* Get metrics for a time window
|
|
84
|
+
*/
|
|
60
85
|
getMetrics(timeWindowMs?: number): CacheMetrics;
|
|
86
|
+
/**
|
|
87
|
+
* Get real-time metrics snapshot
|
|
88
|
+
*/
|
|
61
89
|
getSnapshot(): Partial<CacheMetrics>;
|
|
90
|
+
/**
|
|
91
|
+
* Get top slow operations
|
|
92
|
+
*/
|
|
62
93
|
getSlowOperations(limit?: number): CacheOperationRecord[];
|
|
94
|
+
/**
|
|
95
|
+
* Get most accessed keys
|
|
96
|
+
*/
|
|
63
97
|
getMostAccessedKeys(limit?: number): {
|
|
64
98
|
key: string;
|
|
65
99
|
count: number;
|
|
66
100
|
avgLatency: number;
|
|
67
101
|
}[];
|
|
102
|
+
/**
|
|
103
|
+
* Get error breakdown
|
|
104
|
+
*/
|
|
68
105
|
getErrorBreakdown(): {
|
|
69
106
|
error: string;
|
|
70
107
|
count: number;
|
|
71
108
|
lastOccurrence: Date;
|
|
72
109
|
}[];
|
|
110
|
+
/**
|
|
111
|
+
* Reset all metrics
|
|
112
|
+
*/
|
|
73
113
|
resetMetrics(): void;
|
|
114
|
+
/**
|
|
115
|
+
* Export metrics for external monitoring systems
|
|
116
|
+
*/
|
|
74
117
|
exportMetrics(format?: 'prometheus' | 'json'): string;
|
|
118
|
+
/**
|
|
119
|
+
* Clean up old operation records
|
|
120
|
+
*/
|
|
75
121
|
private cleanupOldRecords;
|
|
122
|
+
/**
|
|
123
|
+
* Create empty metrics object
|
|
124
|
+
*/
|
|
76
125
|
private createEmptyMetrics;
|
|
126
|
+
/**
|
|
127
|
+
* Calculate layer-specific metrics
|
|
128
|
+
*/
|
|
77
129
|
private calculateLayerMetrics;
|
|
130
|
+
/**
|
|
131
|
+
* Calculate average of array
|
|
132
|
+
*/
|
|
78
133
|
private average;
|
|
134
|
+
/**
|
|
135
|
+
* Calculate percentile
|
|
136
|
+
*/
|
|
79
137
|
private percentile;
|
|
138
|
+
/**
|
|
139
|
+
* Get unique key count
|
|
140
|
+
*/
|
|
80
141
|
private getUniqueKeyCount;
|
|
142
|
+
/**
|
|
143
|
+
* Estimate total cache size
|
|
144
|
+
*/
|
|
81
145
|
private estimateTotalSize;
|
|
146
|
+
/**
|
|
147
|
+
* Convert metrics to Prometheus format
|
|
148
|
+
*/
|
|
82
149
|
private toPrometheusFormat;
|
|
83
150
|
}
|
|
@@ -13,15 +13,24 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
13
13
|
exports.CacheMetricsService = void 0;
|
|
14
14
|
const common_1 = require("@nestjs/common");
|
|
15
15
|
const events_1 = require("events");
|
|
16
|
+
/**
|
|
17
|
+
* Cache metrics service
|
|
18
|
+
*
|
|
19
|
+
* Collects and analyzes cache performance metrics
|
|
20
|
+
*/
|
|
16
21
|
let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService extends events_1.EventEmitter {
|
|
17
22
|
constructor() {
|
|
18
23
|
super();
|
|
19
24
|
this.logger = new common_1.Logger(CacheMetricsService_1.name);
|
|
20
25
|
this.operations = [];
|
|
21
|
-
this.maxOperations = 10000;
|
|
22
|
-
this.maxAge = 3600000;
|
|
23
|
-
|
|
26
|
+
this.maxOperations = 10000; // Keep last 10k operations
|
|
27
|
+
this.maxAge = 3600000; // 1 hour
|
|
28
|
+
// Clean up old records periodically
|
|
29
|
+
setInterval(() => this.cleanupOldRecords(), 60000); // Every minute
|
|
24
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Record a cache operation
|
|
33
|
+
*/
|
|
25
34
|
recordOperation(type, layer, hit, latency, error, key) {
|
|
26
35
|
const record = {
|
|
27
36
|
type,
|
|
@@ -33,14 +42,20 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
33
42
|
timestamp: new Date(),
|
|
34
43
|
};
|
|
35
44
|
this.operations.push(record);
|
|
45
|
+
// Maintain maximum operations limit
|
|
36
46
|
if (this.operations.length > this.maxOperations) {
|
|
37
47
|
this.operations.splice(0, this.operations.length - this.maxOperations);
|
|
38
48
|
}
|
|
49
|
+
// Emit event for real-time monitoring
|
|
39
50
|
this.emit('operation', record);
|
|
51
|
+
// Log errors
|
|
40
52
|
if (error) {
|
|
41
53
|
this.logger.warn(`Cache operation error: ${type} on ${layer} - ${error}`);
|
|
42
54
|
}
|
|
43
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Get metrics for a time window
|
|
58
|
+
*/
|
|
44
59
|
getMetrics(timeWindowMs = 300000) {
|
|
45
60
|
const endTime = new Date();
|
|
46
61
|
const startTime = new Date(endTime.getTime() - timeWindowMs);
|
|
@@ -58,6 +73,7 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
58
73
|
const sets = recentOperations.filter((op) => op.type === 'set').length;
|
|
59
74
|
const deletes = recentOperations.filter((op) => op.type === 'delete').length;
|
|
60
75
|
const clears = recentOperations.filter((op) => op.type === 'clear').length;
|
|
76
|
+
// Calculate layer-specific metrics
|
|
61
77
|
const layerOperations = {
|
|
62
78
|
cls: recentOperations.filter((op) => op.layer === 'cls'),
|
|
63
79
|
memory: recentOperations.filter((op) => op.layer === 'memory'),
|
|
@@ -94,6 +110,9 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
94
110
|
},
|
|
95
111
|
};
|
|
96
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Get real-time metrics snapshot
|
|
115
|
+
*/
|
|
97
116
|
getSnapshot() {
|
|
98
117
|
const now = new Date();
|
|
99
118
|
const oneMinuteAgo = new Date(now.getTime() - 60000);
|
|
@@ -124,12 +143,18 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
124
143
|
errorRate: errors / recentOperations.length,
|
|
125
144
|
};
|
|
126
145
|
}
|
|
146
|
+
/**
|
|
147
|
+
* Get top slow operations
|
|
148
|
+
*/
|
|
127
149
|
getSlowOperations(limit = 10) {
|
|
128
150
|
return this.operations
|
|
129
151
|
.filter((op) => op.error === undefined)
|
|
130
152
|
.sort((a, b) => b.latency - a.latency)
|
|
131
153
|
.slice(0, limit);
|
|
132
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Get most accessed keys
|
|
157
|
+
*/
|
|
133
158
|
getMostAccessedKeys(limit = 10) {
|
|
134
159
|
const keyStats = new Map();
|
|
135
160
|
this.operations.forEach((op) => {
|
|
@@ -149,6 +174,9 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
149
174
|
.sort((a, b) => b.count - a.count)
|
|
150
175
|
.slice(0, limit);
|
|
151
176
|
}
|
|
177
|
+
/**
|
|
178
|
+
* Get error breakdown
|
|
179
|
+
*/
|
|
152
180
|
getErrorBreakdown() {
|
|
153
181
|
const errorStats = new Map();
|
|
154
182
|
this.operations.forEach((op) => {
|
|
@@ -173,11 +201,17 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
173
201
|
}))
|
|
174
202
|
.sort((a, b) => b.count - a.count);
|
|
175
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Reset all metrics
|
|
206
|
+
*/
|
|
176
207
|
resetMetrics() {
|
|
177
208
|
this.operations.length = 0;
|
|
178
209
|
this.logger.log('Cache metrics reset');
|
|
179
210
|
this.emit('reset');
|
|
180
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Export metrics for external monitoring systems
|
|
214
|
+
*/
|
|
181
215
|
exportMetrics(format = 'json') {
|
|
182
216
|
const metrics = this.getMetrics();
|
|
183
217
|
if (format === 'prometheus') {
|
|
@@ -185,9 +219,13 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
185
219
|
}
|
|
186
220
|
return JSON.stringify(metrics, null, 2);
|
|
187
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Clean up old operation records
|
|
224
|
+
*/
|
|
188
225
|
cleanupOldRecords() {
|
|
189
226
|
const cutoff = new Date(Date.now() - this.maxAge);
|
|
190
227
|
const initialLength = this.operations.length;
|
|
228
|
+
// Remove old records
|
|
191
229
|
for (let i = this.operations.length - 1; i >= 0; i--) {
|
|
192
230
|
if (this.operations[i].timestamp < cutoff) {
|
|
193
231
|
this.operations.splice(0, i + 1);
|
|
@@ -199,6 +237,9 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
199
237
|
this.logger.debug(`Cleaned up ${removed} old cache operation records`);
|
|
200
238
|
}
|
|
201
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* Create empty metrics object
|
|
242
|
+
*/
|
|
202
243
|
createEmptyMetrics(start, end) {
|
|
203
244
|
return {
|
|
204
245
|
totalOperations: 0,
|
|
@@ -230,6 +271,9 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
230
271
|
},
|
|
231
272
|
};
|
|
232
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* Calculate layer-specific metrics
|
|
276
|
+
*/
|
|
233
277
|
calculateLayerMetrics(operations) {
|
|
234
278
|
const hits = operations.filter((op) => op.hit).length;
|
|
235
279
|
const misses = operations.filter((op) => !op.hit).length;
|
|
@@ -238,20 +282,29 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
238
282
|
hits,
|
|
239
283
|
misses,
|
|
240
284
|
avgLatency: latencies.length > 0 ? this.average(latencies) : 0,
|
|
241
|
-
size: operations.length,
|
|
285
|
+
size: operations.length, // Approximation
|
|
242
286
|
};
|
|
243
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Calculate average of array
|
|
290
|
+
*/
|
|
244
291
|
average(numbers) {
|
|
245
292
|
return numbers.length > 0
|
|
246
293
|
? numbers.reduce((sum, num) => sum + num, 0) / numbers.length
|
|
247
294
|
: 0;
|
|
248
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* Calculate percentile
|
|
298
|
+
*/
|
|
249
299
|
percentile(sortedNumbers, p) {
|
|
250
300
|
if (sortedNumbers.length === 0)
|
|
251
301
|
return 0;
|
|
252
302
|
const index = Math.ceil(sortedNumbers.length * p) - 1;
|
|
253
303
|
return sortedNumbers[Math.max(0, Math.min(index, sortedNumbers.length - 1))];
|
|
254
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Get unique key count
|
|
307
|
+
*/
|
|
255
308
|
getUniqueKeyCount(operations) {
|
|
256
309
|
const keys = new Set();
|
|
257
310
|
operations.forEach((op) => {
|
|
@@ -260,29 +313,40 @@ let CacheMetricsService = CacheMetricsService_1 = class CacheMetricsService exte
|
|
|
260
313
|
});
|
|
261
314
|
return keys.size;
|
|
262
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* Estimate total cache size
|
|
318
|
+
*/
|
|
263
319
|
estimateTotalSize(operations) {
|
|
320
|
+
// Rough estimation: average key size (20 bytes) + average value size (100 bytes)
|
|
264
321
|
const avgValueSize = 120;
|
|
265
322
|
return this.getUniqueKeyCount(operations) * avgValueSize;
|
|
266
323
|
}
|
|
324
|
+
/**
|
|
325
|
+
* Convert metrics to Prometheus format
|
|
326
|
+
*/
|
|
267
327
|
toPrometheusFormat(metrics) {
|
|
268
328
|
const labels = {
|
|
269
329
|
layer: 'cls|memory|redis',
|
|
270
330
|
};
|
|
271
331
|
let output = '';
|
|
332
|
+
// Hit rate
|
|
272
333
|
output += `# HELP cache_hit_rate Cache hit rate\n`;
|
|
273
334
|
output += `# TYPE cache_hit_rate gauge\n`;
|
|
274
335
|
output += `cache_hit_rate ${metrics.hitRate}\n`;
|
|
336
|
+
// Operations
|
|
275
337
|
output += `# HELP cache_operations_total Total cache operations\n`;
|
|
276
338
|
output += `# TYPE cache_operations_total counter\n`;
|
|
277
339
|
output += `cache_operations_total{type="get"} ${metrics.gets}\n`;
|
|
278
340
|
output += `cache_operations_total{type="set"} ${metrics.sets}\n`;
|
|
279
341
|
output += `cache_operations_total{type="delete"} ${metrics.deletes}\n`;
|
|
280
342
|
output += `cache_operations_total{type="clear"} ${metrics.clears}\n`;
|
|
343
|
+
// Latency
|
|
281
344
|
output += `# HELP cache_latency_ms Cache operation latency\n`;
|
|
282
345
|
output += `# TYPE cache_latency_ms histogram\n`;
|
|
283
346
|
output += `cache_latency_ms{quantile="0.5"} ${metrics.avgLatency}\n`;
|
|
284
347
|
output += `cache_latency_ms{quantile="0.95"} ${metrics.p95Latency}\n`;
|
|
285
348
|
output += `cache_latency_ms{quantile="0.99"} ${metrics.p99Latency}\n`;
|
|
349
|
+
// Layer metrics
|
|
286
350
|
Object.entries(metrics.layerMetrics).forEach(([layer, layerMetrics]) => {
|
|
287
351
|
output += `cache_layer_hits_total{layer="${layer}"} ${layerMetrics.hits}\n`;
|
|
288
352
|
output += `cache_layer_misses_total{layer="${layer}"} ${layerMetrics.misses}\n`;
|