@unrdf/knowledge-engine 5.0.1 → 26.4.2
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/package.json +23 -17
- package/src/ai-enhanced-search.mjs +371 -0
- package/src/anomaly-detector.mjs +226 -0
- package/src/artifact-generator.mjs +252 -0
- package/src/browser.mjs +1 -1
- package/src/chatman/disruption-arithmetic.mjs +140 -0
- package/src/chatman/market-dynamics.mjs +140 -0
- package/src/chatman/organizational-dynamics.mjs +140 -0
- package/src/chatman/strategic-dynamics.mjs +140 -0
- package/src/chatman-config-loader.mjs +282 -0
- package/src/chatman-engine.mjs +435 -0
- package/src/chatman-operator.mjs +343 -0
- package/src/dark-field-detector.mjs +332 -0
- package/src/formation-theorems.mjs +345 -0
- package/src/index.mjs +20 -2
- package/src/knowledge-hook-manager.mjs +1 -1
- package/src/lockchain-writer-browser.mjs +2 -2
- package/src/observability.mjs +40 -4
- package/src/query-optimizer.mjs +1 -1
- package/src/resolution-layer.mjs +1 -1
- package/src/transaction.mjs +11 -9
- package/README.md +0 -84
- package/src/browser-shims.mjs +0 -343
- package/src/canonicalize.mjs +0 -414
- package/src/condition-cache.mjs +0 -109
- package/src/condition-evaluator.mjs +0 -722
- package/src/dark-matter-core.mjs +0 -742
- package/src/define-hook.mjs +0 -213
- package/src/effect-sandbox-browser.mjs +0 -283
- package/src/effect-sandbox-worker.mjs +0 -170
- package/src/effect-sandbox.mjs +0 -517
- package/src/engines/index.mjs +0 -11
- package/src/engines/rdf-engine.mjs +0 -299
- package/src/file-resolver.mjs +0 -387
- package/src/hook-executor-batching.mjs +0 -277
- package/src/hook-executor.mjs +0 -870
- package/src/hook-management.mjs +0 -150
- package/src/ken-parliment.mjs +0 -119
- package/src/ken.mjs +0 -149
- package/src/knowledge-engine/builtin-rules.mjs +0 -190
- package/src/knowledge-engine/inference-engine.mjs +0 -418
- package/src/knowledge-engine/knowledge-engine.mjs +0 -317
- package/src/knowledge-engine/pattern-dsl.mjs +0 -142
- package/src/knowledge-engine/pattern-matcher.mjs +0 -215
- package/src/knowledge-engine/rules.mjs +0 -184
- package/src/knowledge-engine.mjs +0 -319
- package/src/knowledge-hook-engine.mjs +0 -360
- package/src/knowledge-substrate-core.mjs +0 -927
- package/src/lite.mjs +0 -222
- package/src/lockchain-writer.mjs +0 -602
- package/src/monitoring/andon-signals.mjs +0 -775
- package/src/parse.mjs +0 -290
- package/src/performance-optimizer.mjs +0 -678
- package/src/policy-pack.mjs +0 -572
- package/src/query-cache.mjs +0 -116
- package/src/query.mjs +0 -306
- package/src/reason.mjs +0 -350
- package/src/schemas.mjs +0 -1063
- package/src/security/error-sanitizer.mjs +0 -257
- package/src/security/path-validator.mjs +0 -194
- package/src/security/sandbox-restrictions.mjs +0 -331
- package/src/security-validator.mjs +0 -389
- package/src/store-cache.mjs +0 -137
- package/src/telemetry.mjs +0 -167
- package/src/utils/adaptive-monitor.mjs +0 -746
- package/src/utils/circuit-breaker.mjs +0 -513
- package/src/utils/edge-case-handler.mjs +0 -503
- package/src/utils/memory-manager.mjs +0 -498
- package/src/utils/ring-buffer.mjs +0 -282
- package/src/validate.mjs +0 -319
- package/src/validators/index.mjs +0 -338
|
@@ -1,746 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Adaptive monitoring with health-based sampling
|
|
3
|
-
* @module knowledge-engine/utils/adaptive-monitor
|
|
4
|
-
*
|
|
5
|
-
* @description
|
|
6
|
-
* TRIZ #21 - Skipping pattern: Implements adaptive monitoring that adjusts its
|
|
7
|
-
* sampling frequency based on system health. When the system is healthy, monitoring
|
|
8
|
-
* intervals increase (skip unnecessary checks). When issues are detected, intervals
|
|
9
|
-
* decrease rapidly for faster response.
|
|
10
|
-
*
|
|
11
|
-
* Key features:
|
|
12
|
-
* - Exponential backoff when system is healthy (reduce overhead)
|
|
13
|
-
* - Rapid interval decrease when issues detected
|
|
14
|
-
* - Health history tracking for trend analysis
|
|
15
|
-
* - Configurable thresholds and bounds
|
|
16
|
-
* - Event-based notifications
|
|
17
|
-
* - Multiple monitor orchestration
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```javascript
|
|
21
|
-
* import { AdaptiveMonitor } from 'unrdf/knowledge-engine/utils/adaptive-monitor';
|
|
22
|
-
*
|
|
23
|
-
* const monitor = new AdaptiveMonitor({
|
|
24
|
-
* baseInterval: 60000, // Start at 1 minute
|
|
25
|
-
* minInterval: 1000, // Min 1 second when unhealthy
|
|
26
|
-
* maxInterval: 300000 // Max 5 minutes when stable
|
|
27
|
-
* });
|
|
28
|
-
*
|
|
29
|
-
* monitor.on('health', ({ healthy, details }) => {
|
|
30
|
-
* if (!healthy) console.warn('System unhealthy:', details);
|
|
31
|
-
* });
|
|
32
|
-
*
|
|
33
|
-
* monitor.start(async () => {
|
|
34
|
-
* const memUsage = process.memoryUsage();
|
|
35
|
-
* return memUsage.heapUsed / memUsage.heapTotal < 0.8;
|
|
36
|
-
* });
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
import { EventEmitter } from 'node:events';
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Default configuration for AdaptiveMonitor
|
|
44
|
-
* @constant {Object}
|
|
45
|
-
*/
|
|
46
|
-
export const DEFAULT_CONFIG = {
|
|
47
|
-
/** Base monitoring interval in ms */
|
|
48
|
-
baseInterval: 60000,
|
|
49
|
-
/** Minimum interval (fastest polling) */
|
|
50
|
-
minInterval: 1000,
|
|
51
|
-
/** Maximum interval (slowest polling) */
|
|
52
|
-
maxInterval: 300000,
|
|
53
|
-
/** Factor to increase interval when healthy */
|
|
54
|
-
healthyBackoffFactor: 1.5,
|
|
55
|
-
/** Factor to decrease interval when unhealthy */
|
|
56
|
-
unhealthyRushFactor: 0.5,
|
|
57
|
-
/** Number of consecutive healthy checks to consider stable */
|
|
58
|
-
stableThreshold: 3,
|
|
59
|
-
/** Number of consecutive unhealthy checks to consider critical */
|
|
60
|
-
criticalThreshold: 3,
|
|
61
|
-
/** Maximum health history entries */
|
|
62
|
-
maxHistorySize: 100,
|
|
63
|
-
/** Enable verbose logging */
|
|
64
|
-
verbose: false,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Health status enumeration
|
|
69
|
-
* @constant {Object.<string, string>}
|
|
70
|
-
*/
|
|
71
|
-
export const HealthStatus = {
|
|
72
|
-
/** System is healthy */
|
|
73
|
-
HEALTHY: 'healthy',
|
|
74
|
-
/** System is unhealthy */
|
|
75
|
-
UNHEALTHY: 'unhealthy',
|
|
76
|
-
/** System health is unknown */
|
|
77
|
-
UNKNOWN: 'unknown',
|
|
78
|
-
/** System is in stable state (multiple healthy checks) */
|
|
79
|
-
STABLE: 'stable',
|
|
80
|
-
/** System is in critical state (multiple unhealthy checks) */
|
|
81
|
-
CRITICAL: 'critical',
|
|
82
|
-
/** System is recovering */
|
|
83
|
-
RECOVERING: 'recovering',
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Adaptive monitor events
|
|
88
|
-
* @constant {Object.<string, string>}
|
|
89
|
-
*/
|
|
90
|
-
export const MonitorEvents = {
|
|
91
|
-
/** Emitted on each health check */
|
|
92
|
-
HEALTH: 'health',
|
|
93
|
-
/** Emitted when status changes */
|
|
94
|
-
STATUS_CHANGE: 'statusChange',
|
|
95
|
-
/** Emitted when interval adjusts */
|
|
96
|
-
INTERVAL_CHANGE: 'intervalChange',
|
|
97
|
-
/** Emitted on monitor start */
|
|
98
|
-
START: 'start',
|
|
99
|
-
/** Emitted on monitor stop */
|
|
100
|
-
STOP: 'stop',
|
|
101
|
-
/** Emitted on check error */
|
|
102
|
-
ERROR: 'error',
|
|
103
|
-
/** Emitted when entering critical state */
|
|
104
|
-
CRITICAL: 'critical',
|
|
105
|
-
/** Emitted when entering stable state */
|
|
106
|
-
STABLE: 'stable',
|
|
107
|
-
/** Emitted when recovery begins */
|
|
108
|
-
RECOVERY: 'recovery',
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Health check result type
|
|
113
|
-
* @typedef {Object} HealthCheckResult
|
|
114
|
-
* @property {boolean} healthy - Whether the check passed
|
|
115
|
-
* @property {number} timestamp - Unix timestamp of check
|
|
116
|
-
* @property {number} duration - Check duration in ms
|
|
117
|
-
* @property {Object} [details] - Additional check details
|
|
118
|
-
* @property {Error} [error] - Error if check failed
|
|
119
|
-
*/
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Adaptive Monitor class with health-based sampling
|
|
123
|
-
* @extends EventEmitter
|
|
124
|
-
*/
|
|
125
|
-
export class AdaptiveMonitor extends EventEmitter {
|
|
126
|
-
/**
|
|
127
|
-
* Create an adaptive monitor
|
|
128
|
-
* @param {Object} [config={}] - Configuration options
|
|
129
|
-
* @param {number} [config.baseInterval=60000] - Base monitoring interval
|
|
130
|
-
* @param {number} [config.minInterval=1000] - Minimum interval
|
|
131
|
-
* @param {number} [config.maxInterval=300000] - Maximum interval
|
|
132
|
-
* @param {number} [config.healthyBackoffFactor=1.5] - Backoff multiplier
|
|
133
|
-
* @param {number} [config.unhealthyRushFactor=0.5] - Rush multiplier
|
|
134
|
-
* @param {number} [config.stableThreshold=3] - Checks for stable status
|
|
135
|
-
* @param {number} [config.criticalThreshold=3] - Checks for critical status
|
|
136
|
-
* @param {number} [config.maxHistorySize=100] - Max history entries
|
|
137
|
-
* @param {boolean} [config.verbose=false] - Enable verbose logging
|
|
138
|
-
*/
|
|
139
|
-
constructor(config = {}) {
|
|
140
|
-
super();
|
|
141
|
-
|
|
142
|
-
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
143
|
-
|
|
144
|
-
// Current state
|
|
145
|
-
this.currentInterval = this.config.baseInterval;
|
|
146
|
-
this.running = false;
|
|
147
|
-
this.paused = false;
|
|
148
|
-
this.status = HealthStatus.UNKNOWN;
|
|
149
|
-
|
|
150
|
-
// Health tracking
|
|
151
|
-
this.healthHistory = [];
|
|
152
|
-
this.consecutiveHealthy = 0;
|
|
153
|
-
this.consecutiveUnhealthy = 0;
|
|
154
|
-
this.totalChecks = 0;
|
|
155
|
-
this.totalHealthy = 0;
|
|
156
|
-
this.totalUnhealthy = 0;
|
|
157
|
-
|
|
158
|
-
// Timing
|
|
159
|
-
this.startTime = null;
|
|
160
|
-
this.lastCheckTime = null;
|
|
161
|
-
this.nextCheckTime = null;
|
|
162
|
-
|
|
163
|
-
// Internal
|
|
164
|
-
this._timeoutId = null;
|
|
165
|
-
this._checkFn = null;
|
|
166
|
-
|
|
167
|
-
// Bind methods for stable references
|
|
168
|
-
this._executeCheck = this._executeCheck.bind(this);
|
|
169
|
-
this._scheduleNext = this._scheduleNext.bind(this);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Start monitoring with the given health check function
|
|
174
|
-
* @param {Function} checkFn - Async function returning boolean or {healthy, details}
|
|
175
|
-
* @returns {AdaptiveMonitor} this for chaining
|
|
176
|
-
*/
|
|
177
|
-
start(checkFn) {
|
|
178
|
-
if (this.running) {
|
|
179
|
-
this._log('Monitor already running');
|
|
180
|
-
return this;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (typeof checkFn !== 'function') {
|
|
184
|
-
throw new TypeError('checkFn must be a function');
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
this._checkFn = checkFn;
|
|
188
|
-
this.running = true;
|
|
189
|
-
this.paused = false;
|
|
190
|
-
this.startTime = Date.now();
|
|
191
|
-
this.currentInterval = this.config.baseInterval;
|
|
192
|
-
|
|
193
|
-
this._log(`Starting monitor with ${this.currentInterval}ms interval`);
|
|
194
|
-
this.emit(MonitorEvents.START, {
|
|
195
|
-
interval: this.currentInterval,
|
|
196
|
-
timestamp: this.startTime,
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
// Execute first check immediately
|
|
200
|
-
this._executeCheck();
|
|
201
|
-
|
|
202
|
-
return this;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Stop monitoring
|
|
207
|
-
* @returns {AdaptiveMonitor} this for chaining
|
|
208
|
-
*/
|
|
209
|
-
stop() {
|
|
210
|
-
if (!this.running) {
|
|
211
|
-
return this;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
this.running = false;
|
|
215
|
-
|
|
216
|
-
if (this._timeoutId) {
|
|
217
|
-
clearTimeout(this._timeoutId);
|
|
218
|
-
this._timeoutId = null;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
const duration = Date.now() - this.startTime;
|
|
222
|
-
this._log(`Stopping monitor after ${duration}ms`);
|
|
223
|
-
|
|
224
|
-
this.emit(MonitorEvents.STOP, {
|
|
225
|
-
duration,
|
|
226
|
-
totalChecks: this.totalChecks,
|
|
227
|
-
timestamp: Date.now(),
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
return this;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Pause monitoring (keeps state, stops checks)
|
|
235
|
-
* @returns {AdaptiveMonitor} this for chaining
|
|
236
|
-
*/
|
|
237
|
-
pause() {
|
|
238
|
-
if (!this.running || this.paused) {
|
|
239
|
-
return this;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
this.paused = true;
|
|
243
|
-
|
|
244
|
-
if (this._timeoutId) {
|
|
245
|
-
clearTimeout(this._timeoutId);
|
|
246
|
-
this._timeoutId = null;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
this._log('Monitor paused');
|
|
250
|
-
return this;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* Resume monitoring after pause
|
|
255
|
-
* @returns {AdaptiveMonitor} this for chaining
|
|
256
|
-
*/
|
|
257
|
-
resume() {
|
|
258
|
-
if (!this.running || !this.paused) {
|
|
259
|
-
return this;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
this.paused = false;
|
|
263
|
-
this._log('Monitor resumed');
|
|
264
|
-
this._executeCheck();
|
|
265
|
-
|
|
266
|
-
return this;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Force an immediate health check
|
|
271
|
-
* @returns {Promise<HealthCheckResult>} Check result
|
|
272
|
-
*/
|
|
273
|
-
async checkNow() {
|
|
274
|
-
return this._executeCheck(true);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Reset the monitor to initial state
|
|
279
|
-
* @returns {AdaptiveMonitor} this for chaining
|
|
280
|
-
*/
|
|
281
|
-
reset() {
|
|
282
|
-
this.stop();
|
|
283
|
-
|
|
284
|
-
this.currentInterval = this.config.baseInterval;
|
|
285
|
-
this.status = HealthStatus.UNKNOWN;
|
|
286
|
-
this.healthHistory = [];
|
|
287
|
-
this.consecutiveHealthy = 0;
|
|
288
|
-
this.consecutiveUnhealthy = 0;
|
|
289
|
-
this.totalChecks = 0;
|
|
290
|
-
this.totalHealthy = 0;
|
|
291
|
-
this.totalUnhealthy = 0;
|
|
292
|
-
this.startTime = null;
|
|
293
|
-
this.lastCheckTime = null;
|
|
294
|
-
this.nextCheckTime = null;
|
|
295
|
-
|
|
296
|
-
this._log('Monitor reset');
|
|
297
|
-
return this;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Get current monitoring statistics
|
|
302
|
-
* @returns {Object} Monitor statistics
|
|
303
|
-
*/
|
|
304
|
-
getStats() {
|
|
305
|
-
const now = Date.now();
|
|
306
|
-
const uptime = this.startTime ? now - this.startTime : 0;
|
|
307
|
-
|
|
308
|
-
return {
|
|
309
|
-
running: this.running,
|
|
310
|
-
paused: this.paused,
|
|
311
|
-
status: this.status,
|
|
312
|
-
currentInterval: this.currentInterval,
|
|
313
|
-
uptime,
|
|
314
|
-
totalChecks: this.totalChecks,
|
|
315
|
-
totalHealthy: this.totalHealthy,
|
|
316
|
-
totalUnhealthy: this.totalUnhealthy,
|
|
317
|
-
healthRate:
|
|
318
|
-
this.totalChecks > 0
|
|
319
|
-
? ((this.totalHealthy / this.totalChecks) * 100).toFixed(2) + '%'
|
|
320
|
-
: 'N/A',
|
|
321
|
-
consecutiveHealthy: this.consecutiveHealthy,
|
|
322
|
-
consecutiveUnhealthy: this.consecutiveUnhealthy,
|
|
323
|
-
lastCheckTime: this.lastCheckTime,
|
|
324
|
-
nextCheckTime: this.nextCheckTime,
|
|
325
|
-
historySize: this.healthHistory.length,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Get health history
|
|
331
|
-
* @param {number} [limit=10] - Maximum entries to return
|
|
332
|
-
* @returns {HealthCheckResult[]} Recent health checks
|
|
333
|
-
*/
|
|
334
|
-
getHistory(limit = 10) {
|
|
335
|
-
return this.healthHistory.slice(-limit);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Get health trend (positive = improving, negative = degrading)
|
|
340
|
-
* @param {number} [window=10] - Number of recent checks to analyze
|
|
341
|
-
* @returns {number} Trend value between -1 and 1
|
|
342
|
-
*/
|
|
343
|
-
getHealthTrend(window = 10) {
|
|
344
|
-
const recent = this.healthHistory.slice(-window);
|
|
345
|
-
|
|
346
|
-
if (recent.length < 2) {
|
|
347
|
-
return 0;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const firstHalf = recent.slice(0, Math.floor(recent.length / 2));
|
|
351
|
-
const secondHalf = recent.slice(Math.floor(recent.length / 2));
|
|
352
|
-
|
|
353
|
-
const firstHealthy = firstHalf.filter(h => h.healthy).length / firstHalf.length;
|
|
354
|
-
const secondHealthy = secondHalf.filter(h => h.healthy).length / secondHalf.length;
|
|
355
|
-
|
|
356
|
-
return secondHealthy - firstHealthy;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Execute a health check
|
|
361
|
-
* @private
|
|
362
|
-
* @param {boolean} [immediate=false] - If true, don't schedule next
|
|
363
|
-
* @returns {Promise<HealthCheckResult>} Check result
|
|
364
|
-
*/
|
|
365
|
-
async _executeCheck(immediate = false) {
|
|
366
|
-
if (!this.running || (this.paused && !immediate)) {
|
|
367
|
-
return null;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const startTime = Date.now();
|
|
371
|
-
let result;
|
|
372
|
-
|
|
373
|
-
try {
|
|
374
|
-
const checkResult = await this._checkFn();
|
|
375
|
-
|
|
376
|
-
// Normalize result
|
|
377
|
-
let healthy, details;
|
|
378
|
-
if (typeof checkResult === 'boolean') {
|
|
379
|
-
healthy = checkResult;
|
|
380
|
-
details = {};
|
|
381
|
-
} else if (checkResult && typeof checkResult === 'object') {
|
|
382
|
-
healthy = checkResult.healthy;
|
|
383
|
-
details = checkResult.details || checkResult;
|
|
384
|
-
} else {
|
|
385
|
-
healthy = !!checkResult;
|
|
386
|
-
details = {};
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
result = {
|
|
390
|
-
healthy,
|
|
391
|
-
timestamp: startTime,
|
|
392
|
-
duration: Date.now() - startTime,
|
|
393
|
-
details,
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
this._recordResult(result);
|
|
397
|
-
} catch (error) {
|
|
398
|
-
result = {
|
|
399
|
-
healthy: false,
|
|
400
|
-
timestamp: startTime,
|
|
401
|
-
duration: Date.now() - startTime,
|
|
402
|
-
error,
|
|
403
|
-
};
|
|
404
|
-
|
|
405
|
-
this._recordResult(result);
|
|
406
|
-
this.emit(MonitorEvents.ERROR, { error, timestamp: startTime });
|
|
407
|
-
this._log(`Check error: ${error.message}`);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
// Schedule next check unless immediate
|
|
411
|
-
if (!immediate && this.running && !this.paused) {
|
|
412
|
-
this._scheduleNext();
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
return result;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* Record a health check result
|
|
420
|
-
* @private
|
|
421
|
-
* @param {HealthCheckResult} result - Check result
|
|
422
|
-
*/
|
|
423
|
-
_recordResult(result) {
|
|
424
|
-
this.lastCheckTime = result.timestamp;
|
|
425
|
-
this.totalChecks++;
|
|
426
|
-
|
|
427
|
-
// Add to history (with size limit)
|
|
428
|
-
this.healthHistory.push(result);
|
|
429
|
-
if (this.healthHistory.length > this.config.maxHistorySize) {
|
|
430
|
-
this.healthHistory.shift();
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Update counters
|
|
434
|
-
const previousStatus = this.status;
|
|
435
|
-
|
|
436
|
-
if (result.healthy) {
|
|
437
|
-
this.totalHealthy++;
|
|
438
|
-
this.consecutiveHealthy++;
|
|
439
|
-
this.consecutiveUnhealthy = 0;
|
|
440
|
-
|
|
441
|
-
// Check for stable state
|
|
442
|
-
if (this.consecutiveHealthy >= this.config.stableThreshold) {
|
|
443
|
-
if (this.status !== HealthStatus.STABLE) {
|
|
444
|
-
this.status = HealthStatus.STABLE;
|
|
445
|
-
this.emit(MonitorEvents.STABLE, { checks: this.consecutiveHealthy });
|
|
446
|
-
}
|
|
447
|
-
} else if (this.status === HealthStatus.CRITICAL || this.status === HealthStatus.UNHEALTHY) {
|
|
448
|
-
this.status = HealthStatus.RECOVERING;
|
|
449
|
-
this.emit(MonitorEvents.RECOVERY, { timestamp: Date.now() });
|
|
450
|
-
} else {
|
|
451
|
-
this.status = HealthStatus.HEALTHY;
|
|
452
|
-
}
|
|
453
|
-
} else {
|
|
454
|
-
this.totalUnhealthy++;
|
|
455
|
-
this.consecutiveUnhealthy++;
|
|
456
|
-
this.consecutiveHealthy = 0;
|
|
457
|
-
|
|
458
|
-
// Check for critical state
|
|
459
|
-
if (this.consecutiveUnhealthy >= this.config.criticalThreshold) {
|
|
460
|
-
if (this.status !== HealthStatus.CRITICAL) {
|
|
461
|
-
this.status = HealthStatus.CRITICAL;
|
|
462
|
-
this.emit(MonitorEvents.CRITICAL, {
|
|
463
|
-
checks: this.consecutiveUnhealthy,
|
|
464
|
-
details: result.details || result.error,
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
} else {
|
|
468
|
-
this.status = HealthStatus.UNHEALTHY;
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// Emit status change if changed
|
|
473
|
-
if (previousStatus !== this.status) {
|
|
474
|
-
this.emit(MonitorEvents.STATUS_CHANGE, {
|
|
475
|
-
previous: previousStatus,
|
|
476
|
-
current: this.status,
|
|
477
|
-
timestamp: Date.now(),
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
// Emit health event
|
|
482
|
-
this.emit(MonitorEvents.HEALTH, result);
|
|
483
|
-
|
|
484
|
-
// Adjust interval
|
|
485
|
-
this._adjustInterval(result.healthy);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* Adjust the monitoring interval based on health
|
|
490
|
-
* @private
|
|
491
|
-
* @param {boolean} isHealthy - Whether the last check was healthy
|
|
492
|
-
*/
|
|
493
|
-
_adjustInterval(isHealthy) {
|
|
494
|
-
const previousInterval = this.currentInterval;
|
|
495
|
-
|
|
496
|
-
if (isHealthy) {
|
|
497
|
-
// Healthy: exponential backoff (increase interval)
|
|
498
|
-
this.currentInterval = Math.min(
|
|
499
|
-
this.currentInterval * this.config.healthyBackoffFactor,
|
|
500
|
-
this.config.maxInterval
|
|
501
|
-
);
|
|
502
|
-
} else {
|
|
503
|
-
// Unhealthy: rush (decrease interval)
|
|
504
|
-
this.currentInterval = Math.max(
|
|
505
|
-
this.currentInterval * this.config.unhealthyRushFactor,
|
|
506
|
-
this.config.minInterval
|
|
507
|
-
);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
// Round to nearest 100ms for cleaner values
|
|
511
|
-
this.currentInterval = Math.round(this.currentInterval / 100) * 100;
|
|
512
|
-
|
|
513
|
-
// Emit if interval changed significantly (>10%)
|
|
514
|
-
if (Math.abs(this.currentInterval - previousInterval) / previousInterval > 0.1) {
|
|
515
|
-
this._log(`Interval adjusted: ${previousInterval}ms -> ${this.currentInterval}ms`);
|
|
516
|
-
this.emit(MonitorEvents.INTERVAL_CHANGE, {
|
|
517
|
-
previous: previousInterval,
|
|
518
|
-
current: this.currentInterval,
|
|
519
|
-
healthy: isHealthy,
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Schedule the next health check
|
|
526
|
-
* @private
|
|
527
|
-
*/
|
|
528
|
-
_scheduleNext() {
|
|
529
|
-
if (this._timeoutId) {
|
|
530
|
-
clearTimeout(this._timeoutId);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
this.nextCheckTime = Date.now() + this.currentInterval;
|
|
534
|
-
this._timeoutId = setTimeout(this._executeCheck, this.currentInterval);
|
|
535
|
-
|
|
536
|
-
// Don't block process exit
|
|
537
|
-
if (this._timeoutId.unref) {
|
|
538
|
-
this._timeoutId.unref();
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* Log a message if verbose mode is enabled
|
|
544
|
-
* @private
|
|
545
|
-
* @param {string} message - Message to log
|
|
546
|
-
*/
|
|
547
|
-
_log(message) {
|
|
548
|
-
if (this.config.verbose) {
|
|
549
|
-
console.log(`[AdaptiveMonitor] ${message}`);
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Create an adaptive monitor with default configuration
|
|
556
|
-
* @param {Object} [config] - Configuration overrides
|
|
557
|
-
* @returns {AdaptiveMonitor} New monitor instance
|
|
558
|
-
*/
|
|
559
|
-
export function createAdaptiveMonitor(config = {}) {
|
|
560
|
-
return new AdaptiveMonitor(config);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* Create a memory usage monitor
|
|
565
|
-
* @param {number} [threshold=0.8] - Memory usage threshold (0-1)
|
|
566
|
-
* @param {Object} [config] - Additional config
|
|
567
|
-
* @returns {AdaptiveMonitor} Configured monitor
|
|
568
|
-
*/
|
|
569
|
-
export function createMemoryMonitor(threshold = 0.8, config = {}) {
|
|
570
|
-
const monitor = new AdaptiveMonitor({
|
|
571
|
-
baseInterval: 30000,
|
|
572
|
-
minInterval: 5000,
|
|
573
|
-
maxInterval: 120000,
|
|
574
|
-
...config,
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
const checkFn = async () => {
|
|
578
|
-
const usage = process.memoryUsage();
|
|
579
|
-
const heapUsage = usage.heapUsed / usage.heapTotal;
|
|
580
|
-
|
|
581
|
-
return {
|
|
582
|
-
healthy: heapUsage < threshold,
|
|
583
|
-
details: {
|
|
584
|
-
heapUsed: usage.heapUsed,
|
|
585
|
-
heapTotal: usage.heapTotal,
|
|
586
|
-
heapUsage: `${(heapUsage * 100).toFixed(1)}%`,
|
|
587
|
-
external: usage.external,
|
|
588
|
-
rss: usage.rss,
|
|
589
|
-
},
|
|
590
|
-
};
|
|
591
|
-
};
|
|
592
|
-
|
|
593
|
-
return { monitor, start: () => monitor.start(checkFn) };
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
/**
|
|
597
|
-
* Create an event loop monitor
|
|
598
|
-
* @param {number} [lagThreshold=100] - Event loop lag threshold in ms
|
|
599
|
-
* @param {Object} [config] - Additional config
|
|
600
|
-
* @returns {AdaptiveMonitor} Configured monitor
|
|
601
|
-
*/
|
|
602
|
-
export function createEventLoopMonitor(lagThreshold = 100, config = {}) {
|
|
603
|
-
const monitor = new AdaptiveMonitor({
|
|
604
|
-
baseInterval: 10000,
|
|
605
|
-
minInterval: 1000,
|
|
606
|
-
maxInterval: 60000,
|
|
607
|
-
...config,
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
const checkFn = async () => {
|
|
611
|
-
const start = Date.now();
|
|
612
|
-
await new Promise(resolve => setImmediate(resolve));
|
|
613
|
-
const lag = Date.now() - start;
|
|
614
|
-
|
|
615
|
-
return {
|
|
616
|
-
healthy: lag < lagThreshold,
|
|
617
|
-
details: {
|
|
618
|
-
lag,
|
|
619
|
-
lagThreshold,
|
|
620
|
-
},
|
|
621
|
-
};
|
|
622
|
-
};
|
|
623
|
-
|
|
624
|
-
return { monitor, start: () => monitor.start(checkFn) };
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
/**
|
|
628
|
-
* Monitor orchestrator for managing multiple monitors
|
|
629
|
-
*/
|
|
630
|
-
export class MonitorOrchestrator extends EventEmitter {
|
|
631
|
-
/**
|
|
632
|
-
* Create a monitor orchestrator
|
|
633
|
-
*/
|
|
634
|
-
constructor() {
|
|
635
|
-
super();
|
|
636
|
-
this.monitors = new Map();
|
|
637
|
-
this.aggregatedStatus = HealthStatus.UNKNOWN;
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
/**
|
|
641
|
-
* Add a monitor
|
|
642
|
-
* @param {string} name - Monitor name
|
|
643
|
-
* @param {AdaptiveMonitor} monitor - Monitor instance
|
|
644
|
-
* @returns {MonitorOrchestrator} this for chaining
|
|
645
|
-
*/
|
|
646
|
-
add(name, monitor) {
|
|
647
|
-
this.monitors.set(name, monitor);
|
|
648
|
-
|
|
649
|
-
// Forward events
|
|
650
|
-
monitor.on(MonitorEvents.HEALTH, result => {
|
|
651
|
-
this.emit(`${name}:health`, result);
|
|
652
|
-
this._updateAggregatedStatus();
|
|
653
|
-
});
|
|
654
|
-
|
|
655
|
-
monitor.on(MonitorEvents.CRITICAL, data => {
|
|
656
|
-
this.emit(`${name}:critical`, data);
|
|
657
|
-
this.emit('critical', { monitor: name, ...data });
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
return this;
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
/**
|
|
664
|
-
* Remove a monitor
|
|
665
|
-
* @param {string} name - Monitor name
|
|
666
|
-
* @returns {boolean} True if removed
|
|
667
|
-
*/
|
|
668
|
-
remove(name) {
|
|
669
|
-
const monitor = this.monitors.get(name);
|
|
670
|
-
if (monitor) {
|
|
671
|
-
monitor.stop();
|
|
672
|
-
this.monitors.delete(name);
|
|
673
|
-
return true;
|
|
674
|
-
}
|
|
675
|
-
return false;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
/**
|
|
679
|
-
* Start all monitors
|
|
680
|
-
*/
|
|
681
|
-
startAll() {
|
|
682
|
-
for (const monitor of this.monitors.values()) {
|
|
683
|
-
if (!monitor.running) {
|
|
684
|
-
// Monitor should have been started with checkFn via .start()
|
|
685
|
-
// This just ensures running state
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
/**
|
|
691
|
-
* Stop all monitors
|
|
692
|
-
*/
|
|
693
|
-
stopAll() {
|
|
694
|
-
for (const monitor of this.monitors.values()) {
|
|
695
|
-
monitor.stop();
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
/**
|
|
700
|
-
* Get aggregated statistics
|
|
701
|
-
* @returns {Object} Aggregated stats
|
|
702
|
-
*/
|
|
703
|
-
getAggregatedStats() {
|
|
704
|
-
const stats = {};
|
|
705
|
-
for (const [name, monitor] of this.monitors.entries()) {
|
|
706
|
-
stats[name] = monitor.getStats();
|
|
707
|
-
}
|
|
708
|
-
return {
|
|
709
|
-
monitors: stats,
|
|
710
|
-
aggregatedStatus: this.aggregatedStatus,
|
|
711
|
-
monitorCount: this.monitors.size,
|
|
712
|
-
};
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
/**
|
|
716
|
-
* Update aggregated status based on all monitors
|
|
717
|
-
* @private
|
|
718
|
-
*/
|
|
719
|
-
_updateAggregatedStatus() {
|
|
720
|
-
const statuses = Array.from(this.monitors.values()).map(m => m.status);
|
|
721
|
-
|
|
722
|
-
if (statuses.includes(HealthStatus.CRITICAL)) {
|
|
723
|
-
this.aggregatedStatus = HealthStatus.CRITICAL;
|
|
724
|
-
} else if (statuses.includes(HealthStatus.UNHEALTHY)) {
|
|
725
|
-
this.aggregatedStatus = HealthStatus.UNHEALTHY;
|
|
726
|
-
} else if (statuses.every(s => s === HealthStatus.STABLE)) {
|
|
727
|
-
this.aggregatedStatus = HealthStatus.STABLE;
|
|
728
|
-
} else if (statuses.includes(HealthStatus.RECOVERING)) {
|
|
729
|
-
this.aggregatedStatus = HealthStatus.RECOVERING;
|
|
730
|
-
} else if (statuses.every(s => s === HealthStatus.HEALTHY || s === HealthStatus.STABLE)) {
|
|
731
|
-
this.aggregatedStatus = HealthStatus.HEALTHY;
|
|
732
|
-
} else {
|
|
733
|
-
this.aggregatedStatus = HealthStatus.UNKNOWN;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
/**
|
|
739
|
-
* Create a monitor orchestrator
|
|
740
|
-
* @returns {MonitorOrchestrator} New orchestrator
|
|
741
|
-
*/
|
|
742
|
-
export function createMonitorOrchestrator() {
|
|
743
|
-
return new MonitorOrchestrator();
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
export default AdaptiveMonitor;
|