Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Projekt końcowy: System diagnostyki Parku Jurajskiego

Dotarłeś do końcowego projektu tego modułu! Przez ostatnie lekcje poznałeś narzędzia, które są fundamentem profesjonalnego programowania: modularyzację kodu, techniki debugowania, profilowanie wydajności oraz zaawansowaną obsługę błędów. Teraz nadszedł czas, aby wszystkie te umiejętności połączyć w jeden kompleksowy system.

Wyobraź sobie, że po serii incydentów w Parku Jurajskim - awariach ogrodzeń, przeciążeniach systemów zasilania i niewyjaśnionych błędach w oprogramowaniu sterującym - zarząd parku postanowił stworzyć zaawansowany system diagnostyczny. System ten ma wykrywać problemy zanim staną się katastrofalne, logować wszystkie zdarzenia, a w przypadku awarii - dostarczać szczegółowe raporty, które pomogą inżynierom szybko zlokalizować i naprawić usterki. Twoje zadanie: zaprojektować i zaimplementować ten system od podstaw.

W tym projekcie zbudujesz modularny system diagnostyczny z własnymi typami błędów, zaawansowaną obsługą wyjątków, systemem logowania i narzędziami do analizy wydajności.

Cel projektu

Stworzysz system diagnostyki Parku Jurajskiego składający się z czterech kluczowych modułów:

  1. Moduł błędów - własna hierarchia typów błędów dla systemów parku
  2. Moduł logowania - zaawansowany logger z poziomami i kontekstem
  3. Moduł diagnostyczny - skanowanie i testowanie podsystemów parku
  4. Moduł raportowania - generowanie szczegółowych raportów diagnostycznych

Wymagania funkcjonalne

1. Hierarchia błędów parku (Własne typy błędów)

Pierwszym krokiem jest stworzenie dedykowanej hierarchii błędów, która pozwoli precyzyjnie identyfikować rodzaj i źródło każdego problemu w parku.

1// Bazowy błąd parku
2class ParkError extends Error {
3  constructor(message, code, sector, severity) {
4    super(message);
5    this.name = "ParkError";
6    this.code = code;
7    this.sector = sector;
8    this.severity = severity; // "low", "medium", "high", "critical"
9    this.timestamp = new Date().toISOString();
10    this.resolved = false;
11  }
12
13  resolve() {
14    this.resolved = true;
15    this.resolvedAt = new Date().toISOString();
16  }
17
18  toReport() {
19    return {
20      type: this.name,
21      message: this.message,
22      code: this.code,
23      sector: this.sector,
24      severity: this.severity,
25      timestamp: this.timestamp,
26      resolved: this.resolved
27    };
28  }
29}
30
31// Błąd ogrodzenia - krytyczny dla bezpieczeństwa
32class FenceError extends ParkError {
33  constructor(sector, fenceId, integrityLevel) {
34    const severity = integrityLevel < 50 ? "critical" : "high";
35    super(
36      `Ogrodzenie ${fenceId} w sektorze ${sector}: integralność ${integrityLevel}%`,
37      "FENCE_INTEGRITY",
38      sector,
39      severity
40    );
41    this.name = "FenceError";
42    this.fenceId = fenceId;
43    this.integrityLevel = integrityLevel;
44  }
45}
46
47// Błąd zasilania
48class PowerError extends ParkError {
49  constructor(sector, powerLevel, isBackupActive) {
50    const severity = !isBackupActive && powerLevel < 20 ? "critical" : "medium";
51    super(
52      `Zasilanie w sektorze ${sector}: ${powerLevel}% - backup: ${isBackupActive ? "aktywny" : "nieaktywny"}`,
53      "POWER_FAILURE",
54      sector,
55      severity
56    );
57    this.name = "PowerError";
58    this.powerLevel = powerLevel;
59    this.isBackupActive = isBackupActive;
60  }
61}
62
63// Błąd czujników
64class SensorError extends ParkError {
65  constructor(sector, sensorId, errorType) {
66    super(
67      `Czujnik ${sensorId} w sektorze ${sector}: ${errorType}`,
68      "SENSOR_MALFUNCTION",
69      sector,
70      "medium"
71    );
72    this.name = "SensorError";
73    this.sensorId = sensorId;
74    this.errorType = errorType;
75  }
76}
77
78// Błąd bezpieczeństwa - zawsze krytyczny
79class SecurityBreachError extends ParkError {
80  constructor(sector, breachType, speciesInvolved) {
81    super(
82      `NARUSZENIE BEZPIECZEŃSTWA w ${sector}: ${breachType} - gatunek: ${speciesInvolved}`,
83      "SECURITY_BREACH",
84      sector,
85      "critical"
86    );
87    this.name = "SecurityBreachError";
88    this.breachType = breachType;
89    this.speciesInvolved = speciesInvolved;
90  }
91}

Każdy typ błędu niesie dodatkowe informacje kontekstowe, co ułatwia diagnostykę i naprawę problemu.

2. System logowania (Logger z poziomami)

Drugi moduł to profesjonalny system logowania, który rejestruje wszystkie zdarzenia z odpowiednim poziomem ważności i kontekstem.

1// Poziomy logowania
2const LogLevel = {
3  DEBUG: 0,
4  INFO: 1,
5  WARN: 2,
6  ERROR: 3,
7  FATAL: 4
8};
9
10const LogLevelNames = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"];
11
12class ParkLogger {
13  constructor(moduleName, minLevel = LogLevel.DEBUG) {
14    this.moduleName = moduleName;
15    this.minLevel = minLevel;
16    this.logs = [];
17    this.maxLogs = 1000; // Limit przechowywanych logów
18  }
19
20  _log(level, message, context = {}) {
21    if (level < this.minLevel) return;
22
23    const entry = {
24      timestamp: new Date().toISOString(),
25      level: LogLevelNames[level],
26      module: this.moduleName,
27      message,
28      context
29    };
30
31    this.logs.push(entry);
32
33    // Rotacja logów - usuwanie najstarszych
34    if (this.logs.length > this.maxLogs) {
35      this.logs = this.logs.slice(-this.maxLogs);
36    }
37
38    // Formatowanie wyjścia
39    const prefix = `[${entry.timestamp}] [${entry.level}] [${this.moduleName}]`;
40    const contextStr = Object.keys(context).length > 0
41      ? ` | ${JSON.stringify(context)}`
42      : "";
43
44    switch (level) {
45      case LogLevel.DEBUG: console.log(`${prefix} ${message}${contextStr}`); break;
46      case LogLevel.INFO:  console.info(`${prefix} ${message}${contextStr}`); break;
47      case LogLevel.WARN:  console.warn(`${prefix} ${message}${contextStr}`); break;
48      case LogLevel.ERROR: console.error(`${prefix} ${message}${contextStr}`); break;
49      case LogLevel.FATAL: console.error(`🚨 ${prefix} ${message}${contextStr}`); break;
50    }
51
52    return entry;
53  }
54
55  debug(message, context) { return this._log(LogLevel.DEBUG, message, context); }
56  info(message, context)  { return this._log(LogLevel.INFO, message, context); }
57  warn(message, context)  { return this._log(LogLevel.WARN, message, context); }
58  error(message, context) { return this._log(LogLevel.ERROR, message, context); }
59  fatal(message, context) { return this._log(LogLevel.FATAL, message, context); }
60
61  // Filtrowanie logów po poziomie
62  getLogsByLevel(level) {
63    const levelName = LogLevelNames[level];
64    return this.logs.filter(log => log.level === levelName);
65  }
66
67  // Statystyki logów
68  getStats() {
69    const stats = { total: this.logs.length };
70    LogLevelNames.forEach(name => {
71      stats[name] = this.logs.filter(l => l.level === name).length;
72    });
73    return stats;
74  }
75}

Logger pozwala na filtrowanie po poziomie, przechowuje historię zdarzeń i automatycznie rotuje stare logi.

3. Moduł diagnostyczny (Skanowanie i try/catch/finally)

Trzeci moduł to serce systemu - skanuje poszczególne podsystemy parku, przechwytuje i klasyfikuje błędy, a następnie podejmuje odpowiednie działania naprawcze.

1class ParkDiagnostics {
2  constructor() {
3    this.logger = new ParkLogger("Diagnostyka", LogLevel.DEBUG);
4    this.errors = [];
5    this.scanResults = [];
6  }
7
8  // Symulacja skanowania ogrodzenia
9  scanFence(sector, fenceId) {
10    this.logger.info(`Skanowanie ogrodzenia ${fenceId} w sektorze ${sector}`);
11
12    try {
13      const integrity = Math.round(40 + Math.random() * 60);
14
15      if (integrity < 80) {
16        throw new FenceError(sector, fenceId, integrity);
17      }
18
19      this.logger.debug(`Ogrodzenie ${fenceId} OK: ${integrity}%`, { sector, fenceId });
20      return { status: "OK", integrity };
21
22    } catch (error) {
23      if (error instanceof FenceError) {
24        this.errors.push(error);
25        this.logger.error(error.message, error.toReport());
26
27        if (error.severity === "critical") {
28          this.logger.fatal(`KRYTYCZNA awaria ogrodzenia! Ewakuacja sektora ${sector}!`);
29          this._initiateEmergencyProtocol(sector);
30        }
31      } else {
32        // Nieoczekiwany błąd - loguj i rzuć dalej
33        this.logger.fatal(`Nieoczekiwany błąd podczas skanowania: ${error.message}`);
34        throw error;
35      }
36
37      return { status: "AWARIA", error: error.toReport() };
38
39    } finally {
40      this.logger.debug(`Zakończono skanowanie ogrodzenia ${fenceId}`);
41    }
42  }
43
44  // Skanowanie zasilania z zagnieżdżonym try/catch
45  scanPower(sector) {
46    this.logger.info(`Skanowanie zasilania w sektorze ${sector}`);
47
48    try {
49      const mainPower = Math.round(Math.random() * 100);
50      let backupStatus = false;
51
52      try {
53        // Próba sprawdzenia zasilania awaryjnego
54        backupStatus = Math.random() > 0.3;
55        if (!backupStatus && mainPower < 30) {
56          throw new PowerError(sector, mainPower, false);
57        }
58      } catch (innerError) {
59        if (innerError instanceof PowerError) {
60          this.errors.push(innerError);
61          this.logger.error(`Problem z zasilaniem: ${innerError.message}`);
62        } else {
63          throw innerError; // Przekaż nieznane błędy do zewnętrznego catch
64        }
65      }
66
67      if (mainPower < 50) {
68        throw new PowerError(sector, mainPower, backupStatus);
69      }
70
71      return { status: "OK", mainPower, backupActive: backupStatus };
72
73    } catch (error) {
74      if (error instanceof PowerError) {
75        this.errors.push(error);
76        this.logger.warn(error.message, error.toReport());
77        return { status: "OSTRZEŻENIE", error: error.toReport() };
78      }
79      throw error;
80    }
81  }
82
83  // Pełna diagnostyka sektora
84  runFullDiagnostics(sector) {
85    this.logger.info(`=== Pełna diagnostyka sektora: ${sector} ===`);
86    const startTime = performance.now();
87
88    const result = {
89      sector,
90      fences: [],
91      power: null,
92      errors: [],
93      duration: 0
94    };
95
96    try {
97      // Skanowanie ogrodzeń (3 ogrodzenia per sektor)
98      for (let i = 1; i <= 3; i++) {
99        const fenceResult = this.scanFence(sector, `FENCE-${sector}-${i}`);
100        result.fences.push(fenceResult);
101      }
102
103      // Skanowanie zasilania
104      result.power = this.scanPower(sector);
105
106    } catch (unexpectedError) {
107      this.logger.fatal(`Nieoczekiwany błąd diagnostyki: ${unexpectedError.message}`);
108      result.criticalFailure = true;
109    } finally {
110      result.duration = `${(performance.now() - startTime).toFixed(2)}ms`;
111      result.errors = this.errors.filter(e => e.sector === sector).map(e => e.toReport());
112      this.scanResults.push(result);
113      this.logger.info(`Diagnostyka ${sector} zakończona w ${result.duration}`);
114    }
115
116    return result;
117  }
118
119  _initiateEmergencyProtocol(sector) {
120    this.logger.fatal(`🚨 PROTOKÓŁ AWARYJNY dla sektora ${sector} - wysyłanie powiadomień`);
121  }
122}

Zwróć uwagę na zagnieżdżone bloki

try/catch
i zastosowanie
finally
do rejestrowania czasu trwania operacji niezależnie od wyniku.

4. Moduł raportowania (Agregacja i analiza danych)

Ostatni moduł zbiera dane ze wszystkich diagnostyk i generuje czytelny raport z rekomendacjami.

1class DiagnosticsReporter {
2  constructor(diagnostics) {
3    this.diagnostics = diagnostics;
4    this.logger = new ParkLogger("Raportowanie", LogLevel.INFO);
5  }
6
7  generateReport() {
8    const allErrors = this.diagnostics.errors;
9    const scanResults = this.diagnostics.scanResults;
10
11    // Grupowanie błędów po typie
12    const errorsByType = {};
13    allErrors.forEach(error => {
14      const type = error.name;
15      if (!errorsByType[type]) errorsByType[type] = [];
16      errorsByType[type].push(error.toReport());
17    });
18
19    // Grupowanie błędów po sektorze
20    const errorsBySector = {};
21    allErrors.forEach(error => {
22      if (!errorsBySector[error.sector]) errorsBySector[error.sector] = [];
23      errorsBySector[error.sector].push(error.toReport());
24    });
25
26    // Zliczanie po severity
27    const severityCounts = { low: 0, medium: 0, high: 0, critical: 0 };
28    allErrors.forEach(error => {
29      severityCounts[error.severity]++;
30    });
31
32    const report = {
33      generatedAt: new Date().toISOString(),
34      summary: {
35        totalErrors: allErrors.length,
36        criticalErrors: severityCounts.critical,
37        sectorsScanned: scanResults.length,
38        overallStatus: severityCounts.critical > 0 ? "KRYTYCZNY" :
39                       severityCounts.high > 0 ? "OSTRZEŻENIE" : "STABILNY"
40      },
41      errorsByType,
42      errorsBySector,
43      severityCounts,
44      recommendations: this._generateRecommendations(allErrors, severityCounts)
45    };
46
47    this._printReport(report);
48    return report;
49  }
50
51  _generateRecommendations(errors, severity) {
52    const recommendations = [];
53
54    if (severity.critical > 0) {
55      recommendations.push("NATYCHMIAST: Ewakuacja sektorów z krytycznymi awariami ogrodzeń");
56      recommendations.push("Wysłanie zespołów naprawczych do uszkodzonych ogrodzeń");
57    }
58
59    if (severity.high > 0) {
60      recommendations.push("PILNE: Przegląd ogrodzeń z obniżoną integralnością");
61    }
62
63    const fenceErrors = errors.filter(e => e instanceof FenceError);
64    if (fenceErrors.length > 3) {
65      recommendations.push("Systemowy przegląd wszystkich ogrodzeń - wykryto liczne awarie");
66    }
67
68    const powerErrors = errors.filter(e => e instanceof PowerError);
69    if (powerErrors.length > 0) {
70      recommendations.push("Sprawdzenie generatorów awaryjnych we wszystkich sektorach");
71    }
72
73    if (recommendations.length === 0) {
74      recommendations.push("Wszystkie systemy stabilne - kontynuować standardowy monitoring");
75    }
76
77    return recommendations;
78  }
79
80  _printReport(report) {
81    console.log("\n" + "=".repeat(60));
82    console.log("   RAPORT DIAGNOSTYCZNY PARKU JURAJSKIEGO");
83    console.log("=".repeat(60));
84    console.log(`Wygenerowano: ${report.generatedAt}`);
85    console.log(`Status: ${report.summary.overallStatus}`);
86    console.log(`Sektory przeskanowane: ${report.summary.sectorsScanned}`);
87    console.log(`Wykryte błędy: ${report.summary.totalErrors} (krytycznych: ${report.summary.criticalErrors})`);
88
89    console.log("\nBłędy wg typu:");
90    Object.entries(report.errorsByType).forEach(([type, errors]) => {
91      console.log(`  ${type}: ${errors.length}`);
92    });
93
94    console.log("\nRekomendacje:");
95    report.recommendations.forEach((rec, i) => {
96      console.log(`  ${i + 1}. ${rec}`);
97    });
98
99    console.log("=".repeat(60));
100  }
101}
102
103// Uruchomienie systemu diagnostycznego
104const diagnostics = new ParkDiagnostics();
105
106const sectors = ["T-Rex-Paddock", "Raptor-Pen", "Herbivore-Valley"];
107sectors.forEach(sector => diagnostics.runFullDiagnostics(sector));
108
109const reporter = new DiagnosticsReporter(diagnostics);
110reporter.generateReport();
111
112// Statystyki loggera
113console.log("\nStatystyki logów:", diagnostics.logger.getStats());

Wskazówki implementacyjne

Przy budowaniu systemu diagnostycznego pamiętaj o następujących zasadach:

Hierarchia błędów powinna odzwierciedlać domenę:

1// Każdy typ błędu = jeden rodzaj problemu w parku
2ParkError          // bazowy - ogólny błąd parku
3├── FenceError     // ogrodzenia - bezpieczeństwo fizyczne
4├── PowerError     // zasilanie - infrastruktura
5├── SensorError    // czujniki - monitoring
6└── SecurityBreachError  // naruszenia - najwyższy priorytet

Używaj instanceof do rozróżniania typów błędów:

1try {
2  riskyOperation();
3} catch (error) {
4  if (error instanceof SecurityBreachError) {
5    // Natychmiastowa reakcja
6  } else if (error instanceof FenceError) {
7    // Protokół naprawczy
8  } else if (error instanceof ParkError) {
9    // Ogólna obsługa błędów parku
10  } else {
11    // Nieznany błąd - loguj i eskaluj
12    throw error;
13  }
14}

Zawsze używaj finally do czyszczenia zasobów:

1function scanWithCleanup() {
2  const connection = openConnection();
3  try {
4    return performScan(connection);
5  } catch (error) {
6    logError(error);
7    return null;
8  } finally {
9    // Zawsze zamknij połączenie - nawet przy błędzie
10    connection.close();
11  }
12}

Powodzenia!

Ten projekt to Twoja szansa na udowodnienie, że potrafisz budować odporne, dobrze zorganizowane systemy. W Parku Jurajskim nie ma miejsca na nieobsłużone wyjątki - każdy błąd musi być przechwycony, zalogowany i obsłużony, zanim sytuacja wymknie się spod kontroli. Jak mówił John Hammond: "Nie szczędziliśmy kosztów!" - a Ty nie szczędź wysiłku przy obsłudze błędów.

Stwórz swój system diagnostyczny w edytorze poniżej. Zaimplementuj własne typy błędów, system logowania i mechanizm diagnostyki przynajmniej dwóch podsystemów parku.

Ir a CodeWorlds