@jaak.ai/stamps 2.1.0 → 2.2.0-dev.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/README.md +42 -9
- package/dist/cjs/{index-Ga0t6BMe.js → index-D6NBn_qu.js} +30 -5
- package/dist/cjs/index-D6NBn_qu.js.map +1 -0
- package/dist/cjs/jaak-stamps-webcomponent.cjs.js +2 -2
- package/dist/cjs/jaak-stamps.cjs.entry.js +20082 -11
- package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
- package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +2 -2
- package/dist/collection/components/my-component/my-component.css +82 -2
- package/dist/collection/components/my-component/my-component.js +475 -9
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/LicenseValidationService.js +111 -0
- package/dist/collection/services/LicenseValidationService.js.map +1 -0
- package/dist/collection/services/MetricsService.js +350 -0
- package/dist/collection/services/MetricsService.js.map +1 -0
- package/dist/collection/services/ServiceContainer.js +66 -1
- package/dist/collection/services/ServiceContainer.js.map +1 -1
- package/dist/collection/services/TracingService.js +442 -0
- package/dist/collection/services/TracingService.js.map +1 -0
- package/dist/collection/services/interfaces/ILicenseValidationService.js +2 -0
- package/dist/collection/services/interfaces/ILicenseValidationService.js.map +1 -0
- package/dist/collection/services/interfaces/IMetricsService.js +2 -0
- package/dist/collection/services/interfaces/IMetricsService.js.map +1 -0
- package/dist/collection/services/interfaces/ITracingService.js +2 -0
- package/dist/collection/services/interfaces/ITracingService.js.map +1 -0
- package/dist/components/index.js +28 -3
- package/dist/components/index.js.map +1 -1
- package/dist/components/jaak-stamps.js +20096 -10
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/{index-Drbzcuq-.js → index-BCfAsrzL.js} +30 -5
- package/dist/esm/index-BCfAsrzL.js.map +1 -0
- package/dist/esm/jaak-stamps-webcomponent.js +3 -3
- package/dist/esm/jaak-stamps.entry.js +20082 -11
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +3 -3
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/p-8c49893d.entry.js +7 -0
- package/dist/jaak-stamps-webcomponent/p-8c49893d.entry.js.map +1 -0
- package/dist/jaak-stamps-webcomponent/p-BCfAsrzL.js +3 -0
- package/dist/jaak-stamps-webcomponent/p-BCfAsrzL.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +23 -0
- package/dist/types/components.d.ts +76 -0
- package/dist/types/services/LicenseValidationService.d.ts +59 -0
- package/dist/types/services/MetricsService.d.ts +74 -0
- package/dist/types/services/ServiceContainer.d.ts +14 -1
- package/dist/types/services/TracingService.d.ts +67 -0
- package/dist/types/services/interfaces/ILicenseValidationService.d.ts +4 -0
- package/dist/types/services/interfaces/IMetricsService.d.ts +70 -0
- package/dist/types/services/interfaces/ITracingService.d.ts +59 -0
- package/package.json +18 -2
- package/dist/cjs/index-Ga0t6BMe.js.map +0 -1
- package/dist/esm/index-Drbzcuq-.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-Drbzcuq-.js +0 -3
- package/dist/jaak-stamps-webcomponent/p-Drbzcuq-.js.map +0 -1
- package/dist/jaak-stamps-webcomponent/p-c06e5d7b.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-c06e5d7b.entry.js.map +0 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* License Validation Service
|
|
3
|
+
* Handles license validation for the jaak-stamps webcomponent
|
|
4
|
+
*/
|
|
5
|
+
export class LicenseValidationService {
|
|
6
|
+
apiEndpoint = '/api/v1/license/validate';
|
|
7
|
+
baseUrl;
|
|
8
|
+
// Environment-specific base URLs
|
|
9
|
+
static ENVIRONMENT_URLS = {
|
|
10
|
+
dev: 'https://api.dev.jaak.ai',
|
|
11
|
+
qa: 'https://api.qa.jaak.ai',
|
|
12
|
+
staging: 'https://api-staging.jaak.ai',
|
|
13
|
+
prod: 'https://api.jaak.ai',
|
|
14
|
+
};
|
|
15
|
+
constructor(environment = 'prod') {
|
|
16
|
+
this.baseUrl = LicenseValidationService.ENVIRONMENT_URLS[environment];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validate license with the API
|
|
20
|
+
* @param license License key to validate
|
|
21
|
+
* @param traceId Optional trace ID from W3C Trace Context
|
|
22
|
+
* @param appId Optional application ID
|
|
23
|
+
* @param metadata Optional additional metadata
|
|
24
|
+
* @returns Promise<LicenseValidationResponse>
|
|
25
|
+
*/
|
|
26
|
+
async validateLicense(license, traceId, appId = 'jaak-stamps-web') {
|
|
27
|
+
// Generate traceId if not provided
|
|
28
|
+
const effectiveTraceId = traceId || this.generateTraceId();
|
|
29
|
+
// Build request body
|
|
30
|
+
const requestBody = {
|
|
31
|
+
license: traceId ? `L${effectiveTraceId}` : license,
|
|
32
|
+
origin: 'web',
|
|
33
|
+
app_id: appId,
|
|
34
|
+
product: 'kyc',
|
|
35
|
+
metadata: {
|
|
36
|
+
sdk_version: '2.1.0',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
const headers = {
|
|
40
|
+
'Content-Type': 'application/json',
|
|
41
|
+
};
|
|
42
|
+
// Add traceparent header with the effective traceId
|
|
43
|
+
headers['traceparent'] = `00-${effectiveTraceId}-${this.generateSpanId()}-01`;
|
|
44
|
+
try {
|
|
45
|
+
const response = await fetch(`${this.baseUrl}${this.apiEndpoint}`, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers,
|
|
48
|
+
body: JSON.stringify(requestBody),
|
|
49
|
+
});
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
const errorData = await response.json().catch(() => ({}));
|
|
52
|
+
throw {
|
|
53
|
+
error: errorData.error || 'license_validation_failed',
|
|
54
|
+
error_description: errorData.error_description || `HTTP ${response.status}: ${response.statusText}`,
|
|
55
|
+
status_code: response.status,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const responseData = await response.json();
|
|
59
|
+
// Add the traceId to the response
|
|
60
|
+
return {
|
|
61
|
+
...responseData,
|
|
62
|
+
traceId: effectiveTraceId,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error.error) {
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
throw {
|
|
70
|
+
error: 'network_error',
|
|
71
|
+
error_description: 'Failed to connect to license validation service',
|
|
72
|
+
status_code: 0,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Generate a random trace ID for W3C Trace Context (32 hex characters)
|
|
78
|
+
*/
|
|
79
|
+
generateTraceId() {
|
|
80
|
+
const array = new Uint8Array(16);
|
|
81
|
+
crypto.getRandomValues(array);
|
|
82
|
+
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Generate a random span ID for W3C Trace Context (16 hex characters)
|
|
86
|
+
*/
|
|
87
|
+
generateSpanId() {
|
|
88
|
+
const array = new Uint8Array(8);
|
|
89
|
+
crypto.getRandomValues(array);
|
|
90
|
+
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extract trace ID from license field
|
|
94
|
+
* License format: "L<32-hex-chars>"
|
|
95
|
+
* Example: "Ld79223833a2fd91971e4c14a33298904" -> "d79223833a2fd91971e4c14a33298904"
|
|
96
|
+
*/
|
|
97
|
+
static extractTraceIdFromLicense(license) {
|
|
98
|
+
if (!license || typeof license !== 'string') {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
// Remove "L" prefix if present
|
|
102
|
+
const traceId = license.startsWith('L') ? license.slice(1) : license;
|
|
103
|
+
// Validate format (32 hex characters)
|
|
104
|
+
if (traceId.length === 32 && /^[a-f0-9]{32}$/i.test(traceId)) {
|
|
105
|
+
return traceId.toLowerCase();
|
|
106
|
+
}
|
|
107
|
+
console.warn('[LicenseValidationService] Invalid license format for trace ID:', license);
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=LicenseValidationService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LicenseValidationService.js","sourceRoot":"","sources":["../../src/services/LicenseValidationService.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * License Validation Service\n * Handles license validation for the jaak-stamps webcomponent\n */\n\nexport type Environment = 'dev' | 'qa' | 'staging' | 'prod';\n\nexport interface LicenseValidationRequest {\n license: string;\n origin: string;\n app_id?: string;\n product?: string;\n product_version?: string;\n metadata?: {\n [key: string]: any;\n };\n}\n\nexport interface LicenseValidationResponse {\n access_token: string;\n token_type: string;\n expires_at: string;\n session_id: string;\n jti: string;\n step: number;\n license: string;\n traceId?: string; // Generated or provided traceId\n}\n\nexport interface LicenseValidationError {\n error: string;\n error_description?: string;\n status_code?: number;\n}\n\nexport class LicenseValidationService {\n private apiEndpoint: string = '/api/v1/license/validate';\n private baseUrl: string;\n\n // Environment-specific base URLs\n private static readonly ENVIRONMENT_URLS: Record<Environment, string> = {\n dev: 'https://api.dev.jaak.ai',\n qa: 'https://api.qa.jaak.ai',\n staging: 'https://api-staging.jaak.ai',\n prod: 'https://api.jaak.ai',\n };\n\n constructor(environment: Environment = 'prod') {\n this.baseUrl = LicenseValidationService.ENVIRONMENT_URLS[environment];\n }\n\n /**\n * Validate license with the API\n * @param license License key to validate\n * @param traceId Optional trace ID from W3C Trace Context\n * @param appId Optional application ID\n * @param metadata Optional additional metadata\n * @returns Promise<LicenseValidationResponse>\n */\n async validateLicense(\n license: string,\n traceId?: string,\n appId: string = 'jaak-stamps-web',\n ): Promise<LicenseValidationResponse> {\n // Generate traceId if not provided\n const effectiveTraceId = traceId || this.generateTraceId();\n\n // Build request body\n const requestBody: LicenseValidationRequest = {\n license: traceId ? `L${effectiveTraceId}` : license,\n origin: 'web',\n app_id: appId,\n product: 'kyc',\n metadata: {\n sdk_version: '2.1.0',\n },\n };\n\n const headers: HeadersInit = {\n 'Content-Type': 'application/json',\n };\n\n // Add traceparent header with the effective traceId\n headers['traceparent'] = `00-${effectiveTraceId}-${this.generateSpanId()}-01`;\n\n try {\n const response = await fetch(`${this.baseUrl}${this.apiEndpoint}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}));\n throw {\n error: errorData.error || 'license_validation_failed',\n error_description: errorData.error_description || `HTTP ${response.status}: ${response.statusText}`,\n status_code: response.status,\n } as LicenseValidationError;\n }\n\n const responseData = await response.json();\n // Add the traceId to the response\n return {\n ...responseData,\n traceId: effectiveTraceId,\n };\n } catch (error) {\n if ((error as any).error) {\n throw error;\n }\n\n throw {\n error: 'network_error',\n error_description: 'Failed to connect to license validation service',\n status_code: 0,\n } as LicenseValidationError;\n }\n }\n\n /**\n * Generate a random trace ID for W3C Trace Context (32 hex characters)\n */\n private generateTraceId(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Generate a random span ID for W3C Trace Context (16 hex characters)\n */\n private generateSpanId(): string {\n const array = new Uint8Array(8);\n crypto.getRandomValues(array);\n return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Extract trace ID from license field\n * License format: \"L<32-hex-chars>\"\n * Example: \"Ld79223833a2fd91971e4c14a33298904\" -> \"d79223833a2fd91971e4c14a33298904\"\n */\n static extractTraceIdFromLicense(license: string): string | null {\n if (!license || typeof license !== 'string') {\n return null;\n }\n\n // Remove \"L\" prefix if present\n const traceId = license.startsWith('L') ? license.slice(1) : license;\n\n // Validate format (32 hex characters)\n if (traceId.length === 32 && /^[a-f0-9]{32}$/i.test(traceId)) {\n return traceId.toLowerCase();\n }\n\n console.warn('[LicenseValidationService] Invalid license format for trace ID:', license);\n return null;\n }\n}\n"]}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
|
|
2
|
+
import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
|
|
3
|
+
export class MetricsService {
|
|
4
|
+
meterProvider = null;
|
|
5
|
+
meter = null;
|
|
6
|
+
collectorUrl;
|
|
7
|
+
serviceName;
|
|
8
|
+
serviceVersion;
|
|
9
|
+
debug;
|
|
10
|
+
customerId;
|
|
11
|
+
tenantId;
|
|
12
|
+
environment;
|
|
13
|
+
exportIntervalMillis;
|
|
14
|
+
metricReader = null;
|
|
15
|
+
// Business Metrics - Counters
|
|
16
|
+
captureCounter = null;
|
|
17
|
+
errorCounter = null;
|
|
18
|
+
modelLoadCounter = null;
|
|
19
|
+
userInteractionCounter = null;
|
|
20
|
+
// Business Metrics - Histograms (for latencies and sizes)
|
|
21
|
+
captureLatencyHistogram = null;
|
|
22
|
+
modelLoadLatencyHistogram = null;
|
|
23
|
+
detectionLatencyHistogram = null;
|
|
24
|
+
imageSizeHistogram = null;
|
|
25
|
+
// Business Metrics - Gauges (for current state)
|
|
26
|
+
activeSessionsGauge = null;
|
|
27
|
+
memoryUsageGauge = null;
|
|
28
|
+
// State tracking
|
|
29
|
+
activeSessions = 0;
|
|
30
|
+
gaugeValues = new Map();
|
|
31
|
+
constructor(config = {}) {
|
|
32
|
+
this.collectorUrl = config.collectorUrl || 'https://collector.jaak.ai/v1/metrics';
|
|
33
|
+
this.serviceName = config.serviceName || 'jaak-stamps-webcomponent';
|
|
34
|
+
this.serviceVersion = config.serviceVersion || '2.1.0';
|
|
35
|
+
this.debug = config.debug || false;
|
|
36
|
+
this.customerId = config.customerId;
|
|
37
|
+
this.tenantId = config.tenantId;
|
|
38
|
+
this.environment = config.environment || 'production';
|
|
39
|
+
this.exportIntervalMillis = config.exportIntervalMillis || 60000; // Export every 60 seconds
|
|
40
|
+
}
|
|
41
|
+
initialize() {
|
|
42
|
+
try {
|
|
43
|
+
// Create OTLP metrics exporter
|
|
44
|
+
const exporter = new OTLPMetricExporter({
|
|
45
|
+
url: this.collectorUrl,
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
// Create periodic exporting metric reader
|
|
51
|
+
this.metricReader = new PeriodicExportingMetricReader({
|
|
52
|
+
exporter: exporter,
|
|
53
|
+
exportIntervalMillis: this.exportIntervalMillis,
|
|
54
|
+
exportTimeoutMillis: 30000,
|
|
55
|
+
});
|
|
56
|
+
// Create meter provider
|
|
57
|
+
this.meterProvider = new MeterProvider({
|
|
58
|
+
readers: [this.metricReader],
|
|
59
|
+
});
|
|
60
|
+
// Get meter
|
|
61
|
+
this.meter = this.meterProvider.getMeter(this.serviceName, this.serviceVersion);
|
|
62
|
+
// Initialize business metrics
|
|
63
|
+
this.initializeMetrics();
|
|
64
|
+
if (this.debug) {
|
|
65
|
+
console.log('[MetricsService] Initialized successfully', {
|
|
66
|
+
collectorUrl: this.collectorUrl,
|
|
67
|
+
serviceName: this.serviceName,
|
|
68
|
+
serviceVersion: this.serviceVersion,
|
|
69
|
+
environment: this.environment,
|
|
70
|
+
customerId: this.customerId,
|
|
71
|
+
tenantId: this.tenantId,
|
|
72
|
+
exportIntervalMillis: this.exportIntervalMillis,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error('[MetricsService] Failed to initialize:', error);
|
|
78
|
+
// Don't throw - metrics should fail silently in production
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
initializeMetrics() {
|
|
82
|
+
if (!this.meter)
|
|
83
|
+
return;
|
|
84
|
+
const baseAttributes = this.getBaseAttributes();
|
|
85
|
+
try {
|
|
86
|
+
// COUNTERS - Track events and totals
|
|
87
|
+
// Total captures performed
|
|
88
|
+
this.captureCounter = this.meter.createCounter('jaak.captures.total', {
|
|
89
|
+
description: 'Total number of document captures',
|
|
90
|
+
unit: '1',
|
|
91
|
+
});
|
|
92
|
+
// Total errors
|
|
93
|
+
this.errorCounter = this.meter.createCounter('jaak.errors.total', {
|
|
94
|
+
description: 'Total number of errors',
|
|
95
|
+
unit: '1',
|
|
96
|
+
});
|
|
97
|
+
// Model loads
|
|
98
|
+
this.modelLoadCounter = this.meter.createCounter('jaak.model.loads.total', {
|
|
99
|
+
description: 'Total number of model loads',
|
|
100
|
+
unit: '1',
|
|
101
|
+
});
|
|
102
|
+
// User interactions
|
|
103
|
+
this.userInteractionCounter = this.meter.createCounter('jaak.user.interactions.total', {
|
|
104
|
+
description: 'Total user interactions (clicks, etc.)',
|
|
105
|
+
unit: '1',
|
|
106
|
+
});
|
|
107
|
+
// HISTOGRAMS - Track distributions of latencies and sizes
|
|
108
|
+
// Capture latency (time from start to completion)
|
|
109
|
+
this.captureLatencyHistogram = this.meter.createHistogram('jaak.capture.duration', {
|
|
110
|
+
description: 'Duration of capture operations',
|
|
111
|
+
unit: 'ms',
|
|
112
|
+
});
|
|
113
|
+
// Model load latency
|
|
114
|
+
this.modelLoadLatencyHistogram = this.meter.createHistogram('jaak.model.load.duration', {
|
|
115
|
+
description: 'Duration of model loading',
|
|
116
|
+
unit: 'ms',
|
|
117
|
+
});
|
|
118
|
+
// Detection latency (per frame)
|
|
119
|
+
this.detectionLatencyHistogram = this.meter.createHistogram('jaak.detection.duration', {
|
|
120
|
+
description: 'Duration of detection inference',
|
|
121
|
+
unit: 'ms',
|
|
122
|
+
});
|
|
123
|
+
// Image sizes
|
|
124
|
+
this.imageSizeHistogram = this.meter.createHistogram('jaak.image.size', {
|
|
125
|
+
description: 'Size of captured images',
|
|
126
|
+
unit: 'bytes',
|
|
127
|
+
});
|
|
128
|
+
// GAUGES - Track current state values
|
|
129
|
+
// Active capture sessions
|
|
130
|
+
this.activeSessionsGauge = this.meter.createObservableGauge('jaak.sessions.active', {
|
|
131
|
+
description: 'Number of active capture sessions',
|
|
132
|
+
unit: '1',
|
|
133
|
+
});
|
|
134
|
+
this.activeSessionsGauge.addCallback((observableResult) => {
|
|
135
|
+
observableResult.observe(this.activeSessions, baseAttributes);
|
|
136
|
+
});
|
|
137
|
+
// Memory usage (if available)
|
|
138
|
+
if ('memory' in performance) {
|
|
139
|
+
this.memoryUsageGauge = this.meter.createObservableGauge('jaak.memory.usage', {
|
|
140
|
+
description: 'JavaScript heap memory usage',
|
|
141
|
+
unit: 'bytes',
|
|
142
|
+
});
|
|
143
|
+
this.memoryUsageGauge.addCallback((observableResult) => {
|
|
144
|
+
const memInfo = performance.memory;
|
|
145
|
+
if (memInfo) {
|
|
146
|
+
observableResult.observe(memInfo.usedJSHeapSize, baseAttributes);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
if (this.debug) {
|
|
151
|
+
console.log('[MetricsService] Business metrics initialized');
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
console.error('[MetricsService] Failed to initialize metrics:', error);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
getBaseAttributes() {
|
|
159
|
+
const attributes = {
|
|
160
|
+
'service.name': this.serviceName,
|
|
161
|
+
'service.version': this.serviceVersion,
|
|
162
|
+
'deployment.environment': this.environment,
|
|
163
|
+
};
|
|
164
|
+
if (this.customerId) {
|
|
165
|
+
attributes['customer.id'] = this.customerId;
|
|
166
|
+
}
|
|
167
|
+
if (this.tenantId) {
|
|
168
|
+
attributes['tenant.id'] = this.tenantId;
|
|
169
|
+
}
|
|
170
|
+
return attributes;
|
|
171
|
+
}
|
|
172
|
+
incrementCounter(name, value = 1, attributes) {
|
|
173
|
+
try {
|
|
174
|
+
const baseAttrs = this.getBaseAttributes();
|
|
175
|
+
const allAttrs = { ...baseAttrs, ...attributes };
|
|
176
|
+
// Use predefined counters or create ad-hoc
|
|
177
|
+
switch (name) {
|
|
178
|
+
case 'captures':
|
|
179
|
+
this.captureCounter?.add(value, allAttrs);
|
|
180
|
+
break;
|
|
181
|
+
case 'errors':
|
|
182
|
+
this.errorCounter?.add(value, allAttrs);
|
|
183
|
+
break;
|
|
184
|
+
case 'model_loads':
|
|
185
|
+
this.modelLoadCounter?.add(value, allAttrs);
|
|
186
|
+
break;
|
|
187
|
+
case 'user_interactions':
|
|
188
|
+
this.userInteractionCounter?.add(value, allAttrs);
|
|
189
|
+
break;
|
|
190
|
+
default:
|
|
191
|
+
// Create ad-hoc counter if needed
|
|
192
|
+
if (this.meter) {
|
|
193
|
+
const counter = this.meter.createCounter(`jaak.${name}.total`, {
|
|
194
|
+
description: `Total ${name}`,
|
|
195
|
+
unit: '1',
|
|
196
|
+
});
|
|
197
|
+
counter.add(value, allAttrs);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (this.debug) {
|
|
201
|
+
console.log(`[MetricsService] Counter incremented: ${name} += ${value}`, attributes);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
console.error('[MetricsService] Failed to increment counter:', error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
recordHistogram(name, value, attributes) {
|
|
209
|
+
try {
|
|
210
|
+
const baseAttrs = this.getBaseAttributes();
|
|
211
|
+
const allAttrs = { ...baseAttrs, ...attributes };
|
|
212
|
+
// Use predefined histograms
|
|
213
|
+
switch (name) {
|
|
214
|
+
case 'capture_duration':
|
|
215
|
+
this.captureLatencyHistogram?.record(value, allAttrs);
|
|
216
|
+
break;
|
|
217
|
+
case 'model_load_duration':
|
|
218
|
+
this.modelLoadLatencyHistogram?.record(value, allAttrs);
|
|
219
|
+
break;
|
|
220
|
+
case 'detection_duration':
|
|
221
|
+
this.detectionLatencyHistogram?.record(value, allAttrs);
|
|
222
|
+
break;
|
|
223
|
+
case 'image_size':
|
|
224
|
+
this.imageSizeHistogram?.record(value, allAttrs);
|
|
225
|
+
break;
|
|
226
|
+
default:
|
|
227
|
+
// Create ad-hoc histogram if needed
|
|
228
|
+
if (this.meter) {
|
|
229
|
+
const histogram = this.meter.createHistogram(`jaak.${name}`, {
|
|
230
|
+
description: `Distribution of ${name}`,
|
|
231
|
+
unit: 'ms',
|
|
232
|
+
});
|
|
233
|
+
histogram.record(value, allAttrs);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
if (this.debug) {
|
|
237
|
+
console.log(`[MetricsService] Histogram recorded: ${name} = ${value}`, attributes);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
console.error('[MetricsService] Failed to record histogram:', error);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
setGauge(name, value, attributes) {
|
|
245
|
+
try {
|
|
246
|
+
// Store gauge value for observable callbacks
|
|
247
|
+
this.gaugeValues.set(name, value);
|
|
248
|
+
// Update known gauges
|
|
249
|
+
if (name === 'active_sessions') {
|
|
250
|
+
this.activeSessions = value;
|
|
251
|
+
}
|
|
252
|
+
if (this.debug) {
|
|
253
|
+
console.log(`[MetricsService] Gauge set: ${name} = ${value}`, attributes);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
console.error('[MetricsService] Failed to set gauge:', error);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async measureDuration(name, fn, attributes) {
|
|
261
|
+
const startTime = performance.now();
|
|
262
|
+
try {
|
|
263
|
+
const result = await fn();
|
|
264
|
+
const duration = performance.now() - startTime;
|
|
265
|
+
this.recordHistogram(name, duration, attributes);
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
const duration = performance.now() - startTime;
|
|
270
|
+
this.recordHistogram(name, duration, { ...attributes, error: true });
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async flush() {
|
|
275
|
+
try {
|
|
276
|
+
if (this.metricReader) {
|
|
277
|
+
await this.metricReader.forceFlush();
|
|
278
|
+
}
|
|
279
|
+
if (this.debug) {
|
|
280
|
+
console.log('[MetricsService] Metrics flushed');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
console.error('[MetricsService] Failed to flush metrics:', error);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
async cleanup() {
|
|
288
|
+
try {
|
|
289
|
+
if (this.metricReader) {
|
|
290
|
+
await this.metricReader.forceFlush();
|
|
291
|
+
await this.metricReader.shutdown();
|
|
292
|
+
}
|
|
293
|
+
if (this.meterProvider) {
|
|
294
|
+
await this.meterProvider.shutdown();
|
|
295
|
+
}
|
|
296
|
+
if (this.debug) {
|
|
297
|
+
console.log('[MetricsService] Cleaned up successfully');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
console.error('[MetricsService] Failed to cleanup:', error);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Helper methods for common business metrics
|
|
305
|
+
/**
|
|
306
|
+
* Record a successful capture
|
|
307
|
+
*/
|
|
308
|
+
recordCapture(side, mode, durationMs) {
|
|
309
|
+
this.incrementCounter('captures', 1, { side, mode });
|
|
310
|
+
this.recordHistogram('capture_duration', durationMs, { side, mode });
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Record an error
|
|
314
|
+
*/
|
|
315
|
+
recordError(errorType, operation) {
|
|
316
|
+
this.incrementCounter('errors', 1, { error_type: errorType, operation });
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Record model load performance
|
|
320
|
+
*/
|
|
321
|
+
recordModelLoad(modelType, durationMs, cached) {
|
|
322
|
+
this.incrementCounter('model_loads', 1, { model_type: modelType, cached });
|
|
323
|
+
this.recordHistogram('model_load_duration', durationMs, { model_type: modelType, cached });
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Record detection performance
|
|
327
|
+
*/
|
|
328
|
+
recordDetection(durationMs, detectionsFound) {
|
|
329
|
+
this.recordHistogram('detection_duration', durationMs, { detections_found: detectionsFound });
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Record image size
|
|
333
|
+
*/
|
|
334
|
+
recordImageSize(side, sizeBytes) {
|
|
335
|
+
this.recordHistogram('image_size', sizeBytes, { side });
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Update active sessions count
|
|
339
|
+
*/
|
|
340
|
+
updateActiveSessions(count) {
|
|
341
|
+
this.setGauge('active_sessions', count);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Record user interaction
|
|
345
|
+
*/
|
|
346
|
+
recordUserInteraction(interactionType) {
|
|
347
|
+
this.incrementCounter('user_interactions', 1, { interaction_type: interactionType });
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
//# sourceMappingURL=MetricsService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MetricsService.js","sourceRoot":"","sources":["../../src/services/MetricsService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,6BAA6B,EAAE,MAAM,4BAA4B,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2CAA2C,CAAC;AAc/E,MAAM,OAAO,cAAc;IACjB,aAAa,GAAyB,IAAI,CAAC;IAC3C,KAAK,GAAiB,IAAI,CAAC;IAClB,YAAY,CAAS;IACrB,WAAW,CAAS;IACpB,cAAc,CAAS;IACvB,KAAK,CAAU;IACf,UAAU,CAAU;IACpB,QAAQ,CAAU;IAClB,WAAW,CAAU;IACrB,oBAAoB,CAAS;IACtC,YAAY,GAAyC,IAAI,CAAC;IAElE,8BAA8B;IACtB,cAAc,GAAmB,IAAI,CAAC;IACtC,YAAY,GAAmB,IAAI,CAAC;IACpC,gBAAgB,GAAmB,IAAI,CAAC;IACxC,sBAAsB,GAAmB,IAAI,CAAC;IAEtD,0DAA0D;IAClD,uBAAuB,GAAqB,IAAI,CAAC;IACjD,yBAAyB,GAAqB,IAAI,CAAC;IACnD,yBAAyB,GAAqB,IAAI,CAAC;IACnD,kBAAkB,GAAqB,IAAI,CAAC;IAEpD,gDAAgD;IACxC,mBAAmB,GAA2B,IAAI,CAAC;IACnD,gBAAgB,GAA2B,IAAI,CAAC;IAExD,iBAAiB;IACT,cAAc,GAAW,CAAC,CAAC;IAC3B,WAAW,GAAwB,IAAI,GAAG,EAAE,CAAC;IAErD,YAAY,SAAwB,EAAE;QACpC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,IAAI,sCAAsC,CAAC;QAClF,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,0BAA0B,CAAC;QACpE,IAAI,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,IAAI,OAAO,CAAC;QACvD,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC;QACnC,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QACpC,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,YAAY,CAAC;QACtD,IAAI,CAAC,oBAAoB,GAAG,MAAM,CAAC,oBAAoB,IAAI,KAAK,CAAC,CAAC,0BAA0B;IAC9F,CAAC;IAED,UAAU;QACR,IAAI,CAAC;YACH,+BAA+B;YAC/B,MAAM,QAAQ,GAAG,IAAI,kBAAkB,CAAC;gBACtC,GAAG,EAAE,IAAI,CAAC,YAAY;gBACtB,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;iBACnC;aACF,CAAC,CAAC;YAEH,0CAA0C;YAC1C,IAAI,CAAC,YAAY,GAAG,IAAI,6BAA6B,CAAC;gBACpD,QAAQ,EAAE,QAAQ;gBAClB,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;gBAC/C,mBAAmB,EAAE,KAAK;aAC3B,CAAC,CAAC;YAEH,wBAAwB;YACxB,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,CAAC;gBACrC,OAAO,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;aAC7B,CAAC,CAAC;YAEH,YAAY;YACZ,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;YAEhF,8BAA8B;YAC9B,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAEzB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,2CAA2C,EAAE;oBACvD,YAAY,EAAE,IAAI,CAAC,YAAY;oBAC/B,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,cAAc,EAAE,IAAI,CAAC,cAAc;oBACnC,WAAW,EAAE,IAAI,CAAC,WAAW;oBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;oBAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;oBACvB,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;iBAChD,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,wCAAwC,EAAE,KAAK,CAAC,CAAC;YAC/D,2DAA2D;QAC7D,CAAC;IACH,CAAC;IAEO,iBAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,OAAO;QAExB,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEhD,IAAI,CAAC;YACH,qCAAqC;YAErC,2BAA2B;YAC3B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,qBAAqB,EAAE;gBACpE,WAAW,EAAE,mCAAmC;gBAChD,IAAI,EAAE,GAAG;aACV,CAAC,CAAC;YAEH,eAAe;YACf,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,mBAAmB,EAAE;gBAChE,WAAW,EAAE,wBAAwB;gBACrC,IAAI,EAAE,GAAG;aACV,CAAC,CAAC;YAEH,cAAc;YACd,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,wBAAwB,EAAE;gBACzE,WAAW,EAAE,6BAA6B;gBAC1C,IAAI,EAAE,GAAG;aACV,CAAC,CAAC;YAEH,oBAAoB;YACpB,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,8BAA8B,EAAE;gBACrF,WAAW,EAAE,wCAAwC;gBACrD,IAAI,EAAE,GAAG;aACV,CAAC,CAAC;YAEH,0DAA0D;YAE1D,kDAAkD;YAClD,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,uBAAuB,EAAE;gBACjF,WAAW,EAAE,gCAAgC;gBAC7C,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;YAEH,qBAAqB;YACrB,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,0BAA0B,EAAE;gBACtF,WAAW,EAAE,2BAA2B;gBACxC,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;YAEH,gCAAgC;YAChC,IAAI,CAAC,yBAAyB,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,yBAAyB,EAAE;gBACrF,WAAW,EAAE,iCAAiC;gBAC9C,IAAI,EAAE,IAAI;aACX,CAAC,CAAC;YAEH,cAAc;YACd,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,iBAAiB,EAAE;gBACtE,WAAW,EAAE,yBAAyB;gBACtC,IAAI,EAAE,OAAO;aACd,CAAC,CAAC;YAEH,sCAAsC;YAEtC,0BAA0B;YAC1B,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,sBAAsB,EAAE;gBAClF,WAAW,EAAE,mCAAmC;gBAChD,IAAI,EAAE,GAAG;aACV,CAAC,CAAC;YAEH,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC,gBAAgB,EAAE,EAAE;gBACxD,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;YAChE,CAAC,CAAC,CAAC;YAEH,8BAA8B;YAC9B,IAAI,QAAQ,IAAI,WAAW,EAAE,CAAC;gBAC5B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,mBAAmB,EAAE;oBAC5E,WAAW,EAAE,8BAA8B;oBAC3C,IAAI,EAAE,OAAO;iBACd,CAAC,CAAC;gBAEH,IAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,gBAAgB,EAAE,EAAE;oBACrD,MAAM,OAAO,GAAI,WAAmB,CAAC,MAAM,CAAC;oBAC5C,IAAI,OAAO,EAAE,CAAC;wBACZ,gBAAgB,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,cAAc,CAAC,CAAC;oBACnE,CAAC;gBACH,CAAC,CAAC,CAAC;YACL,CAAC;YAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;YAC/D,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,gDAAgD,EAAE,KAAK,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAEO,iBAAiB;QACvB,MAAM,UAAU,GAAwB;YACtC,cAAc,EAAE,IAAI,CAAC,WAAW;YAChC,iBAAiB,EAAE,IAAI,CAAC,cAAc;YACtC,wBAAwB,EAAE,IAAI,CAAC,WAAW;SAC3C,CAAC;QAEF,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,UAAU,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC;QAC9C,CAAC;QAED,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,UAAU,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC1C,CAAC;QAED,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,gBAAgB,CAAC,IAAY,EAAE,QAAgB,CAAC,EAAE,UAAgC;QAChF,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3C,MAAM,QAAQ,GAAG,EAAE,GAAG,SAAS,EAAE,GAAG,UAAU,EAAE,CAAC;YAEjD,2CAA2C;YAC3C,QAAQ,IAAI,EAAE,CAAC;gBACb,KAAK,UAAU;oBACb,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBAC1C,MAAM;gBACR,KAAK,QAAQ;oBACX,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBACxC,MAAM;gBACR,KAAK,aAAa;oBAChB,IAAI,CAAC,gBAAgB,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBAC5C,MAAM;gBACR,KAAK,mBAAmB;oBACtB,IAAI,CAAC,sBAAsB,EAAE,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBAClD,MAAM;gBACR;oBACE,kCAAkC;oBAClC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;wBACf,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,QAAQ,IAAI,QAAQ,EAAE;4BAC7D,WAAW,EAAE,SAAS,IAAI,EAAE;4BAC5B,IAAI,EAAE,GAAG;yBACV,CAAC,CAAC;wBACH,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBAC/B,CAAC;YACL,CAAC;YAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,yCAAyC,IAAI,OAAO,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;YACvF,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,+CAA+C,EAAE,KAAK,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IAED,eAAe,CAAC,IAAY,EAAE,KAAa,EAAE,UAAgC;QAC3E,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3C,MAAM,QAAQ,GAAG,EAAE,GAAG,SAAS,EAAE,GAAG,UAAU,EAAE,CAAC;YAEjD,4BAA4B;YAC5B,QAAQ,IAAI,EAAE,CAAC;gBACb,KAAK,kBAAkB;oBACrB,IAAI,CAAC,uBAAuB,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBACtD,MAAM;gBACR,KAAK,qBAAqB;oBACxB,IAAI,CAAC,yBAAyB,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBACxD,MAAM;gBACR,KAAK,oBAAoB;oBACvB,IAAI,CAAC,yBAAyB,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBACxD,MAAM;gBACR,KAAK,YAAY;oBACf,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBACjD,MAAM;gBACR;oBACE,oCAAoC;oBACpC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;wBACf,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,QAAQ,IAAI,EAAE,EAAE;4BAC3D,WAAW,EAAE,mBAAmB,IAAI,EAAE;4BACtC,IAAI,EAAE,IAAI;yBACX,CAAC,CAAC;wBACH,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;oBACpC,CAAC;YACL,CAAC;YAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,wCAAwC,IAAI,MAAM,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;YACrF,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,8CAA8C,EAAE,KAAK,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;IAED,QAAQ,CAAC,IAAY,EAAE,KAAa,EAAE,UAAgC;QACpE,IAAI,CAAC;YACH,6CAA6C;YAC7C,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAElC,sBAAsB;YACtB,IAAI,IAAI,KAAK,iBAAiB,EAAE,CAAC;gBAC/B,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC;YAC9B,CAAC;YAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,+BAA+B,IAAI,MAAM,KAAK,EAAE,EAAE,UAAU,CAAC,CAAC;YAC5E,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAI,IAAY,EAAE,EAAwB,EAAE,UAAgC;QAC/F,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;QAEpC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,EAAE,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAE/C,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;YAEjD,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAC/C,IAAI,CAAC,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACrE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;YACvC,CAAC;YAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;YAClD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,KAAK,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAED,KAAK,CAAC,OAAO;QACX,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;gBACtB,MAAM,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC;gBACrC,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC;YACrC,CAAC;YAED,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;gBACvB,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;YACtC,CAAC;YAED,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;YAC1D,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,6CAA6C;IAE7C;;OAEG;IACH,aAAa,CAAC,IAAsB,EAAE,IAAuB,EAAE,UAAkB;QAC/E,IAAI,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,eAAe,CAAC,kBAAkB,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,SAAiB,EAAE,SAAiB;QAC9C,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED;;OAEG;IACH,eAAe,CAAC,SAAyC,EAAE,UAAkB,EAAE,MAAe;QAC5F,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,CAAC,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3E,IAAI,CAAC,eAAe,CAAC,qBAAqB,EAAE,UAAU,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED;;OAEG;IACH,eAAe,CAAC,UAAkB,EAAE,eAAuB;QACzD,IAAI,CAAC,eAAe,CAAC,oBAAoB,EAAE,UAAU,EAAE,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC,CAAC;IAChG,CAAC;IAED;;OAEG;IACH,eAAe,CAAC,IAAsB,EAAE,SAAiB;QACvD,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,oBAAoB,CAAC,KAAa;QAChC,IAAI,CAAC,QAAQ,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,qBAAqB,CAAC,eAAuB;QAC3C,IAAI,CAAC,gBAAgB,CAAC,mBAAmB,EAAE,CAAC,EAAE,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC,CAAC;IACvF,CAAC;CACF","sourcesContent":["import { IMetricsService } from './interfaces/IMetricsService';\nimport { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';\nimport type { Counter, Histogram, ObservableGauge, Meter } from '@opentelemetry/api';\n\nexport interface MetricsConfig {\n collectorUrl?: string;\n serviceName?: string;\n serviceVersion?: string;\n debug?: boolean;\n customerId?: string;\n tenantId?: string;\n environment?: string;\n exportIntervalMillis?: number;\n}\n\nexport class MetricsService implements IMetricsService {\n private meterProvider: MeterProvider | null = null;\n private meter: Meter | null = null;\n private readonly collectorUrl: string;\n private readonly serviceName: string;\n private readonly serviceVersion: string;\n private readonly debug: boolean;\n private readonly customerId?: string;\n private readonly tenantId?: string;\n private readonly environment?: string;\n private readonly exportIntervalMillis: number;\n private metricReader: PeriodicExportingMetricReader | null = null;\n\n // Business Metrics - Counters\n private captureCounter: Counter | null = null;\n private errorCounter: Counter | null = null;\n private modelLoadCounter: Counter | null = null;\n private userInteractionCounter: Counter | null = null;\n\n // Business Metrics - Histograms (for latencies and sizes)\n private captureLatencyHistogram: Histogram | null = null;\n private modelLoadLatencyHistogram: Histogram | null = null;\n private detectionLatencyHistogram: Histogram | null = null;\n private imageSizeHistogram: Histogram | null = null;\n\n // Business Metrics - Gauges (for current state)\n private activeSessionsGauge: ObservableGauge | null = null;\n private memoryUsageGauge: ObservableGauge | null = null;\n\n // State tracking\n private activeSessions: number = 0;\n private gaugeValues: Map<string, number> = new Map();\n\n constructor(config: MetricsConfig = {}) {\n this.collectorUrl = config.collectorUrl || 'https://collector.jaak.ai/v1/metrics';\n this.serviceName = config.serviceName || 'jaak-stamps-webcomponent';\n this.serviceVersion = config.serviceVersion || '2.1.0';\n this.debug = config.debug || false;\n this.customerId = config.customerId;\n this.tenantId = config.tenantId;\n this.environment = config.environment || 'production';\n this.exportIntervalMillis = config.exportIntervalMillis || 60000; // Export every 60 seconds\n }\n\n initialize(): void {\n try {\n // Create OTLP metrics exporter\n const exporter = new OTLPMetricExporter({\n url: this.collectorUrl,\n headers: {\n 'Content-Type': 'application/json',\n },\n });\n\n // Create periodic exporting metric reader\n this.metricReader = new PeriodicExportingMetricReader({\n exporter: exporter,\n exportIntervalMillis: this.exportIntervalMillis,\n exportTimeoutMillis: 30000,\n });\n\n // Create meter provider\n this.meterProvider = new MeterProvider({\n readers: [this.metricReader],\n });\n\n // Get meter\n this.meter = this.meterProvider.getMeter(this.serviceName, this.serviceVersion);\n\n // Initialize business metrics\n this.initializeMetrics();\n\n if (this.debug) {\n console.log('[MetricsService] Initialized successfully', {\n collectorUrl: this.collectorUrl,\n serviceName: this.serviceName,\n serviceVersion: this.serviceVersion,\n environment: this.environment,\n customerId: this.customerId,\n tenantId: this.tenantId,\n exportIntervalMillis: this.exportIntervalMillis,\n });\n }\n } catch (error) {\n console.error('[MetricsService] Failed to initialize:', error);\n // Don't throw - metrics should fail silently in production\n }\n }\n\n private initializeMetrics(): void {\n if (!this.meter) return;\n\n const baseAttributes = this.getBaseAttributes();\n\n try {\n // COUNTERS - Track events and totals\n\n // Total captures performed\n this.captureCounter = this.meter.createCounter('jaak.captures.total', {\n description: 'Total number of document captures',\n unit: '1',\n });\n\n // Total errors\n this.errorCounter = this.meter.createCounter('jaak.errors.total', {\n description: 'Total number of errors',\n unit: '1',\n });\n\n // Model loads\n this.modelLoadCounter = this.meter.createCounter('jaak.model.loads.total', {\n description: 'Total number of model loads',\n unit: '1',\n });\n\n // User interactions\n this.userInteractionCounter = this.meter.createCounter('jaak.user.interactions.total', {\n description: 'Total user interactions (clicks, etc.)',\n unit: '1',\n });\n\n // HISTOGRAMS - Track distributions of latencies and sizes\n\n // Capture latency (time from start to completion)\n this.captureLatencyHistogram = this.meter.createHistogram('jaak.capture.duration', {\n description: 'Duration of capture operations',\n unit: 'ms',\n });\n\n // Model load latency\n this.modelLoadLatencyHistogram = this.meter.createHistogram('jaak.model.load.duration', {\n description: 'Duration of model loading',\n unit: 'ms',\n });\n\n // Detection latency (per frame)\n this.detectionLatencyHistogram = this.meter.createHistogram('jaak.detection.duration', {\n description: 'Duration of detection inference',\n unit: 'ms',\n });\n\n // Image sizes\n this.imageSizeHistogram = this.meter.createHistogram('jaak.image.size', {\n description: 'Size of captured images',\n unit: 'bytes',\n });\n\n // GAUGES - Track current state values\n\n // Active capture sessions\n this.activeSessionsGauge = this.meter.createObservableGauge('jaak.sessions.active', {\n description: 'Number of active capture sessions',\n unit: '1',\n });\n\n this.activeSessionsGauge.addCallback((observableResult) => {\n observableResult.observe(this.activeSessions, baseAttributes);\n });\n\n // Memory usage (if available)\n if ('memory' in performance) {\n this.memoryUsageGauge = this.meter.createObservableGauge('jaak.memory.usage', {\n description: 'JavaScript heap memory usage',\n unit: 'bytes',\n });\n\n this.memoryUsageGauge.addCallback((observableResult) => {\n const memInfo = (performance as any).memory;\n if (memInfo) {\n observableResult.observe(memInfo.usedJSHeapSize, baseAttributes);\n }\n });\n }\n\n if (this.debug) {\n console.log('[MetricsService] Business metrics initialized');\n }\n } catch (error) {\n console.error('[MetricsService] Failed to initialize metrics:', error);\n }\n }\n\n private getBaseAttributes(): Record<string, any> {\n const attributes: Record<string, any> = {\n 'service.name': this.serviceName,\n 'service.version': this.serviceVersion,\n 'deployment.environment': this.environment,\n };\n\n if (this.customerId) {\n attributes['customer.id'] = this.customerId;\n }\n\n if (this.tenantId) {\n attributes['tenant.id'] = this.tenantId;\n }\n\n return attributes;\n }\n\n incrementCounter(name: string, value: number = 1, attributes?: Record<string, any>): void {\n try {\n const baseAttrs = this.getBaseAttributes();\n const allAttrs = { ...baseAttrs, ...attributes };\n\n // Use predefined counters or create ad-hoc\n switch (name) {\n case 'captures':\n this.captureCounter?.add(value, allAttrs);\n break;\n case 'errors':\n this.errorCounter?.add(value, allAttrs);\n break;\n case 'model_loads':\n this.modelLoadCounter?.add(value, allAttrs);\n break;\n case 'user_interactions':\n this.userInteractionCounter?.add(value, allAttrs);\n break;\n default:\n // Create ad-hoc counter if needed\n if (this.meter) {\n const counter = this.meter.createCounter(`jaak.${name}.total`, {\n description: `Total ${name}`,\n unit: '1',\n });\n counter.add(value, allAttrs);\n }\n }\n\n if (this.debug) {\n console.log(`[MetricsService] Counter incremented: ${name} += ${value}`, attributes);\n }\n } catch (error) {\n console.error('[MetricsService] Failed to increment counter:', error);\n }\n }\n\n recordHistogram(name: string, value: number, attributes?: Record<string, any>): void {\n try {\n const baseAttrs = this.getBaseAttributes();\n const allAttrs = { ...baseAttrs, ...attributes };\n\n // Use predefined histograms\n switch (name) {\n case 'capture_duration':\n this.captureLatencyHistogram?.record(value, allAttrs);\n break;\n case 'model_load_duration':\n this.modelLoadLatencyHistogram?.record(value, allAttrs);\n break;\n case 'detection_duration':\n this.detectionLatencyHistogram?.record(value, allAttrs);\n break;\n case 'image_size':\n this.imageSizeHistogram?.record(value, allAttrs);\n break;\n default:\n // Create ad-hoc histogram if needed\n if (this.meter) {\n const histogram = this.meter.createHistogram(`jaak.${name}`, {\n description: `Distribution of ${name}`,\n unit: 'ms',\n });\n histogram.record(value, allAttrs);\n }\n }\n\n if (this.debug) {\n console.log(`[MetricsService] Histogram recorded: ${name} = ${value}`, attributes);\n }\n } catch (error) {\n console.error('[MetricsService] Failed to record histogram:', error);\n }\n }\n\n setGauge(name: string, value: number, attributes?: Record<string, any>): void {\n try {\n // Store gauge value for observable callbacks\n this.gaugeValues.set(name, value);\n\n // Update known gauges\n if (name === 'active_sessions') {\n this.activeSessions = value;\n }\n\n if (this.debug) {\n console.log(`[MetricsService] Gauge set: ${name} = ${value}`, attributes);\n }\n } catch (error) {\n console.error('[MetricsService] Failed to set gauge:', error);\n }\n }\n\n async measureDuration<T>(name: string, fn: () => T | Promise<T>, attributes?: Record<string, any>): Promise<T> {\n const startTime = performance.now();\n\n try {\n const result = await fn();\n const duration = performance.now() - startTime;\n\n this.recordHistogram(name, duration, attributes);\n\n return result;\n } catch (error) {\n const duration = performance.now() - startTime;\n this.recordHistogram(name, duration, { ...attributes, error: true });\n throw error;\n }\n }\n\n async flush(): Promise<void> {\n try {\n if (this.metricReader) {\n await this.metricReader.forceFlush();\n }\n\n if (this.debug) {\n console.log('[MetricsService] Metrics flushed');\n }\n } catch (error) {\n console.error('[MetricsService] Failed to flush metrics:', error);\n }\n }\n\n async cleanup(): Promise<void> {\n try {\n if (this.metricReader) {\n await this.metricReader.forceFlush();\n await this.metricReader.shutdown();\n }\n\n if (this.meterProvider) {\n await this.meterProvider.shutdown();\n }\n\n if (this.debug) {\n console.log('[MetricsService] Cleaned up successfully');\n }\n } catch (error) {\n console.error('[MetricsService] Failed to cleanup:', error);\n }\n }\n\n // Helper methods for common business metrics\n\n /**\n * Record a successful capture\n */\n recordCapture(side: 'front' | 'back', mode: 'auto' | 'manual', durationMs: number): void {\n this.incrementCounter('captures', 1, { side, mode });\n this.recordHistogram('capture_duration', durationMs, { side, mode });\n }\n\n /**\n * Record an error\n */\n recordError(errorType: string, operation: string): void {\n this.incrementCounter('errors', 1, { error_type: errorType, operation });\n }\n\n /**\n * Record model load performance\n */\n recordModelLoad(modelType: 'detection' | 'classification', durationMs: number, cached: boolean): void {\n this.incrementCounter('model_loads', 1, { model_type: modelType, cached });\n this.recordHistogram('model_load_duration', durationMs, { model_type: modelType, cached });\n }\n\n /**\n * Record detection performance\n */\n recordDetection(durationMs: number, detectionsFound: number): void {\n this.recordHistogram('detection_duration', durationMs, { detections_found: detectionsFound });\n }\n\n /**\n * Record image size\n */\n recordImageSize(side: 'front' | 'back', sizeBytes: number): void {\n this.recordHistogram('image_size', sizeBytes, { side });\n }\n\n /**\n * Update active sessions count\n */\n updateActiveSessions(count: number): void {\n this.setGauge('active_sessions', count);\n }\n\n /**\n * Record user interaction\n */\n recordUserInteraction(interactionType: string): void {\n this.incrementCounter('user_interactions', 1, { interaction_type: interactionType });\n }\n}\n"]}
|
|
@@ -2,6 +2,8 @@ import { EventBusService } from "./EventBusService";
|
|
|
2
2
|
import { StateManagerService } from "./StateManagerService";
|
|
3
3
|
import { CameraService } from "./CameraService";
|
|
4
4
|
import { DetectionService } from "./DetectionService";
|
|
5
|
+
import { TracingService } from "./TracingService";
|
|
6
|
+
import { MetricsService } from "./MetricsService";
|
|
5
7
|
export class ServiceContainer {
|
|
6
8
|
services = new Map();
|
|
7
9
|
constructor(config) {
|
|
@@ -13,11 +15,48 @@ export class ServiceContainer {
|
|
|
13
15
|
const stateManager = new StateManagerService(eventBus);
|
|
14
16
|
const cameraService = new CameraService(eventBus, config.preferredCamera);
|
|
15
17
|
const detectionService = new DetectionService(config.debug, config.useDocumentClassification, config.useDocumentDetector);
|
|
18
|
+
// Initialize tracing service if enabled
|
|
19
|
+
let tracingService = null;
|
|
20
|
+
if (config.enableTelemetry !== false) {
|
|
21
|
+
tracingService = new TracingService({
|
|
22
|
+
collectorUrl: config.telemetryCollectorUrl || 'https://collector.jaak.ai/v1/traces',
|
|
23
|
+
serviceName: 'jaak-stamps-webcomponent',
|
|
24
|
+
serviceVersion: '2.1.0',
|
|
25
|
+
debug: config.debug,
|
|
26
|
+
customerId: config.customerId,
|
|
27
|
+
tenantId: config.tenantId,
|
|
28
|
+
environment: config.environment || 'production',
|
|
29
|
+
propagateTraceHeaderCorsUrls: config.propagateTraceHeaderCorsUrls || [],
|
|
30
|
+
enableAutoInstrumentation: true,
|
|
31
|
+
});
|
|
32
|
+
tracingService.initialize();
|
|
33
|
+
}
|
|
34
|
+
// Initialize metrics service if enabled
|
|
35
|
+
let metricsService = null;
|
|
36
|
+
if (config.enableMetrics !== false) {
|
|
37
|
+
metricsService = new MetricsService({
|
|
38
|
+
collectorUrl: config.metricsCollectorUrl || 'https://collector.jaak.ai/v1/metrics',
|
|
39
|
+
serviceName: 'jaak-stamps-webcomponent',
|
|
40
|
+
serviceVersion: '2.1.0',
|
|
41
|
+
debug: config.debug,
|
|
42
|
+
customerId: config.customerId,
|
|
43
|
+
tenantId: config.tenantId,
|
|
44
|
+
environment: config.environment || 'production',
|
|
45
|
+
exportIntervalMillis: config.metricsExportIntervalMillis || 60000,
|
|
46
|
+
});
|
|
47
|
+
metricsService.initialize();
|
|
48
|
+
}
|
|
16
49
|
// Register services
|
|
17
50
|
this.services.set('eventBus', eventBus);
|
|
18
51
|
this.services.set('stateManager', stateManager);
|
|
19
52
|
this.services.set('cameraService', cameraService);
|
|
20
53
|
this.services.set('detectionService', detectionService);
|
|
54
|
+
if (tracingService) {
|
|
55
|
+
this.services.set('tracingService', tracingService);
|
|
56
|
+
}
|
|
57
|
+
if (metricsService) {
|
|
58
|
+
this.services.set('metricsService', metricsService);
|
|
59
|
+
}
|
|
21
60
|
}
|
|
22
61
|
get(serviceName) {
|
|
23
62
|
const service = this.services.get(serviceName);
|
|
@@ -38,6 +77,22 @@ export class ServiceContainer {
|
|
|
38
77
|
getDetectionService() {
|
|
39
78
|
return this.get('detectionService');
|
|
40
79
|
}
|
|
80
|
+
getTracingService() {
|
|
81
|
+
try {
|
|
82
|
+
return this.get('tracingService');
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
getMetricsService() {
|
|
89
|
+
try {
|
|
90
|
+
return this.get('metricsService');
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
41
96
|
updateConfig(config) {
|
|
42
97
|
// Reinitialize services that depend on configuration changes
|
|
43
98
|
if (config.preferredCamera !== undefined ||
|
|
@@ -47,9 +102,19 @@ export class ServiceContainer {
|
|
|
47
102
|
// For simplicity, this is omitted in this example
|
|
48
103
|
}
|
|
49
104
|
}
|
|
50
|
-
cleanup() {
|
|
105
|
+
async cleanup() {
|
|
51
106
|
// Cleanup services in reverse order
|
|
52
107
|
this.getDetectionService().cleanup();
|
|
108
|
+
// Cleanup metrics service if present
|
|
109
|
+
const metricsService = this.getMetricsService();
|
|
110
|
+
if (metricsService) {
|
|
111
|
+
await metricsService.cleanup();
|
|
112
|
+
}
|
|
113
|
+
// Cleanup tracing service if present
|
|
114
|
+
const tracingService = this.getTracingService();
|
|
115
|
+
if (tracingService) {
|
|
116
|
+
await tracingService.cleanup();
|
|
117
|
+
}
|
|
53
118
|
this.getEventBus().clear();
|
|
54
119
|
this.services.clear();
|
|
55
120
|
}
|