@ahmedhegazee/nestjs-telescope
Version:
Advanced observability and monitoring solution for NestJS applications with ML-powered analytics, enterprise features, and production-ready scaling
280 lines • 12.8 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var TelescopeService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.TelescopeService = void 0;
const common_1 = require("@nestjs/common");
const entry_manager_service_1 = require("./entry-manager.service");
const storage_service_1 = require("../../storage/storage.service");
const circuit_breaker_service_1 = require("./circuit-breaker.service");
let TelescopeService = TelescopeService_1 = class TelescopeService {
constructor(entryManager, storageService, circuitBreakerService, config) {
this.entryManager = entryManager;
this.storageService = storageService;
this.circuitBreakerService = circuitBreakerService;
this.config = config;
this.logger = new common_1.Logger(TelescopeService_1.name);
this.circuitBreakers = {
entryProcessing: 'telescope-entry-processing',
batchProcessing: 'telescope-batch-processing',
storage: 'telescope-storage',
queries: 'telescope-queries',
};
}
async onModuleInit() {
this.initializeCircuitBreakers();
}
initializeCircuitBreakers() {
this.circuitBreakerService.createCircuit(this.circuitBreakers.entryProcessing, {
failureThreshold: 5,
timeoutThreshold: 3000,
resetTimeout: 30000,
halfOpenMaxCalls: 2,
successThreshold: 3,
});
this.circuitBreakerService.createCircuit(this.circuitBreakers.batchProcessing, {
failureThreshold: 3,
timeoutThreshold: 10000,
resetTimeout: 60000,
halfOpenMaxCalls: 1,
successThreshold: 2,
});
this.circuitBreakerService.createCircuit(this.circuitBreakers.storage, {
failureThreshold: 10,
timeoutThreshold: 5000,
resetTimeout: 45000,
halfOpenMaxCalls: 3,
successThreshold: 5,
});
this.circuitBreakerService.createCircuit(this.circuitBreakers.queries, {
failureThreshold: 8,
timeoutThreshold: 8000,
resetTimeout: 30000,
halfOpenMaxCalls: 2,
successThreshold: 3,
});
this.logger.log('Circuit breakers initialized for Telescope service');
}
async record(entry) {
if (!this.config.enabled) {
return;
}
const result = await this.circuitBreakerService.execute(this.circuitBreakers.entryProcessing, async () => {
await this.entryManager.process(entry);
return entry.id;
}, async () => {
this.logger.warn(`Fallback triggered for entry: ${entry.type} - ${entry.id}`);
return entry.id;
});
if (result.success) {
this.logger.debug(`Recorded entry: ${entry.type} - ${entry.id} (${result.executionTime}ms)`);
if (result.fromCache) {
this.logger.warn(`Entry processed via fallback: ${entry.id}`);
}
}
else {
const errorMessage = result.error instanceof Error ? result.error.message : 'Unknown error';
this.logger.error(`Failed to record entry ${entry.id}: ${errorMessage} (Circuit: ${result.circuitState})`);
if (entry.type === 'exception') {
throw result.error || new Error(`Critical entry failed: ${entry.id}`);
}
}
}
async recordBatch(entries) {
if (!this.config.enabled || entries.length === 0) {
return;
}
const result = await this.circuitBreakerService.execute(this.circuitBreakers.batchProcessing, async () => {
await this.entryManager.processBatch(entries);
return entries.length;
}, async () => {
this.logger.warn(`Batch processing failed, falling back to individual processing for ${entries.length} entries`);
let processedCount = 0;
for (const entry of entries) {
try {
await this.record(entry);
processedCount++;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
this.logger.warn(`Failed to process individual entry in fallback: ${entry.id} - ${errorMessage}`);
}
}
return processedCount;
});
if (result.success) {
this.logger.debug(`Recorded batch: ${result.data} entries (${result.executionTime}ms)`);
if (result.fromCache) {
this.logger.warn(`Batch processed via fallback: ${result.data}/${entries.length} entries`);
}
}
else {
const errorMessage = result.error instanceof Error ? result.error.message : 'Unknown error';
this.logger.error(`Failed to record batch: ${errorMessage} (Circuit: ${result.circuitState})`);
}
}
async find(filter) {
const result = await this.circuitBreakerService.execute(this.circuitBreakers.queries, async () => {
return await this.storageService.find(filter);
}, async () => {
this.logger.warn('Storage query failed, returning empty result');
return {
entries: [],
total: 0,
hasMore: false,
};
});
if (!result.success) {
const errorMessage = result.error instanceof Error ? result.error.message : 'Unknown error';
this.logger.error(`Query failed: ${errorMessage} (Circuit: ${result.circuitState})`);
}
return result.data || { entries: [], total: 0, hasMore: false };
}
async findById(id) {
const result = await this.circuitBreakerService.execute(this.circuitBreakers.queries, async () => {
return await this.storageService.findById(id);
}, async () => {
this.logger.warn(`Entry query by ID failed: ${id}`);
return null;
});
if (!result.success) {
const errorMessage = result.error instanceof Error ? result.error.message : 'Unknown error';
this.logger.error(`FindById failed for ${id}: ${errorMessage} (Circuit: ${result.circuitState})`);
}
return result.data || null;
}
async getEntries(filter, startDate, endDate) {
const searchFilter = {
...filter,
...(startDate && { startDate }),
...(endDate && { endDate }),
};
const result = await this.circuitBreakerService.execute(this.circuitBreakers.queries, async () => {
const queryResult = await this.storageService.find(searchFilter);
return queryResult.entries;
}, async () => {
this.logger.warn('Entries query failed, returning empty array');
return [];
});
if (!result.success) {
const errorMessage = result.error instanceof Error ? result.error.message : 'Unknown error';
this.logger.error(`GetEntries failed: ${errorMessage} (Circuit: ${result.circuitState})`);
}
return result.data || [];
}
async clear() {
const result = await this.circuitBreakerService.execute(this.circuitBreakers.storage, async () => {
await this.storageService.clear();
return true;
}, async () => {
this.logger.warn('Clear operation failed, storage may still contain entries');
return false;
});
if (result.success) {
this.logger.log('Telescope entries cleared');
}
else {
const errorMessage = result.error instanceof Error ? result.error.message : 'Unknown error';
this.logger.error(`Clear operation failed: ${errorMessage} (Circuit: ${result.circuitState})`);
throw result.error || new Error('Clear operation failed');
}
}
async prune() {
const cutoffTime = new Date();
cutoffTime.setHours(cutoffTime.getHours() - this.config.storage.retention.hours);
const result = await this.circuitBreakerService.execute(this.circuitBreakers.storage, async () => {
return await this.storageService.prune(cutoffTime);
}, async () => {
this.logger.warn('Prune operation failed, old entries may still exist');
return 0;
});
if (result.success) {
this.logger.log(`Pruned ${result.data} old entries`);
return result.data || 0;
}
else {
const errorMessage = result.error instanceof Error ? result.error.message : 'Unknown error';
this.logger.error(`Prune operation failed: ${errorMessage} (Circuit: ${result.circuitState})`);
return 0;
}
}
async getStats() {
const result = await this.circuitBreakerService.execute(this.circuitBreakers.storage, async () => {
return await this.storageService.getStats();
}, async () => {
this.logger.warn('Storage stats unavailable, returning basic info');
return {
totalEntries: 0,
entriesByType: {},
sizeInBytes: 0,
};
});
const storageStats = result.data || {
totalEntries: 0,
entriesByType: {},
sizeInBytes: 0,
};
const circuitStats = {
entryProcessing: this.circuitBreakerService.getStats(this.circuitBreakers.entryProcessing),
batchProcessing: this.circuitBreakerService.getStats(this.circuitBreakers.batchProcessing),
storage: this.circuitBreakerService.getStats(this.circuitBreakers.storage),
queries: this.circuitBreakerService.getStats(this.circuitBreakers.queries),
};
return {
...storageStats,
config: {
enabled: this.config.enabled,
environment: this.config.environment,
storageDriver: this.config.storage.driver,
retentionHours: this.config.storage.retention.hours,
},
uptime: process.uptime(),
memoryUsage: process.memoryUsage(),
circuitBreakers: circuitStats,
healthStatus: {
storage: result.success,
overallHealth: Object.values(circuitStats).every((stat) => stat && stat.state === 'closed'),
},
};
}
getCircuitBreakerStats() {
return {
entryProcessing: this.circuitBreakerService.getStats(this.circuitBreakers.entryProcessing),
batchProcessing: this.circuitBreakerService.getStats(this.circuitBreakers.batchProcessing),
storage: this.circuitBreakerService.getStats(this.circuitBreakers.storage),
queries: this.circuitBreakerService.getStats(this.circuitBreakers.queries),
};
}
resetCircuitBreaker(type) {
const circuitName = this.circuitBreakers[type];
return this.circuitBreakerService.reset(circuitName);
}
forceOpenCircuitBreaker(type) {
const circuitName = this.circuitBreakers[type];
return this.circuitBreakerService.forceOpen(circuitName);
}
forceCloseCircuitBreaker(type) {
const circuitName = this.circuitBreakers[type];
return this.circuitBreakerService.forceClose(circuitName);
}
};
exports.TelescopeService = TelescopeService;
exports.TelescopeService = TelescopeService = TelescopeService_1 = __decorate([
(0, common_1.Injectable)(),
__param(3, (0, common_1.Inject)('TELESCOPE_CONFIG')),
__metadata("design:paramtypes", [entry_manager_service_1.EntryManagerService,
storage_service_1.StorageService,
circuit_breaker_service_1.CircuitBreakerService, Object])
], TelescopeService);
//# sourceMappingURL=telescope.service.js.map