14 мар. 2025 г.·7 мин чтения

Отслеживайте запросы от UI до API через фоновые задачи

Узнайте, как trace requests from UI to API через фоновые задачи, чтобы support и engineering быстрее находили, где начинается сбой, и перестали гадать.

Отслеживайте запросы от UI до API через фоновые задачи

Почему команды спорят о том, где начался сбой

Команды обычно спорят, потому что смотрят на разные части одного и того же события. Support начинает с сообщения клиента. Engineering начинает с логов. Эти два взгляда почти никогда не совпадают по времени.

Клиент не описывает первую причину. Он описывает симптом, который видит. Если человек нажимает «Generate report», а файл так и не приходит, support слышит: «не сработала генерация отчёта». Для пользователя это может быть правдой, но клик, вызов API и последующий воркер — это три отдельные шага. Первый сбой мог случиться уже после того, как экран выглядел нормально.

Инженеры часто первым делом идут в серверные логи, потому что это самый быстрый способ проверить. Такой подход понятен, но он может скрыть настоящий старт проблемы. Если API вернул 200, кто-то может решить, что продукт сработал. Во многих продуктах это лишь доказывает одно: сервер принял запрос. Это не доказывает, что UI отправил правильные данные, и не доказывает, что очередь или воркер завершили работу.

Проблема усугубляется, когда одно действие пользователя распадается на параллельные ветки. UI может сохранить форму, API — записать строку в базу, а фоновый воркер через несколько секунд отправит письмо или соберёт файл. У каждой ветки своё время, свои логи и свой владелец. Support видит пропавший результат. Engineering видит один успешный запрос. Оба могут быть правы, просто говоря о разных шагах.

Команде нужен один общий способ назвать первый сломанный шаг. Простые формулировки лучше расплывчатых фраз вроде «приложение сломалось». Говорите: «UI вообще не отправил запрос», «API отклонил его» или «фоновая задача не завершилась». Такой маленький сдвиг убирает лишние обвинения, ускоряет передачу задачи и даёт всем одну точку старта.

Три пути, которые нужно пройти

Когда в баг-репорте пишут «у меня всё работало» или «система это вообще не получила», люди обычно проверяют разные части одного и того же события. Это тратит время. Пройдите одно действие пользователя по трём связанным путям по порядку — и спор часто исчезает.

Начните с экрана. Найдите точное действие, которое сделал человек: кнопку, которую нажал, форму, которую отправил, фильтр, который поменял. Вам нужен момент, когда намерение стало запросом. Здесь важна точность по времени. Пользователь, который нажал «Send refund» в 10:14:22 и увидел индикатор загрузки, даёт гораздо больше информации, чем размытое «возвраты сломались».

Потом перейдите к API-запросу, который обрабатывал это действие. Браузер вообще отправил запрос? Какой endpoint его принял? Какой статус вернулся? Именно здесь начинается много путаницы. Support видит сломанный экран и думает, что сервер упал, а engineering не видит серверной ошибки и решает, что пользователь ничего не отправлял. Чтобы trace requests from UI to API, обеим сторонам нужен один и тот же таймстемп, одно и то же имя действия и один и тот же request ID.

И, наконец, проверьте работу, которая произошла позже. Многие действия в продукте не завершаются внутри ответа API. API может принять запрос, сохранить данные и передать остальное в очередь или воркер. Там часто живут письма, отчёты, обновления биллинга, sync-задачи и повторные попытки. Если экран выглядел нормально, а API вернул 200, первый сбой всё равно может сидеть в фоновой обработке.

Один request ID должен связывать все три пути. Добавьте его в логи браузера для действия, в логи API для запроса и в логи воркера для задачи, созданной из этого запроса. Если API создаёт несколько задач, сохраняйте исходный request ID и добавляйте отдельные job ID. Так получается понятная цепочка вместо трёх разрозненных поисков по логам.

Простой пример делает это нагляднее. Клиент нажимает «Send invoice», API сохраняет счёт и ставит email-задачу в очередь, но письмо так и не приходит. Без общего ID support проверяет страницу, engineering — endpoint, и никто вовремя не смотрит на воркер. С общим ID вы видите клик, ответ API и упавшую email-задачу в одном следе.

Что нужно фиксировать в UI

UI — это место, где начинается большинство сообщений о сбоях, поэтому первая запись должна быть простой и точной. «Не сработало» — недостаточно. Вам нужны имя экрана, точная кнопка или действие, которое использовал человек, и время клика с точностью до минуты, а если есть возможность — ещё и с часовым поясом.

Кажется мелочью, но это меняет всё расследование. Сообщение «Checkout page, кнопка Pay now, 14:07 UTC» даёт engineering точку, с которой можно сопоставить API-логи. Сообщение «платёж сломан» оставляет всем только догадки.

UI также должен связывать действие с нужным человеком и контекстом. Сохраняйте user ID, account ID или workspace ID, а также session ID, если он есть в приложении. Если один и тот же пользователь открыл две вкладки, переключил аккаунт или повторил отправку формы, эти детали помогут отличить один плохой запрос от трёх разных.

Понятные состояния интерфейса не менее важны, чем ID. Если человек нажал кнопку, экран должен без двусмысленности показывать одно из трёх: действие ещё выполняется, оно завершилось или оно упало. Размытые сообщения создают ложные баги. Индикатор загрузки, который не заканчивается, часто считают проблемой API, хотя запрос мог уже успешно пройти, а UI просто не получил ответ.

Хорошо работает простой, удобный для support шаблон:

  • покажите имя страницы или экрана
  • сохраните метку действия, на которую нажал пользователь
  • зафиксируйте время действия
  • добавьте видимое сообщение о результате
  • включите request ID, если он есть в приложении

Если возможно, покажите этот request ID так, чтобы support мог скопировать его в заметки или тикет. Даже короткий «reference ID» рядом с сообщением об ошибке может сэкономить 20 минут переписки.

И ещё один важный момент: фиксируйте, что именно увидел пользователь. Точный текст сообщения об успехе, всплывающей ошибки или пустого состояния часто подсказывает, где начался сбой — в браузере, в API или позже, в фоновой обработке.

Что нужно фиксировать в API

Когда запрос попадает в API, фиксируйте несколько фактов, которые объясняют, что решил сервер и почему. Если этих фактов нет, support видит симптом, engineering видит строку в логе, а доказать, где начался сбой, не может ни одна сторона.

Начните с самого запроса. Запишите HTTP-метод, route и результат проверки входных данных. Небольшая запись вроде POST /refunds, customer_id present, email missing, status 422 рассказывает гораздо больше, чем безликий blob с ошибкой.

Хорошая запись API обычно включает:

  • route и method
  • прошли или нет проверки входных данных
  • финальный status code
  • короткую причину ошибки, которую команда может повторить слово в слово
  • любой job ID, созданный после запроса

Не прячьте проблему за размытым 200 или мягким провалом. Если API поставил работу в очередь, но ещё не завершил её, верните 202 и укажите job ID. Если валидация не прошла, скажите, что именно не так. «Missing order_id» полезно. «Bad request» — нет.

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

Support-команде проще работать с одной стабильной причиной ошибки, чем с россыпью меняющихся сообщений. Выберите короткую формулировку вроде «refund record not created» или «email job not queued» и используйте её в ответе API, логах и внутренних заметках. Это сильно сокращает переписку.

Простой пример помогает понять идею. Если UI отправляет запрос на возврат, а API создаёт фоновую задачу, ответ может содержать и request ID, и job ID. Так гораздо легче trace requests from UI to API, а затем перейти в логи воркера. Команды, которые работают в компактных системах, например в AI-first-настройках Олега, особенно зависят от такой чистой передачи, потому что она экономит время на каждом инциденте.

Что нужно фиксировать в фоновой обработке

Спланируйте лёгкую настройку трассировки
Начните с простого решения для трассировки, которое подходит вашему продукту и команде, а не с лишнего зоопарка инструментов.

Фоновые задачи часто скрывают первый реальный сбой. Они выполняются позже, на другой машине и иногда спустя долгое время после клика пользователя. Если вы хотите, чтобы support и engineering сошлись на одной временной линии, у задачи должен быть собственный след.

Начните с момента создания задачи приложением. Зафиксируйте, что задача попала в очередь, когда это произошло, какого она типа и какое действие пользователя её создало. Запись, которая просто говорит «email job created», слишком размыта. Нужна детализация, чтобы ответить на главный вопрос: попала ли работа в очередь вообще?

Затем зафиксируйте, когда воркер действительно подхватил задачу. Этот промежуток важен. Если задача лежала в очереди 20 минут, проблема не в коде отправки письма. Возможно, дело в зависшем воркере, нехватке мощности или приостановленной очереди.

Обычно достаточно небольшого набора полей:

  • исходный request ID
  • job ID и тип задачи
  • время попадания в очередь и время подхвата
  • номер попытки, задержка повтора и следующий запланированный запуск
  • финальный статус, время завершения и сообщение об ошибке

Сохраняйте исходный request ID на задаче с самого первого API-запроса. Это одно поле позволяет trace requests from UI to API и дальше в async-работу без догадок. Когда support пишет «письмо с возвратом так и не пришло», engineering может искать по одному ID и видеть весь путь вместо того, чтобы вручную сопоставлять таймстемпы.

Повторы заслуживают не меньшего внимания, чем ошибки. Логируйте каждый retry, причину и то, сколько система ждала перед следующей попыткой. Задача, которая трижды упала и потом всё же прошла, рассказывает совсем другую историю, чем задача, которая вообще не стартовала.

Финальный результат должен быть однозначным. Пишите «succeeded», «failed», «cancelled» или «dead lettered» с последней ошибкой. Всё более мягкое потом создаёт споры, а споры замедляют исправление.

Если воркеры обрабатывают большой объём, можно уменьшить шумные success-логи через sampling. Но не сэмплируйте события входа в очередь, подхвата, повтора или финальной ошибки. Эти четыре точки обычно и показывают, где начался сбой.

Простой пример: письмо о возврате так и не приходит

Клиент получает возврат и нажимает «Send refund receipt» в админке. Через секунду экран показывает «sent», и support делает вывод, что система отправки почты сработала, а проблема, скорее всего, в ящике клиента. Часто это неверная догадка.

На самом деле всё было точнее. Кнопка отправила запрос в API. API проверил запись о возврате, принял действие и создал email-задачу в очереди. С точки зрения приложения запрос завершился успешно. Страница показала успех не потому, что письмо ушло, а потому, что запрос прошёл.

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

Вот почему полезно trace requests from UI to API, а затем в фоновую обработку. Одно зелёное сообщение в UI не доказывает, что последний шаг прошёл. Оно доказывает только, что прошёл первый.

В этом случае цепочка выглядит так:

  • UI action: «Send refund receipt» нажата в 10:14:03
  • API request: принят в 10:14:04, создана задача с ID 84721
  • Worker start: задача 84721 подхвачена в 10:14:06
  • Worker error: вход в почту не удался в 10:14:07

Такой таймлайн меняет ответ поддержки. Вместо «мы отправили, проверьте spam» support может сказать: «Ваш возврат был обработан, и запрос на отправку чека дошёл до нашей системы. Сбой произошёл позже, на этапе email-задачи, и наша команда уже это исправляет».

Engineering тоже получает более чистую передачу. Им не нужно сначала перепроверять весь flow возврата или админку. Можно сразу идти в логи воркера, настройки почты и историю повторных попыток задачи. Первый сбой произошёл после шага API, и записи это подтверждают.

Как выстроить цепочку шаг за шагом

Проверьте повторы в воркерах
Узнайте, задержки вызваны зависшей очередью, неверными учётными данными или логикой повторов.

У большинства команд уже и так много логов. Настоящая проблема в том, что каждый слой называет один и тот же запрос по-своему, и след рвётся раньше, чем кто-то успевает его пройти.

Начните с одного формата request ID и сделайте его скучным. Простое значение вроде req_8f3a2b91 отлично подойдёт. Не позволяйте браузеру создавать один вариант, API — другой, а воркеру — третий. Если люди не узнают один и тот же ID с первого взгляда, они перестают доверять этому следу.

Когда пользователь нажимает кнопку в UI, прикрепите этот ID к действию и отправьте его в API вместе с запросом. Затем скопируйте тот же ID в payload любой фоновой задачи, которую создаёт API. Не генерируйте новый ID в очереди, если не сохраняете и исходный. Вся идея в том, чтобы пройти по одной истории через каждую передачу.

Логи тоже должны быть одинаково устроены везде. Если фронтенд пишет requestId, API — req_id, а воркер — trace, поиск быстро превращается в хаос. Выберите одно имя поля и используйте его во всех сервисах. Оставьте и несколько общих полей: имя события, user ID или account ID, если он есть, имя сервиса и результат.

Простой план внедрения выглядит так:

  1. Добавьте request ID там, где действие начинается в UI.
  2. Принимайте и логируйте его в API до того, как начнётся любая другая работа.
  3. Копируйте его в payload задачи и логируйте снова, когда воркер стартует.
  4. Записывайте финальный результат с одинаковыми именами полей в каждом месте.

Потом специально проверьте два пути. Прогоните один обычный запрос и убедитесь, что один и тот же ID появился в логах UI, API и воркера. После этого намеренно сломайте один шаг. Остановите воркер, отклоните API-запрос или заблокируйте одну downstream-зависимость. Вам нужно увидеть, где именно рвётся цепочка, потому что именно это потом увидит support.

Support тоже нужен один короткий принцип, которому можно следовать под давлением: собрать request ID и время, когда пользователь увидел проблему. Обычно этого достаточно, чтобы engineering нашёл первый сбой за минуты, а не гадал по трём системам.

Ошибки, которые скрывают первый сбой

Чаще всего команды теряют след, потому что каждая часть продукта рассказывает свою историю. Если вы хотите trace requests from UI to API, нужен один непрерывный журнал того, что сделал пользователь, что принял API и что позже попыталась сделать фоновая задача.

Распространённая ошибка — создавать новый ID на каждом этапе. Браузер пишет один request ID, API создаёт другой, а воркер — третий. Теперь support видит три отдельные события вместо одной цепочки. Оставляйте один общий correlation ID и проводите его через все шаги.

Ещё одна ошибка — говорить пользователю «success» слишком рано. Это происходит, когда UI подтверждает действие, как только API его принял, хотя настоящая работа всё ещё зависит от очереди или воркера. Если задача падает через 20 секунд, клиент помнит сообщение об успехе, а engineering видит упавшую задачу. Такой разрыв очень быстро рождает споры.

Логи могут не содержать как раз ту часть, которая объясняет баг: действия пользователя. Stack trace полезен уже после того, как вы поняли, где искать. Он не скажет, нажал ли человек кнопку дважды, обновил ли страницу, поменял ли email или отправил старую форму. Сохраняйте несколько простых фактов вместе с каждым событием: кто это сделал, на что нажал, что изменилось и когда это произошло.

Повторы создают ещё одну слепую зону. Многие системы сохраняют последнюю ошибку и выбрасывают первую. Это наоборот. Первая ошибка часто указывает на настоящую причину, а последующие повторы показывают только последствия. Сохраняйте исходный сбой до следующей попытки воркера.

Тикеты в поддержку часто приходят без одной детали, без которой поиск почти невозможен: времени. «Письмо о возврате не пришло» слишком широко звучит, если каждый час обрабатываются тысячи задач. Тикет становится гораздо полезнее, если в нём есть:

  • email пользователя или account ID
  • примерное время действия
  • экран, который он использовал
  • сообщение, которое показывал UI

Без этого команды гадают. С этим они находят первый сбой, а не самый громкий симптом.

Быстрая проверка перед эскалацией

Исправьте сломанные цепочки запросов
Поможем связать действия в UI, запросы к API и задачи воркера одним общим ID.

Размытое сообщение очень быстро тратит время. «Сломалось» не даёт support никакой точки входа, и engineering остаётся только догадываться. Более точный отчёт часто убирает 30 минут лишней переписки.

Начните с действия пользователя. Запишите точную кнопку, форму или экран, который он использовал, и что он ожидал увидеть. «Пользователь нажал Pay на invoice #4831 после изменения billing email» гораздо лучше, чем «платёж не прошёл».

Время не менее важно. Зафиксируйте, когда это произошло, как можно точнее, и, если есть сомнения, укажите часовой пояс пользователя. Окно в две минуты часто уже достаточно, чтобы сопоставить UI-события, API-логи и запуск задач.

Перед передачей проблемы проверьте такие пункты:

  • Какое именно действие вызвало проблему?
  • Во сколько пользователь это сделал?
  • API вернул успех, ошибку валидации или серверную ошибку?
  • Если после этого должна была запуститься фоновая задача, она стартовала, повторилась или остановилась?
  • Можете ли вы назвать первый шаг, который сломался, а не финальный симптом?

Последний пункт экономит больше всего времени. Пользователи обычно сообщают о результате, а не о поломке. Они замечают, что письмо не пришло, но настоящий сбой мог случиться раньше: API отклонил запрос, задача вообще не попала в очередь или воркер трижды повторил попытку и сдался.

Если support может сказать: «UI принял действие в 14:06 UTC, API вернул 200, но для этого request ID не стартовала ни одна задача», engineering может сразу идти в разрыв. Если место сбоя не определить, так и скажите. «UI подтвердил успех, результат API неизвестен, статус задачи неизвестен» всё равно лучше, чем расплывчатая эскалация.

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

Что делать дальше

Пытаться trace requests from UI to API сразу во всех функциях обычно создаёт больше шума, чем ясности. Выберите один поток, который важен каждую неделю: например, регистрацию, отправку счёта, сброс пароля или обработку возврата. Один путь даёт команде что-то конкретное для проверки, обсуждения и исправления.

Сделайте первую версию маленькой. Вам не нужен новый стек наблюдаемости в первый же день. Начните с одного общего ID, который проходит через запрос, а затем добавьте три точки логирования: когда действие пользователя начинается в UI, когда API получает его и когда фоновая работа подхватывает или завершает задачу.

Такого простого следа часто уже достаточно, чтобы ответить на вопрос, который чаще всего тормозит команды: где начался сбой? Support перестаёт гадать, а инженеры перестают копаться в несвязанных логах.

Хороший первый шаг выглядит так:

  • выберите один частый пользовательский сценарий
  • добавьте один request или trace ID, который виден на каждом этапе
  • логируйте старт действия в UI, API и worker
  • запишите точные слова, которыми каждая команда будет пользоваться для статусов и типов ошибок

Последний пункт важнее, чем многие ожидают. Если support говорит «зависло», engineering — «в очереди», а product — «обрабатывается», люди просто говорят друг мимо друга. Договоритесь о простых терминах для состояний вроде sent, received, queued, started, failed и retried. Такая небольшая чистка реально экономит время во время инцидентов.

Если ваш продукт быстро вырос, такие пути может быть трудно разложить. Старые системы часто смешивают синхронные запросы, повторы, cron-задачи и вызовы сторонних сервисов так, что первый сбой скрывается. В таком случае внешний взгляд может ускорить работу. Олег Сотников может провести разбор request flow в рамках практичного Fractional CTO-сопровождения и помочь команде понять, где добавить IDs, логи и более понятные правила передачи.

Начните с одного пути на этой неделе. Если команда сможет чисто пройти один запрос от клика до API и до задачи, следующий поток станет намного проще.