Zarządzanie Parkiem Jurajskim nie jest łatwym zadaniem. Gdy pojawiają się problemy z systemem śledzenia dinozaurów lub gdy musimy szybko zdiagnozować, dlaczego protokół blokady nie uruchamia się prawidłowo, kluczową umiejętnością jest debugowanie. Jednym z najważniejszych narzędzi diagnostycznych w JavaScript jest konsola przeglądarki.
W tym ćwiczeniu poznamy narzędzie, które będzie Twoim najlepszym przyjacielem podczas poszukiwania i rozwiązywania problemów - konsolę deweloperską przeglądarki.
Zanim zaczniemy wykorzystywać konsolę, musimy wiedzieć, jak ją otworzyć:
Po otwarciu zobaczysz kilka zakładek, w tym "Console" (Konsola). To właśnie na niej będziemy się skupiać.
Najczęściej używaną funkcją konsoli jest
console.log(). Służy do wyświetlania wartości w konsoli, co jest niezastąpione podczas debugowania:1// Wyświetlanie prostych wartości
2console.log("System śledzenia dinozaurów uruchomiony");
3console.log(42); // liczba
4console.log(true); // wartość boolowska
5
6// Wyświetlanie wielu wartości naraz
7const dinosaur = { name: "Rex", type: "T-Rex", status: "contained" };
8console.log("Dane dinozaura:", dinosaur, "Aktualizacja:", new Date());
9
10// Łączenie tekstu (interpolacja)
11const sector = "B7";
12const threat = "wysoki";
13console.log(`Wykryto dinozaura w sektorze ${sector}. Poziom zagrożenia: ${threat}`);W przypadku naszego systemu Parku Jurajskiego możemy używać
console.log() do śledzenia różnych zdarzeń:1function trackDinosaur(id, location) {
2 console.log(`Śledzenie dinozaura #${id} w lokalizacji ${location}`);
3
4 // Logika śledzenia...
5 const status = checkContainment(id);
6
7 if (status !== "contained") {
8 console.log(`ALERT: Dinozaur #${id} nie jest w wybiegu! Aktualny status: ${status}`);
9 }
10
11 return status;
12}Oprócz standardowego
log, mamy specjalne metody dla różnych poziomów komunikatów:1// Błędy - zwykle pokazywane na czerwono
2console.error("KRYTYCZNY BŁĄD: Awaria systemu ogrodzeń w sektorze T-Rex!");
3
4// Ostrzeżenia - zwykle pokazywane na żółto
5console.warn("Ostrzeżenie: Poziom naładowania baterii w czujnikach ruchu spada poniżej 20%");
6
7// Informacje - często wyróżnione od zwykłych logów
8console.info("Informacja: Zaplanowano przegląd techniczny systemu na jutro, 8:00");Ich używanie pomaga wizualnie rozróżnić typy komunikatów w konsoli, co jest szczególnie ważne, gdy mamy do czynienia z dużą ilością logów.
Gdy mamy do czynienia z tablicami obiektów,
console.table() jest niezastąpiony:1// Tablica dinozaurów w naszym parku
2const dinosaurs = [
3 { id: 1, name: "Rex", species: "T-Rex", danger: 9, location: "Wybieg A" },
4 { id: 2, name: "Blue", species: "Velociraptor", danger: 8, location: "Wybieg B" },
5 { id: 3, name: "Delta", species: "Velociraptor", danger: 8, location: "Wybieg B" },
6 { id: 4, name: "Echo", species: "Velociraptor", danger: 7, location: "Wybieg B" },
7 { id: 5, name: "Trike", species: "Triceratops", danger: 4, location: "Wybieg C" }
8];
9
10// Wyświetla piękną tabelę z danymi
11console.table(dinosaurs);
12
13// Możemy też wybrać tylko niektóre kolumny
14console.table(dinosaurs, ["name", "species", "danger"]);Jest to szczególnie przydatne przy analizie danych, sprawdzaniu wyników zapytań API czy przeglądaniu konfiguracji.
Gdy mamy wiele powiązanych logów, możemy je zgrupować dla przejrzystości:
1// Monitorowanie konkretnego dinozaura
2function monitorDinosaur(dinosaur) {
3 console.group(`Monitoring: ${dinosaur.name} (${dinosaur.species})`);
4
5 console.log(`Lokalizacja: ${dinosaur.location}`);
6 console.log(`Poziom zagrożenia: ${dinosaur.danger}/10`);
7
8 const healthStatus = checkHealth(dinosaur.id);
9 console.log(`Stan zdrowia: ${healthStatus.status}`);
10
11 if (healthStatus.issues.length > 0) {
12 console.warn(`Wykryto problemy zdrowotne: ${healthStatus.issues.join(', ')}`);
13 }
14
15 const securityStatus = checkSecurity(dinosaur.location);
16 console.log(`Stan zabezpieczeń: ${securityStatus.status}`);
17
18 if (securityStatus.issues.length > 0) {
19 console.error(`Problemy z zabezpieczeniami: ${securityStatus.issues.join(', ')}`);
20 }
21
22 console.groupEnd();
23
24 return { healthStatus, securityStatus };
25}
26
27// Grupowanie można również zagnieżdżać
28function monitorAllDinosaurs(dinosaurs) {
29 console.group('Monitoring wszystkich dinozaurów');
30
31 dinosaurs.forEach(dino => {
32 monitorDinosaur(dino);
33 });
34
35 console.groupEnd();
36}
37
38monitorAllDinosaurs(dinosaurs);Grupowanie można również zacząć jako zwinięte, używając
console.groupCollapsed(), co jest przydatne przy dużej liczbie grup.Czasem potrzebujemy wiedzieć, jak długo trwa wykonanie pewnych operacji:
1// Mierzenie czasu ładowania modułu
2console.time('Ładowanie modułu śledzenia');
3
4loadTrackingModule()
5 .then(() => {
6 console.timeEnd('Ładowanie modułu śledzenia');
7 // Wyświetli np.: "Ładowanie modułu śledzenia: 235.41ms"
8 });
9
10// Możemy mieć wiele równoległych timerów
11console.time('Inicjalizacja systemu');
12console.time('Przetwarzanie danych');
13
14initSystem()
15 .then(() => {
16 console.timeEnd('Inicjalizacja systemu');
17 return processData();
18 })
19 .then(() => {
20 console.timeEnd('Przetwarzanie danych');
21 });Jest to niezwykle przydatne do identyfikacji wąskich gardeł w aplikacji i optymalizacji wydajności.
Gdy nie wiesz, skąd dokładnie została wywołana funkcja:
1function monitorFenceStatus(sectorId) {
2 console.trace(`Sprawdzanie statusu ogrodzenia w sektorze ${sectorId}`);
3 // Reszta kodu...
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() wyświetli pełny stos wywołań funkcji, pokazując dokładną ścieżkę wykonania, co jest nieocenione przy debugowaniu skomplikowanych wywołań.Sprawdza warunek i wyświetla błąd, gdy warunek jest fałszywy:
1function feedDinosaur(dinosaur, foodAmount) {
2 // Upewniamy się, że ilość jedzenia jest wystarczająca dla danego gatunku
3 console.assert(
4 foodAmount >= getMinimumFoodRequirement(dinosaur.species),
5 `Niewystarczająca ilość jedzenia dla ${dinosaur.name}! ` +
6 `Wymagane minimum: ${getMinimumFoodRequirement(dinosaur.species)}kg, ` +
7 `Dostarczono: ${foodAmount}kg`
8 );
9
10 // Reszta kodu...
11}
12
13feedDinosaur(dinosaurs[0], 5); // Jeśli 5kg to za mało, zobaczymy błądJest to przydatne do wczesnegowykrywania potencjalnych problemów, bez przerywania działania programu.
Liczenie, ile razy dana funkcja lub blok kodu został wywołany:
1function checkFenceVoltage(sectorId) {
2 console.count(`Sprawdzenie napięcia w sektorze ${sectorId}`);
3 // Reszta kodu...
4}
5
6// Symulacja regularnego sprawdzania
7setInterval(() => checkFenceVoltage('A1'), 5000);
8setInterval(() => checkFenceVoltage('B2'), 8000);Po każdym wywołaniu zobaczymy w konsoli coś w rodzaju:
1Sprawdzenie napięcia w sektorze A1: 1
2Sprawdzenie napięcia w sektorze A1: 2
3Sprawdzenie napięcia w sektorze B2: 1
4Sprawdzenie napięcia w sektorze A1: 3Możemy zresetować licznik używając
console.countReset('nazwa').Gdy chcemy zobaczyć pełną strukturę obiektu:
1const parkSystem = {
2 name: "Jurassic Park",
3 sectors: {
4 A: { dinosaurs: [/*...*/], status: "secure" },
5 B: { dinosaurs: [/*...*/], status: "alert" },
6 //
7 },
8 security: {
9 fences: { status: "online", voltage: 10000 },
10 cameras: { operational: 45, offline: 3 },
11 //
12 },
13 //
14};
15
16// Wyświetla interaktywną, rozwijaną strukturę obiektu
17console.dir(parkSystem);Ta funkcja jest szczególnie przydatna dla złożonych obiektów, gdyż pozwala na przeglądanie zagnieżdżonych właściwości.
Konsola obsługuje również podstawowe formatowanie za pomocą specyfikatorów:
1// Formatowanie tekstu z różnymi stylami CSS
2console.log(
3 "%cUWAGA! %cPoziom zagrożenia krytyczny %cin sektorze 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// Wyświetlanie zmiennych w komunikacie
10console.log("Dinozaur %s ma poziom zagrożenia %d", "Rex", 9);Większość konsoli przeglądarek pozwala na filtrowanie wyświetlanych komunikatów. Możemy filtrować według:
Jest to niezwykle przydatne, gdy mamy do czynienia z dużą ilością logów.
Komunikaty z konsoli możemy zazwyczaj zapisać do pliku, co jest przydatne do analizy logów:
Konsolę można wyczyścić na kilka sposobów:
console.clear()Konsola przeglądarki działa również jako REPL (Read-Eval-Print Loop), co oznacza, że możemy bezpośrednio wykonywać w niej kod JavaScript:
1// Bezpośrednio w konsoli
2const dinos = [{name: "Rex"}, {name: "Blue"}];
3dinos.filter(d => d.name === "Blue");
4
5// Definiowanie funkcji
6function alertSecurity(message) {
7 return `[SECURITY ALERT] ${message} - ${new Date().toISOString()}`;
8}
9
10// Używanie tej funkcji
11alertSecurity("Naruszenie ogrodzenia w sektorze C2");Jest to niezwykle przydatne do testowania małych fragmentów kodu czy debugowania bez modyfikowania kodu źródłowego.
Stwórzmy praktyczną implementację systemu logowania dla naszego Parku Jurajskiego, który wykorzysta różne funkcje konsoli:
1// Logger dla systemu Parku Jurajskiego
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}] Dane tabelaryczne:`);
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 // Formatowanie specjalne dla alertów bezpieczeństwa
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// Przykład użycia
85const securityLogger = new JurassicLogger('ParkSecurity');
86const dinosaurLogger = new JurassicLogger('DinosaurTracker');
87const systemLogger = new JurassicLogger('MainSystem');
88
89// Inicjalizacja systemu
90systemLogger.startTimer('Inicjalizacja systemu');
91systemLogger.log('Rozpoczynanie inicjalizacji systemów Parku Jurajskiego...');
92
93// Symulacja inicjalizacji podsystemów
94setTimeout(() => {
95 systemLogger.info('Podsystemy uruchomione');
96 systemLogger.endTimer('Inicjalizacja systemu');
97
98 // Informacje o dinozaurach
99 const dinoData = [
100 { id: 'TRX-01', name: 'Rex', species: 'T-Rex', status: 'contained', healthLevel: 92 },
101 { id: 'VEL-01', name: 'Blue', species: 'Velociraptor', status: 'contained', healthLevel: 87 },
102 { id: 'VEL-02', name: 'Delta', species: 'Velociraptor', status: 'sedated', healthLevel: 65 },
103 { id: 'TRI-01', name: 'Sarah', species: 'Triceratops', status: 'contained', healthLevel: 95 }
104 ];
105
106 dinosaurLogger.startGroup('Raport statusu dinozaurów');
107 dinosaurLogger.table(dinoData);
108
109 // Sprawdzanie stanu zdrowia dinozaurów
110 dinoData.forEach(dino => {
111 dinosaurLogger.startGroup(`Dinozaur: ${dino.name}`);
112
113 if (dino.healthLevel < 70) {
114 dinosaurLogger.warn(`Poziom zdrowia dinozaura ${dino.name} jest niski: ${dino.healthLevel}%`);
115 } else {
116 dinosaurLogger.log(`Stan zdrowia: ${dino.healthLevel}%`);
117 }
118
119 if (dino.status !== 'contained') {
120 dinosaurLogger.error(`Dinozaur ${dino.name} nie jest w wybiegu! Status: ${dino.status}`);
121 securityLogger.securityAlert(`Dinozaur ${dino.name} (${dino.species}) ma status: ${dino.status}`, 'critical');
122 }
123
124 dinosaurLogger.endGroup();
125 });
126
127 dinosaurLogger.endGroup();
128
129 // Sprawdzanie systemów bezpieczeństwa
130 securityLogger.startGroup('Systemy bezpieczeństwa');
131
132 const fenceStatus = { sector: 'B', voltage: 9800, status: 'active' };
133 securityLogger.log(`Status ogrodzenia w sektorze ${fenceStatus.sector}: ${fenceStatus.status}`);
134 securityLogger.assert(
135 fenceStatus.voltage >= 10000,
136 `Napięcie ogrodzenia w sektorze ${fenceStatus.sector} jest zbyt niskie: ${fenceStatus.voltage}V (min. 10000V)`
137 );
138
139 // Symulacja naruszenia bezpieczeństwa
140 if (Math.random() > 0.7) {
141 securityLogger.trace('Wykryto nieautoryzowany dostęp do panelu sterowania');
142 securityLogger.securityAlert('Wykryto nieautoryzowany dostęp do systemów kontroli!', 'critical');
143 }
144
145 securityLogger.endGroup();
146
147 // Wyświetlanie statystyk logów
148 systemLogger.startGroup('Statystyki logów');
149 console.table([
150 securityLogger.getStats(),
151 dinosaurLogger.getStats(),
152 systemLogger.getStats()
153 ]);
154 systemLogger.endGroup();
155
156}, 2000);Nowoczesne frameworki jak React czy Vue mają własne integracje z konsolą, które dostarczają dodatkowych informacji debugujących:
React DevTools dodaje do konsoli przeglądarki specjalne narzędzia do inspekcji komponentów React, stanu, propów i drzewa komponentów. To niezastąpione narzędzie dla każdego dewelopera React.
Podobnie Vue ma własne narzędzia deweloperskie, które pozwalają na inspekcję komponentów, stanu Vuex i innych aspektów aplikacji Vue.
W rzeczywistych aplikacjach warto rozdzielić logowanie deweloperskie od produkcyjnego:
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 // W przypadku błędów krytycznych, logujemy nawet na produkcji
14 error(...args) {
15 console.error(...args);
16
17 if (this.isProduction) {
18 // Na produkcji możemy wysłać błędy do usługi monitorowania
19 this.sendToErrorMonitoring(args);
20 }
21 }
22
23 // ...inne metody
24
25 sendToErrorMonitoring(errorData) {
26 // Implementacja wysyłania do Sentry, LogRocket itp.
27 }
28}
29
30export default new Logger();Konsola przeglądarki to potężne narzędzie, które znacznie ułatwia debugowanie i monitorowanie aplikacji JavaScript. W kontekście naszego Parku Jurajskiego, dobre praktyki logowania mogą być różnicą między sprawnie działającym parkiem a... cóż, scenariuszem znanym z filmów!
Najważniejsze funkcje do zapamiętania:
console.log() - podstawowe logowanieconsole.error(), console.warn(), console.info() - różne poziomy komunikatówconsole.table() - przejrzyste wyświetlanie danychconsole.group() - organizacja powiązanych logówconsole.time() i console.timeEnd() - mierzenie wydajnościconsole.trace() - diagnozowanie skąd pochodzi wywołanieconsole.assert() - warunkowe komunikaty debugowaniaPamiętaj, aby regularnie używać konsoli podczas tworzenia aplikacji - pomoże Ci to szybciej wykrywać i naprawiać błędy, zanim dinozaury znajdą lukę w Twoim kodzie!