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."
The diagnostics system consists of four modules:
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.
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.
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.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());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 priorityUse 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}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.