We use cookies to enhance your experience on the site
CodeWorlds

Final Project: Jurassic Park Diagnostics System

Welcome to the final exercise of Module 5! We're going to build a complete Jurassic Park Diagnostics System that applies everything we've learned: custom error classes, a professional logging system, nested try/catch blocks, the finally clause, and modular code architecture.

"We need a system that can scan all park subsystems and generate a comprehensive diagnostic report," says Dr. Ellie Sattler. "Fences, power, sensors — and when something goes wrong, the system must log it, classify it by severity, and recommend a response."

System Architecture

The diagnostics system consists of four modules:

  1. Custom error hierarchy — ParkError, FenceError, PowerError, SensorError, SecurityBreachError
  2. Logging system — ParkLogger with log levels (DEBUG, INFO, WARN, ERROR, FATAL)
  3. Diagnostic module — ParkDiagnostics scanning subsystems with nested try/catch/finally
  4. Reporting module — DiagnosticsReporter aggregating results and generating recommendations

1. Custom Error Hierarchy

1// Base error for all park issues
2class ParkError extends Error {
3  constructor(message, code, sector, severity = "medium") {
4    super(message);
5    this.name = "ParkError";
6    this.code = code;
7    this.sector = sector;
8    this.severity = severity;
9    this.timestamp = new Date().toISOString();
10    this.resolved = false;
11
12    if (Error.captureStackTrace) {
13      Error.captureStackTrace(this, this.constructor);
14    }
15  }
16
17  resolve() {
18    this.resolved = true;
19    this.resolvedAt = new Date().toISOString();
20    return this;
21  }
22
23  toReport() {
24    return {
25      name: this.name,
26      message: this.message,
27      code: this.code,
28      sector: this.sector,
29      severity: this.severity,
30      timestamp: this.timestamp,
31      resolved: this.resolved
32    };
33  }
34}
35
36// Fence error — physical security
37class FenceError extends ParkError {
38  constructor(sector, fenceId, integrity) {
39    const severity = integrity < 50 ? "critical" : integrity < 70 ? "high" : "medium";
40    super(
41      `Fence failure ${fenceId} in sector ${sector}: integrity ${integrity}%`,
42      "FENCE_FAILURE",
43      sector,
44      severity
45    );
46    this.name = "FenceError";
47    this.fenceId = fenceId;
48    this.integrity = integrity;
49  }
50}
51
52// Power error — infrastructure
53class PowerError extends ParkError {
54  constructor(sector, powerLevel, backupAvailable) {
55    const severity = !backupAvailable && powerLevel < 30 ? "critical" : "high";
56    super(
57      `Power failure in sector ${sector}: ${powerLevel}%. Backup: ${backupAvailable ? "available" : "unavailable"}`,
58      "POWER_FAILURE",
59      sector,
60      severity
61    );
62    this.name = "PowerError";
63    this.powerLevel = powerLevel;
64    this.backupAvailable = backupAvailable;
65  }
66}
67
68// Sensor error
69class SensorError extends ParkError {
70  constructor(sector, sensorId, issue) {
71    super(
72      `Sensor ${sensorId} failure in sector ${sector}: ${issue}`,
73      "SENSOR_FAILURE",
74      sector,
75      "medium"
76    );
77    this.name = "SensorError";
78    this.sensorId = sensorId;
79    this.issue = issue;
80  }
81}
82
83// Security breach — always critical
84class SecurityBreachError extends ParkError {
85  constructor(sector, breachType, speciesInvolved) {
86    super(
87      `SECURITY BREACH in ${sector}: ${breachType} — species: ${speciesInvolved}`,
88      "SECURITY_BREACH",
89      sector,
90      "critical"
91    );
92    this.name = "SecurityBreachError";
93    this.breachType = breachType;
94    this.speciesInvolved = speciesInvolved;
95  }
96}

Each error type carries additional contextual information, making diagnosis and repair easier.

2. Logging System (Logger with Levels)

The second module is a professional logging system that records all events with appropriate severity and context.

1// Log levels
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; // Stored log limit
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    // Log rotation — remove oldest
34    if (this.logs.length > this.maxLogs) {
35      this.logs = this.logs.slice(-this.maxLogs);
36    }
37
38    // Format output
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  // Filter logs by level
62  getLogsByLevel(level) {
63    const levelName = LogLevelNames[level];
64    return this.logs.filter(log => log.level === levelName);
65  }
66
67  // Log statistics
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}

The logger supports level filtering, stores event history, and automatically rotates old logs.

3. Diagnostic Module (Scanning and try/catch/finally)

The third module is the heart of the system — it scans individual park subsystems, catches and classifies errors, then takes appropriate corrective actions.

1class ParkDiagnostics {
2  constructor() {
3    this.logger = new ParkLogger("Diagnostics", LogLevel.DEBUG);
4    this.errors = [];
5    this.scanResults = [];
6  }
7
8  // Simulate fence scanning
9  scanFence(sector, fenceId) {
10    this.logger.info(`Scanning fence ${fenceId} in sector ${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(`Fence ${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(`CRITICAL fence failure! Evacuate sector ${sector}!`);
29          this._initiateEmergencyProtocol(sector);
30        }
31      } else {
32        // Unexpected error — log and re-throw
33        this.logger.fatal(`Unexpected error during scan: ${error.message}`);
34        throw error;
35      }
36
37      return { status: "FAILURE", error: error.toReport() };
38
39    } finally {
40      this.logger.debug(`Fence ${fenceId} scan completed`);
41    }
42  }
43
44  // Power scanning with nested try/catch
45  scanPower(sector) {
46    this.logger.info(`Scanning power in sector ${sector}`);
47
48    try {
49      const mainPower = Math.round(Math.random() * 100);
50      let backupStatus = false;
51
52      try {
53        // Attempt to check backup power
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(`Power issue: ${innerError.message}`);
62        } else {
63          throw innerError; // Pass unknown errors to outer 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: "WARNING", error: error.toReport() };
78      }
79      throw error;
80    }
81  }
82
83  // Full sector diagnostics
84  runFullDiagnostics(sector) {
85    this.logger.info(`=== Full diagnostics for sector: ${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      // Scan fences (3 fences per sector)
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      // Scan power
104      result.power = this.scanPower(sector);
105
106    } catch (unexpectedError) {
107      this.logger.fatal(`Unexpected diagnostics error: ${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(`Diagnostics ${sector} completed in ${result.duration}`);
114    }
115
116    return result;
117  }
118
119  _initiateEmergencyProtocol(sector) {
120    this.logger.fatal(`🚨 EMERGENCY PROTOCOL for sector ${sector} — sending notifications`);
121  }
122}

Note the nested

try/catch
blocks and the use of
finally
to record operation duration regardless of outcome.

4. Reporting Module (Data Aggregation and Analysis)

The last module collects data from all diagnostics and generates a readable report with recommendations.

1class DiagnosticsReporter {
2  constructor(diagnostics) {
3    this.diagnostics = diagnostics;
4    this.logger = new ParkLogger("Reporting", LogLevel.INFO);
5  }
6
7  generateReport() {
8    const allErrors = this.diagnostics.errors;
9    const scanResults = this.diagnostics.scanResults;
10
11    // Group errors by type
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    // Group errors by sector
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    // Count by 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 ? "CRITICAL" :
39                       severityCounts.high > 0 ? "WARNING" : "STABLE"
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("IMMEDIATELY: Evacuate sectors with critical fence failures");
56      recommendations.push("Deploy repair teams to damaged fences");
57    }
58
59    if (severity.high > 0) {
60      recommendations.push("URGENT: Review fences with reduced integrity");
61    }
62
63    const fenceErrors = errors.filter(e => e instanceof FenceError);
64    if (fenceErrors.length > 3) {
65      recommendations.push("Systematic review of all fences — multiple failures detected");
66    }
67
68    const powerErrors = errors.filter(e => e instanceof PowerError);
69    if (powerErrors.length > 0) {
70      recommendations.push("Check backup generators in all sectors");
71    }
72
73    if (recommendations.length === 0) {
74      recommendations.push("All systems stable — continue standard monitoring");
75    }
76
77    return recommendations;
78  }
79
80  _printReport(report) {
81    console.log("\n" + "=".repeat(60));
82    console.log("   JURASSIC PARK DIAGNOSTIC REPORT");
83    console.log("=".repeat(60));
84    console.log(`Generated: ${report.generatedAt}`);
85    console.log(`Status: ${report.summary.overallStatus}`);
86    console.log(`Sectors scanned: ${report.summary.sectorsScanned}`);
87    console.log(`Errors detected: ${report.summary.totalErrors} (critical: ${report.summary.criticalErrors})`);
88
89    console.log("\nErrors by type:");
90    Object.entries(report.errorsByType).forEach(([type, errors]) => {
91      console.log(`  ${type}: ${errors.length}`);
92    });
93
94    console.log("\nRecommendations:");
95    report.recommendations.forEach((rec, i) => {
96      console.log(`  ${i + 1}. ${rec}`);
97    });
98
99    console.log("=".repeat(60));
100  }
101}
102
103// Run the diagnostic system
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// Logger statistics
113console.log("\nLog statistics:", diagnostics.logger.getStats());

Implementation Tips

When building the diagnostic system, keep these principles in mind:

The error hierarchy should reflect the domain:

1// Each error type = one kind of park problem
2ParkError          // base — general park error
3├── FenceError     // fences — physical security
4├── PowerError     // power — infrastructure
5├── SensorError    // sensors — monitoring
6└── SecurityBreachError  // breaches — highest priority

Use instanceof to distinguish error types:

1try {
2  riskyOperation();
3} catch (error) {
4  if (error instanceof SecurityBreachError) {
5    // Immediate response
6  } else if (error instanceof FenceError) {
7    // Repair protocol
8  } else if (error instanceof ParkError) {
9    // General park error handling
10  } else {
11    // Unknown error — log and escalate
12    throw error;
13  }
14}

Always use finally for cleanup:

1function scanWithCleanup() {
2  const connection = openConnection();
3  try {
4    return performScan(connection);
5  } catch (error) {
6    logError(error);
7    return null;
8  } finally {
9    // Always close the connection — even on error
10    connection.close();
11  }
12}

Good Luck!

This project is your chance to prove you can build resilient, well-organized systems. In Jurassic Park there's no room for unhandled exceptions — every error must be caught, logged, and handled before the situation gets out of control. As John Hammond said: "We spared no expense!" — and you shouldn't spare effort when it comes to error handling.

Create your diagnostic system in the editor below. Implement your own error types, logging system, and a diagnostic mechanism for at least two park subsystems.

Go to CodeWorlds