UNPKG

@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
"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