observability-toolkit 1.8.2 → 1.8.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/README.md +60 -0
- package/dist/backends/index.d.ts +43 -0
- package/dist/backends/index.d.ts.map +1 -1
- package/dist/backends/index.js +41 -0
- package/dist/backends/index.js.map +1 -1
- package/dist/backends/index.test.d.ts +5 -0
- package/dist/backends/index.test.d.ts.map +1 -0
- package/dist/backends/index.test.js +156 -0
- package/dist/backends/index.test.js.map +1 -0
- package/dist/backends/local-jsonl-boolean-search.test.js +15 -12
- package/dist/backends/local-jsonl-boolean-search.test.js.map +1 -1
- package/dist/backends/local-jsonl-cache.test.d.ts +2 -0
- package/dist/backends/local-jsonl-cache.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-cache.test.js +295 -0
- package/dist/backends/local-jsonl-cache.test.js.map +1 -0
- package/dist/backends/local-jsonl-circuit-breaker.test.d.ts +2 -0
- package/dist/backends/local-jsonl-circuit-breaker.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-circuit-breaker.test.js +180 -0
- package/dist/backends/local-jsonl-circuit-breaker.test.js.map +1 -0
- package/dist/backends/local-jsonl-export.test.d.ts +2 -0
- package/dist/backends/local-jsonl-export.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-export.test.js +704 -0
- package/dist/backends/local-jsonl-export.test.js.map +1 -0
- package/dist/backends/local-jsonl-index.test.d.ts +2 -0
- package/dist/backends/local-jsonl-index.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-index.test.js +554 -0
- package/dist/backends/local-jsonl-index.test.js.map +1 -0
- package/dist/backends/local-jsonl-logs.test.d.ts +2 -0
- package/dist/backends/local-jsonl-logs.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-logs.test.js +612 -0
- package/dist/backends/local-jsonl-logs.test.js.map +1 -0
- package/dist/backends/local-jsonl-metrics.test.d.ts +2 -0
- package/dist/backends/local-jsonl-metrics.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-metrics.test.js +876 -0
- package/dist/backends/local-jsonl-metrics.test.js.map +1 -0
- package/dist/backends/local-jsonl-traces.test.d.ts +2 -0
- package/dist/backends/local-jsonl-traces.test.d.ts.map +1 -0
- package/dist/backends/local-jsonl-traces.test.js +1729 -0
- package/dist/backends/local-jsonl-traces.test.js.map +1 -0
- package/dist/backends/local-jsonl.d.ts +9 -0
- package/dist/backends/local-jsonl.d.ts.map +1 -1
- package/dist/backends/local-jsonl.js +348 -227
- package/dist/backends/local-jsonl.js.map +1 -1
- package/dist/backends/local-jsonl.test.js +290 -21
- package/dist/backends/local-jsonl.test.js.map +1 -1
- package/dist/backends/signoz-api-circuit-breaker.test.d.ts +6 -0
- package/dist/backends/signoz-api-circuit-breaker.test.d.ts.map +1 -0
- package/dist/backends/signoz-api-circuit-breaker.test.js +548 -0
- package/dist/backends/signoz-api-circuit-breaker.test.js.map +1 -0
- package/dist/backends/signoz-api-rate-limiter.test.d.ts +6 -0
- package/dist/backends/signoz-api-rate-limiter.test.d.ts.map +1 -0
- package/dist/backends/signoz-api-rate-limiter.test.js +389 -0
- package/dist/backends/signoz-api-rate-limiter.test.js.map +1 -0
- package/dist/backends/signoz-api-ssrf.test.d.ts +6 -0
- package/dist/backends/signoz-api-ssrf.test.d.ts.map +1 -0
- package/dist/backends/signoz-api-ssrf.test.js +216 -0
- package/dist/backends/signoz-api-ssrf.test.js.map +1 -0
- package/dist/backends/signoz-api-test-helpers.d.ts +80 -0
- package/dist/backends/signoz-api-test-helpers.d.ts.map +1 -0
- package/dist/backends/signoz-api-test-helpers.js +79 -0
- package/dist/backends/signoz-api-test-helpers.js.map +1 -0
- package/dist/backends/signoz-api.d.ts +16 -0
- package/dist/backends/signoz-api.d.ts.map +1 -1
- package/dist/backends/signoz-api.js +71 -9
- package/dist/backends/signoz-api.js.map +1 -1
- package/dist/backends/signoz-api.test.d.ts +9 -0
- package/dist/backends/signoz-api.test.d.ts.map +1 -1
- package/dist/backends/signoz-api.test.js +14 -1027
- package/dist/backends/signoz-api.test.js.map +1 -1
- package/dist/lib/cache.d.ts +47 -1
- package/dist/lib/cache.d.ts.map +1 -1
- package/dist/lib/cache.js +40 -3
- package/dist/lib/cache.js.map +1 -1
- package/dist/lib/circuit-breaker.d.ts +83 -0
- package/dist/lib/circuit-breaker.d.ts.map +1 -0
- package/dist/lib/circuit-breaker.js +125 -0
- package/dist/lib/circuit-breaker.js.map +1 -0
- package/dist/lib/circuit-breaker.test.d.ts +2 -0
- package/dist/lib/circuit-breaker.test.d.ts.map +1 -0
- package/dist/lib/circuit-breaker.test.js +263 -0
- package/dist/lib/circuit-breaker.test.js.map +1 -0
- package/dist/lib/constants-symlink.test.d.ts +12 -0
- package/dist/lib/constants-symlink.test.d.ts.map +1 -0
- package/dist/lib/constants-symlink.test.js +357 -0
- package/dist/lib/constants-symlink.test.js.map +1 -0
- package/dist/lib/constants.d.ts +43 -0
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +154 -24
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/constants.test.js +156 -7
- package/dist/lib/constants.test.js.map +1 -1
- package/dist/lib/edge-cases.test.d.ts +11 -0
- package/dist/lib/edge-cases.test.d.ts.map +1 -0
- package/dist/lib/edge-cases.test.js +634 -0
- package/dist/lib/edge-cases.test.js.map +1 -0
- package/dist/lib/error-sanitizer.d.ts.map +1 -1
- package/dist/lib/error-sanitizer.js +62 -26
- package/dist/lib/error-sanitizer.js.map +1 -1
- package/dist/lib/error-sanitizer.test.js +186 -0
- package/dist/lib/error-sanitizer.test.js.map +1 -1
- package/dist/lib/error-types.d.ts +54 -0
- package/dist/lib/error-types.d.ts.map +1 -0
- package/dist/lib/error-types.js +154 -0
- package/dist/lib/error-types.js.map +1 -0
- package/dist/lib/error-types.test.d.ts +2 -0
- package/dist/lib/error-types.test.d.ts.map +1 -0
- package/dist/lib/error-types.test.js +196 -0
- package/dist/lib/error-types.test.js.map +1 -0
- package/dist/lib/file-utils.test.js +3 -3
- package/dist/lib/file-utils.test.js.map +1 -1
- package/dist/lib/indexer.test.js +157 -24
- package/dist/lib/indexer.test.js.map +1 -1
- package/dist/lib/input-validator.d.ts +17 -0
- package/dist/lib/input-validator.d.ts.map +1 -1
- package/dist/lib/input-validator.fuzz.test.d.ts +12 -0
- package/dist/lib/input-validator.fuzz.test.d.ts.map +1 -0
- package/dist/lib/input-validator.fuzz.test.js +290 -0
- package/dist/lib/input-validator.fuzz.test.js.map +1 -0
- package/dist/lib/input-validator.js +62 -3
- package/dist/lib/input-validator.js.map +1 -1
- package/dist/lib/input-validator.test.js +129 -1
- package/dist/lib/input-validator.test.js.map +1 -1
- package/dist/lib/logger.d.ts +46 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +81 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/logger.test.d.ts +2 -0
- package/dist/lib/logger.test.d.ts.map +1 -0
- package/dist/lib/logger.test.js +122 -0
- package/dist/lib/logger.test.js.map +1 -0
- package/dist/lib/query-sanitizer.d.ts +51 -3
- package/dist/lib/query-sanitizer.d.ts.map +1 -1
- package/dist/lib/query-sanitizer.js +105 -31
- package/dist/lib/query-sanitizer.js.map +1 -1
- package/dist/lib/query-sanitizer.test.js +102 -1
- package/dist/lib/query-sanitizer.test.js.map +1 -1
- package/dist/lib/server-utils.d.ts +88 -0
- package/dist/lib/server-utils.d.ts.map +1 -0
- package/dist/lib/server-utils.js +173 -0
- package/dist/lib/server-utils.js.map +1 -0
- package/dist/lib/shared-schemas.d.ts +81 -0
- package/dist/lib/shared-schemas.d.ts.map +1 -0
- package/dist/lib/shared-schemas.js +80 -0
- package/dist/lib/shared-schemas.js.map +1 -0
- package/dist/lib/shared-schemas.test.d.ts +5 -0
- package/dist/lib/shared-schemas.test.d.ts.map +1 -0
- package/dist/lib/shared-schemas.test.js +106 -0
- package/dist/lib/shared-schemas.test.js.map +1 -0
- package/dist/lib/toon-encoder.d.ts +26 -0
- package/dist/lib/toon-encoder.d.ts.map +1 -0
- package/dist/lib/toon-encoder.js +61 -0
- package/dist/lib/toon-encoder.js.map +1 -0
- package/dist/lib/toon-encoder.test.d.ts +5 -0
- package/dist/lib/toon-encoder.test.d.ts.map +1 -0
- package/dist/lib/toon-encoder.test.js +85 -0
- package/dist/lib/toon-encoder.test.js.map +1 -0
- package/dist/server.d.ts +1 -49
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +154 -162
- package/dist/server.js.map +1 -1
- package/dist/server.test.js +198 -7
- package/dist/server.test.js.map +1 -1
- package/dist/test-helpers/env-utils.d.ts +87 -0
- package/dist/test-helpers/env-utils.d.ts.map +1 -0
- package/dist/test-helpers/env-utils.js +132 -0
- package/dist/test-helpers/env-utils.js.map +1 -0
- package/dist/test-helpers/file-utils.d.ts +67 -0
- package/dist/test-helpers/file-utils.d.ts.map +1 -1
- package/dist/test-helpers/file-utils.js +165 -2
- package/dist/test-helpers/file-utils.js.map +1 -1
- package/dist/test-helpers/fuzz-generators.d.ts +58 -0
- package/dist/test-helpers/fuzz-generators.d.ts.map +1 -0
- package/dist/test-helpers/fuzz-generators.js +216 -0
- package/dist/test-helpers/fuzz-generators.js.map +1 -0
- package/dist/test-helpers/index.d.ts +11 -0
- package/dist/test-helpers/index.d.ts.map +1 -0
- package/dist/test-helpers/index.js +30 -0
- package/dist/test-helpers/index.js.map +1 -0
- package/dist/test-helpers/memfs-utils.d.ts +181 -0
- package/dist/test-helpers/memfs-utils.d.ts.map +1 -0
- package/dist/test-helpers/memfs-utils.js +292 -0
- package/dist/test-helpers/memfs-utils.js.map +1 -0
- package/dist/test-helpers/memfs-utils.test.d.ts +5 -0
- package/dist/test-helpers/memfs-utils.test.d.ts.map +1 -0
- package/dist/test-helpers/memfs-utils.test.js +338 -0
- package/dist/test-helpers/memfs-utils.test.js.map +1 -0
- package/dist/test-helpers/mock-backends.d.ts +113 -2
- package/dist/test-helpers/mock-backends.d.ts.map +1 -1
- package/dist/test-helpers/mock-backends.js +199 -3
- package/dist/test-helpers/mock-backends.js.map +1 -1
- package/dist/test-helpers/mock-backends.test.d.ts +5 -0
- package/dist/test-helpers/mock-backends.test.d.ts.map +1 -0
- package/dist/test-helpers/mock-backends.test.js +368 -0
- package/dist/test-helpers/mock-backends.test.js.map +1 -0
- package/dist/test-helpers/race-condition-helpers.d.ts +85 -0
- package/dist/test-helpers/race-condition-helpers.d.ts.map +1 -0
- package/dist/test-helpers/race-condition-helpers.js +279 -0
- package/dist/test-helpers/race-condition-helpers.js.map +1 -0
- package/dist/test-helpers/schema-validators.d.ts +32 -0
- package/dist/test-helpers/schema-validators.d.ts.map +1 -0
- package/dist/test-helpers/schema-validators.js +125 -0
- package/dist/test-helpers/schema-validators.js.map +1 -0
- package/dist/test-helpers/test-data-builders.d.ts +260 -0
- package/dist/test-helpers/test-data-builders.d.ts.map +1 -0
- package/dist/test-helpers/test-data-builders.js +337 -0
- package/dist/test-helpers/test-data-builders.js.map +1 -0
- package/dist/test-helpers/test-data-builders.test.d.ts +2 -0
- package/dist/test-helpers/test-data-builders.test.d.ts.map +1 -0
- package/dist/test-helpers/test-data-builders.test.js +306 -0
- package/dist/test-helpers/test-data-builders.test.js.map +1 -0
- package/dist/test-helpers/tool-validators.d.ts +28 -0
- package/dist/test-helpers/tool-validators.d.ts.map +1 -0
- package/dist/test-helpers/tool-validators.js +71 -0
- package/dist/test-helpers/tool-validators.js.map +1 -0
- package/dist/tools/context-stats.d.ts +1 -0
- package/dist/tools/context-stats.d.ts.map +1 -1
- package/dist/tools/context-stats.js +9 -5
- package/dist/tools/context-stats.js.map +1 -1
- package/dist/tools/context-stats.test.js +24 -10
- package/dist/tools/context-stats.test.js.map +1 -1
- package/dist/tools/get-trace-url.js +2 -2
- package/dist/tools/get-trace-url.js.map +1 -1
- package/dist/tools/health-check.js +2 -2
- package/dist/tools/health-check.js.map +1 -1
- package/dist/tools/query-evaluations.d.ts +21 -18
- package/dist/tools/query-evaluations.d.ts.map +1 -1
- package/dist/tools/query-evaluations.js +33 -19
- package/dist/tools/query-evaluations.js.map +1 -1
- package/dist/tools/query-evaluations.test.js +60 -63
- package/dist/tools/query-evaluations.test.js.map +1 -1
- package/dist/tools/query-llm-events.d.ts +19 -15
- package/dist/tools/query-llm-events.d.ts.map +1 -1
- package/dist/tools/query-llm-events.js +31 -15
- package/dist/tools/query-llm-events.js.map +1 -1
- package/dist/tools/query-llm-events.test.js +277 -12
- package/dist/tools/query-llm-events.test.js.map +1 -1
- package/dist/tools/query-logs.d.ts +22 -22
- package/dist/tools/query-logs.d.ts.map +1 -1
- package/dist/tools/query-logs.js +9 -9
- package/dist/tools/query-logs.js.map +1 -1
- package/dist/tools/query-logs.test.js +19 -72
- package/dist/tools/query-logs.test.js.map +1 -1
- package/dist/tools/query-metrics.d.ts +14 -14
- package/dist/tools/query-metrics.d.ts.map +1 -1
- package/dist/tools/query-metrics.js +9 -9
- package/dist/tools/query-metrics.js.map +1 -1
- package/dist/tools/query-metrics.test.js +12 -25
- package/dist/tools/query-metrics.test.js.map +1 -1
- package/dist/tools/query-traces.d.ts +28 -28
- package/dist/tools/query-traces.d.ts.map +1 -1
- package/dist/tools/query-traces.js +18 -18
- package/dist/tools/query-traces.js.map +1 -1
- package/dist/tools/query-traces.test.js +58 -54
- package/dist/tools/query-traces.test.js.map +1 -1
- package/dist/tools/setup-claudeignore.js +7 -7
- package/dist/tools/setup-claudeignore.js.map +1 -1
- package/dist/tools/setup-claudeignore.test.js +4 -25
- package/dist/tools/setup-claudeignore.test.js.map +1 -1
- package/package.json +4 -2
|
@@ -12,6 +12,7 @@ import { listFiles, streamJsonl, parseDateFromFilename, getDateString, paginateR
|
|
|
12
12
|
import { QueryCache, makeCacheKey } from '../lib/cache.js';
|
|
13
13
|
import { getIndexPath, readIndex, isIndexStale, queryIndex, readLinesByNumber, } from '../lib/indexer.js';
|
|
14
14
|
import { sanitizePath } from '../lib/error-sanitizer.js';
|
|
15
|
+
import { CircuitBreaker } from '../lib/circuit-breaker.js';
|
|
15
16
|
import { existsSync } from 'fs';
|
|
16
17
|
function startTiming() {
|
|
17
18
|
const start = performance.now();
|
|
@@ -20,6 +21,38 @@ function startTiming() {
|
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
const SLOW_QUERY_THRESHOLD_MS = 500;
|
|
24
|
+
/**
|
|
25
|
+
* LRU regex cache to avoid recompiling frequently-used patterns.
|
|
26
|
+
* Maximum 100 patterns cached; uses Map insertion order for LRU eviction.
|
|
27
|
+
*/
|
|
28
|
+
const regexCache = new Map();
|
|
29
|
+
const MAX_CACHED_REGEX_PATTERNS = 100;
|
|
30
|
+
/**
|
|
31
|
+
* Get a cached regex or compile and cache a new one.
|
|
32
|
+
* Returns null for invalid patterns (logs warning).
|
|
33
|
+
*/
|
|
34
|
+
function getCachedRegex(pattern) {
|
|
35
|
+
const cached = regexCache.get(pattern);
|
|
36
|
+
if (cached) {
|
|
37
|
+
return cached;
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const regex = new RegExp(pattern);
|
|
41
|
+
// LRU eviction: remove oldest entry if at capacity
|
|
42
|
+
if (regexCache.size >= MAX_CACHED_REGEX_PATTERNS) {
|
|
43
|
+
const firstKey = regexCache.keys().next().value;
|
|
44
|
+
if (firstKey !== undefined) {
|
|
45
|
+
regexCache.delete(firstKey);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
regexCache.set(pattern, regex);
|
|
49
|
+
return regex;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
console.warn(`Invalid regex pattern: ${pattern}`);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
23
56
|
/**
|
|
24
57
|
* Insert item into a sorted array, maintaining sort order and max size.
|
|
25
58
|
* More efficient than sort+slice for top-K selection (O(n) vs O(n log n)).
|
|
@@ -29,6 +62,8 @@ function insertSortedBounded(arr, item, maxSize, compareFn) {
|
|
|
29
62
|
let low = 0;
|
|
30
63
|
let high = arr.length;
|
|
31
64
|
while (low < high) {
|
|
65
|
+
// Use unsigned right shift (>>>) to compute midpoint without integer overflow
|
|
66
|
+
// when (low + high) exceeds Number.MAX_SAFE_INTEGER
|
|
32
67
|
const mid = (low + high) >>> 1;
|
|
33
68
|
if (compareFn(arr[mid], item) <= 0) {
|
|
34
69
|
low = mid + 1;
|
|
@@ -431,9 +466,23 @@ export class LocalJsonlBackend {
|
|
|
431
466
|
llmEventCache = new QueryCache();
|
|
432
467
|
evaluationCache = new QueryCache();
|
|
433
468
|
useIndexes;
|
|
469
|
+
circuitBreaker;
|
|
434
470
|
constructor(telemetryDir, useIndexes = true) {
|
|
435
471
|
this.telemetryDir = telemetryDir || TELEMETRY_DIR;
|
|
436
472
|
this.useIndexes = useIndexes;
|
|
473
|
+
this.circuitBreaker = new CircuitBreaker({ name: 'local-file-io' });
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Get circuit breaker state (for health check and testing)
|
|
477
|
+
*/
|
|
478
|
+
getCircuitBreakerState() {
|
|
479
|
+
return this.circuitBreaker.getState();
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Reset circuit breaker (for testing)
|
|
483
|
+
*/
|
|
484
|
+
resetCircuitBreaker() {
|
|
485
|
+
this.circuitBreaker.reset();
|
|
437
486
|
}
|
|
438
487
|
/**
|
|
439
488
|
* Clear all query caches
|
|
@@ -479,6 +528,11 @@ export class LocalJsonlBackend {
|
|
|
479
528
|
console.warn(`[obs-toolkit] Slow query: queryTraces took ${durationMs.toFixed(1)}ms`);
|
|
480
529
|
}
|
|
481
530
|
};
|
|
531
|
+
// Check circuit breaker - fail fast if open
|
|
532
|
+
if (!this.circuitBreaker.canRequest()) {
|
|
533
|
+
logTiming();
|
|
534
|
+
return [];
|
|
535
|
+
}
|
|
482
536
|
// Check cache first
|
|
483
537
|
const cacheKey = makeCacheKey('traces', options);
|
|
484
538
|
const cached = this.traceCache.get(cacheKey);
|
|
@@ -490,16 +544,10 @@ export class LocalJsonlBackend {
|
|
|
490
544
|
const results = [];
|
|
491
545
|
const limit = options.limit || 100;
|
|
492
546
|
const offset = options.offset || 0;
|
|
493
|
-
//
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
spanNameRegex = new RegExp(options.spanNameRegex);
|
|
498
|
-
}
|
|
499
|
-
catch {
|
|
500
|
-
console.warn(`Invalid spanNameRegex pattern: ${options.spanNameRegex}`);
|
|
501
|
-
}
|
|
502
|
-
}
|
|
547
|
+
// Use cached regex to avoid recompilation for frequently-used patterns
|
|
548
|
+
const spanNameRegex = options.spanNameRegex
|
|
549
|
+
? getCachedRegex(options.spanNameRegex)
|
|
550
|
+
: null;
|
|
503
551
|
// Build index query options for indexable filters
|
|
504
552
|
const indexOptions = {
|
|
505
553
|
traceId: options.traceId,
|
|
@@ -558,59 +606,69 @@ export class LocalJsonlBackend {
|
|
|
558
606
|
return false;
|
|
559
607
|
return true;
|
|
560
608
|
};
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
609
|
+
try {
|
|
610
|
+
for (const file of files) {
|
|
611
|
+
// Try to use index for pre-filtering
|
|
612
|
+
const matchingLines = this.tryUseIndex(file, 'traces', indexOptions);
|
|
613
|
+
if (matchingLines !== null) {
|
|
614
|
+
// Use indexed query - read only matching lines
|
|
615
|
+
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
616
|
+
for (const raw of rawRecords) {
|
|
617
|
+
const span = normalizeSpan(raw);
|
|
618
|
+
if (!span)
|
|
619
|
+
continue;
|
|
620
|
+
if (!applyFilters(span))
|
|
621
|
+
continue;
|
|
622
|
+
results.push(span);
|
|
623
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
624
|
+
const paginated = paginateResults(results, offset, limit);
|
|
625
|
+
this.traceCache.set(cacheKey, paginated);
|
|
626
|
+
this.circuitBreaker.recordSuccess();
|
|
627
|
+
logTiming();
|
|
628
|
+
return paginated;
|
|
629
|
+
}
|
|
579
630
|
}
|
|
580
631
|
}
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
if (!span)
|
|
587
|
-
continue;
|
|
588
|
-
// Apply indexable filters (since no index was used)
|
|
589
|
-
if (options.traceId && span.traceId !== options.traceId)
|
|
590
|
-
continue;
|
|
591
|
-
if (options.spanName && !span.name.includes(options.spanName))
|
|
592
|
-
continue;
|
|
593
|
-
if (options.serviceName) {
|
|
594
|
-
const svc = span.attributes?.['service.name'];
|
|
595
|
-
if (svc !== options.serviceName)
|
|
632
|
+
else {
|
|
633
|
+
// Fall back to full file scan
|
|
634
|
+
for await (const raw of streamJsonl(file)) {
|
|
635
|
+
const span = normalizeSpan(raw);
|
|
636
|
+
if (!span)
|
|
596
637
|
continue;
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
638
|
+
// Apply indexable filters (since no index was used)
|
|
639
|
+
if (options.traceId && span.traceId !== options.traceId)
|
|
640
|
+
continue;
|
|
641
|
+
if (options.spanName && !span.name.includes(options.spanName))
|
|
642
|
+
continue;
|
|
643
|
+
if (options.serviceName) {
|
|
644
|
+
const svc = span.attributes?.['service.name'];
|
|
645
|
+
if (svc !== options.serviceName)
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
if (!applyFilters(span))
|
|
649
|
+
continue;
|
|
650
|
+
results.push(span);
|
|
651
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
652
|
+
const paginated = paginateResults(results, offset, limit);
|
|
653
|
+
this.traceCache.set(cacheKey, paginated);
|
|
654
|
+
this.circuitBreaker.recordSuccess();
|
|
655
|
+
logTiming();
|
|
656
|
+
return paginated;
|
|
657
|
+
}
|
|
606
658
|
}
|
|
607
659
|
}
|
|
608
660
|
}
|
|
661
|
+
const paginated = paginateResults(results, offset, limit);
|
|
662
|
+
this.traceCache.set(cacheKey, paginated);
|
|
663
|
+
this.circuitBreaker.recordSuccess();
|
|
664
|
+
logTiming();
|
|
665
|
+
return paginated;
|
|
666
|
+
}
|
|
667
|
+
catch (error) {
|
|
668
|
+
this.circuitBreaker.recordFailure();
|
|
669
|
+
logTiming();
|
|
670
|
+
throw error;
|
|
609
671
|
}
|
|
610
|
-
const paginated = paginateResults(results, offset, limit);
|
|
611
|
-
this.traceCache.set(cacheKey, paginated);
|
|
612
|
-
logTiming();
|
|
613
|
-
return paginated;
|
|
614
672
|
}
|
|
615
673
|
async queryLogs(options) {
|
|
616
674
|
const timer = startTiming();
|
|
@@ -620,6 +678,11 @@ export class LocalJsonlBackend {
|
|
|
620
678
|
console.warn(`[obs-toolkit] Slow query: queryLogs took ${durationMs.toFixed(1)}ms`);
|
|
621
679
|
}
|
|
622
680
|
};
|
|
681
|
+
// Check circuit breaker - fail fast if open
|
|
682
|
+
if (!this.circuitBreaker.canRequest()) {
|
|
683
|
+
logTiming();
|
|
684
|
+
return [];
|
|
685
|
+
}
|
|
623
686
|
// Check cache first
|
|
624
687
|
const cacheKey = makeCacheKey('logs', options);
|
|
625
688
|
const cached = this.logCache.get(cacheKey);
|
|
@@ -678,54 +741,64 @@ export class LocalJsonlBackend {
|
|
|
678
741
|
}
|
|
679
742
|
return true;
|
|
680
743
|
};
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
744
|
+
try {
|
|
745
|
+
for (const file of files) {
|
|
746
|
+
// Try to use index for pre-filtering
|
|
747
|
+
const matchingLines = this.tryUseIndex(file, 'logs', indexOptions);
|
|
748
|
+
if (matchingLines !== null) {
|
|
749
|
+
// Use indexed query - read only matching lines
|
|
750
|
+
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
751
|
+
for (const raw of rawRecords) {
|
|
752
|
+
const log = normalizeLog(raw, options.extractFields);
|
|
753
|
+
if (!log)
|
|
754
|
+
continue;
|
|
755
|
+
if (!applyFilters(log))
|
|
756
|
+
continue;
|
|
757
|
+
results.push(log);
|
|
758
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
759
|
+
const paginated = paginateResults(results, offset, limit);
|
|
760
|
+
this.logCache.set(cacheKey, paginated);
|
|
761
|
+
this.circuitBreaker.recordSuccess();
|
|
762
|
+
logTiming();
|
|
763
|
+
return paginated;
|
|
764
|
+
}
|
|
699
765
|
}
|
|
700
766
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
767
|
+
else {
|
|
768
|
+
// Fall back to full file scan
|
|
769
|
+
for await (const raw of streamJsonl(file)) {
|
|
770
|
+
const log = normalizeLog(raw, options.extractFields);
|
|
771
|
+
if (!log)
|
|
772
|
+
continue;
|
|
773
|
+
// Apply indexable filters (since no index was used)
|
|
774
|
+
if (options.severity && log.severity.toUpperCase() !== options.severity.toUpperCase())
|
|
775
|
+
continue;
|
|
776
|
+
if (options.traceId && log.traceId !== options.traceId)
|
|
777
|
+
continue;
|
|
778
|
+
if (!applyFilters(log))
|
|
779
|
+
continue;
|
|
780
|
+
results.push(log);
|
|
781
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
782
|
+
const paginated = paginateResults(results, offset, limit);
|
|
783
|
+
this.logCache.set(cacheKey, paginated);
|
|
784
|
+
this.circuitBreaker.recordSuccess();
|
|
785
|
+
logTiming();
|
|
786
|
+
return paginated;
|
|
787
|
+
}
|
|
721
788
|
}
|
|
722
789
|
}
|
|
723
790
|
}
|
|
791
|
+
const paginated = paginateResults(results, offset, limit);
|
|
792
|
+
this.logCache.set(cacheKey, paginated);
|
|
793
|
+
this.circuitBreaker.recordSuccess();
|
|
794
|
+
logTiming();
|
|
795
|
+
return paginated;
|
|
796
|
+
}
|
|
797
|
+
catch (error) {
|
|
798
|
+
this.circuitBreaker.recordFailure();
|
|
799
|
+
logTiming();
|
|
800
|
+
throw error;
|
|
724
801
|
}
|
|
725
|
-
const paginated = paginateResults(results, offset, limit);
|
|
726
|
-
this.logCache.set(cacheKey, paginated);
|
|
727
|
-
logTiming();
|
|
728
|
-
return paginated;
|
|
729
802
|
}
|
|
730
803
|
async queryMetrics(options) {
|
|
731
804
|
const timer = startTiming();
|
|
@@ -735,6 +808,11 @@ export class LocalJsonlBackend {
|
|
|
735
808
|
console.warn(`[obs-toolkit] Slow query: queryMetrics took ${durationMs.toFixed(1)}ms`);
|
|
736
809
|
}
|
|
737
810
|
};
|
|
811
|
+
// Check circuit breaker - fail fast if open
|
|
812
|
+
if (!this.circuitBreaker.canRequest()) {
|
|
813
|
+
logTiming();
|
|
814
|
+
return [];
|
|
815
|
+
}
|
|
738
816
|
// Check cache first
|
|
739
817
|
const cacheKey = makeCacheKey('metrics', options);
|
|
740
818
|
const cached = this.metricCache.get(cacheKey);
|
|
@@ -750,49 +828,58 @@ export class LocalJsonlBackend {
|
|
|
750
828
|
const indexOptions = {
|
|
751
829
|
metricName: options.metricName,
|
|
752
830
|
};
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
831
|
+
try {
|
|
832
|
+
outer: for (const file of files) {
|
|
833
|
+
// Try to use index for pre-filtering
|
|
834
|
+
const matchingLines = this.tryUseIndex(file, 'metrics', indexOptions);
|
|
835
|
+
if (matchingLines !== null) {
|
|
836
|
+
// Use indexed query - read only matching lines
|
|
837
|
+
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
838
|
+
for (const raw of rawRecords) {
|
|
839
|
+
const point = normalizeMetric(raw);
|
|
840
|
+
if (!point)
|
|
841
|
+
continue;
|
|
842
|
+
results.push(point);
|
|
843
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
844
|
+
break outer;
|
|
845
|
+
}
|
|
766
846
|
}
|
|
767
847
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
848
|
+
else {
|
|
849
|
+
// Fall back to full file scan
|
|
850
|
+
for await (const raw of streamJsonl(file)) {
|
|
851
|
+
const point = normalizeMetric(raw);
|
|
852
|
+
if (!point)
|
|
853
|
+
continue;
|
|
854
|
+
// Apply filters (since no index was used)
|
|
855
|
+
if (options.metricName && !point.name.includes(options.metricName))
|
|
856
|
+
continue;
|
|
857
|
+
results.push(point);
|
|
858
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
859
|
+
break outer;
|
|
860
|
+
}
|
|
781
861
|
}
|
|
782
862
|
}
|
|
783
863
|
}
|
|
864
|
+
// Apply aggregation if requested
|
|
865
|
+
if (options.aggregation && results.length > 0) {
|
|
866
|
+
const aggregated = this.aggregate(results, options.aggregation, options.groupBy, options.timeBucket);
|
|
867
|
+
this.metricCache.set(cacheKey, aggregated);
|
|
868
|
+
this.circuitBreaker.recordSuccess();
|
|
869
|
+
logTiming();
|
|
870
|
+
return aggregated;
|
|
871
|
+
}
|
|
872
|
+
const paginated = paginateResults(results, offset, limit);
|
|
873
|
+
this.metricCache.set(cacheKey, paginated);
|
|
874
|
+
this.circuitBreaker.recordSuccess();
|
|
875
|
+
logTiming();
|
|
876
|
+
return paginated;
|
|
784
877
|
}
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
const aggregated = this.aggregate(results, options.aggregation, options.groupBy, options.timeBucket);
|
|
788
|
-
this.metricCache.set(cacheKey, aggregated);
|
|
878
|
+
catch (error) {
|
|
879
|
+
this.circuitBreaker.recordFailure();
|
|
789
880
|
logTiming();
|
|
790
|
-
|
|
881
|
+
throw error;
|
|
791
882
|
}
|
|
792
|
-
const paginated = paginateResults(results, offset, limit);
|
|
793
|
-
this.metricCache.set(cacheKey, paginated);
|
|
794
|
-
logTiming();
|
|
795
|
-
return paginated;
|
|
796
883
|
}
|
|
797
884
|
aggregate(points, aggregation, groupBy, timeBucket) {
|
|
798
885
|
// Parse time bucket if provided
|
|
@@ -907,6 +994,11 @@ export class LocalJsonlBackend {
|
|
|
907
994
|
console.warn(`[obs-toolkit] Slow query: queryLLMEvents took ${durationMs.toFixed(1)}ms`);
|
|
908
995
|
}
|
|
909
996
|
};
|
|
997
|
+
// Check circuit breaker - fail fast if open
|
|
998
|
+
if (!this.circuitBreaker.canRequest()) {
|
|
999
|
+
logTiming();
|
|
1000
|
+
return [];
|
|
1001
|
+
}
|
|
910
1002
|
// Check cache first
|
|
911
1003
|
const cacheKey = makeCacheKey('llm-events', options);
|
|
912
1004
|
const cached = this.llmEventCache.get(cacheKey);
|
|
@@ -955,58 +1047,68 @@ export class LocalJsonlBackend {
|
|
|
955
1047
|
}
|
|
956
1048
|
return true;
|
|
957
1049
|
};
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
1050
|
+
try {
|
|
1051
|
+
for (const file of files) {
|
|
1052
|
+
// Try to use index for pre-filtering
|
|
1053
|
+
const matchingLines = this.tryUseIndex(file, 'llm-events', indexOptions);
|
|
1054
|
+
if (matchingLines !== null) {
|
|
1055
|
+
// Use indexed query - read only matching lines
|
|
1056
|
+
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
1057
|
+
for (const event of rawRecords) {
|
|
1058
|
+
if (!event.timestamp || !event.name)
|
|
1059
|
+
continue;
|
|
1060
|
+
if (!applyFilters(event))
|
|
1061
|
+
continue;
|
|
1062
|
+
results.push({
|
|
1063
|
+
timestamp: event.timestamp,
|
|
1064
|
+
name: event.name,
|
|
1065
|
+
attributes: event.attributes,
|
|
1066
|
+
});
|
|
1067
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1068
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1069
|
+
this.llmEventCache.set(cacheKey, paginated);
|
|
1070
|
+
this.circuitBreaker.recordSuccess();
|
|
1071
|
+
logTiming();
|
|
1072
|
+
return paginated;
|
|
1073
|
+
}
|
|
979
1074
|
}
|
|
980
1075
|
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1076
|
+
else {
|
|
1077
|
+
// Fall back to full file scan
|
|
1078
|
+
for await (const event of streamJsonl(file)) {
|
|
1079
|
+
if (!event.timestamp || !event.name)
|
|
1080
|
+
continue;
|
|
1081
|
+
// Apply indexable filters (since no index was used)
|
|
1082
|
+
if (options.eventName && !event.name.includes(options.eventName))
|
|
1083
|
+
continue;
|
|
1084
|
+
if (!applyFilters(event))
|
|
1085
|
+
continue;
|
|
1086
|
+
results.push({
|
|
1087
|
+
timestamp: event.timestamp,
|
|
1088
|
+
name: event.name,
|
|
1089
|
+
attributes: event.attributes,
|
|
1090
|
+
});
|
|
1091
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1092
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1093
|
+
this.llmEventCache.set(cacheKey, paginated);
|
|
1094
|
+
this.circuitBreaker.recordSuccess();
|
|
1095
|
+
logTiming();
|
|
1096
|
+
return paginated;
|
|
1097
|
+
}
|
|
1002
1098
|
}
|
|
1003
1099
|
}
|
|
1004
1100
|
}
|
|
1101
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1102
|
+
this.llmEventCache.set(cacheKey, paginated);
|
|
1103
|
+
this.circuitBreaker.recordSuccess();
|
|
1104
|
+
logTiming();
|
|
1105
|
+
return paginated;
|
|
1106
|
+
}
|
|
1107
|
+
catch (error) {
|
|
1108
|
+
this.circuitBreaker.recordFailure();
|
|
1109
|
+
logTiming();
|
|
1110
|
+
throw error;
|
|
1005
1111
|
}
|
|
1006
|
-
const paginated = paginateResults(results, offset, limit);
|
|
1007
|
-
this.llmEventCache.set(cacheKey, paginated);
|
|
1008
|
-
logTiming();
|
|
1009
|
-
return paginated;
|
|
1010
1112
|
}
|
|
1011
1113
|
async queryEvaluations(options) {
|
|
1012
1114
|
const timer = startTiming();
|
|
@@ -1016,6 +1118,11 @@ export class LocalJsonlBackend {
|
|
|
1016
1118
|
console.warn(`[obs-toolkit] Slow query: queryEvaluations took ${durationMs.toFixed(1)}ms`);
|
|
1017
1119
|
}
|
|
1018
1120
|
};
|
|
1121
|
+
// Check circuit breaker - fail fast if open
|
|
1122
|
+
if (!this.circuitBreaker.canRequest()) {
|
|
1123
|
+
logTiming();
|
|
1124
|
+
return [];
|
|
1125
|
+
}
|
|
1019
1126
|
// Check cache first
|
|
1020
1127
|
const cacheKey = makeCacheKey('evaluations', options);
|
|
1021
1128
|
const cached = this.evaluationCache.get(cacheKey);
|
|
@@ -1057,62 +1164,76 @@ export class LocalJsonlBackend {
|
|
|
1057
1164
|
}
|
|
1058
1165
|
return true;
|
|
1059
1166
|
};
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1167
|
+
try {
|
|
1168
|
+
for (const file of files) {
|
|
1169
|
+
// Try to use index for pre-filtering
|
|
1170
|
+
const matchingLines = this.tryUseIndex(file, 'evaluations', indexOptions);
|
|
1171
|
+
if (matchingLines !== null) {
|
|
1172
|
+
// Use indexed query - read only matching lines
|
|
1173
|
+
const rawRecords = await readLinesByNumber(file, matchingLines);
|
|
1174
|
+
for (const raw of rawRecords) {
|
|
1175
|
+
const evaluation = normalizeEvaluation(raw);
|
|
1176
|
+
if (!evaluation)
|
|
1177
|
+
continue;
|
|
1178
|
+
if (!applyFilters(evaluation))
|
|
1179
|
+
continue;
|
|
1180
|
+
results.push(evaluation);
|
|
1181
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1182
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1183
|
+
this.evaluationCache.set(cacheKey, paginated);
|
|
1184
|
+
this.circuitBreaker.recordSuccess();
|
|
1185
|
+
logTiming();
|
|
1186
|
+
return paginated;
|
|
1187
|
+
}
|
|
1078
1188
|
}
|
|
1079
1189
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1190
|
+
else {
|
|
1191
|
+
// Fall back to full file scan
|
|
1192
|
+
for await (const raw of streamJsonl(file)) {
|
|
1193
|
+
const evaluation = normalizeEvaluation(raw);
|
|
1194
|
+
if (!evaluation)
|
|
1195
|
+
continue;
|
|
1196
|
+
// Apply indexable filters (since no index was used)
|
|
1197
|
+
if (options.traceId && evaluation.traceId !== options.traceId)
|
|
1198
|
+
continue;
|
|
1199
|
+
if (options.evaluationName && !evaluation.evaluationName.includes(options.evaluationName))
|
|
1200
|
+
continue;
|
|
1201
|
+
if (options.scoreLabel && evaluation.scoreLabel !== options.scoreLabel)
|
|
1202
|
+
continue;
|
|
1203
|
+
if (options.responseId && evaluation.responseId !== options.responseId)
|
|
1204
|
+
continue;
|
|
1205
|
+
if (options.evaluator && evaluation.evaluator !== options.evaluator)
|
|
1206
|
+
continue;
|
|
1207
|
+
if (!applyFilters(evaluation))
|
|
1208
|
+
continue;
|
|
1209
|
+
results.push(evaluation);
|
|
1210
|
+
if (hasReachedLimit(results.length, offset, limit)) {
|
|
1211
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1212
|
+
this.evaluationCache.set(cacheKey, paginated);
|
|
1213
|
+
this.circuitBreaker.recordSuccess();
|
|
1214
|
+
logTiming();
|
|
1215
|
+
return paginated;
|
|
1216
|
+
}
|
|
1106
1217
|
}
|
|
1107
1218
|
}
|
|
1108
1219
|
}
|
|
1220
|
+
const paginated = paginateResults(results, offset, limit);
|
|
1221
|
+
this.evaluationCache.set(cacheKey, paginated);
|
|
1222
|
+
this.circuitBreaker.recordSuccess();
|
|
1223
|
+
logTiming();
|
|
1224
|
+
return paginated;
|
|
1225
|
+
}
|
|
1226
|
+
catch (error) {
|
|
1227
|
+
this.circuitBreaker.recordFailure();
|
|
1228
|
+
logTiming();
|
|
1229
|
+
throw error;
|
|
1109
1230
|
}
|
|
1110
|
-
const paginated = paginateResults(results, offset, limit);
|
|
1111
|
-
this.evaluationCache.set(cacheKey, paginated);
|
|
1112
|
-
logTiming();
|
|
1113
|
-
return paginated;
|
|
1114
1231
|
}
|
|
1115
1232
|
async healthCheck() {
|
|
1233
|
+
// Check circuit breaker state
|
|
1234
|
+
if (this.circuitBreaker.getState() === 'open') {
|
|
1235
|
+
return { status: 'error', message: 'Circuit breaker open - file I/O temporarily unavailable' };
|
|
1236
|
+
}
|
|
1116
1237
|
if (!existsSync(this.telemetryDir)) {
|
|
1117
1238
|
// Security: sanitize path to prevent information disclosure
|
|
1118
1239
|
return { status: 'error', message: `Telemetry directory not found: ${sanitizePath(this.telemetryDir)}` };
|