UNPKG

@ahmedhegazee/nestjs-telescope

Version:

Advanced observability and monitoring solution for NestJS applications with ML-powered analytics, enterprise features, and production-ready scaling

323 lines 13.4 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 BullAdapterService_1; Object.defineProperty(exports, "__esModule", { value: true }); exports.BullAdapterService = void 0; const common_1 = require("@nestjs/common"); const job_watcher_service_1 = require("./job-watcher.service"); const core_1 = require("@nestjs/core"); let BullAdapterService = BullAdapterService_1 = class BullAdapterService { constructor(jobWatcherService, moduleRef) { this.jobWatcherService = jobWatcherService; this.moduleRef = moduleRef; this.logger = new common_1.Logger(BullAdapterService_1.name); this.registeredQueues = new Map(); this.eventListeners = new Map(); } async onModuleInit() { await this.discoverQueues(); } async discoverQueues() { try { const bullQueues = await this.findBullQueues(); for (const queue of bullQueues) { this.registerQueue(queue); } this.logger.log(`Discovered and registered ${bullQueues.length} Bull queues`); } catch (error) { this.logger.warn('Failed to auto-discover Bull queues:', error.message); } } async findBullQueues() { const queues = []; try { const bullQueues = await this.findQueuesFromBull(); queues.push(...bullQueues); const bullmqQueues = await this.findQueuesFromBullMQ(); queues.push(...bullmqQueues); } catch (error) { this.logger.debug('No Bull queues found via auto-discovery'); } return queues; } async findQueuesFromBull() { const queues = []; try { const bullModule = await this.moduleRef.get('BullModule', { strict: false }); if (bullModule && bullModule.queues) { for (const [name, queue] of Object.entries(bullModule.queues)) { queues.push(queue); } } } catch (error) { } return queues; } async findQueuesFromBullMQ() { const queues = []; try { const bullMQModule = await this.moduleRef.get('BullMQModule', { strict: false }); if (bullMQModule && bullMQModule.queues) { for (const [name, queue] of Object.entries(bullMQModule.queues)) { queues.push(queue); } } } catch (error) { } return queues; } registerQueue(queue) { if (!queue || !queue.name) { this.logger.warn('Invalid queue provided for registration'); return; } if (this.registeredQueues.has(queue.name)) { this.logger.warn(`Queue ${queue.name} is already registered`); return; } this.registeredQueues.set(queue.name, queue); this.setupQueueEventListeners(queue); this.logger.log(`Registered Bull queue: ${queue.name}`); } setupQueueEventListeners(queue) { const listeners = new Map(); const onJobActive = (job) => this.handleJobActive(job, queue); const onJobCompleted = (job, result) => this.handleJobCompleted(job, result, queue); const onJobFailed = (job, error) => this.handleJobFailed(job, error, queue); const onJobProgress = (job, progress) => this.handleJobProgress(job, progress, queue); const onJobStalled = (job) => this.handleJobStalled(job, queue); const onJobWaiting = (job) => this.handleJobWaiting(job, queue); const onJobDelayed = (job) => this.handleJobDelayed(job, queue); const onJobPaused = (job) => this.handleJobPaused(job, queue); queue.on('active', onJobActive); queue.on('completed', onJobCompleted); queue.on('failed', onJobFailed); queue.on('progress', onJobProgress); queue.on('stalled', onJobStalled); queue.on('waiting', onJobWaiting); queue.on('delayed', onJobDelayed); queue.on('paused', onJobPaused); listeners.set('active', onJobActive); listeners.set('completed', onJobCompleted); listeners.set('failed', onJobFailed); listeners.set('progress', onJobProgress); listeners.set('stalled', onJobStalled); listeners.set('waiting', onJobWaiting); listeners.set('delayed', onJobDelayed); listeners.set('paused', onJobPaused); this.eventListeners.set(queue.name, listeners); } handleJobActive(job, queue) { const context = this.createJobContext(job, queue, job_watcher_service_1.JobStatus.ACTIVE); context.startTime = new Date(); this.jobWatcherService.trackJob(context); } handleJobCompleted(job, result, queue) { const context = this.createJobContext(job, queue, job_watcher_service_1.JobStatus.COMPLETED); context.result = result; context.endTime = new Date(); if (job.processedOn && job.timestamp) { context.duration = job.finishedOn - job.processedOn; context.performance = { ...context.performance, queueWaitTime: job.processedOn - job.timestamp, executionTime: context.duration, }; } this.jobWatcherService.trackJob(context); } handleJobFailed(job, error, queue) { const context = this.createJobContext(job, queue, job_watcher_service_1.JobStatus.FAILED); context.error = { message: error.message, stack: error.stack, name: error.name, failedReason: job.failedReason, }; context.endTime = new Date(); if (job.processedOn && job.timestamp) { context.duration = job.finishedOn - job.processedOn; context.performance = { ...context.performance, queueWaitTime: job.processedOn - job.timestamp, executionTime: context.duration, }; } this.jobWatcherService.trackJob(context); } handleJobProgress(job, progress, queue) { const context = this.createJobContext(job, queue, job_watcher_service_1.JobStatus.ACTIVE); context.progress = progress; this.jobWatcherService.trackJob(context); } handleJobStalled(job, queue) { const context = this.createJobContext(job, queue, job_watcher_service_1.JobStatus.STALLED); this.jobWatcherService.trackJob(context); } handleJobWaiting(job, queue) { const context = this.createJobContext(job, queue, job_watcher_service_1.JobStatus.WAITING); this.jobWatcherService.trackJob(context); } handleJobDelayed(job, queue) { const context = this.createJobContext(job, queue, job_watcher_service_1.JobStatus.DELAYED); context.delay = job.delay; this.jobWatcherService.trackJob(context); } handleJobPaused(job, queue) { const context = this.createJobContext(job, queue, job_watcher_service_1.JobStatus.PAUSED); this.jobWatcherService.trackJob(context); } createJobContext(job, queue, status) { return { id: `${queue.name}_${job.id}_${Date.now()}`, jobId: String(job.id), queueName: queue.name, jobName: job.name, timestamp: new Date(), status, priority: job.opts?.priority || 0, data: job.data, options: job.opts, attempts: job.attemptsMade, maxAttempts: job.opts?.attempts || 3, delay: job.delay, progress: job.progress, performance: { memoryUsage: process.memoryUsage().heapUsed, cpuUsage: process.cpuUsage().user + process.cpuUsage().system, }, traceId: job.data?.traceId || job.opts?.traceId, requestId: job.data?.requestId || job.opts?.requestId, userId: job.data?.userId || job.opts?.userId, sessionId: job.data?.sessionId || job.opts?.sessionId, }; } async updateQueueStats() { for (const [name, queue] of this.registeredQueues) { try { const stats = await queue.getJobCounts(); const context = { id: `queue_stats_${name}_${Date.now()}`, jobId: 'queue_stats', queueName: name, jobName: 'queue_stats', timestamp: new Date(), status: job_watcher_service_1.JobStatus.ACTIVE, priority: 0, attempts: 0, maxAttempts: 0, queueStats: stats, }; this.jobWatcherService.trackJob(context); } catch (error) { this.logger.warn(`Failed to get stats for queue ${name}:`, error.message); } } } unregisterQueue(queueName) { const queue = this.registeredQueues.get(queueName); if (!queue) { this.logger.warn(`Queue ${queueName} is not registered`); return; } const listeners = this.eventListeners.get(queueName); if (listeners) { for (const [event, listener] of listeners) { queue.off(event, listener); } this.eventListeners.delete(queueName); } this.registeredQueues.delete(queueName); this.logger.log(`Unregistered Bull queue: ${queueName}`); } getRegisteredQueues() { return Array.from(this.registeredQueues.keys()); } isQueueRegistered(queueName) { return this.registeredQueues.has(queueName); } async getQueueMetrics(queueName) { const queue = this.registeredQueues.get(queueName); if (!queue) { throw new Error(`Queue ${queueName} is not registered`); } try { const [waiting, active, completed, failed, delayed, paused] = await Promise.all([ queue.waiting ? queue.waiting() : queue.getWaiting(), queue.active ? queue.active() : queue.getActive(), queue.completed ? queue.completed() : queue.getCompleted(), queue.failed ? queue.failed() : queue.getFailed(), queue.delayed ? queue.delayed() : queue.getDelayed(), queue.paused ? queue.paused() : queue.getPaused(), ]); return { queueName, counts: { waiting: waiting.length, active: active.length, completed: completed.length, failed: failed.length, delayed: delayed.length, paused: paused.length, }, jobs: { waiting: waiting.map(job => this.serializeJob(job)), active: active.map(job => this.serializeJob(job)), completed: completed.slice(-10).map(job => this.serializeJob(job)), failed: failed.slice(-10).map(job => this.serializeJob(job)), delayed: delayed.map(job => this.serializeJob(job)), paused: paused.map(job => this.serializeJob(job)), }, }; } catch (error) { this.logger.error(`Failed to get metrics for queue ${queueName}:`, error); throw error; } } serializeJob(job) { return { id: job.id, name: job.name, data: job.data, progress: job.progress, attemptsMade: job.attemptsMade, delay: job.delay, timestamp: job.timestamp, processedOn: job.processedOn, finishedOn: job.finishedOn, }; } onDestroy() { for (const [queueName, listeners] of this.eventListeners) { const queue = this.registeredQueues.get(queueName); if (queue) { for (const [event, listener] of listeners) { queue.off(event, listener); } } } this.eventListeners.clear(); this.registeredQueues.clear(); } }; exports.BullAdapterService = BullAdapterService; exports.BullAdapterService = BullAdapterService = BullAdapterService_1 = __decorate([ (0, common_1.Injectable)(), __metadata("design:paramtypes", [job_watcher_service_1.JobWatcherService, core_1.ModuleRef]) ], BullAdapterService); //# sourceMappingURL=bull-adapter.service.js.map