apm-optima 1.0.4 → 2.0.0
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 +54 -23
- package/dashboard-out/404/index.html +1 -1
- package/dashboard-out/404.html +1 -1
- package/dashboard-out/__next.__PAGE__.txt +4 -4
- package/dashboard-out/__next._full.txt +17 -17
- package/dashboard-out/__next._head.txt +4 -4
- package/dashboard-out/__next._index.txt +10 -10
- package/dashboard-out/__next._tree.txt +2 -2
- package/dashboard-out/_next/static/chunks/{2xjcsu4pgp14_.js → 062oipeclh9bq.js} +1 -1
- package/dashboard-out/_next/static/chunks/0ncbimuip0-o7.css +3 -0
- package/dashboard-out/_next/static/chunks/0xtgenoch3q77.js +1 -0
- package/dashboard-out/_next/static/chunks/1xln1-s87x5yp.js +27 -0
- package/dashboard-out/_not-found/__next._full.txt +15 -15
- package/dashboard-out/_not-found/__next._head.txt +4 -4
- package/dashboard-out/_not-found/__next._index.txt +10 -10
- package/dashboard-out/_not-found/__next._not-found.__PAGE__.txt +2 -2
- package/dashboard-out/_not-found/__next._not-found.txt +3 -3
- package/dashboard-out/_not-found/__next._tree.txt +2 -2
- package/dashboard-out/_not-found/index.html +1 -1
- package/dashboard-out/_not-found/index.txt +15 -15
- package/dashboard-out/index.html +1 -1
- package/dashboard-out/index.txt +17 -17
- package/dist/adapters/session-export.handler.d.ts +4 -0
- package/dist/adapters/session-export.handler.js +29 -0
- package/dist/adapters/socket.options.d.ts +3 -0
- package/dist/adapters/socket.options.js +16 -0
- package/dist/config/config.manager.d.ts +80 -3
- package/dist/config/config.manager.js +269 -89
- package/dist/config/config.types.d.ts +89 -13
- package/dist/core/delivery/buckets-request.d.ts +2 -0
- package/dist/core/delivery/buckets-request.js +18 -0
- package/dist/core/delivery/index.d.ts +1 -0
- package/dist/core/delivery/index.js +1 -0
- package/dist/core/delivery/metrics.publisher.d.ts +23 -10
- package/dist/core/delivery/metrics.publisher.js +22 -22
- package/dist/core/delivery/websocket.events.d.ts +14 -3
- package/dist/core/delivery/websocket.events.js +13 -2
- package/dist/core/domain/correlation.types.d.ts +37 -0
- package/dist/core/domain/correlation.types.js +2 -0
- package/dist/core/domain/dashboard.types.d.ts +16 -69
- package/dist/core/domain/dashboard.types.js +29 -0
- package/dist/core/domain/endpoints.types.d.ts +3 -0
- package/dist/core/domain/event.types.d.ts +14 -0
- package/dist/core/domain/event.types.js +2 -0
- package/dist/core/domain/incident.types.d.ts +50 -0
- package/dist/core/domain/incident.types.js +2 -0
- package/dist/core/domain/index.d.ts +5 -0
- package/dist/core/domain/index.js +5 -0
- package/dist/core/domain/live-bucket.types.d.ts +86 -0
- package/dist/core/domain/live-bucket.types.js +2 -0
- package/dist/core/domain/metrics.types.d.ts +41 -11
- package/dist/core/domain/session.types.d.ts +93 -0
- package/dist/core/domain/session.types.js +2 -0
- package/dist/core/domain/telemetry.types.d.ts +1 -0
- package/dist/core/organizers/event.manager.d.ts +19 -0
- package/dist/core/organizers/event.manager.js +73 -0
- package/dist/core/organizers/index.d.ts +2 -0
- package/dist/core/organizers/index.js +18 -0
- package/dist/core/organizers/interval.manager.d.ts +18 -0
- package/dist/core/organizers/interval.manager.js +54 -0
- package/dist/core/storage/index.d.ts +1 -0
- package/dist/core/storage/index.js +1 -0
- package/dist/core/storage/local.repository.d.ts +12 -12
- package/dist/core/storage/local.repository.js +38 -38
- package/dist/core/storage/persistence/persistence-writer.d.ts +48 -0
- package/dist/core/storage/persistence/persistence-writer.js +191 -0
- package/dist/core/storage/persistence/segment-index.d.ts +27 -0
- package/dist/core/storage/persistence/segment-index.js +206 -0
- package/dist/core/storage/persistence/session-registry.d.ts +28 -0
- package/dist/core/storage/persistence/session-registry.js +127 -0
- package/dist/core/storage/persistence/session-summary.d.ts +3 -0
- package/dist/core/storage/persistence/session-summary.js +112 -0
- package/dist/core/storage/persistence.layer.d.ts +69 -0
- package/dist/core/storage/persistence.layer.js +404 -0
- package/dist/core/storage/runtime/anomaly-detector.d.ts +11 -0
- package/dist/core/storage/runtime/anomaly-detector.js +42 -0
- package/dist/core/storage/stores/analytics.store.d.ts +5 -5
- package/dist/core/storage/stores/analytics.store.js +39 -12
- package/dist/core/storage/stores/bucket-metric.d.ts +85 -0
- package/dist/core/storage/stores/bucket-metric.js +168 -0
- package/dist/core/storage/stores/bucket-view.d.ts +44 -0
- package/dist/core/storage/stores/bucket-view.js +63 -0
- package/dist/core/storage/stores/bucket.store.d.ts +39 -36
- package/dist/core/storage/stores/bucket.store.js +112 -60
- package/dist/core/storage/stores/dashboard.store.d.ts +11 -4
- package/dist/core/storage/stores/dashboard.store.js +179 -47
- package/dist/core/storage/stores/endpoint.store.d.ts +1 -0
- package/dist/core/storage/stores/endpoint.store.js +21 -5
- package/dist/core/storage/stores/index.d.ts +0 -7
- package/dist/core/storage/stores/index.js +0 -7
- package/dist/core/storage/stores/metrics.store.d.ts +1 -1
- package/dist/core/storage/stores/metrics.store.js +1 -0
- package/dist/core/storage/stores/runtime.store.d.ts +77 -0
- package/dist/core/storage/stores/runtime.store.js +222 -0
- package/dist/core/storage/utility/common.d.ts +1 -0
- package/dist/core/storage/utility/common.js +11 -0
- package/dist/core/storage/utility/file-buffer.d.ts +31 -0
- package/dist/core/storage/utility/file-buffer.js +238 -0
- package/dist/core/storage/utility/histogram.d.ts +1 -2
- package/dist/core/storage/utility/histogram.js +1 -2
- package/dist/core/storage/utility/index.d.ts +2 -0
- package/dist/core/storage/utility/index.js +2 -0
- package/dist/core/storage/utility/persistence-files.d.ts +3 -0
- package/dist/core/storage/utility/persistence-files.js +34 -0
- package/dist/core/storage/utility/persistence-writer.d.ts +39 -0
- package/dist/core/storage/utility/persistence-writer.js +183 -0
- package/dist/core/storage/utility/session-export.d.ts +3 -0
- package/dist/core/storage/utility/session-export.js +31 -0
- package/dist/core/storage/utility/session-registry.d.ts +21 -0
- package/dist/core/storage/utility/session-registry.js +91 -0
- package/dist/core/storage/utility/session-window.d.ts +8 -0
- package/dist/core/storage/utility/session-window.js +43 -0
- package/dist/core/telemetry/analytics.service.d.ts +18 -0
- package/dist/core/telemetry/analytics.service.js +100 -0
- package/dist/core/telemetry/collector.service.d.ts +17 -15
- package/dist/core/telemetry/collector.service.js +23 -80
- package/dist/core/telemetry/correlation.service.d.ts +57 -9
- package/dist/core/telemetry/correlation.service.js +246 -54
- package/dist/core/telemetry/dashboard-live.service.d.ts +10 -0
- package/dist/core/telemetry/dashboard-live.service.js +110 -0
- package/dist/core/telemetry/dashboard.service.d.ts +20 -0
- package/dist/core/telemetry/dashboard.service.js +140 -0
- package/dist/core/telemetry/incident.service.d.ts +71 -0
- package/dist/core/telemetry/incident.service.js +239 -0
- package/dist/core/telemetry/logger.d.ts +4 -0
- package/dist/core/telemetry/logger.js +5 -2
- package/dist/core/telemetry/runtime.service.d.ts +14 -0
- package/dist/core/telemetry/runtime.service.js +56 -0
- package/dist/core/telemetry/telemetry-query.service.d.ts +82 -0
- package/dist/core/telemetry/telemetry-query.service.js +231 -0
- package/dist/core/telemetry/telemetry.service.d.ts +10 -4
- package/dist/core/telemetry/telemetry.service.js +60 -10
- package/dist/core/telemetry/utility/correlation-analysis.d.ts +9 -0
- package/dist/core/telemetry/utility/correlation-analysis.js +143 -0
- package/dist/core/telemetry/utility/correlation-finding.d.ts +70 -0
- package/dist/core/telemetry/utility/correlation-finding.js +41 -0
- package/dist/core/telemetry/utility/correlation-input.d.ts +59 -0
- package/dist/core/telemetry/utility/correlation-input.js +139 -0
- package/dist/core/telemetry/utility/correlation-replay.d.ts +24 -0
- package/dist/core/telemetry/utility/correlation-replay.js +46 -0
- package/dist/core/utility/correlation.analysis.d.ts +9 -0
- package/dist/core/utility/correlation.analysis.js +144 -0
- package/dist/core/utility/statistics.d.ts +65 -0
- package/dist/core/utility/statistics.js +141 -0
- package/dist/express/index.d.ts +1 -1
- package/dist/express/index.js +7 -5
- package/dist/express/metrics-websocket.adapter.d.ts +8 -2
- package/dist/express/metrics-websocket.adapter.js +52 -17
- package/dist/express/metrics.adapter.d.ts +1 -0
- package/dist/express/metrics.adapter.js +8 -1
- package/dist/express/metrics.bootstrap.d.ts +6 -1
- package/dist/express/metrics.bootstrap.js +48 -22
- package/dist/express/metrics.dashboard.d.ts +2 -1
- package/dist/express/metrics.dashboard.js +4 -1
- package/dist/express/metrics.middleware.d.ts +2 -1
- package/dist/express/metrics.middleware.js +23 -20
- package/dist/nest/metrics-websocket.adapter.d.ts +19 -3
- package/dist/nest/metrics-websocket.adapter.js +101 -21
- package/dist/nest/metrics.adapter.d.ts +4 -1
- package/dist/nest/metrics.adapter.js +24 -1
- package/dist/nest/metrics.bootstrap.service.d.ts +7 -3
- package/dist/nest/metrics.bootstrap.service.js +56 -21
- package/dist/nest/metrics.interceptor.d.ts +3 -3
- package/dist/nest/metrics.interceptor.js +11 -8
- package/dist/nest/metrics.module.d.ts +3 -0
- package/dist/nest/metrics.module.js +27 -9
- package/dist/runtime/index.d.ts +32 -0
- package/dist/runtime/index.js +104 -0
- package/dist/simulation/simulation.d.ts +5 -16
- package/dist/simulation/simulation.js +5 -8
- package/package.json +2 -1
- package/dashboard-out/_next/static/chunks/20t9qbodfrw_z.js +0 -3
- package/dashboard-out/_next/static/chunks/23soo6qkun-yv.js +0 -1
- package/dashboard-out/_next/static/chunks/3xoopocxi2vi8.css +0 -3
- /package/dashboard-out/_next/static/{u8vU2V6fhASeGVCpWU3eW → 17egwqgk36_DSRF55LJZG}/_buildManifest.js +0 -0
- /package/dashboard-out/_next/static/{u8vU2V6fhASeGVCpWU3eW → 17egwqgk36_DSRF55LJZG}/_clientMiddlewareManifest.js +0 -0
- /package/dashboard-out/_next/static/{u8vU2V6fhASeGVCpWU3eW → 17egwqgk36_DSRF55LJZG}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { PersistenceRepository } from '../storage/persistence.layer';
|
|
2
|
+
import { ApplicationEvent } from '../domain';
|
|
3
|
+
type AppPartialEvent = Omit<ApplicationEvent, 'timestamp' | 'applicationVersion'>;
|
|
4
|
+
type AsyncListener = (event: ApplicationEvent) => void | Promise<void>;
|
|
5
|
+
type ApplicationEventManagerInstance = Omit<ApplicationEventManager, 'start' | 'stop'>;
|
|
6
|
+
export declare class ApplicationEventManager {
|
|
7
|
+
private persistence;
|
|
8
|
+
private applicationVersion;
|
|
9
|
+
static instance: ApplicationEventManagerInstance | null;
|
|
10
|
+
private listeners;
|
|
11
|
+
private initilized;
|
|
12
|
+
constructor(persistence: PersistenceRepository, applicationVersion?: string);
|
|
13
|
+
start(): void;
|
|
14
|
+
stop(): void;
|
|
15
|
+
on(listener: AsyncListener): void;
|
|
16
|
+
off(listener: AsyncListener): void;
|
|
17
|
+
emit(event: AppPartialEvent): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ApplicationEventManager = void 0;
|
|
7
|
+
const logger_1 = __importDefault(require("../telemetry/logger"));
|
|
8
|
+
class ApplicationEventManager {
|
|
9
|
+
persistence;
|
|
10
|
+
applicationVersion;
|
|
11
|
+
static instance = null;
|
|
12
|
+
listeners = [];
|
|
13
|
+
initilized = false;
|
|
14
|
+
constructor(persistence, applicationVersion = '') {
|
|
15
|
+
this.persistence = persistence;
|
|
16
|
+
this.applicationVersion = applicationVersion;
|
|
17
|
+
if (ApplicationEventManager.instance) {
|
|
18
|
+
throw new Error('ApplicationEventManager is a singleton class. Use ApplicationEventManager.getInstance() to access the instance.');
|
|
19
|
+
}
|
|
20
|
+
ApplicationEventManager.instance = this;
|
|
21
|
+
}
|
|
22
|
+
start() {
|
|
23
|
+
if (this.initilized)
|
|
24
|
+
return;
|
|
25
|
+
this.initilized = true;
|
|
26
|
+
this.on((event) => this.persistence.onApplicationEvent(event));
|
|
27
|
+
}
|
|
28
|
+
stop() {
|
|
29
|
+
if (!this.initilized)
|
|
30
|
+
return;
|
|
31
|
+
for (const listener of this.listeners) {
|
|
32
|
+
this.off(listener);
|
|
33
|
+
}
|
|
34
|
+
this.listeners = [];
|
|
35
|
+
this.initilized = false;
|
|
36
|
+
}
|
|
37
|
+
on(listener) {
|
|
38
|
+
this.listeners.push(listener);
|
|
39
|
+
}
|
|
40
|
+
off(listener) {
|
|
41
|
+
this.listeners = this.listeners.filter(l => l !== listener);
|
|
42
|
+
}
|
|
43
|
+
async emit(event) {
|
|
44
|
+
if (!this.initilized)
|
|
45
|
+
return;
|
|
46
|
+
const fullEvent = {
|
|
47
|
+
timestamp: new Date().toISOString(),
|
|
48
|
+
type: event.type,
|
|
49
|
+
applicationVersion: this.applicationVersion,
|
|
50
|
+
reason: event.reason || '',
|
|
51
|
+
...(event.details ? { details: event.details } : {}),
|
|
52
|
+
};
|
|
53
|
+
// Using Promise.allSettled to ensure all listeners are called, even if some fail
|
|
54
|
+
// But this is causing strange race conditions, so we will call them sequentially for now
|
|
55
|
+
// const results = await Promise.allSettled(
|
|
56
|
+
// this.listeners.map(listener => Promise.resolve(listener(fullEvent)))
|
|
57
|
+
// );
|
|
58
|
+
// for (const result of results) {
|
|
59
|
+
// if (result.status === 'rejected') {
|
|
60
|
+
// Logger.error('Error in event listener:', result.reason);
|
|
61
|
+
// }
|
|
62
|
+
// }
|
|
63
|
+
for (const listener of this.listeners) {
|
|
64
|
+
try {
|
|
65
|
+
await listener(fullEvent);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
logger_1.default.error('Error in event listener:', error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.ApplicationEventManager = ApplicationEventManager;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./interval.manager"), exports);
|
|
18
|
+
__exportStar(require("./event.manager"), exports);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ReadonlyConfig } from "../../config";
|
|
2
|
+
interface TickCallback {
|
|
3
|
+
(): void;
|
|
4
|
+
}
|
|
5
|
+
interface TickCallbackAsync {
|
|
6
|
+
(): Promise<void>;
|
|
7
|
+
}
|
|
8
|
+
type IntervalType = 'tick' | 'publisher' | 'persistence';
|
|
9
|
+
export declare class IntervalManager {
|
|
10
|
+
private readonly config;
|
|
11
|
+
private intervals;
|
|
12
|
+
private initialized;
|
|
13
|
+
private started;
|
|
14
|
+
constructor(callbacks: Record<IntervalType, TickCallback | TickCallbackAsync | null>, config: ReadonlyConfig);
|
|
15
|
+
startIntervals(): void;
|
|
16
|
+
stopIntervals(): void;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IntervalManager = void 0;
|
|
4
|
+
class IntervalManager {
|
|
5
|
+
config;
|
|
6
|
+
intervals = { tick: null, publisher: null, persistence: null, };
|
|
7
|
+
initialized = false;
|
|
8
|
+
started = false;
|
|
9
|
+
constructor(callbacks, config) {
|
|
10
|
+
this.config = config;
|
|
11
|
+
this.intervals.tick = callbacks.tick ? {
|
|
12
|
+
id: null,
|
|
13
|
+
callback: callbacks.tick,
|
|
14
|
+
intervalMs: config.tickIntervalMs,
|
|
15
|
+
} : null;
|
|
16
|
+
this.intervals.publisher = callbacks.publisher ? {
|
|
17
|
+
id: null,
|
|
18
|
+
callback: callbacks.publisher,
|
|
19
|
+
intervalMs: config.publisher.intervalMs,
|
|
20
|
+
} : null;
|
|
21
|
+
if (config.persistence !== false) {
|
|
22
|
+
this.intervals.persistence = callbacks.persistence ? {
|
|
23
|
+
id: null,
|
|
24
|
+
callback: callbacks.persistence,
|
|
25
|
+
intervalMs: config.persistence.archiveIntervalMs,
|
|
26
|
+
} : null;
|
|
27
|
+
}
|
|
28
|
+
this.initialized = true;
|
|
29
|
+
}
|
|
30
|
+
startIntervals() {
|
|
31
|
+
if (!this.initialized || this.started)
|
|
32
|
+
return;
|
|
33
|
+
for (const intervalType in this.intervals) {
|
|
34
|
+
const interval = this.intervals[intervalType];
|
|
35
|
+
if (interval && interval.callback) {
|
|
36
|
+
interval.id = setInterval(interval.callback, interval.intervalMs);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
this.started = true;
|
|
40
|
+
}
|
|
41
|
+
stopIntervals() {
|
|
42
|
+
if (!this.initialized || !this.started)
|
|
43
|
+
return;
|
|
44
|
+
for (const intervalType in this.intervals) {
|
|
45
|
+
const interval = this.intervals[intervalType];
|
|
46
|
+
if (interval && interval.id) {
|
|
47
|
+
clearInterval(interval.id);
|
|
48
|
+
interval.id = null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
this.started = false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.IntervalManager = IntervalManager;
|
|
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
17
17
|
__exportStar(require("./stores"), exports);
|
|
18
18
|
__exportStar(require("./utility"), exports);
|
|
19
19
|
__exportStar(require("./local.repository"), exports);
|
|
20
|
+
__exportStar(require("./persistence.layer"), exports);
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import type { TelemetryRequest } from '../domain';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
readonly
|
|
2
|
+
import type { ReadonlyConfig } from '../../config';
|
|
3
|
+
import { BucketStore } from './stores';
|
|
4
|
+
import { PersistenceRepository } from './persistence.layer';
|
|
5
|
+
import { RuntimeStore } from './stores/runtime.store';
|
|
6
|
+
export declare class LocalRepository {
|
|
7
|
+
private readonly persistence;
|
|
8
|
+
private readonly conf;
|
|
8
9
|
readonly bucket: BucketStore;
|
|
9
|
-
readonly
|
|
10
|
-
|
|
11
|
-
readonly system: SystemStore;
|
|
12
|
-
constructor();
|
|
10
|
+
readonly runtime: RuntimeStore;
|
|
11
|
+
constructor(persistence: PersistenceRepository, conf: ReadonlyConfig);
|
|
13
12
|
record(request: TelemetryRequest): void;
|
|
14
13
|
tick(): void;
|
|
14
|
+
closeLastInterval(): Promise<void>;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
private closeInterval;
|
|
15
17
|
}
|
|
16
|
-
export declare const storage: LocalRepository;
|
|
17
|
-
export {};
|
|
@@ -1,52 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
const
|
|
3
|
+
exports.LocalRepository = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
5
|
const stores_1 = require("./stores");
|
|
6
|
+
const runtime_store_1 = require("./stores/runtime.store");
|
|
6
7
|
class LocalRepository {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
alerts;
|
|
10
|
-
metrics;
|
|
11
|
-
// Historics Store
|
|
12
|
-
health;
|
|
8
|
+
persistence;
|
|
9
|
+
conf;
|
|
13
10
|
bucket;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.bucket = new stores_1.BucketStore(this.endpoint, this.health, this.metrics, config.ringBufferSize);
|
|
27
|
-
this.dashboard = new stores_1.DashboardStore(this.bucket, this.health, this.endpoint, this.alerts);
|
|
28
|
-
this.analytics = new stores_1.AnalyticsStore(this.bucket, this.endpoint, config.publisher.slowLatencyThresholdMs);
|
|
29
|
-
this.system = new stores_1.SystemStore();
|
|
11
|
+
runtime;
|
|
12
|
+
constructor(persistence, conf) {
|
|
13
|
+
this.persistence = persistence;
|
|
14
|
+
this.conf = conf;
|
|
15
|
+
this.runtime = new runtime_store_1.RuntimeStore(conf);
|
|
16
|
+
this.bucket = new stores_1.BucketStore(this.runtime, {
|
|
17
|
+
sessionId: persistence.sessionId,
|
|
18
|
+
instanceId: (0, crypto_1.randomUUID)(),
|
|
19
|
+
intervalMs: conf.collection.bucketIntervalMs,
|
|
20
|
+
historySize: conf.cache.liveBuckets,
|
|
21
|
+
maxEndpoints: conf.collection.maxEndpointsPerBucket,
|
|
22
|
+
});
|
|
30
23
|
}
|
|
31
24
|
record(request) {
|
|
32
|
-
|
|
25
|
+
this.bucket.record({
|
|
33
26
|
method: request.method,
|
|
34
27
|
route: request.endpoint,
|
|
35
28
|
duration: request.responseTime,
|
|
36
|
-
statusCode: request.statusCode
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
29
|
+
statusCode: request.statusCode,
|
|
30
|
+
origin: request.origin
|
|
31
|
+
});
|
|
32
|
+
this.persistence.onHttpRequest({
|
|
33
|
+
method: request.method,
|
|
34
|
+
path: request.endpoint,
|
|
35
|
+
statusCode: request.statusCode,
|
|
36
|
+
durationMs: request.responseTime,
|
|
37
|
+
timestamp: new Date(request.timestamp).toISOString()
|
|
42
38
|
});
|
|
43
|
-
if (status === 'ANOMALY') {
|
|
44
|
-
this.alerts.warning(`Anomaly detected: ${request.method} ${request.endpoint} took ${request.responseTime}ms`);
|
|
45
|
-
}
|
|
46
|
-
this.bucket.recordRequest(request.method, request.endpoint);
|
|
47
39
|
}
|
|
48
|
-
tick() {
|
|
49
|
-
|
|
40
|
+
tick() { void this.closeInterval(false); }
|
|
41
|
+
closeLastInterval() { return this.closeInterval(true); }
|
|
42
|
+
dispose() { this.runtime.dispose(); }
|
|
43
|
+
async closeInterval(final) {
|
|
44
|
+
const bucket = this.bucket.flush({ force: final, seal: final });
|
|
45
|
+
if (!bucket)
|
|
46
|
+
return;
|
|
47
|
+
const ok = await this.persistence.onBucketClosed(bucket);
|
|
48
|
+
if (ok)
|
|
49
|
+
this.bucket.markCommitted(bucket.sequence);
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
exports.
|
|
52
|
+
exports.LocalRepository = LocalRepository;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type StoredRecord } from '../utility/index';
|
|
2
|
+
export type PersistenceCategory = 'http_requests' | 'events' | 'correlation' | 'metric_buckets';
|
|
3
|
+
export type StorageStatus = 'OK' | 'DEGRADED';
|
|
4
|
+
export interface WriterOptions {
|
|
5
|
+
maxQueue: number;
|
|
6
|
+
maxRetries: number;
|
|
7
|
+
retryDelayMs: number;
|
|
8
|
+
onStatusChange?: (status: StorageStatus) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface RecordInput<T> {
|
|
11
|
+
recordId: string;
|
|
12
|
+
payload: T;
|
|
13
|
+
}
|
|
14
|
+
export declare class PersistenceWriter {
|
|
15
|
+
private readonly queue;
|
|
16
|
+
private runner;
|
|
17
|
+
private accepting;
|
|
18
|
+
private readonly maxQueue;
|
|
19
|
+
private readonly maxRetries;
|
|
20
|
+
private readonly retryDelayMs;
|
|
21
|
+
private readonly onStatusChange?;
|
|
22
|
+
private _status;
|
|
23
|
+
private _lostRecords;
|
|
24
|
+
private _completedJobs;
|
|
25
|
+
private _lastError;
|
|
26
|
+
constructor(opts: WriterOptions);
|
|
27
|
+
get status(): StorageStatus;
|
|
28
|
+
get lostRecords(): number;
|
|
29
|
+
get completedJobs(): number;
|
|
30
|
+
get lastError(): {
|
|
31
|
+
description: string;
|
|
32
|
+
message: string;
|
|
33
|
+
at: string;
|
|
34
|
+
} | null;
|
|
35
|
+
get isAccepting(): boolean;
|
|
36
|
+
get pending(): number;
|
|
37
|
+
enqueueAppend<T>(baseDir: string, category: PersistenceCategory, items: RecordInput<T>[], opts?: {
|
|
38
|
+
date?: Date;
|
|
39
|
+
}): Promise<number>;
|
|
40
|
+
enqueueTask(description: string, task: () => Promise<void>): Promise<number>;
|
|
41
|
+
whenIdle(): Promise<void>;
|
|
42
|
+
close(): Promise<void>;
|
|
43
|
+
private push;
|
|
44
|
+
private kick;
|
|
45
|
+
private drain;
|
|
46
|
+
private markLost;
|
|
47
|
+
}
|
|
48
|
+
export declare function dedupeRecords<T>(source: AsyncIterable<StoredRecord<T>>, onConflict?: (id: string) => void): AsyncGenerator<StoredRecord<T>>;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.PersistenceWriter = void 0;
|
|
40
|
+
exports.dedupeRecords = dedupeRecords;
|
|
41
|
+
const fsp = __importStar(require("fs/promises"));
|
|
42
|
+
const logger_1 = __importDefault(require("../../telemetry/logger"));
|
|
43
|
+
const index_1 = require("../utility/index");
|
|
44
|
+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
45
|
+
class PersistenceWriter {
|
|
46
|
+
queue = [];
|
|
47
|
+
runner = null;
|
|
48
|
+
accepting = true;
|
|
49
|
+
maxQueue;
|
|
50
|
+
maxRetries;
|
|
51
|
+
retryDelayMs;
|
|
52
|
+
onStatusChange;
|
|
53
|
+
_status = 'OK';
|
|
54
|
+
_lostRecords = 0;
|
|
55
|
+
_completedJobs = 0;
|
|
56
|
+
_lastError = null;
|
|
57
|
+
constructor(opts) {
|
|
58
|
+
this.maxQueue = opts.maxQueue;
|
|
59
|
+
this.maxRetries = opts.maxRetries;
|
|
60
|
+
this.retryDelayMs = opts.retryDelayMs;
|
|
61
|
+
this.onStatusChange = opts.onStatusChange;
|
|
62
|
+
}
|
|
63
|
+
get status() { return this._status; }
|
|
64
|
+
get lostRecords() { return this._lostRecords; }
|
|
65
|
+
get completedJobs() { return this._completedJobs; }
|
|
66
|
+
get lastError() { return this._lastError; }
|
|
67
|
+
get isAccepting() { return this.accepting; }
|
|
68
|
+
get pending() { return this.queue.length; }
|
|
69
|
+
enqueueAppend(baseDir, category, items, opts = {}) {
|
|
70
|
+
if (items.length === 0)
|
|
71
|
+
return Promise.resolve(this._completedJobs);
|
|
72
|
+
const createdAt = new Date().toISOString();
|
|
73
|
+
const records = items.map(i => ({
|
|
74
|
+
id: i.recordId,
|
|
75
|
+
createdAt,
|
|
76
|
+
schemaVersion: 1,
|
|
77
|
+
category,
|
|
78
|
+
payload: i.payload
|
|
79
|
+
}));
|
|
80
|
+
let chunk;
|
|
81
|
+
try {
|
|
82
|
+
chunk = records.map(r => JSON.stringify(r)).join('\n') + '\n';
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
this.markLost(items.length);
|
|
86
|
+
return Promise.reject(new Error(`Serialization failed for ${category}: ${err}`));
|
|
87
|
+
}
|
|
88
|
+
const filePath = (0, index_1.constructFilePath)({ baseDir, category, date: opts.date });
|
|
89
|
+
return this.push({
|
|
90
|
+
description: `Append ${records.length} records to ${filePath}`,
|
|
91
|
+
recordCount: items.length,
|
|
92
|
+
run: async (attempt) => {
|
|
93
|
+
await fsp.appendFile(filePath, attempt > 0 ? '\n' + chunk : chunk, 'utf-8');
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
enqueueTask(description, task) {
|
|
98
|
+
return this.push({ description, recordCount: 0, run: () => task() });
|
|
99
|
+
}
|
|
100
|
+
async whenIdle() {
|
|
101
|
+
while (this.runner)
|
|
102
|
+
await this.runner;
|
|
103
|
+
}
|
|
104
|
+
async close() {
|
|
105
|
+
this.accepting = false;
|
|
106
|
+
await this.whenIdle();
|
|
107
|
+
}
|
|
108
|
+
push(partial) {
|
|
109
|
+
if (!this.accepting) {
|
|
110
|
+
this.markLost(partial.recordCount);
|
|
111
|
+
return Promise.reject(new Error(`Writer closed; rejected: ${partial.description}`));
|
|
112
|
+
}
|
|
113
|
+
if (this.queue.length >= this.maxQueue) {
|
|
114
|
+
this.markLost(partial.recordCount);
|
|
115
|
+
logger_1.default.error(`PersistenceWriter: queue full (${this.maxQueue}); rejected: ${partial.description}`);
|
|
116
|
+
return Promise.reject(new Error('Persistence queue full'));
|
|
117
|
+
}
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
this.queue.push({ ...partial, resolve, reject });
|
|
120
|
+
this.kick();
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
kick() {
|
|
124
|
+
if (this.runner)
|
|
125
|
+
return;
|
|
126
|
+
this.runner = this.drain()
|
|
127
|
+
.catch(err => logger_1.default.error('PersistenceWriter: unexpected drain error', err))
|
|
128
|
+
.finally(() => {
|
|
129
|
+
this.runner = null;
|
|
130
|
+
if (this.queue.length > 0)
|
|
131
|
+
this.kick();
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
async drain() {
|
|
135
|
+
while (this.queue.length > 0) {
|
|
136
|
+
const job = this.queue[0];
|
|
137
|
+
let lastErr = null;
|
|
138
|
+
let ok = false;
|
|
139
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
140
|
+
try {
|
|
141
|
+
await job.run(attempt);
|
|
142
|
+
ok = true;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
catch (err) {
|
|
146
|
+
lastErr = err;
|
|
147
|
+
if (attempt < this.maxRetries)
|
|
148
|
+
await sleep(this.retryDelayMs * 2 ** attempt);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
this.queue.shift();
|
|
152
|
+
if (ok) {
|
|
153
|
+
this._completedJobs++;
|
|
154
|
+
job.resolve(this._completedJobs);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this._lastError = {
|
|
158
|
+
description: job.description,
|
|
159
|
+
message: lastErr instanceof Error ? lastErr.message : String(lastErr),
|
|
160
|
+
at: new Date().toISOString()
|
|
161
|
+
};
|
|
162
|
+
this.markLost(job.recordCount);
|
|
163
|
+
logger_1.default.error(`PersistenceWriter: giving up on "${job.description}":`, lastErr);
|
|
164
|
+
job.reject(lastErr instanceof Error ? lastErr : new Error(String(lastErr)));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
markLost(count) {
|
|
169
|
+
this._lostRecords += count;
|
|
170
|
+
if (this._status !== 'DEGRADED') {
|
|
171
|
+
this._status = 'DEGRADED';
|
|
172
|
+
this.onStatusChange?.(this._status);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.PersistenceWriter = PersistenceWriter;
|
|
177
|
+
async function* dedupeRecords(source, onConflict) {
|
|
178
|
+
const seen = new Map();
|
|
179
|
+
for await (const record of source) {
|
|
180
|
+
const fingerprint = JSON.stringify(record.payload);
|
|
181
|
+
const prev = seen.get(record.id);
|
|
182
|
+
if (prev === undefined) {
|
|
183
|
+
seen.set(record.id, fingerprint);
|
|
184
|
+
yield record;
|
|
185
|
+
}
|
|
186
|
+
else if (prev !== fingerprint) {
|
|
187
|
+
logger_1.default.error(`Record ID conflict: ${record.id}`);
|
|
188
|
+
onConflict?.(record.id);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type MetricBucket } from '../stores/bucket-metric';
|
|
2
|
+
export interface SegmentEntry {
|
|
3
|
+
date: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
minSequence: number;
|
|
6
|
+
maxSequence: number;
|
|
7
|
+
minStartMs: number;
|
|
8
|
+
maxEndMs: number;
|
|
9
|
+
count: number;
|
|
10
|
+
}
|
|
11
|
+
export declare class SegmentIndex {
|
|
12
|
+
private readonly baseDir;
|
|
13
|
+
private readonly dir;
|
|
14
|
+
private readonly byDate;
|
|
15
|
+
private readonly dirty;
|
|
16
|
+
constructor(baseDir: string);
|
|
17
|
+
rebuild(): Promise<void>;
|
|
18
|
+
noteWritten(bucket: MetricBucket): void;
|
|
19
|
+
find(sessionId: string, fromMs: number, toMs: number): string[];
|
|
20
|
+
persistClosed(): Promise<void>;
|
|
21
|
+
private indexPath;
|
|
22
|
+
private resolveExistingFile;
|
|
23
|
+
private loadOrScan;
|
|
24
|
+
private tryLoad;
|
|
25
|
+
private scan;
|
|
26
|
+
private persist;
|
|
27
|
+
}
|