Логи, моніторинг і спостережуваність для QA
Зміст
Уяви: автотест упав на кроці «оплатити», на скриншоті — червоний тост «щось пішло не так», у коді тесту все правильно. Слабкий QA напише в баг-репорт «оплата не працює» й піде далі. Сильний відкриє серверні логи, знайде за ідентифікатором запиту, що сервіс payments повернув 500 через таймаут до банківського шлюзу, і напише репорт, від якого розробник одразу знає, куди дивитися. Різниця між цими двома людьми — уміння читати те, що система розповідає про себе назовні. Це і є спостережуваність (observability): здатність зрозуміти, що коїться всередині системи, дивлячись лише на її зовнішні сигнали — логи, метрики й трейси.
Це канонічна глава сайту про логи та спостережуваність — інші розділи (тестування мікросервісів, дебаг флакі-тестів, діагностика падінь у CI) посилаються сюди. І це глава-поглиблення: за першого проходу розділу її можна пропустити й повернутися тоді, коли впаде перший тест, причину якого не видно ні на скриншоті, ні в коді, — і доведеться лізти в логи. На співбесіді тему питають помірно, але вміння сказати «я подивився б у логи за correlation id» одразу відрізняє інженера від «клікача».
Три сигнали: логи, метрики, трейси
Спостережуваність традиційно розкладають на три типи телеметрії, і QA має розуміти, який сигнал на яке питання відповідає:
| Сигнал | Що це | На яке питання відповідає |
|---|---|---|
| Логи (logs) | Дискретні записи подій із деталями | Що саме сталося з конкретним запитом? |
| Метрики (metrics) | Числа, агреговані в часі (лічильники, гістограми) | Скільки, як швидко, наскільки погано в цілому? |
| Трейси (traces) | Шлях одного запиту крізь усі сервіси | Де саме в ланцюжку сервісів затримка чи помилка? |
Для щоденної роботи QA робочий кінь — саме логи: коли треба зрозуміти чому впав один конкретний сценарій, потрібні деталі події, а не усереднене число. Важлива межа: метрики під навантаженням — RPS, перцентилі, дашборди APM під час load-тесту — це матеріал розділу performance-тестування, а не цієї глави. Тут — логи й діагностика поодинокого бага.
Рівні логів і структуровані логи
Не кожен запис у логах однаково важливий, тому логери позначають кожен запис рівнем (log level). Прикладні логери (log4j, pino, Winston, Logback) користуються впорядкованою шкалою рівнів — точний набір трохи різниться від логера до логера, але типовий вигляд такий: TRACE < DEBUG < INFO < WARN < ERROR < FATAL:
- TRACE / DEBUG — детальна діагностика для розробника: значення змінних, вхід/вихід із функції. У проді зазвичай вимкнені (забагато шуму).
- INFO — нормальний перебіг подій: «користувач залогінився», «замовлення створено». Це «пульс» застосунку.
- WARN — щось підозріле, але система впоралася: спрацював ретрай, поле прийшло порожнім і взяли дефолт. Часто саме тут ховається корінь майбутнього бага.
- ERROR — операція впала: виняток,
500, незловлена помилка. Перше місце, куди дивиться QA. - FATAL — сервіс не може працювати далі й падає.
Рівень задає поріг: якщо логер налаштований на INFO, усе нижче (DEBUG, TRACE) відкидається. Тому «у логах порожньо» часто означає не «нічого не сталося», а «рівень логування вищий, ніж запис, який ти шукаєш». Окремо існує системна шкала severity зі стандарту syslog (RFC 5424) з вісьмома рівнями — її корисно впізнавати, але прикладні застосунки частіше на шкалі вище.
Другий вимір — структура запису. Порівняй два ERROR про ту саму подію:
2026-07-18 10:32:11 ERROR Payment failed for user 4821 order 99123
{"ts":"2026-07-18T10:32:11Z","level":"error","msg":"payment failed","userId":4821,"orderId":99123,"requestId":"abc-123","service":"payments"}
Перший — плоский текст: людині читабельно, машині — ні. Щоб знайти всі помилки оплати по користувачу 4821, доведеться писати регулярку й молитися, що формат ніде не зʼїхав. Другий — структурований лог (structured logging), зазвичай JSON: кожна деталь лежить в окремому полі. У Kibana такий запис шукається запитом на кшталт level:error AND service:payments AND userId:4821 — точно, швидко, без регулярок. Тому в мікросервісах де-факто стандарт — структуровані логи: у баг-репорт вкладаєш не «щось червоне на екрані», а конкретний JSON-запис із усіма полями.
Request id і correlation id: як зшити запит
Головна проблема діагностики в розподіленій системі: один клік користувача породжує ланцюжок викликів між сервісами, і логи всіх сервісів звалені в одну купу. Як зрозуміти, які саме рядки з мільйона стосуються твого запиту?
Відповідь — наскрізний ідентифікатор. Розрізняють кілька близьких понять:
- Request id — унікальний ідентифікатор одного HTTP-запиту. Часто генерується на вході (шлюз, балансувальник) і повертається клієнту в заголовку відповіді.
- Correlation id — ідентифікатор однієї логічної операції, що може охоплювати кілька запитів і кілька сервісів (наприклад, увесь флоу оформлення замовлення). Його передають від сервісу до сервісу, щоб зшити всю бізнес-транзакцію.
- Trace id / span id — ті самі ідеї, формалізовані в розподіленому трасуванні (distributed tracing): trace id — весь шлях, span id — окремий відрізок усередині сервісу.
Поширені (де-факто, не стандартизовані) заголовки — X-Request-Id та X-Correlation-Id. Стандартизований варіант для трасування описує специфікація W3C Trace Context із заголовком traceparent, на який спирається OpenTelemetry. Механіка проста: сервіс бере ідентифікатор із вхідного заголовка (або генерує свій), кладе в кожен лог-запис і передає далі за ланцюжком.
Практичний прийом для AQA: витягни цей заголовок із відповіді й поклади в повідомлення про фейл — розробник за одним значенням підніме з логів увесь маршрут запиту крізь усі сервіси.
const res = await page.waitForResponse('**/api/orders');
const requestId = res.headers()['x-request-id'];
// цей id — у повідомлення про фейл; за ним знайдуть весь ланцюжок у логах
expect(res.status(), `orders failed, requestId=${requestId}`).toBe(201);
Оглядово про трейсинг: спеціальні системи (Jaeger, Grafana Tempo, комерційні APM) малюють цей ланцюжок як каскад відрізків у часі — видно, який сервіс скільки тривав і де застрягло. Для QA це відповідь на «повільно — а де саме повільно»; глибше трасування під навантаженням — тема performance-розділу.
Фронт, бек чи дані: куди дивитись
Коли на екрані щось не так, перше рішення QA — локалізувати шар, бо фронт, бек і дані лагодять різні люди й дивляться в різні місця. Логи й браузерні інструменти дають на це чітку відповідь.
Розберемо гілки:
- Фронтенд. Дивишся в консоль браузера (вкладка Console у DevTools): незловлений виняток JavaScript, помилка рендеру,
undefinedтам, де чекали обʼєкт. Мережа при цьому може взагалі мовчати — запит не пішов або пішов не той. Механіку помилок клієнта розбирає розділ веб (виконання JavaScript). - Бекенд. У вкладці Network відповідь
4xxабо5xx— сигнал іти в серверні логи.5xx— майже завжди баг сервера (незловлений виняток, впала залежність);4xx— частіше клієнт надіслав щось не те (розбір кодів — глава про статус-коди). За request id з відповіді знаходиш у логах точний запис із стек-трейсом. - Дані. Найпідступніший випадок: статус
200, помилок ніде немає, а на екрані не те число чи не той список. Тут логи покажуть, що запит і відповідь коректні за формою, — отже, проблема в стані даних: не той рядок у базі, застарілий кеш, неконсистентність між сервісами. Тоді дорога — у базу (перевірити фактичний стан) і в логи запису даних.
Ключова навичка: не писати «баг у застосунку», доки не звузив шар. Півгодини з логами й Network економлять розробнику години й роблять баг-репорт таким, що його не повертають із «не відтворюється».
Sentry: error tracking для веба
Гортати сирі логи вручну — боляче, коли помилок тисячі. Sentry (і аналоги) — це система відстеження помилок (error tracking), яка ловить винятки автоматично, і на фронтенді, і на бекенді: команда бачить кожну помилку з контекстом одразу, не чекаючи скарги користувача.
Що Sentry дає QA конкретно:
- Стек-трейс із контекстом — точний рядок коду, версія релізу, браузер/ОС, дані користувача. Для мінімізованого фронтенд-коду Sentry за допомогою source maps показує оригінальні назви функцій, а не
a.b.c. - Breadcrumbs — ланцюжок дій і запитів, що передували падінню. Фактично готові кроки відтворення.
- Групування в issue — тисяча однакових падінь злипаються в одну проблему з лічильником «скільки разів, скільки користувачів». Видно масштаб, а не шум.
- Привʼязка до релізу — Sentry показує, у якій версії помилка зʼявилася вперше. Це прямий інструмент регресійного аналізу: «зламалося в релізі
2.14.0».
Для баг-репорту це золото: замість словесного переказу вкладаєш посилання на конкретний Sentry-issue — там уже є стек, оточення, частота й перше входження. Аналоги з тим самим принципом: Rollbar, Bugsnag, GlitchTip (open-source), а також модулі error tracking у Datadog і New Relic. Назви й UI різні, ідея одна.
Куди течуть логи: ELK/Kibana і Grafana (оглядово)
У проді логи не лежать файлом на одному сервері — сервісів десятки, і логи централізують: збирають в одне сховище з пошуком. Два імені, які QA чує найчастіше.
ELK / Elastic Stack — зв’язка Elasticsearch (сховище з пошуком) + Logstash (збір і обробка) + Kibana (веб-інтерфейс пошуку й візуалізації); часто збором займаються ще й агенти Beats. Практично для QA: відкриваєш Kibana, задаєш часовий проміжок і фільтр (наприклад, requestId:"abc-123" або level:error AND service:payments) — і бачиш усі релевантні записи з усіх сервісів в одній стрічці. Це головний інструмент «покажи мені, що сталося з цим запитом».
Grafana — платформа дашбордів; історично для метрик (у парі з Prometheus), але з Grafana Loki вміє й логи, а з Tempo — трейси. Для QA Grafana найчастіше — це графіки: сплеск 5xx після деплою, зростання часу відповіді. Помітив аномалію на графіку в Grafana → пірнув по деталі в Kibana/Loki за конкретним запитом.
| Інструмент | Головне призначення для QA |
|---|---|
| Kibana (ELK) | Пошук і фільтрація сирих логів за полями й id |
| Grafana | Дашборди-графіки: тренди, сплески, аномалії |
| Sentry | Автоматичний перехоплювач винятків із контекстом |
Не обовʼязково знати їх глибоко — треба вміти зайти, поставити фільтр за часом і id, витягти релевантні рядки. Локальний аналог тих самих дій у контейнері чи на стенді — docker logs та tail -f/grep (див. глави Docker і тестові середовища і Термінал і Linux для QA).
Логи як доказ у баг-репорті
Уся ця робота замикається на баг-репорті. Логи — доказова база: вони перетворюють «у мене не працює» на відтворюваний факт (структура й атачі баг-репорту — тема глави про баг-репорт і життєвий цикл дефекту в розділі основ тестування). Що варто вкладати:
- Ідентифікатор — request/correlation id проблемного запиту й точний час (з таймзоною). За ними розробник підніме повний контекст.
- Релевантні рядки, а не дамп — кілька записів навколо помилки зі стек-трейсом, а не мегабайт логів. Виріж зайве, лиши суть.
- Посилання на Sentry-issue / Kibana-запит — коли доступ спільний, лінк цінніший за копіпаст.
- Артефакти клієнта — консоль браузера, HAR-файл із вкладки Network, скриншот/відео. У автотестах їх варто збирати автоматично й прикладати саме на фейл.
Приклад: збираємо консоль і невдалі відповіді під час прогону й кладемо у звіт, щоб при падінні доказ уже був під рукою.
import { test, expect } from '@playwright/test';
test('оформлення замовлення', async ({ page }, testInfo) => {
const clientLogs: string[] = [];
page.on('console', (m) => clientLogs.push(`[${m.type()}] ${m.text()}`));
page.on('response', (r) => {
if (r.status() >= 400) clientLogs.push(`${r.status()} ${r.url()}`);
});
await page.goto('/checkout');
// ... кроки сценарію ...
// логи клієнта — у звіт; при фейлі це готовий доказ для баг-репорту
await testInfo.attach('client-logs.txt', {
body: clientLogs.join('\n'),
contentType: 'text/plain',
});
});
Такий атач знімає половину питань «а що там було в консолі?» ще до того, як їх поставлять.
Типові помилки
- «У логах порожньо, отже помилки не було» — виглядає як чистий прогін, а насправді рівень логування вищий за запис, який шукаєш (DEBUG вимкнено в проді), або ти дивишся не в той сервіс/не той часовий проміжок. Порожнеча в логах — це відсутність видимих на цьому рівні записів, а не відсутність події.
- «200 OK — значить, усе добре» — виглядає як успіх, а насправді статус
200каже лише «сервер відповів», а не «відповів правильно». Баг даних живе саме тут: форма коректна, вміст — ні. Логи покажуть, що запит валідний, і виведуть тебе на стан у базі. - «Баг у застосунку» без локалізації шару — виглядає як діагноз, а насправді це капітуляція. Поки не сказано «фронт / бек / дані», репорт неадресний і повернеться. Консоль + Network + серверні логи звужують шар за хвилини.
- «Вкладу весь лог, хай розбираються» — виглядає як щедрий доказ, а насправді мегабайт нерелевантних рядків ховає суть. Потрібні кілька записів навколо помилки з id і стек-трейсом, а не дамп.
- «Correlation id — це для девопсів» — виглядає як чужий інструмент, а насправді це найшвидший шлях QA зшити запит крізь мікросервіси. Один id у баг-репорті економить розробнику годину пошуку.
- «Sentry показав помилку — це новий баг» — виглядає як свіжа знахідка, а насправді Sentry групує й датує: спершу глянь «перше входження» й реліз. Часто це давня відома проблема, а не регресія твого тесту.
Підсумок
- Спостережуваність тримається на трьох сигналах — логах, метриках і трейсах. Для діагностики поодинокого бага робочий кінь QA — логи; метрики під навантаженням — то вже performance-розділ.
- Рівень логу — це поріг видимості, а структура — це можливість пошуку. «Порожньо в логах» частіше означає «вищий рівень», а структурований JSON шукається за полями, а не регулярками.
- Request/correlation id зшиває запит крізь усі сервіси. Витягни його з відповіді, поклади в баг-репорт — і весь маршрут витягується одним фільтром у Kibana.
- Локалізуй шар перед діагнозом. Консоль браузера → фронт;
4xx/5xxу Network → бек за id у серверних логах;200з не тими даними → стан у базі. - Логи — доказова база баг-репорту. Sentry-issue, request id, релевантні рядки й HAR перетворюють «не працює» на відтворюваний факт; у автотестах доказ збирають автоматично на фейл.
Що питають на співбесіді
- «Тест/сценарій упав — які твої дії?» — очікують не «перезапущу», а метод: консоль браузера й Network, за статусом визначити шар, витягти request id, знайти запис у серверних логах. Перевіряють системність діагностики.
- «Як зрозуміти, це баг фронтенду, бекенду чи даних?» — сильна відповідь дає рішуче дерево: виняток у консолі → фронт;
5xx/4xx→ бек;200з неправильними даними → база. Дивляться, чи не звалюєш усе в «баг застосунку». - «Що таке рівні логів і навіщо вони?» — чекають шкалу
DEBUG…ERRORі думку, що рівень задає поріг, тому в проді DEBUG зазвичай вимкнений; бонус — згадка структурованих логів. - «Що таке correlation id / request id і навіщо він QA?» — перевіряють розуміння наскрізного ідентифікатора в мікросервісах і того, що він зшиває запит у логах; згадка W3C Trace Context/OpenTelemetry — сильний плюс.
- «Користувався Sentry / Kibana / Grafana? Для чого?» — не потрібне глибоке знання; достатньо чесно розвести: Sentry ловить винятки з контекстом, Kibana шукає сирі логи, Grafana малює тренди. Головне — розуміти, який інструмент на яке питання.
- «Що прикладаєш до баг-репорту, крім скриншота?» — очікують логи з id і стек-трейсом, HAR, посилання на Sentry, точний час; і думку, що вкладати треба релевантне, а не весь дамп.
Джерела
- W3C Trace Context — рекомендація W3C про стандартний заголовок
traceparentдля наскрізного трасування запитів між сервісами. - OpenTelemetry — Documentation — вендор-нейтральний стандарт телеметрії (логи, метрики, трейси) і словник понять spans/traces.
- RFC 5424 — The Syslog Protocol — першоджерело системної шкали severity (розділ 6.2.1), корисне для розуміння рівнів логів.
- Sentry Documentation — офіційна документація error tracking: стек-трейси, breadcrumbs, релізи, source maps.
- Kibana Docs — Explore and analyze (Elastic) та Grafana Loki Documentation — офіційні доки двох поширених систем централізованих логів.
Що таке спостережуваність (observability) і навіщо вона QA?
Спостережуваність — це можливість зрозуміти, що діється всередині системи, маючи на руках лише її зовнішні сигнали: логи, метрики й трейси. Ідея в тому, що ти не залазиш дебагером у чужий продакшн, а читаєш те, що застосунок сам про себе розповідає назовні. Для QA це прямий інструмент діагностики: замість «оплата чомусь не працює» ти можеш сказати, який сервіс, яким кодом і чому відмовив. Практична межа між сильним і слабким тестувальником часто проходить саме тут — один пише «щось червоне на екрані», інший приносить конкретний запис із логів. На співбесіді тему питають помірно, але сама фраза «я подивився б у логи за ідентифікатором запиту» одразу читається як досвід.
Які три сигнали телеметрії розрізняють і на яке питання відповідає кожен?
Класична трійця — логи, метрики й трейси, і кожен відповідає на своє питання. Логи (logs) — це окремі записи про події з деталями, вони кажуть, що саме сталося з конкретним запитом. Метрики (metrics) — це числа, агреговані в часі (лічильники, гістограми), вони кажуть, скільки й наскільки погано в цілому: скільки помилок за хвилину, який перцентиль часу відповіді. Трейси (traces) — це шлях одного запиту крізь усі сервіси, вони показують, де саме в ланцюжку застрягло. Для щоденної діагностики поодинокого бага робочий кінь QA — саме логи, бо потрібні деталі події, а не усереднене число. Метрики під навантаженням (RPS, перцентилі, дашборди APM) — це вже територія performance-тестування.
Що таке рівні логів (log levels) і навіщо вони?
Рівень — це позначка важливості кожного запису, і логери шикують їх у впорядковану шкалу, типово TRACE < DEBUG < INFO < WARN < ERROR < FATAL (точний набір трохи різниться між log4j, pino, Winston тощо). DEBUG/TRACE — детальна діагностика для розробника, у проді її зазвичай вимикають через шум. INFO — нормальний перебіг: користувач залогінився, замовлення створено. WARN — щось підозріле, але система впоралася (спрацював ретрай, узяли дефолт), і саме тут часто ховається корінь майбутнього бага. ERROR — операція впала, це перше місце, куди дивиться QA. Головне — рівень задає поріг: якщо логер налаштований на INFO, усе нижче просто відкидається, тому шукати DEBUG-записи в проді часто марно.
Чому «у логах порожньо» не означає «помилки не було»?
Бо рівень логування — це поріг видимості, а не факт відсутності подій. Якщо логер стоїть на INFO, усі DEBUG- і TRACE-записи відкидаються ще на етапі запису, тож потрібний тобі рядок може просто не потрапити в лог. До того ж «порожньо» часто означає, що ти дивишся не в той сервіс або не в той часовий проміжок. Тому правильний висновок із порожнечі — «немає видимих на цьому рівні записів у цьому місці», а не «нічого не сталося». Практично: перевір рівень логування, звір таймзону й час події, переконайся, що фільтр націлений на потрібний сервіс.
Чим структурований лог кращий за плоский текстовий рядок?
Плоский рядок на кшталт ERROR Payment failed for user 4821 читабельний людині, але машині — ні: щоб знайти всі помилки оплати по цьому користувачу, доведеться писати регулярку й сподіватися, що формат ніде не поплив. Структурований лог (structured logging) — зазвичай JSON, де кожна деталь лежить в окремому полі: level, service, userId, requestId, msg. Такий запис у Kibana шукається точним фільтром типу level:error AND service:payments AND userId:4821 — швидко й без регулярок. Саме тому в мікросервісах де-факто стандарт — структуровані логи. Практичний наслідок для QA: у баг-репорт вкладаєш конкретний JSON-запис з усіма полями, а не опис «щось червоне на екрані».
У чому різниця між request id і correlation id?
Обидва — наскрізні ідентифікатори, але різного масштабу. Request id — унікальний ідентифікатор одного HTTP-запиту, його часто генерує шлюз чи балансувальник на вході й повертає клієнту в заголовку відповіді. Correlation id — ідентифікатор однієї логічної операції, яка може охоплювати кілька запитів і кілька сервісів, наприклад увесь флоу оформлення замовлення; його передають від сервісу до сервісу, щоб зшити всю бізнес-транзакцію. Тобто correlation id ширший: під ним може жити кілька request id. Для QA обидва — спосіб з мільйона рядків витягти рівно ті, що стосуються твого кліку: береш id з відповіді, кладеш у баг-репорт, і розробник одним фільтром піднімає весь маршрут.
Що таке W3C Trace Context і як він пов'язаний з OpenTelemetry?
Де-факто заголовки X-Request-Id і X-Correlation-Id поширені, але не стандартизовані — кожна команда називає їх по-своєму. W3C Trace Context — це офіційна рекомендація, яка стандартизує наскрізне трасування через заголовок traceparent: у ньому лежить trace id усього шляху й span id поточного відрізка. OpenTelemetry — вендор-нейтральний стандарт телеметрії (логи, метрики, трейси й спільний словник понять) — спирається саме на цей формат для передачі контексту між сервісами. Механіка проста: сервіс бере ідентифікатор із вхідного заголовка або генерує свій, кладе його в кожен лог і передає далі за ланцюжком. Для QA згадка traceparent/OpenTelemetry на співбесіді — сильний плюс: показує, що ти розумієш не лише «є якийсь id», а й як його стандартизували.
Автотест упав, на скриншоті — «щось пішло не так». Які твої дії?
Не перезапускати наосліп, а йти по шарах. Спершу консоль браузера: якщо там незловлений виняток JavaScript — проблема на фронтенді, і мережа могла взагалі мовчати. Якщо консоль чиста, дивлюся вкладку Network: статус 4xx/5xx — сигнал іти в серверні логи. Витягую з відповіді request/correlation id і за ним у Kibana (чи в docker logs на стенді) знаходжу точний запис зі стек-трейсом. Паралельно перевіряю, чи це справді баг продукту, а не тесту: відтворюю той самий запит вручну через curl/Postman, звіряю токен, змінні, середовище. Головна дисципліна — не заявляти причину без доказу; «стек-трейс указує на конкретний контролер» коштує рівно стільки, скільки під ним підтверджень.
Як зрозуміти, це баг фронтенду, бекенду чи даних?
Через рішуче дерево, бо ці три шари лагодять різні люди. Виняток у консолі браузера (JS-помилка, undefined там, де чекали об'єкт) — фронтенд, код клієнта. Статус 4xx/5xx у Network — бекенд: 5xx майже завжди баг сервера (незловлений виняток, впала залежність), 4xx частіше клієнт надіслав щось не те; за request id знаходиш серверний запис. Найпідступніший випадок — статус 200, помилок ніде немає, а на екрані не те число чи не той список: якщо запит сформовано правильно, це проблема стану даних (не той рядок у базі, застарілий кеш, неконсистентність між сервісами), і дорога — у базу та в логи запису даних. Ключова навичка — не писати «баг у застосунку», доки шар не звужено: неадресний репорт повертається з «не відтворюється».
Чому статус «200 OK» ще не означає, що все добре?
Бо 200 каже лише «сервер прийняв запит і відповів», а не «відповів правильно». Форма відповіді може бути бездоганною — валідний JSON, потрібні поля — а вміст неправильним: не те число, застарілий список, чужі дані. Це класична пастка багів даних: помилок ніде немає, тому недосвідчений тестувальник закриває перевірку як зелену. Логи тут підтвердять, що запит і відповідь коректні за формою, і саме це виведе тебе на справжнє джерело — стан у базі чи неконсистентність між сервісами. Практичний висновок: асертити треба не лише статус, а й фактичний вміст відповіді проти очікуваного.
Що таке Sentry і що він конкретно дає QA?
Sentry — це система відстеження помилок (error tracking), яка автоматично перехоплює винятки і на фронтенді, і на бекенді, тож команда бачить кожну помилку з контекстом одразу, не чекаючи скарги користувача. Для QA цінні чотири речі. Стек-трейс із контекстом — точний рядок коду, версія релізу, браузер/ОС; для мінімізованого фронтенду Sentry через source maps показує оригінальні назви функцій, а не a.b.c. Breadcrumbs — ланцюжок дій і запитів перед падінням, фактично готові кроки відтворення. Групування в issue — тисяча однакових падінь злипаються в одну проблему з лічильником користувачів, видно масштаб. Прив'язка до релізу — Sentry показує, у якій версії помилка з'явилася вперше, і це прямий інструмент регресійного аналізу. У баг-репорт замість переказу вкладаєш посилання на issue.
Sentry показав помилку — це точно новий баг від мого тесту?
Не поспішай: Sentry не лише ловить винятки, а й групує та датує їх, тому спершу глянь два поля — «перше входження» (first seen) і реліз. Часто виявляється, що це давня відома проблема, яка живе в проді тижнями, а твій прогін просто вкотре її зачепив. Якщо ж помилка вперше з'явилася в релізі, який ти тестуєш, — оце вже кандидат на свіжу регресію, і прив'язка до версії стає доказом «зламалося у 2.14.0». Лічильник «скільки разів, скільки користувачів» додатково відрізняє системний баг від разового збою. Тобто послідовність така: побачив issue → перевір first seen і release → лише потім роби висновок «нове/старе».
Чим Kibana відрізняється від Grafana і для чого кожен потрібен QA?
Це інструменти під різні питання. Kibana — веб-інтерфейс до Elasticsearch у стеку ELK, її сила — пошук і фільтрація сирих логів за полями: задаєш часовий проміжок і фільтр типу requestId:"abc-123" або level:error AND service:payments і бачиш усі релевантні записи з усіх сервісів в одній стрічці. Це головний інструмент «покажи, що сталося з цим запитом». Grafana — платформа дашбордів, історично для метрик у парі з Prometheus (а з Loki вміє й логи, з Tempo — трейси); для QA це насамперед графіки трендів: сплеск 5xx після деплою, зростання часу відповіді. Типовий робочий цикл: помітив аномалію на графіку в Grafana → пірнув по деталі в Kibana за конкретним запитом. Глибокого знання не треба — треба вміти зайти, поставити фільтр за часом і id, витягти релевантні рядки.
Що таке ELK / Elastic Stack?
Це зв'язка з трьох (плюс) компонентів для централізованого збору логів. Elasticsearch — сховище з повнотекстовим і польовим пошуком, де осідають усі записи. Logstash — конвеєр збору й обробки логів (парсинг, збагачення, нормалізація) перед укладанням у сховище; часто збором на хостах займаються ще й легкі агенти Beats. Kibana — веб-інтерфейс для пошуку, фільтрації й візуалізації того, що лежить в Elasticsearch. Сенс усього стека для QA простий: логи десятків сервісів не валяються файлами на різних машинах, а стікаються в одне місце з пошуком, тож замість SSH на кожен сервер ти в одному вікні фільтруєш за id і часом. Локальний аналог тих самих дій на стенді — docker logs та grep.
Що таке distributed tracing і trace id / span id, і чим воно корисне QA?
Розподілене трасування (distributed tracing) — це формалізація ідеї наскрізного id: trace id позначає весь шлях запиту крізь систему, а span id — окремий відрізок усередині одного сервісу. Спеціальні системи (Jaeger, Grafana Tempo, комерційні APM) складають ці спани в каскад відрізків у часі — видно, який сервіс скільки тривав і де саме застрягло. Для QA це пряма відповідь на питання «повільно — а де конкретно повільно»: не «сторінка вантажиться 8 секунд», а «6 з них з'їв виклик до сервісу платежів». Формат передачі контексту стандартизує W3C Trace Context через traceparent, на нього спирається OpenTelemetry. Глибоке трасування під навантаженням — тема performance-розділу, але сам механізм корисно впізнавати.
Що і як правильно прикладати до баг-репорту з логів?
Логи — це доказова база, яка перетворює «у мене не працює» на відтворюваний факт, тож прикладати треба чотири речі. Ідентифікатор — request/correlation id проблемного запиту й точний час із таймзоною, щоб розробник підняв повний контекст. Релевантні рядки, а не дамп — кілька записів навколо помилки зі стек-трейсом; мегабайт нерелевантних логів ховає суть, а не доводить її. Посилання на Sentry-issue чи Kibana-запит — коли доступ спільний, лінк цінніший за копіпаст, бо веде до живого контексту. Артефакти клієнта — консоль браузера, HAR-файл із вкладки Network, скриншот або відео. В автотестах усе це варто збирати автоматично й чіпляти саме на фейл, щоб доказ був під рукою ще до питання «а що там було в консолі?».
Correlation id — це ж інструмент девопсів, навіщо він тестувальнику?
Це поширена, але шкідлива думка. Correlation id — найшвидший спосіб для QA зшити один запит крізь мікросервіси: без нього ти дивишся на купу логів десятків сервісів і не знаєш, які рядки стосуються твого кліку. Один id, витягнутий із відповіді й покладений у баг-репорт, економить розробнику годину пошуку — він одним фільтром піднімає весь маршрут запиту від шлюзу до бази. Тобто це не «чужий» інструмент, а частина базової діагностики тестувальника нарівні з читанням статус-кодів. На співбесіді відповідь «я витягну correlation id з відповіді й покладу у фейл» показує, що ти працюєш із розподіленою системою предметно, а не наосліп.
Три кейси, що показують шлях від «щось червоне на екрані» до адресного баг-репорту: як за симптомом звузити шар, як в автотесті витягти request id і зібрати докази на фейл, і як прочитати структурований лог у Kibana за correlation id. Скрізь — що дивитися і який висновок робити.
Кейс 1. Локалізація шару: фронт, бек чи дані
Тест падає на кроці «оплатити», на скриншоті — тост «щось пішло не так». Спокуса написати «оплата не працює» велика, але це неадресний репорт. Спершу — три швидкі перевірки, кожна відповідає на своє питання.
| Що перевіряю | Що бачу | Висновок про шар |
|---|---|---|
| Консоль браузера (DevTools → Console) | Незловлений виняток JS, undefined замість об'єкта | Фронтенд: код клієнта |
| Вкладка Network, статус запиту | 4xx або 5xx | Бекенд: іти в серверні логи за id |
Network при статусі 200 | Відповідь валідна, але дані не ті | Ймовірно, стан даних у базі |
У нашому випадку консоль чиста, а в Network запит POST /api/payments повернув 500. Це вже не «оплата не працює», а «сервіс платежів упав з необробленим винятком». Наступний крок — витягти з відповіді ідентифікатор запиту й піти по ньому в логи. Копіюю значення заголовка x-request-id з відповіді (вкладка Headers) — далі воно стане ключем пошуку.
Пастка, якої тут уникаємо: якби статус був 200, а на екрані просто не та сума — жодної помилки в консолі й Network не було б, і шукати причину треба було б у стані даних, а не в коді. 200 каже «сервер відповів», а не «відповів правильно».
Кейс 2. Playwright: витягти request id і зібрати докази на фейл
Найкращий момент зібрати докази — сам прогін, поки контекст ще живий. Дві типові дії: покласти ідентифікатор запиту в повідомлення про фейл (щоб розробник підняв маршрут одним фільтром) і зібрати консоль та невдалі відповіді як атач до звіту.
import { test, expect } from '@playwright/test';
test('оформлення замовлення лишає докази на фейл', async ({ page }, testInfo) => {
const clientLogs: string[] = [];
page.on('console', (m) => clientLogs.push(`[${m.type()}] ${m.text()}`));
page.on('response', (r) => {
if (r.status() >= 400) clientLogs.push(`${r.status()} ${r.url()}`);
});
await page.goto('/checkout');
// ... кроки сценарію до кнопки «оплатити» ...
const res = await page.waitForResponse('**/api/payments');
const requestId = res.headers()['x-request-id'];
// id — прямо в меседж асерту: за ним у логах підніметься весь ланцюжок
expect(res.status(), `payments failed, requestId=${requestId}`).toBe(201);
// консоль і невдалі відповіді — у звіт; при падінні доказ уже готовий
await testInfo.attach('client-logs.txt', {
body: clientLogs.join('\n'),
contentType: 'text/plain',
});
});
Що дивитися і чому:
requestIdу меседжі асерту, а не десь у логах тесту. Коли асерт червоніє, значення видно прямо в звіті поруч зі статусом — розробнику не треба питати «а який був запит».- Слухачі
consoleіresponseнавішені до дій, а не після. Інакше перші події (саме ті, що передували падінню) не потраплять у збірку. >= 400ловить і4xx, і5xx. У доказ ідуть усі невдалі відповіді сторінки, а не лише той запит, на якому впав асерт, — часто корінь в сусідньому виклику.testInfo.attachкладе файл у звіт Playwright. Це і є «HAR/консоль автоматично на фейл» — половина питань «що там було в консолі?» знімається до того, як їх поставлять.
Кейс 3. Читаємо структурований лог у Kibana за correlation id
request id з Кейсу 1 у нас abc-123. Відкриваємо Kibana, ставимо часовий проміжок навколо падіння й фільтр requestId:"abc-123". У розподіленій системі один клік породжує ланцюжок викликів, тож в одній стрічці побачимо записи кількох сервісів — усі з тим самим id.
{"ts":"2026-07-18T10:32:10Z","level":"info","service":"gateway","msg":"POST /api/payments","requestId":"abc-123","userId":4821}
{"ts":"2026-07-18T10:32:10Z","level":"info","service":"orders","msg":"order reserved","requestId":"abc-123","orderId":99123}
{"ts":"2026-07-18T10:32:21Z","level":"warn","service":"payments","msg":"gateway retry 1/1","requestId":"abc-123"}
{"ts":"2026-07-18T10:32:31Z","level":"error","service":"payments","msg":"bank gateway timeout","requestId":"abc-123","orderId":99123,"downstream":"acme-bank","status":504}
Що дивитися і чому:
- Читаємо стрічку зверху вниз як історію запиту.
gatewayприйняв,ordersзарезервував, аpaymentsчерез 21 секунду упав — час між записами (10:32:10 → 10:32:31) сам підказує, де застрягло. WARNза 10 секунд доERROR— не шум, а корінь. Спрацював ретрай до банківського шлюзу (gateway retry 1/1), і саме тут ховається причина: не «оплата зламалася», а таймаут до зовнішньогоacme-bank.- Фільтр звужуємо полями, а не регуляркою. Якщо записів забагато, додаємо
level:error AND service:payments— точно й швидко саме тому, що лог структурований. - У баг-репорт іде цей фрагмент, а не весь дамп. Чотири рядки навколо помилки з id, часом і полем
downstream:"acme-bank"кажуть розробнику більше, ніж мегабайт логів: викликpaymentsдо банку (acme-bank) упав таймаутом зіstatus:504, тож клієнт і отримав500. Це вже адресний факт, а не «щось пішло не так».
Три сигнали спостережуваності
- Можу пояснити, що спостережуваність (observability) — це розуміння нутра системи за її зовнішніми сигналами: логами, метриками, трейсами.
- Знаю різницю логи vs метрики vs трейси: логи кажуть що сталося, метрики — скільки/наскільки погано в цілому, трейси — де саме в ланцюжку.
- Розумію, чому робочий кінь QA для одного бага — логи (деталі події), а метрики під навантаженням (RPS, перцентилі, дашборди APM) — це performance-розділ.
Рівні й структура логів
- Знаю шкалу рівнів
TRACE < DEBUG < INFO < WARN < ERROR < FATALі що рівень задає поріг: нижче за налаштований рівень записи просто відкидаються. - Розумію пастку «у логах порожньо»: це відсутність видимих на цьому рівні записів, а не відсутність події (плюс не той сервіс чи час).
- Знаю різницю плоский текст vs структурований лог (JSON): кожна деталь в окремому полі (
level,service,userId,requestId, час), тож шукається фільтром за полями, а не регуляркою.
Наскрізні ідентифікатори
- Знаю різницю request id vs correlation id: перший — один HTTP-запит, другий — одна логічна операція крізь кілька сервісів.
- Розумію, що trace id / span id — ті самі ідеї у формалізованому вигляді: trace id — весь шлях, span id — відрізок усередині сервісу.
- Знаю заголовки
X-Request-Id/X-Correlation-Id(де-факто) і стандартнийtraceparent(W3C Trace Context, на нього спирається OpenTelemetry). - Знаю прийом AQA: витягти id з відповіді й покласти в повідомлення про фейл, щоб підняти весь маршрут одним фільтром.
Локалізація шару: фронт / бек / дані
- Можу за симптомом звузити шар: виняток у консолі → фронт;
4xx/5xxу Network → бек (5xxмайже завжди баг сервера,4xxчастіше кривий запит клієнта);200з не тими даними → база. - Знаю пастку «
200 OK— значить усе добре»: статус каже лише «сервер відповів», а не «відповів правильно». - Можу пояснити, чому не варто писати «баг у застосунку» до локалізації шару — неадресний репорт повертається з «не відтворюється».
Інструменти: Sentry, Kibana, Grafana
- Знаю, що Sentry — error tracking: автоматично ловить винятки з контекстом (стек через source maps, breadcrumbs як кроки відтворення, групування в issue, прив'язка до релізу).
- Розумію пастку «Sentry показав помилку — це новий баг»: спершу дивись «перше входження» (first seen) і реліз, часто це давня проблема.
- Знаю різницю Kibana vs Grafana: Kibana шукає сирі логи за полями/id, Grafana малює тренди й аномалії на дашбордах.
- Можу розшифрувати ELK: Elasticsearch (сховище+пошук) + Logstash (збір/обробка) + Kibana (інтерфейс), плюс агенти Beats; локальні аналоги на стенді —
docker logs,tail -f,grep.
Логи як доказ у баг-репорті
- Знаю, що вкладати: request/correlation id, точний час із таймзоною, релевантні рядки зі стек-трейсом, лінк на Sentry/Kibana, HAR.
- Розумію, чому «вкладу весь лог» — погано: мегабайт нерелевантних рядків ховає суть замість доводити її.
- Можу налаштувати автозбір доказів у Playwright: слухати
consoleі невдалі відповіді, чіпляти їх на фейл черезtestInfo.attach.
На яке питання відповідає сигнал «логи» краще за метрики й трейси?
Питання
Що таке спостережуваність (observability)?