Алгоритм анализа падений в Java-автотестах: от Allure до Kubernetes

Курс формирует системный подход к диагностике ошибок в микросервисной архитектуре на примере проекта aeqs-api-test. Вы научитесь классифицировать сбои, эффективно использовать логи и проверять состояние инфраструктуры для быстрого поиска первопричины.

Классификация падений: интерпретация Stacktrace и Allure-отчетов

Классификация падений: интерпретация Stacktrace и Allure-отчетов

Представьте типичную пятницу: вы запускаете регресс проекта aeqs-api-test (наша система управления потоком клиентов), и тест assignClientToManagerTest загорается красным. Вы тратите час, проверяя базу данных, дергая разработчиков и перезапуская поды в Kubernetes, чтобы в итоге обнаружить, что в тесте просто отвалилась авторизация из-за протухшего токена.

Около 80% времени при отладке автотестов тратится впустую из-за неверного первого шага. Умение за 30 секунд прочитать отчет Allure и понять, в какую сторону копать, — это главный навык инженера по автоматизации.

В этой главе мы научимся отфильтровывать информационный шум и безошибочно определять класс проблемы.

Allure: Симптомы болезни

Когда мы открываем упавший тест в Allure, первое, на что нужно обратить внимание — это статус падения. Allure (и большинство тестовых фреймворков) концептуально разделяет все неуспешные тесты на две категории: Failed и Broken.

Это не просто разница в цвете иконки, это фундаментальное различие в природе ошибки.

Статус в Allure Цвет Смысл Типичные исключения Где искать проблему
Failed (Упал) Красный Тест технически отработал корректно, но бизнес-логика приложения не совпала с ожиданиями. AssertionError, ComparisonFailure В требованиях (Jira/Confluence) или в коде продукта (баг).
Broken (Сломан) Желтый Тест не смог выполниться до конца из-за технической ошибки (упал в процессе подготовки или выполнения). NullPointerException, ConnectException, TimeoutException В коде самого автотеста, тестовых данных или инфраструктуре.

Если тест желтый — бессмысленно идти к разработчику продукта. Проблема на нашей стороне: не поднялся браузер, недоступен тестовый стенд, или мы пытаемся обратиться к пустому объекту. Если тест красный — инфраструктура в порядке, API ответило, но ответило не то.

Анатомия Stacktrace: Ищем иголку в стоге сена

Определив статус, мы смотрим в Stacktrace — историю вызовов функций, которая привела к ошибке. Для новичка он выглядит как пугающая стена текста, но на деле 90% этих строк — информационный мусор фреймворков.

Stacktrace читается сверху вниз, но истинная причина часто кроется в самом низу, после слов Caused by: (Вызвано тем-то).

Правило чтения стектрейса простое: ищите свой код. Ваш проект имеет определенный пакет (package). Например, в нашем случае это ru.aeqs.api. Все строки, начинающиеся с java.base, org.junit, io.restassured или jdk.internal — это внутренности языков и библиотек. Они лишь передают ошибку наверх. Истинное место падения — это верхняя строка стектрейса, которая относится к вашему проекту.

Практика: разбор падения в aeqs-api-test

Рассмотрим реальный пример. Наш тест создает клиента в очереди и проверяет, что API возвращает статус IN_PROGRESS. Тест упал со статусом Broken. В Allure мы видим следующий лог:

java.lang.RuntimeException: Failed to create client flow
    at ru.aeqs.api.steps.ClientSteps.createFlow(ClientSteps.java:45)
    at ru.aeqs.api.tests.ClientQueueTest.assignClientToManagerTest(ClientQueueTest.java:22)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    ... 25 more
Caused by: java.net.ConnectException: Connection refused: connect
    at java.base/sun.nio.ch.Net.connect0(Native Method)
    at org.apache.http.conn.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:75)
    at ru.aeqs.api.clients.RestClient.sendPost(RestClient.java:89)
    at ru.aeqs.api.steps.ClientSteps.createFlow(ClientSteps.java:43)
    ... 28 more

Давайте применим наш алгоритм:

  1. Игнорируем шум: Строки org.junit... и java.base... пропускаем.
  2. Ищем корень: Видим блок Caused by: java.net.ConnectException: Connection refused. Это означает, что приложение физически не смогло установить сетевое соединение.
  3. Ищем свой код: В блоке Caused by спускаемся до первой строки нашего проекта — ru.aeqs.api.clients.RestClient.sendPost(RestClient.java:89).

Вывод: ошибка произошла в нашем классе RestClient на 89-й строке, когда тест пытался отправить POST-запрос, но сервер отклонил подключение. Это типичная инфраструктурная проблема (упал тестовый стенд или неверный URL).

Алгоритм первичной классификации

Теперь мы можем собрать наши знания в единый алгоритм действий. Каждый раз, когда вы видите упавший тест, задайте себе три вопроса в строгой последовательности:

  1. Это Failed (Assertion) или Broken (Exception)?
    • Если Failed — идем изучать логику ответа API и сверять с требованиями.
    • Если Broken — переходим к шагу 2.
  2. Где в моем коде произошла ошибка?
    • Находим блок Caused by (если есть).
    • Находим верхнюю строку с пакетом ru.aeqs.api....
  3. К какому слою относится исключение?
    • Сетевой слой (ConnectException, SocketTimeoutException) → Проверяем доступность стенда.
    • Слой данных (NullPointerException, IndexOutOfBoundsException) → Проверяем тестовые данные (возможно, клиент не создался в базе).
    • Слой парсинга (JsonMappingException) → Изменился контракт API, мы не можем прочитать ответ.

Этот алгоритм — ваш фильтр. Он позволяет за минуту отсечь ложные пути. Если мы классифицировали ошибку как сбой бизнес-логики (API вернуло 500 ошибку вместо 200, и сработал AssertionError), нам уже недостаточно просто Allure-отчета. Нам нужно заглянуть "под капот" самого приложения. Как именно искать причины таких ответов в логах микросервисов, мы подробно разберем в следующей главе.

Диагностика API-слоя: анализ HTTP-ответов и логов приложения в ELK/Splunk

Диагностика API-слоя: анализ HTTP-ответов и логов приложения в ELK/Splunk

Вы открываете Allure-отчет, видите красный статус Broken, разворачиваете Stacktrace и натыкаетесь на стену: feign.FeignException$InternalServerError: [500] during [POST] to [http://aeqs-api/clients/assign]. На этом стек вызовов вашего автотеста обрывается. Тестовый фреймворк честно сообщает, что код упал, потому что сервер вернул ошибку. Но настоящая причина падения осталась по ту сторону сетевого запроса — внутри микросервиса.

Чтобы выяснить, что именно сломалось, нам нужно пересечь границу между тестовым фреймворком и тестируемым приложением.

HTTP-ответ как первичный фильтр

Прежде чем погружаться в дебри серверных логов, необходимо проанализировать HTTP-контракт. В Allure (при правильной настройке HTTP-клиента, например, через фильтры RestAssured) к упавшему шагу всегда прикреплены Request и Response.

Первое, на что мы смотрим — статус-код ответа. Он мгновенно определяет вектор дальнейшего расследования.

Класс статуса Кто виноват Что это значит для автотестировщика
4xx (Client Error) Автотест (клиент) Тест отправил некорректные данные, забыл токен авторизации или запросил несуществующий ресурс. Сервер отработал штатно — он распознал ошибку и отказался ее обрабатывать.
5xx (Server Error) Сервер (приложение) Запрос был составлен верно, но микросервис не смог его обработать из-за внутренней ошибки (баг в коде, падение базы данных, недоступность соседнего сервиса).

Если сервер возвращает 400 Bad Request, идти в логи приложения чаще всего бессмысленно. Сервер уже ответил вам в теле ответа (Response Body), что именно ему не понравилось.

Если же мы видим 500 Internal Server Error или 503 Service Unavailable, тело ответа обычно содержит лишь сухую заглушку вроде {"error": "Internal Server Error"}. Сервер скрывает детали ошибки от внешнего клиента из соображений безопасности. Вся ценная информация записана в систему централизованного логирования (ELK Stack, Splunk, Graylog).

Мост между Allure и Kibana: Trace ID

Представьте, что микросервис aeqs-api обрабатывает сотни запросов в секунду. Если вы откроете Kibana (интерфейс для поиска по логам в стеке ELK) и просто отфильтруете логи по времени падения теста и уровню ERROR, вы увидите десятки чужих ошибок. Как найти ту самую, которая уронила ваш тест?

Для этого используется сквозной идентификатор запроса.

Trace ID (Correlation ID) — это уникальная строка, которая генерируется при первом входе запроса в систему и передается через HTTP-заголовки во все микросервисы, участвующие в обработке этого запроса.

В Allure-отчете в прикрепленном Request вы найдете заголовок (обычно он называется X-Request-Id, X-B3-TraceId или traceparent). Например: X-Request-Id: a1b2c3d4-5678-90ef.

Этот же идентификатор микросервис заботливо прикрепляет к каждой строке лога, которую он пишет в ELK, пока обрабатывает ваш запрос.

Анализ серверного Stacktrace в ELK

Получив Trace ID, мы переходим в Kibana. В строке поиска (KQL — Kibana Query Language) мы пишем простой запрос:

trace_id: "a1b2c3d4-5678-90ef"

Теперь перед нами не миллион логов, а ровно 10–20 строк, описывающих жизненный цикл исключительно нашего тестового запроса. Среди них мы ищем строку с уровнем level: "ERROR". Развернув ее, мы увидим серверный Stacktrace — то, что произошло под капотом.

Рассмотрим реальный пример из проекта aeqs-api-test. Тест assignClientToManagerTest пытается назначить VIP-клиента на свободного менеджера и падает с 500 ошибкой.

Найдя лог по Trace ID в Kibana, мы видим следующее сообщение об ошибке (Message):

java.lang.NullPointerException: Cannot invoke "com.aeqs.domain.Manager.getDepartment()" because "manager" is null
    at com.aeqs.service.AssignmentService.assign(AssignmentService.java:42)
    at com.aeqs.controller.ClientController.assignClient(ClientController.java:28)
    ...

Теперь картина кардинально изменилась. Мы знаем не просто факт "сервер ответил 500", а точную причину: сервис попытался получить отдел у менеджера, но объект менеджера оказался равен null.

Почему менеджер оказался null?

  1. Возможно, тест передал ID несуществующего менеджера (тогда это ошибка подготовки тестовых данных).
  2. Возможно, изменились требования, и теперь менеджеры без отдела не могут принимать VIP-клиентов (тогда нужно идти в Jira/Confluence).
  3. А может быть, сервис менеджеров (отдельный под в Kubernetes) просто не ответил вовремя, и база вернула пустой результат.

Серверный лог дал нам точную точку отказа в коде. Дальнейший шаг — сопоставить это поведение с бизнес-требованиями или проверить состояние инфраструктуры, чтобы понять: это баг приложения, устаревший тест или упавший кластер.

Проверка бизнес-логики: сопоставление фактического поведения с требованиями в Jira/Confluence

Проверка бизнес-логики: сопоставление фактического поведения с требованиями в Jira/Confluence

Мы нашли NullPointerException в логах Kibana: переменная manager оказалась пустой при попытке назначить менеджера клиенту. Расследование окончено, можно заводить баг-репорт? Нет. Стек-трейс показывает где упал код, но не отвечает на главный вопрос: почему приложение вообще попыталось выполнить это действие и было ли оно правильным с точки зрения бизнеса.

Код делает то, что в нём написано. Автотест проверяет то, что в нём запрограммировал автоматизатор. Когда они расходятся, нам нужна третья точка опоры — бизнес-требования.

От логов к исходным данным

Чтобы понять причину ошибки, нужно восстановить контекст вызова. Сервер не генерирует ошибки сам по себе — он реагирует на входящие данные.

Возвращаемся в Allure-отчет проекта aeqs-api-test и смотрим тело запроса (Payload), которое автотест отправил на эндпоинт /api/v1/clients/assign. Мы видим следующую структуру:

{
  "clientId": "778899",
  "clientType": "ECONOMY",
  "region": "EU"
}

Тест ожидает, что в ответ придет HTTP-статус 200 и идентификатор назначенного менеджера. Вместо этого сервер падает с пятисотой ошибкой (NPE). Теперь у нас есть конкретные вводные: система ломается при попытке назначить менеджера клиенту типа ECONOMY.

Поиск истины в Confluence и Jira

Следующий шаг — выяснить, как система должна вести себя при таких вводных. Для этого мы обращаемся к документации.

Шаг 1. Базовые правила в Confluence

Confluence — это энциклопедия проекта. Здесь хранится API-контракт и общее описание бизнес-процессов. Мы открываем страницу спецификации сервиса распределения потока клиентов и находим таблицу маршрутизации.

Правило гласит: если score50score \geq 50 (где scorescore — внутренний рейтинг клиента), ему назначается персональный менеджер. Для клиентов с типом ECONOMY рейтинг по умолчанию равен 10. Следовательно, система вообще не должна назначать им персонального менеджера, а должна отправлять их в общую очередь.

Ошибка в коде — это не всегда неправильно реализованная логика. Часто это отсутствие защиты от граничных условий, которые диктуются бизнес-правилами.

Разработчик реализовал логику для новых типов клиентов, но забыл добавить проверку в старом методе назначения. Метод попытался достать менеджера для клиента ECONOMY, получил пустоту и выбросил NullPointerException.

Шаг 2. Проверка истории изменений в Jira

Документация в Confluence имеет свойство устаревать. Если вы видите, что тест падает, а логика приложения противоречит Confluence, не спешите заводить баг. Сначала проверьте Jira.

Jira — это пульс проекта. Именно здесь фиксируются последние решения бизнеса, которые могли еще не доехать до официальной документации.

Мы ищем в Jira тикеты по ключевым словам ECONOMY и assign. Находим недавно закрытую задачу: "Реализовать общую очередь для клиентов базового тарифа". Теперь картина складывается полностью:

  1. Разработчик реализовал фичу, но допустил техническую ошибку (NPE вместо корректного ответа или пропуска шага).
  2. Автотест устарел. Он был написан во времена, когда всем клиентам назначался менеджер, и до сих пор ожидает успешного назначения для типа ECONOMY.

Трилемма упавшего теста

Собрав данные из Allure, логов, Confluence и Jira, мы всегда приходим к одному из трех сценариев разрешения проблемы.

Сценарий Симптомы Действие автотестировщика
Баг в приложении Код нарушает актуальные требования из Jira/Confluence. Завести баг-репорт с указанием шагов, логов и ссылки на требования.
Устаревший тест Код работает по новым требованиям, а тест ждет старого поведения. Актуализировать автотест (изменить ожидаемый результат или тестовые данные).
Слепое пятно аналитики Новая фича сломала старую логику, в требованиях конфликт. Привлечь аналитика для уточнения требований, временно отключить тест (Mute/Ignore).

В нашем случае с aeqs-api-test мы столкнулись с комбинацией: нужно завести баг на NPE (сервер должен возвращать понятную ошибку валидации 400 Bad Request или статус об успешном помещении в очередь 200 OK, а не падать с 500) и одновременно переписать автотест, чтобы он проверял помещение ECONOMY-клиента в очередь, а не назначение менеджера.

Но что, если требования гласят: "Для VIP-клиентов менеджер запрашивается из внешней CRM-системы", и NPE падает именно потому, что CRM ничего не вернула? В этом случае проблема лежит не в коде нашего приложения и не в устаревшем тесте. Мы сталкиваемся с недоступностью инфраструктуры, и для подтверждения этой гипотезы нам придется спуститься на уровень Kubernetes.

Инфраструктурный слой: мониторинг доступности сервисов и подов в Kubernetes

Инфраструктурный слой: мониторинг доступности сервисов и подов в Kubernetes

Вы посмотрели в Allure и увидели статус Broken. Вы скопировали Trace ID, открыли Kibana, но вместо привычного NullPointerException обнаружили обрыв цепочки логов или java.net.ConnectException. Вы сверили Payload с требованиями в Confluence — данные идеальны. Код написан верно, бизнес-логика не менялась, но тест упорно падает. Почему? Потому что код не выполняется в вакууме. Он работает на физических или виртуальных серверах, объединенных оркестратором, и иногда ломается сам «вакуум».

Когда микросервис пытается отправить данные во внешнюю CRM, но получает 503 Service Unavailable или SocketTimeoutException, проблема выходит за рамки Java-кода. Мы спускаемся на инфраструктурный слой — в Kubernetes.

Когда пора проверять инфраструктуру?

В первой главе мы разделили падения на Failed (ошибка логики) и Broken (техническая проблема). Инфраструктурный сбой — это всегда Broken, но со специфическими симптомами.

Вам пора открывать консоль Kubernetes, если в логах или стектрейсе вы видите:

  • Сетевые исключения: ConnectException, UnknownHostException, SocketTimeoutException.
  • Серверные ошибки маршрутизации: 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout.
  • Отсутствие логов: запрос ушел, но в ELK/Kibana по вашему Trace ID нет вообще никаких записей от целевого сервиса.

Базовые абстракции Kubernetes для QA

Автотестировщику не нужно быть DevOps-инженером и писать сложные манифесты развертывания. Достаточно понимать две ключевые абстракции, чтобы уметь находить упавшее приложение.

Pod (Под) — это минимальная единица в Kubernetes. Грубо говоря, это контейнер, внутри которого крутится запущенный процесс вашего Java-приложения. Поды смертны: они могут зависнуть, упасть из-за нехватки памяти или быть удалены оркестратором.

Service (Сервис) — это постоянный «адресная книга» и балансировщик. Поскольку поды постоянно умирают и рождаются с новыми IP-адресами, Service дает им единое стабильное имя (например, crm-integration-service), по которому к ним обращаются другие микросервисы.

Если тест получает 503 Service Unavailable, это значит, что Service жив (он принял запрос), но за ним нет ни одного живого Pod'а, которому можно было бы передать работу.

Диагностика состояния подов

Для проверки инфраструктуры используется утилита командной строки kubectl. Основная команда, с которой начинается любой дебаг:

kubectl get pods -n aeqs-namespace

(где -n aeqs-namespace — указание пространства имен вашего проекта)

В ответ вы получите таблицу со списком всех подов. Ключевая колонка здесь — STATUS.

Статус Что означает для автотеста Действие
Running Приложение запущено и работает штатно. Искать проблему в коде или логах (Kibana). Инфраструктура в порядке.
Pending Под не может запуститься. Кластеру не хватает ресурсов (CPU/RAM), чтобы выделить место для приложения. Тесты будут падать по таймауту. Писать DevOps: кластер перегружен.
CrashLoopBackOff Приложение стартует, падает с критической ошибкой, Kubernetes пытается его перезапустить, и оно снова падает. Запрашивать логи самого пода. Kibana здесь не поможет.
Evicted Kubernetes принудительно «выселил» под, так как физический узел (нода) исчерпал ресурсы диска или памяти. Перезапустить окружение или сбросить кэши на ноде.

Почему Kibana иногда врет: анатомия CrashLoopBackOff

Вернемся к нашему проекту aeqs-api-test. Тест assignClientToManagerTest падает. В Allure мы видим, что сервис управления потоком клиентов не смог достучаться до crm-integration-service.

Выполняем проверку:

kubectl get pods -n aeqs-namespace | grep crm-integration

Вывод консоли:

NAME                                       READY   STATUS             RESTARTS   AGE
crm-integration-service-845b9d...          0/1     CrashLoopBackOff   12         1h

Под находится в цикличной перезагрузке (CrashLoopBackOff). Вы идете в Kibana, чтобы посмотреть логи ошибки, но там пусто. Почему?

Централизованное логирование (ELK) работает асинхронно. Процесс-агент (например, Filebeat) периодически собирает логи и отправляет их в Kibana. Если Java-приложение падает в первые секунды запуска из-за фатальной ошибки конфигурации или нехватки памяти, контейнер уничтожается быстрее, чем агент успевает отправить предсмертные логи в центральное хранилище.

В таких случаях мы читаем логи напрямую из Kubernetes с помощью команды:

kubectl logs pod/crm-integration-service-845b9d... -n aeqs-namespace

Именно там, в сыром выводе консоли упавшего контейнера, мы находим истинную причину (Root Cause): java.lang.OutOfMemoryError: Java heap space

Контейнер был принудительно остановлен механизмом OOMKilled. Это происходит, когда выполняется математическое условие:

Memoryactual>LimitconfiguredMemory_{actual} > Limit_{configured}

где MemoryactualMemory_{actual} — текущее потребление оперативной памяти Java-приложением, а LimitconfiguredLimit_{configured} — жесткое ограничение, заданное в манифесте Kubernetes для этого пода. Приложение попыталось загрузить в память слишком большой справочник клиентов, превысило лимит и было убито оркестратором на уровне операционной системы.

Теперь картина ясна полностью. Это не баг бизнес-логики и не устаревшие требования. Это инфраструктурный отказ из-за некорректно настроенных лимитов памяти.

Мы прошли весь путь от верхнего уровня до самого основания. Мы умеем читать симптомы в Allure, искать контекст в логах приложения по Trace ID, сверять поведение с Jira и, наконец, проверять пульс самих серверов в Kubernetes. В следующей главе мы сведем все эти инструменты в единый, безотказный алгоритм локализации любой ошибки.

Синтез и локализация: построение итогового алгоритма поиска Root Cause

Синтез и локализация: построение итогового алгоритма поиска Root Cause

Представьте утренний прогон: 50 упавших тестов. Allure пестрит красным и желтым. Вы открываете первый попавшийся лог, видите 500-ю ошибку, идете в Kibana, не находите там нужный Trace ID, переключаетесь в консоль Kubernetes, проверяете логи подов... и через час понимаете, что всё это время смотрели не на тот микросервис. Хаотичный поиск съедает время, демотивирует и превращает отладку в лотерею.

Без четкой системы сложность поиска первопричины (Root Cause) составляет O(N×M)O(N \times M), где NN — количество микросервисов, а MM — объем логов и метрик. Наша задача — свести это время к O(1)O(1), используя строгий алгоритм отсечения лишнего.

Воронка локализации

Поиск Root Cause — это не угадывание, это процесс последовательного сужения зоны поиска. Мы двигаемся от самого абстрактного слоя к самому конкретному, опираясь на инструменты, которые разобрали в предыдущих главах.

Каждый упавший тест должен пройти через «воронку локализации», состоящую из четырех слоев:

  1. Слой отчета (Allure): Фиксация факта падения и его первичная классификация.
  2. Слой маршрутизации (HTTP/Сетевой протокол): Выбор вектора поиска на основе ответа системы.
  3. Слой приложения (ELK/Kibana): Анализ внутренней логики сервисов.
  4. Слой инфраструктуры (Kubernetes): Проверка физической доступности вычислительных ресурсов.
  5. Слой требований (Jira/Confluence): Сверка с бизнес-ожиданиями.

Root Cause (первопричина) — это самый глубокий уровень проблемы, устранение которого гарантирует, что данный сбой больше не повторится. Исправление следствия (например, переписывание теста под новый баг) первопричиной не является.

Универсальный алгоритм поиска

Чтобы не теряться в инструментах, используйте следующий пошаговый алгоритм. Каждый шаг отвечает на один конкретный вопрос и определяет ваше следующее действие.

Шаг 1. Сбор улик (Точка входа)

Откройте упавший тест в Allure и ответьте на два вопроса:

  • Какой статус у теста? Если Failed (AssertionError) — система отработала, но результат не сошелся с ожиданиями. Если Broken (Exception) — тест не смог выполниться технически.
  • Что в блоке Caused by? Найдите самую нижнюю причину в Stacktrace.

Шаг 2. Определение вектора поиска (Маршрутизация)

Посмотрите на HTTP-ответ сервера или характер сетевой ошибки в логе Allure. Это главный перекресток алгоритма:

Индикатор в Allure Ваш следующий шаг Инструмент
HTTP 200 OK + Failed Логика приложения работает, но бизнес-результат неверный. Идем проверять Payload и актуальность требований. Jira / Confluence
HTTP 4xx (400, 404, 409) Клиентская ошибка. Проверяем тестовые данные (Payload) на валидность и сверяем с контрактом API. Swagger / Jira
HTTP 5xx (500) Ошибка на стороне сервера. Копируем Trace ID и идем искать Stacktrace приложения. ELK / Kibana
Timeout, 502, 503, ConnectException Инфраструктурный сбой. Сервер не ответил или недоступен. Идем проверять состояние контейнеров. Kubernetes

Шаг 3. Локализация (Погружение)

В зависимости от выбранного на предыдущем шаге вектора, вы применяете один из трех сценариев глубокого бурения:

Сценарий А: Инфраструктурный (Kubernetes) Если вы получили Connection refused или 503 Service Unavailable, не тратьте время на Kibana — логи туда могли просто не доехать.

  1. Выполните kubectl get pods в нужном неймспейсе.
  2. Ищите статусы CrashLoopBackOff, Pending или Evicted.
  3. Если под постоянно перезапускается, используйте kubectl logs <pod-name> --previous, чтобы увидеть причину смерти (например, OOMKilled).

Сценарий Б: Аппликационный (ELK) Если API вернуло 500 Internal Server Error, приложение живо, но сломалось в процессе обработки.

  1. Вставьте Trace ID в строку поиска Kibana.
  2. Отфильтруйте логи по уровню ERROR.
  3. Найдите серверный Stacktrace, определите конкретный класс и строку кода (например, NullPointerException в AssignmentService.java:42).

Сценарий В: Бизнес-логика (Jira) Если тест упал с Failed или 409 Conflict.

  1. Возьмите тестовые данные (Payload) из Allure.
  2. Найдите в Jira последние эпики по затронутому эндпоинту.
  3. Разрешите трилемму: это баг бэкенда, тест устарел, или требования противоречат друг другу?

Практический синтез: распутываем сложный клубок

Давайте посмотрим, как алгоритм работает целиком на примере сложного падения в проекте aeqs-api-test.

Ситуация: Тест assignClientToManagerTest падает.

Шаг 1 (Allure): Статус Broken. В логах мы видим HTTP-ответ: 500 Internal Server Error. Мысль: Это не проблема тестовых данных и не таймаут. Сервис жив, но внутри произошла ошибка.

Шаг 2 (Маршрутизация): Раз ошибка 500, берем из заголовков ответа X-Request-Id: req-8f73b1 и идем в слой приложения.

Шаг 3 (ELK): В Kibana по нашему Trace ID мы находим лог сервиса aeqs-api. Но вместо ожидаемого NullPointerException мы видим:

java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.

Мысль: API-сервис жив, но он не смог достучаться до базы данных.

Шаг 4 (Смена вектора на Kubernetes): Ошибка базы данных — это инфраструктурный уровень. Идем в консоль оркестратора. Выполняем проверку подов: kubectl get pods -n aeqs-backend

Видим картину:

NAME                     READY   STATUS    RESTARTS   AGE
aeqs-api-7b8c...         1/1     Running   0          5h
postgres-db-0            0/1     Evicted   0          10m

Шаг 5 (Формулирование Root Cause): Мы дошли до дна воронки. Под базы данных перешел в статус Evicted (выселен оркестратором, скорее всего из-за нехватки места на диске ноды). Из-за этого пул коннектов в aeqs-api исчерпал таймаут ожидания (30 секунд), выкинул SQLTransientConnectionException, что привело к ответу 500 для нашего автотеста, который в итоге получил статус Broken.

Итог: Автотест написан верно. Приложение работает штатно. Багов в бизнес-логике нет. Root Cause — инфраструктурный инцидент (нехватка ресурсов на ноде БД). Заводить баг на разработчиков API не нужно, нужно писать DevOps-инженерам.

Заключение

Анализ падений — это не искусство, а строгая дисциплина. Используя воронку локализации, вы перестаете слепо перебирать логи. Вы начинаете читать отчет Allure как карту, где HTTP-статусы и типы исключений — это дорожные знаки, указывающие, какой инструмент (ELK, Kubernetes или Jira) нужно открыть следующим.

На этом курс завершен. Теперь у вас есть полный арсенал для того, чтобы уверенно находить первопричину любого падения, от простой опечатки в JSON до сложных каскадных сбоев в микросервисной архитектуре.