@varia-bly/variably-sdk
Version:
Official JavaScript/TypeScript SDK for Variably feature flags, experimentation, and real-time dynamic configurations
230 lines • 6.73 kB
JavaScript
"use strict";
/**
* Metrics collection for Variably SDK
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NoOpMetricsCollector = exports.MetricsCollector = void 0;
class MetricsCollector {
constructor() {
this.apiCalls = 0;
this.cacheHits = 0;
this.cacheMisses = 0;
this.errors = 0;
this.latencies = [];
this.flagsEvaluated = 0;
this.gatesEvaluated = 0;
this.eventsTracked = 0;
this.maxLatencyHistory = 1000; // Keep last 1000 latency measurements
this.startTime = new Date();
}
/**
* Record an API call
*/
recordApiCall() {
this.apiCalls++;
}
/**
* Record a cache hit
*/
recordCacheHit() {
this.cacheHits++;
}
/**
* Record a cache miss
*/
recordCacheMiss() {
this.cacheMisses++;
}
/**
* Record an error
*/
recordError() {
this.errors++;
}
/**
* Record request latency
*/
recordLatency(latencyMs) {
this.latencies.push(latencyMs);
// Keep only the most recent latency measurements
if (this.latencies.length > this.maxLatencyHistory) {
this.latencies = this.latencies.slice(-this.maxLatencyHistory);
}
}
/**
* Record a flag evaluation
*/
recordFlagEvaluation() {
this.flagsEvaluated++;
}
/**
* Record a gate evaluation
*/
recordGateEvaluation() {
this.gatesEvaluated++;
}
/**
* Record an event being tracked
*/
recordEventTracked() {
this.eventsTracked++;
}
/**
* Get current metrics snapshot
*/
getMetrics() {
const totalCacheOperations = this.cacheHits + this.cacheMisses;
const totalOperations = this.apiCalls + this.flagsEvaluated + this.gatesEvaluated;
return {
apiCalls: this.apiCalls,
cacheHits: this.cacheHits,
cacheMisses: this.cacheMisses,
errors: this.errors,
averageLatency: this.calculateAverageLatency(),
cacheHitRate: totalCacheOperations > 0 ? this.cacheHits / totalCacheOperations : 0,
errorRate: totalOperations > 0 ? this.errors / totalOperations : 0,
flagsEvaluated: this.flagsEvaluated,
gatesEvaluated: this.gatesEvaluated,
eventsTracked: this.eventsTracked,
startTime: this.startTime
};
}
/**
* Get detailed latency statistics
*/
getLatencyStats() {
if (this.latencies.length === 0) {
return {
min: 0,
max: 0,
average: 0,
median: 0,
p95: 0,
p99: 0,
count: 0
};
}
const sorted = [...this.latencies].sort((a, b) => a - b);
const count = sorted.length;
return {
min: sorted[0],
max: sorted[count - 1],
average: this.calculateAverageLatency(),
median: this.calculatePercentile(sorted, 50),
p95: this.calculatePercentile(sorted, 95),
p99: this.calculatePercentile(sorted, 99),
count
};
}
/**
* Reset all metrics
*/
reset() {
this.startTime = new Date();
this.apiCalls = 0;
this.cacheHits = 0;
this.cacheMisses = 0;
this.errors = 0;
this.latencies = [];
this.flagsEvaluated = 0;
this.gatesEvaluated = 0;
this.eventsTracked = 0;
}
/**
* Export metrics in a format suitable for external monitoring systems
*/
exportMetrics() {
const metrics = this.getMetrics();
const latencyStats = this.getLatencyStats();
return {
'variably.api_calls.total': metrics.apiCalls,
'variably.cache.hits': metrics.cacheHits,
'variably.cache.misses': metrics.cacheMisses,
'variably.cache.hit_rate': metrics.cacheHitRate,
'variably.errors.total': metrics.errors,
'variably.error_rate': metrics.errorRate,
'variably.flags.evaluated': metrics.flagsEvaluated,
'variably.gates.evaluated': metrics.gatesEvaluated,
'variably.events.tracked': metrics.eventsTracked,
'variably.latency.average': metrics.averageLatency,
'variably.latency.min': latencyStats.min,
'variably.latency.max': latencyStats.max,
'variably.latency.median': latencyStats.median,
'variably.latency.p95': latencyStats.p95,
'variably.latency.p99': latencyStats.p99,
'variably.uptime.seconds': (Date.now() - metrics.startTime.getTime()) / 1000
};
}
/**
* Calculate average latency
*/
calculateAverageLatency() {
if (this.latencies.length === 0) {
return 0;
}
const sum = this.latencies.reduce((acc, latency) => acc + latency, 0);
return sum / this.latencies.length;
}
/**
* Calculate percentile from sorted array
*/
calculatePercentile(sortedArray, percentile) {
if (sortedArray.length === 0) {
return 0;
}
const index = (percentile / 100) * (sortedArray.length - 1);
const lower = Math.floor(index);
const upper = Math.ceil(index);
const weight = index % 1;
if (upper >= sortedArray.length) {
return sortedArray[sortedArray.length - 1];
}
return sortedArray[lower] * (1 - weight) + sortedArray[upper] * weight;
}
}
exports.MetricsCollector = MetricsCollector;
/**
* No-op metrics collector for when metrics are disabled
*/
class NoOpMetricsCollector {
recordApiCall() { }
recordCacheHit() { }
recordCacheMiss() { }
recordError() { }
recordLatency() { }
recordFlagEvaluation() { }
recordGateEvaluation() { }
recordEventTracked() { }
getMetrics() {
return {
apiCalls: 0,
cacheHits: 0,
cacheMisses: 0,
errors: 0,
averageLatency: 0,
cacheHitRate: 0,
errorRate: 0,
flagsEvaluated: 0,
gatesEvaluated: 0,
eventsTracked: 0,
startTime: new Date()
};
}
getLatencyStats() {
return {
min: 0,
max: 0,
average: 0,
median: 0,
p95: 0,
p99: 0,
count: 0
};
}
reset() { }
exportMetrics() {
return {};
}
}
exports.NoOpMetricsCollector = NoOpMetricsCollector;
//# sourceMappingURL=metrics.js.map