Що в записі логу й що означають рівні
Лог — це журнал подій, які застосунок сам про себе записує. Для QA це найкоротший шлях від «щось зламалось» до «ось де й чому». Типовий запис містить:
- timestamp — час події (краще в UTC, з мілісекундами і зоною, наприклад ISO 8601);
- рівень (severity);
- джерело — сервіс, інстанс, клас чи модуль, інколи потік;
- повідомлення — що сталося;
- контекст — request id / trace id, user id (псевдонімізований), параметри, код помилки, стек виклику.
Рівні від найдетальнішого до найкритичнішого:
- TRACE — дуже докладний хід виконання, зазвичай вимкнений;
- DEBUG — деталі для розробника: значення змінних, гілки логіки; на проді зазвичай вимкнений;
- INFO — звичайні значущі події: старт сервісу, замовлення створено;
- WARN — щось дивне, але система впоралась: повторна спроба, застаріле API, повільна відповідь;
- ERROR — операція не вдалася (запит завершився помилкою), потрібна увага;
- FATAL — застосунок не може продовжувати роботу (є не в усіх бібліотеках).
Поріг рівня задається конфігурацією: на рівні INFO ви не побачите DEBUG. Тому «в логах нічого немає» інколи означає лише, що потрібний рівень вимкнений на цьому оточенні.
Що перевіряє QA: що помилка справді потрапляє в ERROR (а не губиться), що валідаційні відмови користувача (400) не засмічують ERROR, що в записі є контекст для розслідування, і що в ньому немає паролів, токенів і персональних даних.
Структуровані й текстові логи
Текстовий лог — вільний рядок для людини:
2026-10-12 09:14:03,512 ERROR [checkout] Payment failed for order 8841: gateway timeout
Його легко читати очима, але важко аналізувати: щоб дістати order id чи тривалість, потрібні regex, які ламаються при зміні формулювання.
Структурований лог — зазвичай один JSON-об’єкт на рядок (NDJSON / JSON Lines):
{"ts":"2026-10-12T09:14:03.512Z","level":"error","service":"checkout","msg":"payment failed","orderId":8841,"traceId":"4bf92f3577b34da6a3ce929d0e0e4736","error":"gateway timeout","durationMs":30012}
Поля можна фільтрувати й агрегувати без парсингу тексту: «усі помилки сервісу checkout за останню годину, згруповані за error». Тому в сучасних системах логи пишуть структурованими, а повідомлення роблять стабільним (без змінних усередині), а змінне виносять у поля.
Практичні правила:
- одна подія — один рядок; стек-трейс — у полі, а не розмазаний на десятки рядків (інакше агрегатор розірве запис);
- іменування полів єдине в усіх сервісах (наприклад,
traceId, userId);
- не логувати великі тіла запитів/відповідей цілком;
- для людини локально можна використовувати «гарний» формат, на проді — JSON.
Для QA: структуровані логи дають змогу автотесту точно знайти запис за полем, а не за підрядком у повідомленні.
Де шукати логи й як локалізувати баг за ними
Де лежать логи:
- браузер: DevTools → Console (JS-помилки,
console.error, помилки ресурсів), Network (статуси й тіла відповідей);
- бекенд: stdout/stderr контейнера (
docker logs, kubectl logs), файли на сервері, а в зрілих системах — централізований сховок (ELK/OpenSearch, Loki, Datadog);
- CI: консоль джоби та артефакти (логи сервісів, звіти);
- мобільні: Android —
adb logcat / Logcat в Android Studio; iOS — консоль Xcode, Console.app, log stream; краш-звіти — Crashlytics/Sentry.
Як локалізувати баг:
- Відтворіть і зафіксуйте точний час та оточення (з урахуванням часової зони!).
- У DevTools подивіться Console і Network: чи є червоні запити? Який статус? Якщо запит пішов і повернув 5xx — проблема на бекенді, якщо запит не пішов або є JS-виняток — на фронтенді; 4xx — часто невірні дані чи авторизація.
- Візьміть request id / trace id з заголовка відповіді чи тіла помилки й знайдіть за ним записи у логах бекенду.
- Знайдіть першу помилку в ланцюжку, а не останню: каскадні збої породжують десятки наступних ERROR.
- Подивіться, що було до неї: WARN, повторні спроби, зміни конфігурації, деплой.
Що додати в баг-репорт: кроки, очікуваний/фактичний результат, оточення і версію збірки, час із зоною, request/trace id, відповідний уривок логу (без секретів), скріншот чи HAR/trace. Така заявка економить розробнику час на відтворення.