Managing Jurassic Park is no easy task. When problems arise in the dinosaur tracking system or we need to quickly diagnose why the lockdown protocol isn't starting correctly, debugging is a critical skill. One of the most important diagnostic tools in JavaScript is the browser console.
In this exercise we'll learn about a tool that will be your best friend when searching for and solving problems — the browser developer console.
Before we start using the console, we need to know how to open it:
After opening you'll see several tabs, including "Console". That's where we'll focus.
The most commonly used console function is
console.log(). It displays values in the console, which is indispensable during debugging:1// Displaying simple values
2console.log("Dinosaur tracking system started");
3console.log(42); // number
4console.log(true); // boolean value
5
6// Displaying multiple values at once
7const dinosaur = { name: "Rex", type: "T-Rex", status: "contained" };
8console.log("Dinosaur data:", dinosaur, "Update:", new Date());
9
10// String interpolation
11const sector = "B7";
12const threat = "high";
13console.log(`Dinosaur detected in sector ${sector}. Threat level: ${threat}`);In our Jurassic Park system we can use
console.log() to track various events:1function trackDinosaur(id, location) {
2 console.log(`Tracking dinosaur #${id} at location ${location}`);
3
4 // Tracking logic...
5 const status = checkContainment(id);
6
7 if (status !== "contained") {
8 console.log(`ALERT: Dinosaur #${id} is not in its enclosure! Current status: ${status}`);
9 }
10
11 return status;
12}In addition to the standard
log, we have special methods for different message levels:1// Errors — usually displayed in red
2console.error("CRITICAL ERROR: Fence system failure in T-Rex sector!");
3
4// Warnings — usually displayed in yellow
5console.warn("Warning: Motion sensor battery level dropping below 20%");
6
7// Info — often distinguished from regular logs
8console.info("Info: Technical system review scheduled for tomorrow, 8:00");Using them helps visually distinguish message types in the console, which is especially important when dealing with large volumes of logs.
When dealing with arrays of objects,
console.table() is invaluable:1// Array of dinosaurs in our park
2const dinosaurs = [
3 { id: 1, name: "Rex", species: "T-Rex", danger: 9, location: "Enclosure A" },
4 { id: 2, name: "Blue", species: "Velociraptor", danger: 8, location: "Enclosure B" },
5 { id: 3, name: "Delta", species: "Velociraptor", danger: 8, location: "Enclosure B" },
6 { id: 4, name: "Echo", species: "Velociraptor", danger: 7, location: "Enclosure B" },
7 { id: 5, name: "Trike", species: "Triceratops", danger: 4, location: "Enclosure C" }
8];
9
10// Displays a beautiful table with the data
11console.table(dinosaurs);
12
13// We can also select specific columns
14console.table(dinosaurs, ["name", "species", "danger"]);This is especially useful for data analysis, checking API query results, or reviewing configurations.
When we have many related logs, we can group them for clarity:
1// Monitoring a specific dinosaur
2function monitorDinosaur(dinosaur) {
3 console.group(`Monitoring: ${dinosaur.name} (${dinosaur.species})`);
4
5 console.log(`Location: ${dinosaur.location}`);
6 console.log(`Danger level: ${dinosaur.danger}/10`);
7
8 const healthStatus = checkHealth(dinosaur.id);
9 console.log(`Health status: ${healthStatus.status}`);
10
11 if (healthStatus.issues.length > 0) {
12 console.warn(`Health issues detected: ${healthStatus.issues.join(', ')}`);
13 }
14
15 const securityStatus = checkSecurity(dinosaur.location);
16 console.log(`Security status: ${securityStatus.status}`);
17
18 if (securityStatus.issues.length > 0) {
19 console.error(`Security issues: ${securityStatus.issues.join(', ')}`);
20 }
21
22 console.groupEnd();
23
24 return { healthStatus, securityStatus };
25}
26
27// Grouping can also be nested
28function monitorAllDinosaurs(dinosaurs) {
29 console.group('Monitoring all dinosaurs');
30
31 dinosaurs.forEach(dino => {
32 monitorDinosaur(dino);
33 });
34
35 console.groupEnd();
36}
37
38monitorAllDinosaurs(dinosaurs);You can also start a group collapsed using
console.groupCollapsed(), which is useful with many groups.Sometimes we need to know how long certain operations take:
1// Measuring module load time
2console.time('Loading tracking module');
3
4loadTrackingModule()
5 .then(() => {
6 console.timeEnd('Loading tracking module');
7 // Displays e.g.: "Loading tracking module: 235.41ms"
8 });
9
10// We can have multiple parallel timers
11console.time('System initialization');
12console.time('Data processing');
13
14initSystem()
15 .then(() => {
16 console.timeEnd('System initialization');
17 return processData();
18 })
19 .then(() => {
20 console.timeEnd('Data processing');
21 });This is extremely useful for identifying bottlenecks in applications and optimizing performance.
When you don't know exactly where a function was called from:
1function monitorFenceStatus(sectorId) {
2 console.trace(`Checking fence status in sector ${sectorId}`);
3 // Rest of code...
4}
5
6function checkAllFences() {
7 const sectors = ['A1', 'B2', 'C3'];
8 sectors.forEach(monitorFenceStatus);
9}
10
11function dailySecurityCheck() {
12 checkAllFences();
13}
14
15dailySecurityCheck();console.trace() will display the full call stack, showing the exact execution path, which is invaluable when debugging complex calls.Checks a condition and displays an error when the condition is false:
1function feedDinosaur(dinosaur, foodAmount) {
2 // Make sure the food amount is sufficient for the species
3 console.assert(
4 foodAmount >= getMinimumFoodRequirement(dinosaur.species),
5 `Insufficient food for ${dinosaur.name}! ` +
6 `Required minimum: ${getMinimumFoodRequirement(dinosaur.species)}kg, ` +
7 `Provided: ${foodAmount}kg`
8 );
9
10 // Rest of code...
11}
12
13feedDinosaur(dinosaurs[0], 5); // If 5kg is too little, we'll see an errorThis is useful for early detection of potential problems without stopping program execution.
Counting how many times a function or code block has been called:
1function checkFenceVoltage(sectorId) {
2 console.count(`Voltage check in sector ${sectorId}`);
3 // Rest of code...
4}
5
6// Simulate regular checks
7setInterval(() => checkFenceVoltage('A1'), 5000);
8setInterval(() => checkFenceVoltage('B2'), 8000);After each call we'll see in the console something like:
1Voltage check in sector A1: 1
2Voltage check in sector A1: 2
3Voltage check in sector B2: 1
4Voltage check in sector A1: 3We can reset the counter using
console.countReset('name').When we want to see the full structure of an object:
1const parkSystem = {
2 name: "Jurassic Park",
3 sectors: {
4 A: { dinosaurs: [/*...*/], status: "secure" },
5 B: { dinosaurs: [/*...*/], status: "alert" }
6 },
7 security: {
8 fences: { status: "online", voltage: 10000 },
9 cameras: { operational: 45, offline: 3 }
10 }
11};
12
13// Displays an interactive, expandable object structure
14console.dir(parkSystem);This function is especially useful for complex objects, as it allows browsing nested properties.
The console also supports basic formatting with format specifiers:
1// Styling text with CSS
2console.log(
3 "%cWARNING! %cCritical danger level %cin sector B!",
4 "color: red; font-size: 20px; font-weight: bold;",
5 "color: orange; font-size: 16px;",
6 "color: black; font-weight: bold;"
7);
8
9// Displaying variables in a message
10console.log("Dinosaur %s has danger level %d", "Rex", 9);Most browser consoles allow filtering displayed messages. We can filter by:
This is extremely useful when dealing with large volumes of logs.
Console messages can usually be saved to a file, which is useful for log analysis:
The console can be cleared in several ways:
console.clear()The browser console also works as a REPL (Read-Eval-Print Loop), meaning we can execute JavaScript code directly in it:
1// Directly in console
2const dinos = [{name: "Rex"}, {name: "Blue"}];
3dinos.filter(d => d.name === "Blue");
4
5// Defining functions
6function alertSecurity(message) {
7 return `[SECURITY ALERT] ${message} - ${new Date().toISOString()}`;
8}
9
10// Using the function
11alertSecurity("Fence breach in sector C2");This is extremely useful for testing small code snippets or debugging without modifying source code.
Let's create a practical implementation of a logging system for our Jurassic Park that makes use of various console functions:
1// Logger for the Jurassic Park system
2class JurassicLogger {
3 constructor(moduleName) {
4 this.moduleName = moduleName;
5 this.logCount = 0;
6 this.errorCount = 0;
7 this.warnCount = 0;
8 }
9
10 log(...args) {
11 this.logCount++;
12 console.log(`[${this.moduleName}] `, ...args);
13 }
14
15 error(...args) {
16 this.errorCount++;
17 console.error(`[${this.moduleName}] ERROR:`, ...args);
18 }
19
20 warn(...args) {
21 this.warnCount++;
22 console.warn(`[${this.moduleName}] WARNING:`, ...args);
23 }
24
25 info(...args) {
26 console.info(`[${this.moduleName}] INFO:`, ...args);
27 }
28
29 startGroup(label) {
30 console.group(`[${this.moduleName}] ${label}`);
31 }
32
33 endGroup() {
34 console.groupEnd();
35 }
36
37 startTimer(label) {
38 console.time(`[${this.moduleName}] ${label}`);
39 }
40
41 endTimer(label) {
42 console.timeEnd(`[${this.moduleName}] ${label}`);
43 }
44
45 table(data, columns) {
46 console.log(`[${this.moduleName}] Tabular data:`);
47 console.table(data, columns);
48 }
49
50 getStats() {
51 return {
52 moduleName: this.moduleName,
53 logs: this.logCount,
54 errors: this.errorCount,
55 warnings: this.warnCount
56 };
57 }
58
59 trace(message) {
60 console.log(`[${this.moduleName}] TRACE: ${message}`);
61 console.trace();
62 }
63
64 assert(condition, message) {
65 console.assert(condition, `[${this.moduleName}] Assertion failed: ${message}`);
66 }
67
68 // Special formatting for security alerts
69 securityAlert(message, level = 'warning') {
70 const styles = {
71 warning: "background: #FFA500; color: black; padding: 2px 5px; border-radius: 3px;",
72 critical: "background: #FF0000; color: white; padding: 2px 5px; border-radius: 3px; font-weight: bold;",
73 info: "background: #0066FF; color: white; padding: 2px 5px; border-radius: 3px;"
74 };
75
76 console.log(
77 `%c[${this.moduleName}] SECURITY ALERT %c${message}`,
78 styles[level] || styles.warning,
79 "color: black;"
80 );
81 }
82}
83
84// Example usage
85const securityLogger = new JurassicLogger('ParkSecurity');
86const dinosaurLogger = new JurassicLogger('DinosaurTracker');
87const systemLogger = new JurassicLogger('MainSystem');
88
89// System initialization
90systemLogger.startTimer('System initialization');
91systemLogger.log('Starting Jurassic Park system initialization...');
92
93setTimeout(() => {
94 systemLogger.info('Subsystems started');
95 systemLogger.endTimer('System initialization');
96
97 // Dinosaur information
98 const dinoData = [
99 { id: 'TRX-01', name: 'Rex', species: 'T-Rex', status: 'contained', healthLevel: 92 },
100 { id: 'VEL-01', name: 'Blue', species: 'Velociraptor', status: 'contained', healthLevel: 87 },
101 { id: 'VEL-02', name: 'Delta', species: 'Velociraptor', status: 'sedated', healthLevel: 65 },
102 { id: 'TRI-01', name: 'Sarah', species: 'Triceratops', status: 'contained', healthLevel: 95 }
103 ];
104
105 dinosaurLogger.startGroup('Dinosaur status report');
106 dinosaurLogger.table(dinoData);
107
108 // Check dinosaur health
109 dinoData.forEach(dino => {
110 dinosaurLogger.startGroup(`Dinosaur: ${dino.name}`);
111
112 if (dino.healthLevel < 70) {
113 dinosaurLogger.warn(`Dinosaur ${dino.name} health level is low: ${dino.healthLevel}%`);
114 } else {
115 dinosaurLogger.log(`Health status: ${dino.healthLevel}%`);
116 }
117
118 if (dino.status !== 'contained') {
119 dinosaurLogger.error(`Dinosaur ${dino.name} is not in its enclosure! Status: ${dino.status}`);
120 securityLogger.securityAlert(`Dinosaur ${dino.name} (${dino.species}) status: ${dino.status}`, 'critical');
121 }
122
123 dinosaurLogger.endGroup();
124 });
125
126 dinosaurLogger.endGroup();
127
128 // Check security systems
129 securityLogger.startGroup('Security systems');
130
131 const fenceStatus = { sector: 'B', voltage: 9800, status: 'active' };
132 securityLogger.log(`Fence status in sector ${fenceStatus.sector}: ${fenceStatus.status}`);
133 securityLogger.assert(
134 fenceStatus.voltage >= 10000,
135 `Fence voltage in sector ${fenceStatus.sector} is too low: ${fenceStatus.voltage}V (min. 10000V)`
136 );
137
138 securityLogger.endGroup();
139
140 // Display log stats
141 systemLogger.startGroup('Log statistics');
142 console.table([
143 securityLogger.getStats(),
144 dinosaurLogger.getStats(),
145 systemLogger.getStats()
146 ]);
147 systemLogger.endGroup();
148
149}, 2000);In real applications it's worth separating development logging from production logging:
1// logger.js
2class Logger {
3 constructor(isProduction = process.env.NODE_ENV === 'production') {
4 this.isProduction = isProduction;
5 }
6
7 log(...args) {
8 if (!this.isProduction) {
9 console.log(...args);
10 }
11 }
12
13 // For critical errors, we log even in production
14 error(...args) {
15 console.error(...args);
16
17 if (this.isProduction) {
18 // In production we can send errors to a monitoring service
19 this.sendToErrorMonitoring(args);
20 }
21 }
22
23 sendToErrorMonitoring(errorData) {
24 // Implementation for sending to Sentry, LogRocket, etc.
25 }
26}
27
28export default new Logger();The browser console is a powerful tool that greatly simplifies debugging and monitoring JavaScript applications. In the context of our Jurassic Park, good logging practices can be the difference between a smoothly running park and... well, the scenario from the movies!
Key functions to remember:
console.log() — basic loggingconsole.error(), console.warn(), console.info() — different message levelsconsole.table() — clean data displayconsole.group() — organizing related logsconsole.time() and console.timeEnd() — measuring performanceconsole.trace() — diagnosing where a call comes fromconsole.assert() — conditional debug messagesRemember to regularly use the console during development — it will help you detect and fix bugs faster, before the dinosaurs find a hole in your code!