1. Логи запуску — не «шум у консолі»
На цьому етапі ланцюжок старту вже зібрано: SpringApplication.run(...) піднімає застосунок, усередині нього вибирається WebApplicationType, а в гілці SERVLET Boot створює вебконтекст і запускає вбудований Tomcat. Логи запуску корисні тим, що показують увесь цей маршрут зовні. Вони не відкривають нову механіку, а дають зовнішній слід уже знайомого процесу.
Коли застосунок стартує, консоль починає швидко прокручуватися десятками рядків — і початківець зазвичай робить два рухи: заплющує очі й сподівається на краще. Але логи запуску — це не «балаканина Spring», а найперший звіт про те, що реально піднялося. Навчімося читати його як карту.
У звичайній Java-програмі ви часто точно знаєте, що відбувається: ось main(), ось new, ось виклики методів. У Spring Boot усе навпаки: ви написали зовсім мало коду, а відбувається багато. І це нормально — Boot саме для цього й існує. Але за цим «багато відбувається» обов’язково має бути спостережуваність: десь же вам потрібно побачити, що застосунок справді стартував, що він у вебрежимі, що сервер слухає порт, що контекст встиг зібратися.
Логи запуску — це перша й дуже чесна версія такої спостережуваності. Вони дають відповіді на прості запитання: який клас стартував, у якому режимі працює застосунок, чи піднявся вбудований сервер, на якому порті він слухає, чи дійшли ми до фінального рядка «Started … in … seconds». Якщо ви навчитеся бачити ці маркери, то перестанете ставитися до запуску як до ворожіння на кавовій гущі: «ніби працює… чи ні…».
І ще одна важлива думка. Логи — це не тільки про успіх. Логи особливо корисні, коли щось зламалося. У такому разі вони перетворюються на дорожні знаки: за останнім «успішним» рядком можна зрозуміти, на якому кроці запуск зупинився. Це набагато краще, ніж стратегія «додати ще одну анотацію і подивитися, що буде» — спойлер: буде сумно.
2. Розбір старту catalog-service
Щоб логи справді допомагали, потрібно знати, що саме в них шукати. Ми не будемо розбирати кожен рядок і кожен внутрішній клас Spring Boot 4 — це прямий шлях до смутку й раптової філософії. Замість цього візьмемо реальний запуск catalog-service і виділимо кілька «маячків», які швидко дають картину старту.
Нижче — скорочений і спрощений фрагмент типових логів запуску. У реальності рядків буде більше, але смислові маркери дуже схожі. Вважайте це «картою метро», а не фотографією кожної цеглини в стіні.
# Ключові маркери, на які варто звернути увагу: Starting..., Tomcat started..., Started ... in ... seconds
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ... banner ... )
2026-03-19T10:00:00.120 INFO 12345 --- [main] c.e.c.CatalogServiceApplication : Запуск CatalogServiceApplication з Java 25 та PID 12345
2026-03-19T10:00:00.121 INFO 12345 --- [main] c.e.c.CatalogServiceApplication : Активний профіль не задано, використовується 1 профіль за замовчуванням: "default"
2026-03-19T10:00:01.050 INFO 12345 --- [main] o.s.b.w.e.t.TomcatWebServer : Tomcat ініціалізовано з портом 8080 (http)
2026-03-19T10:00:01.120 INFO 12345 --- [main] o.a.catalina.core.StandardService: Запуск служби [Tomcat]
2026-03-19T10:00:01.330 INFO 12345 --- [main] o.s.b.w.s.c.ServletWebServerAppCtx: Ініціалізацію Root WebApplicationContext завершено за 640 мс
2026-03-19T10:00:01.450 INFO 12345 --- [main] o.s.b.w.e.t.TomcatWebServer : Tomcat запущено на порту 8080 (http) з контекстним шляхом ""
2026-03-19T10:00:01.470 INFO 12345 --- [main] c.e.c.CatalogServiceApplication : CatalogServiceApplication запущено за 1,52 секунди (процес виконується 1,70)
Тепер важлива навичка: не намагайтеся ввібрати все одразу. Спочатку достатньо вивчити кілька точок прив’язки.
| Що ми хочемо зрозуміти | Який рядок зазвичай про це говорить | Як це інтерпретувати |
|---|---|---|
| Старт узагалі почався | банер або перший рядок Starting ... | Процес живий, Boot почав запуск |
| Який застосунок стартує | Starting CatalogServiceApplication ... | Це ваш головний клас; якщо не він — ви запускаєте не те |
| Що з профілями | No active profile set ... або The following profiles are active ... | Це підказка про «режим середовища»; зараз достатньо помітити сам факт |
| Чи є вебсервер | рядки про Tomcat або про інший контейнер | Якщо вони є — застосунок піднявся як вебсервіс |
| Контекст зібрався | Root WebApplicationContext: initialization completed ... | Контейнер бінів дійшов до стану «готово» |
| Сервер слухає порт | Tomcat started on port ... | Можна намагатися перейти на http://localhost:<port> |
| Запуск завершився успішно | Started ... in ... seconds | Це фінальна позначка: застосунок піднявся |
Тепер розберімо, що взагалі означає «форма» одного рядка. Зазвичай вона виглядає приблизно так:
# Найкорисніше на старті — дивитися на <повідомлення>: воно зазвичай містить «маячки»
<час> <рівень> <pid> --- [потік] <імʼя логера> : <повідомлення>
На старті вам найважливіша остання частина — повідомлення. Час, PID і ім’я логера корисні, але вони знадобляться трохи пізніше, коли вигляд логів Spring перестане лякати.
Дуже практична звичка: ви очима шукаєте в логу пари слів, а не читаєте його як книжку. У нашому разі це Starting, profiles, Tomcat, port, Root WebApplicationContext, Started. Це і є «маячки».
Banner: зручний маркер старту
Banner у Spring Boot люблять по-різному: хтось вважає його милою заставкою, а хтось — зайвим ASCII-артом, який заважає жити. Насправді банер — просто маркер «ось тут почався запуск». Корисно розуміти, що він не впливає на роботу застосунку і може бути вимкнений одним методом.
Часта пастка початківця: «банер показався — отже застосунок працює». На жаль, банер може показатися і перед красивим падінням застосунку через дві секунди. Тому критерій успіху — не банер, а наявність фінального рядка Started ... і, для вебрежиму, рядка про Tomcat started on port ....
Якщо вам хочеться зробити логи компактнішими — наприклад, коли ви десятий раз перезапускаєте застосунок і вже знаєте цей ASCII-арт напам’ять, — банер можна вимкнути прямо в main() через об’єкт SpringApplication.
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
public static void main(String[] args) {
// Фрагмент усередині вашого головного класу застосунку (наприклад, CatalogServiceApplication)
SpringApplication app = new SpringApplication(CatalogServiceApplication.class);
// Вимикаємо ASCII-банер: це впливає лише на виведення в консоль, не на роботу застосунку
app.setBannerMode(Banner.Mode.OFF);
// Запускаємо застосунок (після цього в логах одразу підуть рядки Starting...)
app.run(args);
}
Після цього стартові логи починатимуться одразу з рядків Starting .... Тобто «інформації» в логах не стане менше — просто зникне декоративна частина. Приблизно так:
2026-03-19T10:10:00.120 INFO ... : Запуск CatalogServiceApplication з Java 25 та PID 12345
2026-03-19T10:10:00.121 INFO ... : Активний профіль не задано, використовується 1 профіль за замовчуванням: "default"
...
Корисно тримати в голові: якщо ви бачите в колеги логи без банера, це не «інша версія Spring Boot» і не «зламаний запуск». Просто банер вимкнули. Boot не ображається — він дорослий.
3. Tomcat і порт у логах
У гілці SERVLET вбудований сервер живе всередині того самого процесу, і це обов’язково відображається в логах запуску. Тепер закріпімо це очима: за якими рядками зрозуміти, що сервер не просто «створився», а справді почав слухати порт. Це важлива звичка, коли ви перевіряєте, що сервіс живий.
Найприкладніша частина логів запуску для вебсервісу — це рядки про сервер і порт. У типовому випадку ви побачите не один рядок, а невеликий «мінісюжет»: сервер створюється, запускається й починає слухати порт.
Ось набір рядків, які найчастіше трапляються. Можливі невеликі відмінності за форматом, але сенс той самий:
# Важливо розрізняти: initialized (створили) vs started on port (слухаємо порт)
Tomcat ініціалізовано з портом 8080 (http)
Запуск служби [Tomcat]
Запуск сервлетного рушія: [Apache Tomcat/...]
Tomcat запущено на порту 8080 (http) з контекстним шляхом ""
Тут важлива різниця між двома словами, які зовні схожі:
initialized означає, що сервер створили і підготували. Це ще не гарантія, що він слухає мережу.
started on port означає, що сервер справді запущено і він почав слухати порт. Ось це вже момент «можна йти в браузер, у Postman або хоча б у curl».
Якщо у вас поки немає жодного контролера — а на цьому етапі курсу його зазвичай ще немає, — спроба відкрити http://localhost:8080 може завершитися 404 Not Found. І це нормально: 404 означає «сервер живий, але не знайшов обробник для цього шляху». Це набагато краще, ніж «браузер не може підключитися», бо останнє зазвичай означає, що сервер не стартував або слухає інший порт.
Ще один корисний прийом, який пов’язує цю лекцію з WebApplicationType. Якщо ми явно переведемо застосунок у режим NONE, то ознаки Tomcat зникнуть із логів. Це хороший спосіб побачити залежність «режим запуску ↔ картина логів» буквально на власні очі.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
public static void main(String[] args) {
// Фрагмент усередині вашого головного класу застосунку (наприклад, CatalogServiceApplication)
SpringApplication app = new SpringApplication(CatalogServiceApplication.class);
// Явно вимикаємо вебрежим: Boot не підніматиме вбудований сервер (Tomcat)
app.setWebApplicationType(WebApplicationType.NONE);
// Запускаємо застосунок: у логах зникнуть рядки про Tomcat і порт
app.run(args);
}
І після цього ви побачите, що «історія з Tomcat» зникла. Логи будуть коротшими й не міститимуть рядків про порт:
2026-03-19T10:20:00.120 INFO ... : Запуск CatalogServiceApplication з Java 25 та PID 12345
2026-03-19T10:20:00.121 INFO ... : Активний профіль не задано, використовується 1 профіль за замовчуванням: "default"
2026-03-19T10:20:01.470 INFO ... : CatalogServiceApplication запущено за 1,10 секунди
До речі, маленький момент, який часто дивує початківців. У вебрежимі процес зазвичай продовжує жити — адже є серверні потоки, які слухають порт. У NONE-режимі застосунок може швидко стартувати й одразу завершитися, тому що йому, по суті, нічого «тримати увімкненим». Це не помилка, а наслідок того, що вебсервер більше не утримує процес живим.
4. Логи як таймлайн і діагностика
Будь-який запуск — це ланцюжок фаз, і логи записують його як таймлайн. Якщо щось пішло не так, завдання розробника просте: знайти, на якій фазі все зупинилося і який був останній «живий» рядок. Зараз потренуємося читати логи зверху вниз і робити швидкий діагноз без магії та паніки.
Дуже зручно тримати в голові простий образ: запуск Boot-застосунку — не один стрибок, а послідовність кроків. Якщо ви візуально складете ці кроки в голові, логи почнуть читатися легше, тому що кожен рядок «приклеюється» до своєї фази.
Ось проста схема — дуже спрощена, але корисна саме для рівня junior:
flowchart TD
A["main()"] --> B["SpringApplication.run(...)"]
B --> C["Підготовка запуску"]
C --> D["Створення ApplicationContext"]
D --> E["Ініціалізація контексту"]
E --> F{"WebApplicationType = SERVLET?"}
F -->|Так| G["Старт вбудованого сервера (Tomcat)"]
F -->|Ні| H["Немає вебсервера"]
G --> I["Started ... in ... seconds"]
H --> I
Тепер головна практична навичка діагностики: якщо запуск упав, не потрібно одразу читати весь stack trace як роман. У Spring Boot зазвичай є дуже корисний блок, який виглядає приблизно так:
# Boot часто пише «людський» діагноз: спочатку Description/Action, а вже потім довгий stack trace
APPLICATION FAILED TO START
Description:
Вебсервер не вдалося запустити. Порт 8080 уже використовується.
Action:
Знайдіть і зупиніть процес, який слухає порт 8080, або налаштуйте цей застосунок так, щоб він слухав інший порт.
Оце — золото. Boot буквально говорить вам людською мовою, що сталося і що робити. І лише після цього зазвичай іде великий stack trace. Stack trace теж важливий, але порядок такий: спочатку читаємо Description і Action, а вже потім пірнаємо глибше, якщо інформації не вистачило.
Ще одна хитрість, яка економить час: шукайте в логах фінальний рядок
Started CatalogServiceApplication in ...
Якщо його немає — запуск не завершився успішно. Тоді дивіться, якою була остання зрозуміла успішна стадія. Наприклад, якщо ви бачите Starting ..., але не бачите Tomcat started on port ..., значить проблема сталася десь до моменту, коли сервер почав слухати порт. Якщо бачите Tomcat initialized, але не бачите Tomcat started on port, значить сервер не зміг нормально запуститися. А якщо бачите Tomcat started on port, але потім пішли помилки, значить сервер устиг піднятися, а застосунок упав на подальших кроках. Так теж буває.
Якщо хочеться ще раз прив’язати логи до самого механізму старту, можна зробити маленький «детектор старту» прямо в main(): взяти ApplicationContext, який повернувся з run(...), і просто вивести кількість зареєстрованих бінів.
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
public static void main(String[] args) {
// SpringApplication.run(...) повертає вже зібраний контекст
ApplicationContext ctx = SpringApplication.run(CatalogServiceApplication.class, args);
// Швидка перевірка: якщо цей рядок надрукувався, контекст справді встиг зібратися
System.out.println("Кількість бінів = " + ctx.getBeanDefinitionCount());
// Приклад виведення: Кількість бінів = 150+ (точне число залежить від залежностей і версії Boot)
}
Якщо ви бачите в консолі цей рядок, значить run(...) дійшов до моменту, коли контекст зібрано і керування повернулося у ваш main. А якщо застосунок при цьому залишається «живим», то зазвичай це означає, що вебсервер піднявся і тримає процес.
Раз уже таймлайн старту читається по логах, наступне інженерне запитання вже не про читання чужої роботи, а про власну участь у ній: де взагалі доречно виконувати свій код на старті, як отримати аргументи й як реагувати на події життєвого циклу без хаосу в main.
5. Типові помилки під час читання логів запуску
Помилки під час читання логів зазвичай не «технічні», а психологічні: ми дивимося не туди, поспішаємо, хапаємося за перший-ліпший рядок і починаємо лікувати симптом, а не причину. Цей блок — коротке щеплення від найчастіших граблів. Він заощадить вам години, а іноді й нервові клітини.
Помилка №1: дивитися тільки на банер і заспокоюватися.
Банер — це як заставка серіалу: приємно, але сюжет починається після неї. Застосунок може успішно надрукувати банер і впасти далі. Тому критерій успіху — не банер, а наявність фінального рядка Started ... і, для вебрежиму, рядка про Tomcat started on port ....
Помилка №2: плутати “initialized” і “started”.
Рядок Tomcat initialized ... часто виглядає як «ну все, сервер піднявся». Ні. Він означає, що сервер створили. Найважливіше — Tomcat started on port ...: ось це маркер того, що він слухає порт. На старті корисно прямо привчити себе шукати слово started у контексті порту.
Помилка №3: читати stack trace знизу вгору… але не в той бік.
Інтуїтивно хочеться скролити в самий низ і брати останній рядок. Але в Boot зазвичай є APPLICATION FAILED TO START із Description та Action — це ваш швидкий рецепт. Далі, якщо потрібно, у stack trace шукайте зрозуміле повідомлення і перші Caused by:. Не намагайтеся одразу розбирати 200 рядків внутрішніх викликів Spring — це схоже на спробу навчитися керувати автомобілем, вивчаючи схему виробництва шин.
Помилка №4: не розрізняти вивід Gradle/IDE і вивід застосунку.
Під час запуску через IDE або ./gradlew bootRun поруч із логами застосунку може бути вивід збірки, tasks та інших службових повідомлень. Початківець іноді починає «лікувати Spring» за рядком Gradle або навпаки. Хороша звичка: очима знаходити блок, де починається Starting ... — це і є початок логів застосунку.
Помилка №5: вважати рядок про profiles «шумом».
Навіть якщо ви поки не використовуєте профілі свідомо, рядок про них — дуже корисний факт. Він говорить, у якому режимі застосунок стартує — хоча б default. Коли поведінка несподівано відрізняється від очікуваної, цей рядок часто стає першим поясненням: «ага, старт був не в тому режимі, який я очікував».
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ