vyvchy
    Теми розділу

    09 · Git і CI/CD

    Логи, моніторинг і спостережуваність для 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. Механіка проста: сервіс бере ідентифікатор із вхідного заголовка (або генерує свій), кладе в кожен лог-запис і передає далі за ланцюжком.

    abc-123

    abc-123

    abc-123

    abc-123

    лог abc-123

    лог abc-123

    лог abc-123

    лог abc-123

    Браузер

    API Gateway

    Auth service

    Orders service

    Payments service

    Централізовані логи

    QA шукає abc-123

    abc-123

    abc-123

    abc-123

    abc-123

    лог abc-123

    лог abc-123

    лог abc-123

    лог abc-123

    Браузер

    API Gateway

    Auth service

    Orders service

    Payments service

    Централізовані логи

    QA шукає abc-123

    Практичний прийом для 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 — локалізувати шар, бо фронт, бек і дані лагодять різні люди й дивляться в різні місця. Логи й браузерні інструменти дають на це чітку відповідь.

    Так, JS-помилка

    Ні

    4xx або 5xx

    200, але дані не ті

    Так

    Ні

    Симптом на екрані

    Виняток у консолі браузера?

    Фронтенд: код клієнта

    Який статус у вкладці Network?

    Бек: серверні логи за request id

    Запит сформовано правильно?

    Дані: не той стан у базі

    Фронтенд: відправив не той запит

    Так, JS-помилка

    Ні

    4xx або 5xx

    200, але дані не ті

    Так

    Ні

    Симптом на екрані

    Виняток у консолі браузера?

    Фронтенд: код клієнта

    Який статус у вкладці Network?

    Бек: серверні логи за request id

    Запит сформовано правильно?

    Дані: не той стан у базі

    Фронтенд: відправив не той запит

    Розберемо гілки:

    • Фронтенд. Дивишся в консоль браузера (вкладка 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 — офіційні доки двох поширених систем централізованих логів.