Kurs NestJS · Moduł 6: Obsługa błędów i monitoring
PROJEKT: Kompletny System Monitorowania Rzymskiego Legionu
W tej lekcji4
Główny prefekcie technologii! Konsul Caesar.js powierzył Ci ostateczne wyzwanie - stworzenie kompletnego systemu monitorowania dla całego rzymskiego legionu. To będzie prawdziwy test wszystkich umiejętności zdobytych w module Error Handling & Monitoring!
Specyfikacja Projektu
Twój system musi być kompletnym rozwiązaniem do monitorowania legionu składającego się z wielu rzymskich kohort. Każdy fort to osobna instancja aplikacji NestJS, a Ty budujesz centralny system monitorowania.
Wymagania Funkcjonalne
1. Multi-Legion Monitoring
- Centralne Dashboard - przegląd wszystkich kohort w legionie
- Legion Registration - automatyczna rejestracja nowych kohort
- Health Checks - monitoring zdrowia każdego fortu
- Legion Analytics - analityka całego legionu
2. Real-time Error Tracking
- Error Aggregation - zbieranie błędów ze wszystkich kohort
- Error Classification - kategoryzacja błędów według typu i ważności
- Error Correlation - łączenie podobnych błędów
- Alert System - inteligentne powiadomienia
3. Performance Monitoring
- System Metrics - CPU, pamięć, dysk, sieć
- Application Metrics - response time, throughput, error rate
- Business Metrics - custom metrics specyficzne dla piractwa
- SLA Monitoring - śledzenie celów wydajnościowych
4. Advanced Alerting
- Smart Alerts - alerty oparte na machine learning
- Escalation Policies - zasady eskalacji alertów
- Multi-channel Notifications - Slack, email, SMS, webhook
- Alert Correlation - grupowanie powiązanych alertów
Architektura Systemu
1// Architecture Overview
2/*
3┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
4│ Legion Alpha │ │ Legion Beta │ │ Legion Gamma │
5│ (NestJS) │ │ (NestJS) │ │ (NestJS) │
6│ │ │ │ │ │
7│ - Error Agent │ │ - Error Agent │ │ - Error Agent │
8│ - Metrics Agent │ │ - Metrics Agent │ │ - Metrics Agent │
9│ - Health Check │ │ - Health Check │ │ - Health Check │
10└─────────┬───────┘ └─────────┬───────┘ └─────────┬───────┘
11 │ │ │
12 └──────────────────────┼──────────────────────┘
13 │
14 ┌─────────────┴─────────────┐
15 │ Legion Monitor Center │
16 │ (Main NestJS App) │
17 │ │
18 │ - Data Aggregation │
19 │ - Alert Management │
20 │ - Dashboard API │
21 │ - Analytics Engine │
22 │ - Notification System │
23 └─────────────┬─────────────┘
24 │
25 ┌─────────────┴─────────────┐
26 │ External Systems │
27 │ │
28 │ - Prometheus/Grafana │
29 │ - Elasticsearch/Kibana │
30 │ - Slack/Teams │
31 │ - PagerDuty │
32 └───────────────────────────┘
33*/Implementacja Systemu
1. Legion Agent (dla każdego fortu)
1// agents/cohort-agent.service.ts
2import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
3import { Cron } from '@nestjs/schedule';
4import { HttpService } from '@nestjs/axios';
5import { ConfigService } from '@nestjs/config';
6
7export interface LegionMetrics {
8 cohortId: string;
9 cohortName: string;
10 centurion: string;
11 timestamp: Date;
12 system: {
13 cpu: number;
14 memory: number;
15 disk: number;
16 network: {
17 bytesIn: number;
18 bytesOut: number;
19 };
20 };
21 application: {
22 uptime: number;
23 activeConnections: number;
24 requestsPerSecond: number;
25 errorRate: number;
26 responseTime: {
27 avg: number;
28 p95: number;
29 p99: number;
30 };
31 };
32 business: {
33 tributesFound: number;
34 legionActivity: number;
35 missionsCompleted: number;
36 battlesWon: number;
37 };
38 health: {
39 overall: 'healthy' | 'warning' | 'critical';
40 services: Record<string, 'up' | 'down' | 'degraded'>;
41 };
42}
43
44@Injectable()
45export class LegionAgentService implements OnModuleInit {
46 private readonly logger = new Logger(LegionAgentService.name);
47 private readonly cohortId: string;
48 private readonly legionCenterUrl: string;
49 private isRegistered = false;
50
51 constructor(
52 private readonly httpService: HttpService,
53 private readonly configService: ConfigService,
54 ) {
55 this.cohortId = this.configService.get('COHORT_ID') || this.generateLegionId();
56 this.legionCenterUrl = this.configService.get('LEGION_CENTER_URL');
57 }
58
59 async onModuleInit() {
60 await this.registerWithLegion();
61 }
62
63 private async registerWithLegion() {
64 try {
65 const registrationData = {
66 cohortId: this.cohortId,
67 cohortName: this.configService.get('COHORT_NAME', 'Unknown Vessel'),
68 centurion: this.configService.get('CENTURION_NAME', 'Anonymous Centurion'),
69 version: process.env.npm_package_version || '1.0.0',
70 startTime: new Date(),
71 location: {
72 latitude: parseFloat(this.configService.get('COHORT_LAT', '0')),
73 longitude: parseFloat(this.configService.get('COHORT_LNG', '0')),
74 },
75 };
76
77 await this.httpService.post(
78 `${this.legionCenterUrl}/legion/cohorts/register`,
79 registrationData
80 ).toPromise();
81
82 this.isRegistered = true;
83 this.logger.log(` Legion ${this.cohortId} registered with legion center`);
84 } catch (error) {
85 this.logger.error('Failed to register with legion center:', error.message);
86 }
87 }
88
89 @Cron('0 * * * * *') // Every minute
90 async sendMetricsToLegion() {
91 if (!this.isRegistered) {
92 await this.registerWithLegion();
93 return;
94 }
95
96 try {
97 const metrics = await this.collectLegionMetrics();
98
99 await this.httpService.post(
100 `${this.legionCenterUrl}/legion/metrics`,
101 metrics
102 ).toPromise();
103
104 this.logger.debug(` Metrics sent to legion center`);
105 } catch (error) {
106 this.logger.error('Failed to send metrics to legion center:', error.message);
107 }
108 }
109
110 async collectLegionMetrics(): Promise<LegionMetrics> {
111 const memUsage = process.memoryUsage();
112 const uptime = process.uptime();
113
114 return {
115 cohortId: this.cohortId,
116 cohortName: this.configService.get('COHORT_NAME', 'Unknown Vessel'),
117 centurion: this.configService.get('CENTURION_NAME', 'Anonymous Centurion'),
118 timestamp: new Date(),
119
120 system: {
121 cpu: await this.getCpuUsage(),
122 memory: Math.round((memUsage.heapUsed / memUsage.heapTotal) * 100),
123 disk: await this.getDiskUsage(),
124 network: await this.getNetworkStats(),
125 },
126
127 application: {
128 uptime,
129 activeConnections: await this.getActiveConnections(),
130 requestsPerSecond: await this.getRequestsPerSecond(),
131 errorRate: await this.getErrorRate(),
132 responseTime: await this.getResponseTimeStats(),
133 },
134
135 business: {
136 tributesFound: await this.getTributesFound(),
137 legionActivity: await this.getLegionActivity(),
138 missionsCompleted: await this.getMissionsCompleted(),
139 battlesWon: await this.getBattlesWon(),
140 },
141
142 health: await this.getHealthStatus(),
143 };
144 }
145
146 private async getCpuUsage(): Promise<number> {
147 return new Promise((resolve) => {
148 const startUsage = process.cpuUsage();
149 setTimeout(() => {
150 const endUsage = process.cpuUsage(startUsage);
151 const totalUsage = endUsage.user + endUsage.system;
152 const percentage = (totalUsage / 1000000) * 100;
153 resolve(Math.min(100, percentage));
154 }, 100);
155 });
156 }
157
158 private async getDiskUsage(): Promise<number> {
159 // Simplified disk usage - in real implementation use fs.statvfs
160 return Math.random() * 100;
161 }
162
163 private async getNetworkStats() {
164 // Simplified network stats - in real implementation read from /proc/net/dev
165 return {
166 bytesIn: Math.floor(Math.random() * 1000000),
167 bytesOut: Math.floor(Math.random() * 1000000),
168 };
169 }
170
171 private async getActiveConnections(): Promise<number> {
172 // Implementation depends on your server setup
173 return Math.floor(Math.random() * 100);
174 }
175
176 private async getRequestsPerSecond(): Promise<number> {
177 // Implementation using metrics collected by interceptors
178 return Math.random() * 1000;
179 }
180
181 private async getErrorRate(): Promise<number> {
182 // Implementation using error tracking
183 return Math.random() * 10;
184 }
185
186 private async getResponseTimeStats() {
187 return {
188 avg: 50 + Math.random() * 200,
189 p95: 100 + Math.random() * 400,
190 p99: 200 + Math.random() * 800,
191 };
192 }
193
194 private async getTributesFound(): Promise<number> {
195 // Business metric - implement based on your tribute service
196 return Math.floor(Math.random() * 50);
197 }
198
199 private async getLegionActivity(): Promise<number> {
200 // Business metric - implement based on your legion service
201 return Math.floor(Math.random() * 100);
202 }
203
204 private async getMissionsCompleted(): Promise<number> {
205 // Business metric - implement based on your mission service
206 return Math.floor(Math.random() * 20);
207 }
208
209 private async getBattlesWon(): Promise<number> {
210 // Business metric - implement based on your battle service
211 return Math.floor(Math.random() * 10);
212 }
213
214 private async getHealthStatus() {
215 // Implement comprehensive health checks
216 const services = {
217 database: Math.random() > 0.1 ? 'up' : 'down',
218 redis: Math.random() > 0.05 ? 'up' : 'down',
219 api: Math.random() > 0.02 ? 'up' : 'degraded',
220 auth: Math.random() > 0.01 ? 'up' : 'down',
221 };
222
223 const downServices = Object.values(services).filter(s => s === 'down').length;
224 const degradedServices = Object.values(services).filter(s => s === 'degraded').length;
225
226 let overall: 'healthy' | 'warning' | 'critical' = 'healthy';
227 if (downServices > 0) {
228 overall = 'critical';
229 } else if (degradedServices > 0) {
230 overall = 'warning';
231 }
232
233 return { overall, services };
234 }
235
236 private generateLegionId(): string {
237 return `cohort-${Math.random().toString(36).substr(2, 9)}`;
238 }
239
240 async sendErrorToLegion(error: any, context?: any) {
241 if (!this.isRegistered) return;
242
243 try {
244 const errorData = {
245 cohortId: this.cohortId,
246 timestamp: new Date(),
247 error: {
248 message: error.message,
249 stack: error.stack,
250 type: error.constructor.name,
251 code: error.code,
252 },
253 context: {
254 url: context?.url,
255 method: context?.method,
256 user: context?.user?.id,
257 requestId: context?.requestId,
258 ...context,
259 },
260 severity: this.classifyErrorSeverity(error),
261 };
262
263 await this.httpService.post(
264 `${this.legionCenterUrl}/legion/errors`,
265 errorData
266 ).toPromise();
267
268 this.logger.debug(' Error sent to legion center');
269 } catch (err) {
270 this.logger.error('Failed to send error to legion center:', err.message);
271 }
272 }
273
274 private classifyErrorSeverity(error: any): 'low' | 'medium' | 'high' | 'critical' {
275 if (error.name?.includes('Database') || error.code === 'ECONNREFUSED') {
276 return 'critical';
277 }
278 if (error.status >= 500) {
279 return 'high';
280 }
281 if (error.status >= 400) {
282 return 'medium';
283 }
284 return 'low';
285 }
286}2. Legion Center - Centralne Centrum Monitorowania
1// legion-center/legion-monitor.service.ts
2import { Injectable, Logger } from '@nestjs/common';
3import { EventEmitter2 } from '@nestjs/event-emitter';
4import { Cron } from '@nestjs/schedule';
5
6export interface Legion {
7 cohorts: Map<string, LegionInfo>;
8 totalLegions: number;
9 healthyLegions: number;
10 warningLegions: number;
11 criticalLegions: number;
12 lastUpdate: Date;
13}
14
15export interface LegionInfo {
16 cohortId: string;
17 cohortName: string;
18 centurion: string;
19 lastSeen: Date;
20 metrics: LegionMetrics;
21 status: 'healthy' | 'warning' | 'critical' | 'offline';
22 alerts: Alert[];
23 errors: ErrorEvent[];
24}
25
26@Injectable()
27export class LegionMonitorService {
28 private readonly logger = new Logger(LegionMonitorService.name);
29 private legion: Legion = {
30 cohorts: new Map(),
31 totalLegions: 0,
32 healthyLegions: 0,
33 warningLegions: 0,
34 criticalLegions: 0,
35 lastUpdate: new Date(),
36 };
37
38 constructor(private readonly eventEmitter: EventEmitter2) {}
39
40 registerLegion(registrationData: any) {
41 const cohortInfo: LegionInfo = {
42 cohortId: registrationData.cohortId,
43 cohortName: registrationData.cohortName,
44 centurion: registrationData.centurion,
45 lastSeen: new Date(),
46 metrics: null,
47 status: 'healthy',
48 alerts: [],
49 errors: [],
50 };
51
52 this.legion.cohorts.set(registrationData.cohortId, cohortInfo);
53 this.updateLegionStats();
54
55 this.logger.log(` Legion registered: ${cohortInfo.cohortName} (${cohortInfo.cohortId})`);
56
57 this.eventEmitter.emit('cohort.registered', cohortInfo);
58 }
59
60 updateLegionMetrics(cohortId: string, metrics: LegionMetrics) {
61 const cohort = this.legion.cohorts.get(cohortId);
62 if (!cohort) {
63 this.logger.warn(`Received metrics from unregistered cohort: ${cohortId}`);
64 return;
65 }
66
67 cohort.metrics = metrics;
68 cohort.lastSeen = new Date();
69 cohort.status = this.determineLegionStatus(cohort);
70
71 this.updateLegionStats();
72 this.analyzeMetricsForAlerts(cohort);
73
74 this.eventEmitter.emit('cohort.metrics.updated', { cohortId, metrics });
75 }
76
77 recordLegionError(cohortId: string, errorData: any) {
78 const cohort = this.legion.cohorts.get(cohortId);
79 if (!cohort) {
80 this.logger.warn(`Received error from unregistered cohort: ${cohortId}`);
81 return;
82 }
83
84 const errorEvent: ErrorEvent = {
85 id: `error-${Date.now()}-${Math.random()}`,
86 timestamp: new Date(),
87 cohortId,
88 ...errorData,
89 };
90
91 cohort.errors.push(errorEvent);
92
93 // Keep only last 100 errors per cohort
94 if (cohort.errors.length > 100) {
95 cohort.errors = cohort.errors.slice(-100);
96 }
97
98 this.processError(errorEvent);
99 this.eventEmitter.emit('cohort.error', errorEvent);
100 }
101
102 private determineLegionStatus(cohort: LegionInfo): 'healthy' | 'warning' | 'critical' | 'offline' {
103 if (!cohort.metrics) return 'offline';
104
105 const timeSinceLastSeen = Date.now() - cohort.lastSeen.getTime();
106 if (timeSinceLastSeen > 300000) { // 5 minutes
107 return 'offline';
108 }
109
110 if (cohort.metrics.health.overall === 'critical') {
111 return 'critical';
112 }
113
114 const { cpu, memory } = cohort.metrics.system;
115 const { errorRate } = cohort.metrics.application;
116
117 if (cpu > 90 || memory > 90 || errorRate > 10) {
118 return 'critical';
119 }
120
121 if (cpu > 70 || memory > 70 || errorRate > 5 || cohort.metrics.health.overall === 'warning') {
122 return 'warning';
123 }
124
125 return 'healthy';
126 }
127
128 private updateLegionStats() {
129 this.legion.totalLegions = this.legion.cohorts.size;
130 this.legion.healthyLegions = 0;
131 this.legion.warningLegions = 0;
132 this.legion.criticalLegions = 0;
133
134 for (const cohort of this.legion.cohorts.values()) {
135 switch (cohort.status) {
136 case 'healthy':
137 this.legion.healthyLegions++;
138 break;
139 case 'warning':
140 this.legion.warningLegions++;
141 break;
142 case 'critical':
143 this.legion.criticalLegions++;
144 break;
145 }
146 }
147
148 this.legion.lastUpdate = new Date();
149 }
150
151 private analyzeMetricsForAlerts(cohort: LegionInfo) {
152 const alerts: Alert[] = [];
153
154 // CPU Alert
155 if (cohort.metrics.system.cpu > 90) {
156 alerts.push(this.createAlert(
157 'high-cpu',
158 'critical',
159 `High CPU usage on ${cohort.cohortName}`,
160 `CPU usage is ${cohort.metrics.system.cpu.toFixed(1)}%`,
161 cohort.cohortId
162 ));
163 }
164
165 // Memory Alert
166 if (cohort.metrics.system.memory > 90) {
167 alerts.push(this.createAlert(
168 'high-memory',
169 'critical',
170 `High memory usage on ${cohort.cohortName}`,
171 `Memory usage is ${cohort.metrics.system.memory.toFixed(1)}%`,
172 cohort.cohortId
173 ));
174 }
175
176 // Error Rate Alert
177 if (cohort.metrics.application.errorRate > 10) {
178 alerts.push(this.createAlert(
179 'high-error-rate',
180 'high',
181 `High error rate on ${cohort.cohortName}`,
182 `Error rate is ${cohort.metrics.application.errorRate.toFixed(2)}%`,
183 cohort.cohortId
184 ));
185 }
186
187 // Response Time Alert
188 if (cohort.metrics.application.responseTime.p99 > 5000) {
189 alerts.push(this.createAlert(
190 'slow-response',
191 'medium',
192 `Slow response times on ${cohort.cohortName}`,
193 `P99 response time is ${cohort.metrics.application.responseTime.p99.toFixed(0)}ms`,
194 cohort.cohortId
195 ));
196 }
197
198 // Business Metrics Alerts
199 if (cohort.metrics.business.tributesFound === 0) {
200 alerts.push(this.createAlert(
201 'no-tributes',
202 'low',
203 `No tributes found on ${cohort.cohortName}`,
204 'Legion legion might need motivation!',
205 cohort.cohortId
206 ));
207 }
208
209 // Add new alerts and trigger them
210 for (const alert of alerts) {
211 if (!cohort.alerts.some(a => a.type === alert.type && a.severity === alert.severity)) {
212 cohort.alerts.push(alert);
213 this.eventEmitter.emit('alert.triggered', alert);
214 }
215 }
216 }
217
218 @Cron('0 */5 * * * *') // Every 5 minutes
219 checkLegionHealth() {
220 const offlineLegions = [];
221 const now = Date.now();
222
223 for (const [cohortId, cohort] of this.legion.cohorts) {
224 const timeSinceLastSeen = now - cohort.lastSeen.getTime();
225
226 if (timeSinceLastSeen > 300000) { // 5 minutes offline
227 cohort.status = 'offline';
228 offlineLegions.push(cohort);
229 }
230 }
231
232 if (offlineLegions.length > 0) {
233 for (const cohort of offlineLegions) {
234 const alert = this.createAlert(
235 'cohort-offline',
236 'critical',
237 `Legion ${cohort.cohortName} is offline`,
238 `No communication for ${Math.round((now - cohort.lastSeen.getTime()) / 60000)} minutes`,
239 cohort.cohortId
240 );
241
242 this.eventEmitter.emit('alert.triggered', alert);
243 }
244 }
245
246 this.updateLegionStats();
247 }
248
249 getLegionOverview(): Legion {
250 return this.legion;
251 }
252
253 getLegionDetails(cohortId: string): LegionInfo | null {
254 return this.legion.cohorts.get(cohortId) || null;
255 }
256
257 getLegionMetrics() {
258 const cohorts = Array.from(this.legion.cohorts.values());
259
260 return {
261 legion: this.legion,
262 aggregatedMetrics: {
263 totalCpu: cohorts.reduce((sum, cohort) => sum + (cohort.metrics?.system.cpu || 0), 0),
264 totalMemory: cohorts.reduce((sum, cohort) => sum + (cohort.metrics?.system.memory || 0), 0),
265 totalErrors: cohorts.reduce((sum, cohort) => sum + cohort.errors.length, 0),
266 totalTributes: cohorts.reduce((sum, cohort) => sum + (cohort.metrics?.business.tributesFound || 0), 0),
267 averageResponseTime: cohorts.reduce((sum, cohort) => sum + (cohort.metrics?.application.responseTime.avg || 0), 0) / cohorts.length,
268 },
269 topPerformers: this.getTopPerformingLegions(),
270 worstPerformers: this.getWorstPerformingLegions(),
271 };
272 }
273
274 private getTopPerformingLegions(): LegionInfo[] {
275 return Array.from(this.legion.cohorts.values())
276 .filter(cohort => cohort.metrics && cohort.status === 'healthy')
277 .sort((a, b) => b.metrics.business.tributesFound - a.metrics.business.tributesFound)
278 .slice(0, 5);
279 }
280
281 private getWorstPerformingLegions(): LegionInfo[] {
282 return Array.from(this.legion.cohorts.values())
283 .filter(cohort => cohort.metrics && cohort.status !== 'healthy')
284 .sort((a, b) => b.metrics.application.errorRate - a.metrics.application.errorRate)
285 .slice(0, 5);
286 }
287
288 private createAlert(type: string, severity: string, title: string, description: string, cohortId: string): Alert {
289 return {
290 id: `alert-${Date.now()}-${Math.random()}`,
291 type,
292 severity,
293 title,
294 description,
295 cohortId,
296 timestamp: new Date(),
297 resolved: false,
298 };
299 }
300
301 private processError(errorEvent: ErrorEvent) {
302 // Implement error processing logic
303 // - Error correlation
304 // - Pattern detection
305 // - Automatic alert generation
306
307 this.logger.debug(`Processing error from ${errorEvent.cohortId}: ${errorEvent.error.message}`);
308 }
309}Zadania do Wykonania
Część 1: Podstawowy System (4-6 godzin)
- Legion Agent Implementation
- Zaimplementuj LegionAgentService z rzeczywistymi metrykami
- Dodaj error tracking interceptor
- Stwórz health check endpoints
- Dodaj automatyczną rejestrację z legion center
- Legion Center Core
- Zaimplementuj LegionMonitorService
- Stwórz API endpoints dla rejestracji i metryk
- Dodaj podstawowy dashboard endpoint
- Zaimplementuj real-time WebSocket komunikację
Część 2: Advanced Features (6-8 godzin)
- Intelligent Alerting
- Machine learning do predykcji problemów
- Alert correlation i deduplication
- Escalation policies
- Multi-channel notifications (Slack, email, SMS)
- Analytics Engine
- Trend analysis i forecasting
- Anomaly detection
- Performance baselines
- Business metrics correlation
Część 3: Enterprise Features (8-10 godzin)
- Advanced Monitoring
- Custom metrics framework
- Distributed tracing
- Log aggregation
- SLA monitoring
- Integration & Deployment
- Prometheus/Grafana integration
- Docker containerization
- Kubernetes deployment
- CI/CD pipeline
Część 4: Testing & Documentation (4-6 godzin)
- Comprehensive Testing
- Unit tests dla wszystkich komponentów
- Integration tests dla legion communication
- Load testing dla skalowalności
- E2E testing kompletnego workflow
- Documentation & Deployment Guide
- API documentation (Swagger)
- Deployment guide
- Monitoring runbooks
- Troubleshooting guide
Kryteria Oceny
Excellency Criteria (90-100%)
Kompletna implementacja wszystkich wymaganych funkcjonalności Wydajność - system obsługuje >100 kohort simultanously Skalowalność - łatwe dodawanie nowych typów metryk i alertów Reliability - system działa stabilnie przez 24h bez przerw Security - proper authentication i authorization Code Quality - clean code, proper patterns, comprehensive tests Documentation - complete documentation i deployment guides
Advanced Features (Bonus Points)
Machine Learning - predictive alerting i anomaly detection Auto-scaling - automatic resource adjustment based on metrics Multi-region - support dla legion rozproszonych geograficznie Mobile App - React Native app dla legion monitoring Voice Alerts - integration z Amazon Alexa dla voice notifications
Powodzenia, prefekcie! Twój legion liczy na Ciebie - stwórz system monitorowania, który będzie legendą Imperium technologii!
Kod do tej lekcji: src/monitoring/legion-monitoring-project.ts
1// PROJEKT: Kompletny System Monitorowania Legionu
2// Laczenie Exception Filters, Logging, Health Checks i Alertow
3import { Injectable, Logger } from '@nestjs/common';
4import { Controller, Get } from '@nestjs/common';
5
6// ===========================================
7// 1. Centralny system monitorowania
8// ===========================================
9
10@Injectable()
11export class LegionMonitoringService {
12 private logger = new Logger('LegionMonitor');
13
14 // Rejestr kohort w legionie
15 private cohorts = new Map<string, {
16 name: string;
17 status: 'operational' | 'warning' | 'critical' | 'offline';
18 lastHealthCheck: Date;
19 metrics: { cpu: number; memory: number; errors: number };
20 }>();
21
22 // Rejestracja kohorty
23 registerCohort(id: string, name: string) {
24 this.cohorts.set(id, {
25 name,
26 status: 'operational',
27 lastHealthCheck: new Date(),
28 metrics: { cpu: 0, memory: 0, errors: 0 },
29 });
30 this.logger.log('Zarejestrowano kohorta: ' + name);
31 }
32
33 // Aktualizacja metryk
34 updateMetrics(id: string, metrics: { cpu: number; memory: number; errors: number }) {
35 const cohort = this.cohorts.get(id);
36 if (!cohort) return;
37
38 cohort.metrics = metrics;
39 cohort.lastHealthCheck = new Date();
40
41 // Automatyczna zmiana statusu na podstawie metryk
42 if (metrics.errors > 10 || metrics.cpu > 95) {
43 cohort.status = 'critical';
44 this.logger.error('CRITICAL: ' + cohort.name);
45 } else if (metrics.errors > 5 || metrics.cpu > 80) {
46 cohort.status = 'warning';
47 this.logger.warn('WARNING: ' + cohort.name);
48 } else {
49 cohort.status = 'operational';
50 }
51 }
52
53 // Dashboard calej legionu
54 getLegionDashboard() {
55 const cohorts = Array.from(this.cohorts.entries()).map(([id, data]) => ({
56 id, ...data,
57 }));
58
59 const operational = cohorts.filter(s => s.status === 'operational').length;
60 const warnings = cohorts.filter(s => s.status === 'warning').length;
61 const critical = cohorts.filter(s => s.status === 'critical').length;
62
63 return {
64 totalCohorts: cohorts.length,
65 operational,
66 warnings,
67 critical,
68 legionHealth: critical > 0 ? 'ALERT' : warnings > 0 ? 'CAUTION' : 'OK',
69 cohorts,
70 generatedAt: new Date().toISOString(),
71 };
72 }
73}
74
75// ===========================================
76// 2. Dashboard Controller
77// ===========================================
78
79@Controller('legion-monitor')
80export class LegionMonitorController {
81 constructor(private monitorService: LegionMonitoringService) {}
82
83 @Get('dashboard')
84 getDashboard() {
85 return this.monitorService.getLegionDashboard();
86 }
87}
88
89// ===========================================
90// 3. Symulacja
91// ===========================================
92
93const monitor = new LegionMonitoringService();
94monitor.registerCohort('S1', 'Imperator');
95monitor.registerCohort('S2', 'Aquila');
96monitor.registerCohort('S3', 'Victoria');
97
98monitor.updateMetrics('S1', { cpu: 45, memory: 60, errors: 0 });
99monitor.updateMetrics('S2', { cpu: 85, memory: 70, errors: 7 });
100monitor.updateMetrics('S3', { cpu: 20, memory: 30, errors: 0 });
101
102const dashboard = monitor.getLegionDashboard();
103console.log('=== Legion Dashboard ===');
104console.log('Kohorty:', dashboard.totalCohorts);
105console.log('Operational:', dashboard.operational);
106console.log('Warnings:', dashboard.warnings);
107console.log('Critical:', dashboard.critical);
108console.log('Status legionu:', dashboard.legionHealth);
109Widzisz błąd w tej lekcji?