monsqlize 1.0.1 → 1.0.5
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/CHANGELOG.md +204 -2464
- package/README.md +735 -1198
- package/index.d.ts +942 -18
- package/lib/cache.js +8 -8
- package/lib/common/validation.js +64 -1
- package/lib/connect.js +3 -3
- package/lib/errors.js +10 -0
- package/lib/index.js +173 -9
- package/lib/infrastructure/ssh-tunnel-ssh2.js +211 -0
- package/lib/infrastructure/ssh-tunnel.js +40 -0
- package/lib/infrastructure/uri-parser.js +35 -0
- package/lib/lock/Lock.js +66 -0
- package/lib/lock/errors.js +27 -0
- package/lib/lock/index.js +12 -0
- package/lib/logger.js +1 -1
- package/lib/model/examples/test.js +225 -29
- package/lib/model/features/soft-delete.js +348 -0
- package/lib/model/features/version.js +156 -0
- package/lib/model/index.js +756 -0
- package/lib/mongodb/common/accessor-helpers.js +17 -3
- package/lib/mongodb/connect.js +68 -13
- package/lib/mongodb/index.js +153 -6
- package/lib/mongodb/management/collection-ops.js +4 -4
- package/lib/mongodb/management/index-ops.js +18 -18
- package/lib/mongodb/management/validation-ops.js +3 -3
- package/lib/mongodb/queries/aggregate.js +14 -5
- package/lib/mongodb/queries/chain.js +52 -45
- package/lib/mongodb/queries/count.js +16 -6
- package/lib/mongodb/queries/distinct.js +15 -6
- package/lib/mongodb/queries/find-and-count.js +22 -13
- package/lib/mongodb/queries/find-by-ids.js +5 -5
- package/lib/mongodb/queries/find-one-by-id.js +1 -1
- package/lib/mongodb/queries/find-one.js +12 -3
- package/lib/mongodb/queries/find-page.js +12 -0
- package/lib/mongodb/queries/find.js +15 -6
- package/lib/mongodb/queries/watch.js +11 -2
- package/lib/mongodb/writes/common/batch-retry.js +64 -0
- package/lib/mongodb/writes/delete-batch.js +322 -0
- package/lib/mongodb/writes/delete-many.js +20 -11
- package/lib/mongodb/writes/delete-one.js +18 -9
- package/lib/mongodb/writes/find-one-and-delete.js +19 -10
- package/lib/mongodb/writes/find-one-and-replace.js +36 -20
- package/lib/mongodb/writes/find-one-and-update.js +36 -20
- package/lib/mongodb/writes/increment-one.js +22 -7
- package/lib/mongodb/writes/index.js +17 -13
- package/lib/mongodb/writes/insert-batch.js +46 -37
- package/lib/mongodb/writes/insert-many.js +22 -13
- package/lib/mongodb/writes/insert-one.js +18 -9
- package/lib/mongodb/writes/replace-one.js +33 -17
- package/lib/mongodb/writes/result-handler.js +14 -14
- package/lib/mongodb/writes/update-batch.js +358 -0
- package/lib/mongodb/writes/update-many.js +34 -18
- package/lib/mongodb/writes/update-one.js +33 -17
- package/lib/mongodb/writes/upsert-one.js +25 -9
- package/lib/operators.js +1 -1
- package/lib/redis-cache-adapter.js +3 -3
- package/lib/slow-query-log/base-storage.js +69 -0
- package/lib/slow-query-log/batch-queue.js +96 -0
- package/lib/slow-query-log/config-manager.js +195 -0
- package/lib/slow-query-log/index.js +237 -0
- package/lib/slow-query-log/mongodb-storage.js +323 -0
- package/lib/slow-query-log/query-hash.js +38 -0
- package/lib/transaction/DistributedCacheLockManager.js +240 -5
- package/lib/transaction/Transaction.js +1 -1
- package/lib/utils/objectid-converter.js +566 -0
- package/package.json +18 -6
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 慢查询日志存储接口
|
|
3
|
+
* 所有存储适配器必须实现此接口
|
|
4
|
+
*
|
|
5
|
+
* @version 1.3.0
|
|
6
|
+
* @since 2025-12-22
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
class ISlowQueryLogStorage {
|
|
10
|
+
/**
|
|
11
|
+
* 初始化存储(创建集合/表、索引等)
|
|
12
|
+
* @returns {Promise<void>}
|
|
13
|
+
*/
|
|
14
|
+
async initialize() {
|
|
15
|
+
throw new Error('ISlowQueryLogStorage.initialize() must be implemented');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 保存单条慢查询日志
|
|
20
|
+
* @param {Object} log - 慢查询日志对象
|
|
21
|
+
* @param {string} log.db - 数据库名
|
|
22
|
+
* @param {string} log.collection - 集合名
|
|
23
|
+
* @param {string} log.operation - 操作类型
|
|
24
|
+
* @param {Object} log.queryShape - 查询模式(已脱敏)
|
|
25
|
+
* @param {number} log.executionTimeMs - 执行时间(毫秒)
|
|
26
|
+
* @param {Date} log.timestamp - 时间戳
|
|
27
|
+
* @param {Object} [log.metadata] - 扩展元数据
|
|
28
|
+
* @returns {Promise<void>}
|
|
29
|
+
*/
|
|
30
|
+
async save(log) {
|
|
31
|
+
throw new Error('ISlowQueryLogStorage.save() must be implemented');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 批量保存慢查询日志
|
|
36
|
+
* @param {Object[]} logs - 慢查询日志数组
|
|
37
|
+
* @returns {Promise<void>}
|
|
38
|
+
*/
|
|
39
|
+
async saveBatch(logs) {
|
|
40
|
+
throw new Error('ISlowQueryLogStorage.saveBatch() must be implemented');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 查询慢查询日志
|
|
45
|
+
* @param {Object} filter - 查询条件
|
|
46
|
+
* @param {string} [filter.db] - 数据库名
|
|
47
|
+
* @param {string} [filter.collection] - 集合名
|
|
48
|
+
* @param {string} [filter.operation] - 操作类型
|
|
49
|
+
* @param {Object} options - 查询选项
|
|
50
|
+
* @param {Object} [options.sort] - 排序规则
|
|
51
|
+
* @param {number} [options.limit] - 限制数量
|
|
52
|
+
* @param {number} [options.skip] - 跳过数量
|
|
53
|
+
* @returns {Promise<Object[]>}
|
|
54
|
+
*/
|
|
55
|
+
async query(filter, options) {
|
|
56
|
+
throw new Error('ISlowQueryLogStorage.query() must be implemented');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 关闭连接
|
|
61
|
+
* @returns {Promise<void>}
|
|
62
|
+
*/
|
|
63
|
+
async close() {
|
|
64
|
+
throw new Error('ISlowQueryLogStorage.close() must be implemented');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = { ISlowQueryLogStorage };
|
|
69
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 批量队列管理器
|
|
3
|
+
* 用于批量写入慢查询日志,优化性能
|
|
4
|
+
*
|
|
5
|
+
* @version 1.3.0
|
|
6
|
+
* @since 2025-12-22
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
class BatchQueue {
|
|
10
|
+
/**
|
|
11
|
+
* 创建批量队列
|
|
12
|
+
* @param {Object} storage - 存储适配器实例
|
|
13
|
+
* @param {Object} options - 队列配置
|
|
14
|
+
* @param {number} [options.batchSize=10] - 批量大小
|
|
15
|
+
* @param {number} [options.flushInterval=5000] - 刷新间隔(毫秒)
|
|
16
|
+
* @param {number} [options.maxBufferSize=100] - 最大缓冲区大小
|
|
17
|
+
* @param {Object} [logger] - 日志记录器
|
|
18
|
+
*/
|
|
19
|
+
constructor(storage, options = {}, logger) {
|
|
20
|
+
this.storage = storage;
|
|
21
|
+
this.buffer = [];
|
|
22
|
+
this.batchSize = options.batchSize || 10;
|
|
23
|
+
this.flushInterval = options.flushInterval || 5000;
|
|
24
|
+
this.maxBufferSize = options.maxBufferSize || 100;
|
|
25
|
+
this.timer = null;
|
|
26
|
+
this.flushing = false;
|
|
27
|
+
this.logger = logger || console;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 添加日志到队列
|
|
32
|
+
* @param {Object} log - 慢查询日志对象
|
|
33
|
+
* @returns {Promise<void>}
|
|
34
|
+
*/
|
|
35
|
+
async add(log) {
|
|
36
|
+
this.buffer.push(log);
|
|
37
|
+
|
|
38
|
+
// 防止内存溢出:达到最大缓冲区,立即刷新
|
|
39
|
+
if (this.buffer.length >= this.maxBufferSize) {
|
|
40
|
+
await this.flush();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// 达到批量大小,立即刷新
|
|
45
|
+
if (this.buffer.length >= this.batchSize) {
|
|
46
|
+
await this.flush();
|
|
47
|
+
} else if (!this.timer) {
|
|
48
|
+
// 启动定时器(防止数据积压)
|
|
49
|
+
this.timer = setTimeout(() => this.flush(), this.flushInterval);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* 刷新队列(批量写入)
|
|
55
|
+
* @returns {Promise<void>}
|
|
56
|
+
*/
|
|
57
|
+
async flush() {
|
|
58
|
+
// 防止并发刷新
|
|
59
|
+
if (this.flushing || this.buffer.length === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.flushing = true;
|
|
64
|
+
const logs = this.buffer.splice(0); // 清空缓冲区
|
|
65
|
+
clearTimeout(this.timer);
|
|
66
|
+
this.timer = null;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
// 批量写入
|
|
70
|
+
await this.storage.saveBatch(logs);
|
|
71
|
+
if (this.logger.debug) {
|
|
72
|
+
this.logger.debug(`[SlowQueryLog] Batch flushed: ${logs.length} logs`);
|
|
73
|
+
}
|
|
74
|
+
} catch (err) {
|
|
75
|
+
// ⚠️ 失败不阻塞主流程,仅记录错误
|
|
76
|
+
if (this.logger.error) {
|
|
77
|
+
this.logger.error('[SlowQueryLog] Failed to save slow query logs batch:', err);
|
|
78
|
+
}
|
|
79
|
+
} finally {
|
|
80
|
+
this.flushing = false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 关闭队列(确保数据不丢失)
|
|
86
|
+
* @returns {Promise<void>}
|
|
87
|
+
*/
|
|
88
|
+
async close() {
|
|
89
|
+
clearTimeout(this.timer);
|
|
90
|
+
this.timer = null;
|
|
91
|
+
await this.flush(); // 最后刷新一次
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = { BatchQueue };
|
|
96
|
+
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 慢查询日志配置管理器
|
|
3
|
+
* 负责配置合并、验证和默认值处理
|
|
4
|
+
*
|
|
5
|
+
* @version 1.3.0
|
|
6
|
+
* @since 2025-12-22
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// 默认配置
|
|
10
|
+
const DEFAULT_CONFIG = {
|
|
11
|
+
enabled: false,
|
|
12
|
+
|
|
13
|
+
storage: {
|
|
14
|
+
type: null, // null = 自动推断
|
|
15
|
+
useBusinessConnection: true,
|
|
16
|
+
uri: null,
|
|
17
|
+
|
|
18
|
+
mongodb: {
|
|
19
|
+
database: 'admin',
|
|
20
|
+
collection: 'slow_query_logs',
|
|
21
|
+
ttl: 7 * 24 * 3600, // 7天
|
|
22
|
+
ttlField: 'lastSeen'
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
deduplication: {
|
|
27
|
+
enabled: true,
|
|
28
|
+
strategy: 'aggregate',
|
|
29
|
+
keepRecentExecutions: 0
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
batch: {
|
|
33
|
+
enabled: true,
|
|
34
|
+
size: 10,
|
|
35
|
+
interval: 5000,
|
|
36
|
+
maxBufferSize: 100
|
|
37
|
+
},
|
|
38
|
+
|
|
39
|
+
filter: {
|
|
40
|
+
excludeDatabases: [],
|
|
41
|
+
excludeCollections: [],
|
|
42
|
+
excludeOperations: [],
|
|
43
|
+
minExecutionTimeMs: 0
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
advanced: {
|
|
47
|
+
autoCreateIndexes: true,
|
|
48
|
+
validateConnection: true,
|
|
49
|
+
errorHandling: 'log', // log | throw | silent
|
|
50
|
+
debug: false
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
class SlowQueryLogConfigManager {
|
|
55
|
+
/**
|
|
56
|
+
* 合并用户配置与默认配置
|
|
57
|
+
* @param {*} userConfig - 用户配置(可以是boolean或object)
|
|
58
|
+
* @param {string} businessType - 业务库类型
|
|
59
|
+
* @returns {Object} 合并后的完整配置
|
|
60
|
+
*/
|
|
61
|
+
static mergeConfig(userConfig, businessType) {
|
|
62
|
+
// 场景1:未配置(默认禁用)
|
|
63
|
+
if (userConfig === undefined || userConfig === null) {
|
|
64
|
+
return { ...deepClone(DEFAULT_CONFIG), enabled: false };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 场景2:boolean快捷配置
|
|
68
|
+
if (typeof userConfig === 'boolean') {
|
|
69
|
+
const config = deepClone(DEFAULT_CONFIG);
|
|
70
|
+
config.enabled = userConfig;
|
|
71
|
+
|
|
72
|
+
// 自动推断storage.type
|
|
73
|
+
if (userConfig && businessType) {
|
|
74
|
+
config.storage.type = businessType;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 场景3:对象配置(深度合并)
|
|
81
|
+
if (typeof userConfig === 'object') {
|
|
82
|
+
const merged = deepMerge(deepClone(DEFAULT_CONFIG), userConfig);
|
|
83
|
+
|
|
84
|
+
// 智能推断:如果提供了storage配置,自动启用
|
|
85
|
+
if (userConfig.storage && merged.enabled === false) {
|
|
86
|
+
merged.enabled = true;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 自动推断storage.type
|
|
90
|
+
if (merged.storage.type === null && businessType) {
|
|
91
|
+
if (merged.storage.useBusinessConnection) {
|
|
92
|
+
merged.storage.type = businessType;
|
|
93
|
+
} else {
|
|
94
|
+
merged.storage.type = 'mongodb'; // 独立连接默认MongoDB
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return merged;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
throw new Error('Invalid slowQueryLog config type. Expected boolean or object');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* 验证配置合法性
|
|
106
|
+
* @param {Object} config - 配置对象
|
|
107
|
+
* @param {string} businessType - 业务库类型
|
|
108
|
+
* @throws {Error} 配置错误时抛出异常
|
|
109
|
+
*/
|
|
110
|
+
static validate(config, businessType) {
|
|
111
|
+
const { storage } = config;
|
|
112
|
+
|
|
113
|
+
// 验证storage.type
|
|
114
|
+
const validTypes = ['mongodb', 'postgresql', 'mysql', 'file', 'custom'];
|
|
115
|
+
if (storage.type && !validTypes.includes(storage.type)) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Invalid storage.type: ${storage.type}. ` +
|
|
118
|
+
`Valid types are: ${validTypes.join(', ')}`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 验证复用连接的类型一致性
|
|
123
|
+
if (storage.useBusinessConnection === true) {
|
|
124
|
+
if (storage.type && storage.type !== businessType) {
|
|
125
|
+
throw new Error(
|
|
126
|
+
`Cannot use business connection when storage type (${storage.type}) ` +
|
|
127
|
+
`differs from business type (${businessType}). ` +
|
|
128
|
+
`Set useBusinessConnection=false and provide storage.uri`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
// 验证独立连接的uri
|
|
133
|
+
if (!storage.uri) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
'storage.uri is required when useBusinessConnection=false'
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 验证deduplication.strategy
|
|
141
|
+
const validStrategies = ['aggregate', 'none'];
|
|
142
|
+
if (!validStrategies.includes(config.deduplication.strategy)) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Invalid deduplication.strategy: ${config.deduplication.strategy}. ` +
|
|
145
|
+
`Valid strategies are: ${validStrategies.join(', ')}`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 验证TTL
|
|
150
|
+
if (storage.mongodb.ttl < 0) {
|
|
151
|
+
throw new Error('storage.mongodb.ttl must be positive');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 验证batch配置
|
|
155
|
+
if (config.batch.size < 1 || config.batch.size > 1000) {
|
|
156
|
+
throw new Error('batch.size must be between 1 and 1000');
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (config.batch.interval < 100) {
|
|
160
|
+
throw new Error('batch.interval must be >= 100ms');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* 深度克隆对象
|
|
169
|
+
* @param {Object} obj - 源对象
|
|
170
|
+
* @returns {Object} 克隆后的对象
|
|
171
|
+
*/
|
|
172
|
+
function deepClone(obj) {
|
|
173
|
+
return JSON.parse(JSON.stringify(obj));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* 深度合并对象
|
|
178
|
+
* @param {Object} target - 目标对象
|
|
179
|
+
* @param {Object} source - 源对象
|
|
180
|
+
* @returns {Object} 合并后的对象
|
|
181
|
+
*/
|
|
182
|
+
function deepMerge(target, source) {
|
|
183
|
+
for (const key in source) {
|
|
184
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
185
|
+
target[key] = target[key] || {};
|
|
186
|
+
deepMerge(target[key], source[key]);
|
|
187
|
+
} else {
|
|
188
|
+
target[key] = source[key];
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return target;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module.exports = { SlowQueryLogConfigManager, DEFAULT_CONFIG };
|
|
195
|
+
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 慢查询日志模块导出
|
|
3
|
+
*
|
|
4
|
+
* @version 1.3.0
|
|
5
|
+
* @since 2025-12-22
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { ISlowQueryLogStorage } = require('./base-storage');
|
|
9
|
+
const { MongoDBSlowQueryLogStorage } = require('./mongodb-storage');
|
|
10
|
+
const { BatchQueue } = require('./batch-queue');
|
|
11
|
+
const { generateQueryHash } = require('./query-hash');
|
|
12
|
+
const { SlowQueryLogConfigManager, DEFAULT_CONFIG } = require('./config-manager');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* 慢查询日志管理器
|
|
16
|
+
* 统一管理存储、队列、配置
|
|
17
|
+
*/
|
|
18
|
+
class SlowQueryLogManager {
|
|
19
|
+
/**
|
|
20
|
+
* 创建慢查询日志管理器
|
|
21
|
+
* @param {Object} userConfig - 用户配置
|
|
22
|
+
* @param {Object} [businessClient] - 业务数据库客户端(复用连接时)
|
|
23
|
+
* @param {string} businessType - 业务数据库类型
|
|
24
|
+
* @param {Object} logger - 日志记录器
|
|
25
|
+
*/
|
|
26
|
+
constructor(userConfig, businessClient, businessType, logger) {
|
|
27
|
+
this.logger = logger || console;
|
|
28
|
+
|
|
29
|
+
// 合并配置
|
|
30
|
+
this.config = SlowQueryLogConfigManager.mergeConfig(userConfig, businessType);
|
|
31
|
+
|
|
32
|
+
// 验证配置
|
|
33
|
+
SlowQueryLogConfigManager.validate(this.config, businessType);
|
|
34
|
+
|
|
35
|
+
// 初始化存储适配器
|
|
36
|
+
this.storage = this.createStorage(businessClient);
|
|
37
|
+
|
|
38
|
+
// 初始化批量队列
|
|
39
|
+
if (this.config.batch.enabled) {
|
|
40
|
+
this.queue = new BatchQueue(this.storage, this.config.batch, this.logger);
|
|
41
|
+
} else {
|
|
42
|
+
this.queue = null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
this.initialized = false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 创建存储适配器
|
|
50
|
+
* @param {Object} businessClient - 业务数据库客户端
|
|
51
|
+
* @returns {ISlowQueryLogStorage}
|
|
52
|
+
*/
|
|
53
|
+
createStorage(businessClient) {
|
|
54
|
+
const { storage } = this.config;
|
|
55
|
+
|
|
56
|
+
// 根据存储类型创建适配器
|
|
57
|
+
switch (storage.type) {
|
|
58
|
+
case 'mongodb': {
|
|
59
|
+
const client = storage.useBusinessConnection ? businessClient : null;
|
|
60
|
+
return new MongoDBSlowQueryLogStorage(
|
|
61
|
+
storage.mongodb,
|
|
62
|
+
client,
|
|
63
|
+
this.logger
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
case 'postgresql':
|
|
68
|
+
case 'mysql':
|
|
69
|
+
case 'file':
|
|
70
|
+
case 'custom':
|
|
71
|
+
throw new Error(
|
|
72
|
+
`Storage type '${storage.type}' is not yet implemented. ` +
|
|
73
|
+
`Currently only 'mongodb' is supported in v1.3`
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
default:
|
|
77
|
+
throw new Error(`Unknown storage type: ${storage.type}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* 初始化(延迟初始化)
|
|
83
|
+
* @returns {Promise<void>}
|
|
84
|
+
*/
|
|
85
|
+
async initialize() {
|
|
86
|
+
if (this.initialized) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
await this.storage.initialize();
|
|
92
|
+
this.initialized = true;
|
|
93
|
+
|
|
94
|
+
if (this.logger.info) {
|
|
95
|
+
this.logger.info('[SlowQueryLog] Manager initialized successfully');
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (this.logger.error) {
|
|
99
|
+
this.logger.error('[SlowQueryLog] Failed to initialize manager:', err);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 根据错误处理策略决定是否抛出异常
|
|
103
|
+
if (this.config.advanced.errorHandling === 'throw') {
|
|
104
|
+
throw err;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* 保存慢查询日志
|
|
111
|
+
* @param {Object} log - 慢查询日志对象
|
|
112
|
+
* @returns {Promise<void>}
|
|
113
|
+
*/
|
|
114
|
+
async save(log) {
|
|
115
|
+
// 延迟初始化
|
|
116
|
+
if (!this.initialized) {
|
|
117
|
+
await this.initialize();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 过滤检查
|
|
121
|
+
if (this.shouldFilter(log)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
if (this.queue) {
|
|
127
|
+
// 批量模式:添加到队列
|
|
128
|
+
await this.queue.add(log);
|
|
129
|
+
} else {
|
|
130
|
+
// 实时模式:直接保存
|
|
131
|
+
await this.storage.save(log);
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (this.logger.error) {
|
|
135
|
+
this.logger.error('[SlowQueryLog] Failed to save log:', err);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 保存失败不抛异常(不影响主流程)
|
|
139
|
+
if (this.config.advanced.errorHandling === 'throw') {
|
|
140
|
+
throw err;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 检查是否应该过滤该日志
|
|
147
|
+
* @param {Object} log - 慢查询日志对象
|
|
148
|
+
* @returns {boolean}
|
|
149
|
+
*/
|
|
150
|
+
shouldFilter(log) {
|
|
151
|
+
const { filter } = this.config;
|
|
152
|
+
|
|
153
|
+
// 检查数据库过滤
|
|
154
|
+
if (filter.excludeDatabases.length > 0) {
|
|
155
|
+
if (filter.excludeDatabases.includes(log.db)) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// 检查集合过滤
|
|
161
|
+
if (filter.excludeCollections.length > 0) {
|
|
162
|
+
const coll = log.collection || log.coll;
|
|
163
|
+
if (filter.excludeCollections.includes(coll)) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 检查操作类型过滤
|
|
169
|
+
if (filter.excludeOperations.length > 0) {
|
|
170
|
+
const op = log.operation || log.op;
|
|
171
|
+
if (filter.excludeOperations.includes(op)) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// 检查最小执行时间
|
|
177
|
+
if (filter.minExecutionTimeMs > 0) {
|
|
178
|
+
const ms = log.executionTimeMs || log.ms || 0;
|
|
179
|
+
if (ms < filter.minExecutionTimeMs) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* 查询慢查询日志
|
|
189
|
+
* @param {Object} filter - 查询条件
|
|
190
|
+
* @param {Object} options - 查询选项
|
|
191
|
+
* @returns {Promise<Object[]>}
|
|
192
|
+
*/
|
|
193
|
+
async query(filter, options) {
|
|
194
|
+
if (!this.initialized) {
|
|
195
|
+
await this.initialize();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return this.storage.query(filter, options);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 关闭管理器
|
|
203
|
+
* @returns {Promise<void>}
|
|
204
|
+
*/
|
|
205
|
+
async close() {
|
|
206
|
+
try {
|
|
207
|
+
// 关闭队列(确保数据不丢失)
|
|
208
|
+
if (this.queue) {
|
|
209
|
+
await this.queue.close();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 关闭存储
|
|
213
|
+
await this.storage.close();
|
|
214
|
+
|
|
215
|
+
this.initialized = false;
|
|
216
|
+
|
|
217
|
+
if (this.logger.info) {
|
|
218
|
+
this.logger.info('[SlowQueryLog] Manager closed');
|
|
219
|
+
}
|
|
220
|
+
} catch (err) {
|
|
221
|
+
if (this.logger.error) {
|
|
222
|
+
this.logger.error('[SlowQueryLog] Failed to close manager:', err);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
module.exports = {
|
|
229
|
+
SlowQueryLogManager,
|
|
230
|
+
SlowQueryLogConfigManager,
|
|
231
|
+
MongoDBSlowQueryLogStorage,
|
|
232
|
+
BatchQueue,
|
|
233
|
+
generateQueryHash,
|
|
234
|
+
DEFAULT_CONFIG,
|
|
235
|
+
ISlowQueryLogStorage
|
|
236
|
+
};
|
|
237
|
+
|