Коли API-запит завершується помилкою, повідомлення "щось пішло не так" майже нічого не дає для діагностики.
У той самий момент система може обробляти сотні інших запитів. У журналах можуть бути десятки схожих помилок. І потрібно зрозуміти, який саме запис стосується відповіді, яку отримав конкретний клієнт.
Для цього я використовую Correlation ID - ідентифікатор одного запиту, який проходить разом із ним через систему.
Схема проста: прийняти коректний ідентифікатор від клієнта або створити свій, зберегти його в контексті запиту і повернути у відповіді.
Один запит отримує один ідентифікатор
HTTP request | vприйняти або створити ID | vmiddleware | vконтролер | vприкладна логіка | vдіагностика / логиМені важливо, щоб ідентифікатор з’являвся якомога ближче до входу в API.
Тоді той самий номер можна використовувати незалежно від того, де саме завершиться обробка: успішною відповіддю, перевіркою вхідних даних або внутрішньою помилкою.
У Laravel для цього добре підходить middleware.
Якщо клієнт передав коректний ID, я можу його продовжити
У розподіленій системі запит може прийти не безпосередньо від користувача, а від іншого сервісу.
Якщо попередня система вже створила ідентифікатор, корисно не розривати цей ланцюжок без причини.
Але я не приймаю довільний заголовок без перевірки. Значення має відповідати дозволеному формату та розумному обмеженню довжини.
Якщо значення відсутнє або не проходить перевірку, система створює власний ідентифікатор.
private function resolveCorrelationId(?string $candidate): string{ if ( $candidate !== null && $this->isAcceptableCorrelationId($candidate) ) { return $candidate; } return (string) Str::uuid();}Middleware додає ID до контексту запиту
final class CorrelationIdMiddleware{ private const HEADER = 'X-Correlation-ID'; public function handle( Request $request, Closure $next, ): Response { $correlationId = $this->resolveCorrelationId( $request->header(self::HEADER), ); $request->attributes->set( 'correlation_id', $correlationId, );Middleware виконує три невеликі дії.
- Читає
X-Correlation-IDабо створює новий. - Кладе значення в атрибути поточного запиту.
- Повертає той самий ID у заголовку відповіді.
Після цього іншим частинам застосунку не потрібно повторно вирішувати, звідки взявся ідентифікатор.
Я повертаю той самий ID клієнту
HTTP/1.1 409 ConflictX-Correlation-ID: 8d29b96f-... { "code": "request_conflict", "message": "The request cannot be completed.", "correlationId": "8d29b96f-..."}Для клієнта цей ID особливо корисний при помилці.
Замість повідомлення "у мене був 500 приблизно о 14:20" можна передати конкретний ідентифікатор запиту.
На внутрішній стороні той самий номер використовується для пошуку технічної інформації.
Я також люблю повертати значення в HTTP-заголовку, тому що тоді воно доступне незалежно від конкретної структури тіла відповіді.
Той самий ID додаю до діагностичного контексту
Log::error('Request processing failed', [ 'correlation_id' => $request->attributes->get( 'correlation_id', ), 'exception' => $exception::class,]);Сам ідентифікатор не замінює нормальну діагностику.
Він лише дає спільний ключ, за яким можна пов’язати різні записи, створені під час обробки одного запиту.
Залежно від системи це можуть бути звичайні журнали застосунку, error tracker, записи про зовнішні виклики або інші технічні події.
Важливо, щоб потрібні записи мали один і той самий ідентифікатор.
Correlation ID особливо корисний у безпечних API-помилках
return response()->json( [ 'code' => 'request_conflict', 'message' => 'The request cannot be completed.', 'correlationId' => $request->attributes->get( 'correlation_id', ), ], 409,);Публічна помилка не повинна містити stack trace, внутрішній SQL або текст довільного винятку лише для того, щоб розробнику було легше знайти причину.
Клієнту можна повернути стабільний і безпечний опис, а технічні подробиці залишити всередині.
Ідентифікатор запиту з’єднує ці дві сторони.
Саме тому цей механізм добре доповнює окремий контракт помилок API.
Correlation ID не є ідентичністю або секретом
Ідентифікатор запиту має дуже вузьке призначення: допомогти простежити обробку одного запиту.
Я не використовую його як:
- автентифікацію;
- дозвіл на виконання операції;
- секрет;
- ідентифікатор користувача або бізнес-сутності;
- заміну ключа ідемпотентності.
Те, що значення унікальне, ще не означає, що воно підходить для всіх задач, де потрібен ідентифікатор.
Не кожен клас повинен знати про HTTP-заголовок
Ще одна межа, яку я намагаюся зберігати: внутрішньому коду не обов’язково знати, що зовні значення прийшло саме через X-Correlation-ID.
HTTP-заголовок належить транспортному рівню.
Усередині достатньо мати вже визначений ідентифікатор у контексті поточного запиту або передавати його туди, де він справді потрібен.
Так формат зовнішнього API не починає проникати в кожен сервіс і кожен клас.
Один ID стає ще кориснішим між кількома сервісами
Client | | ID: abc-123 vService A | | ID: abc-123 vService B | | ID: abc-123 vService CУ межах одного Laravel-застосунку ідентифікатор уже корисний.
Але його цінність ще більше зростає, коли один вхідний запит породжує звернення до інших сервісів.
Якщо наступний сервіс підтримує таку саму домовленість, ідентифікатор можна передати далі.
Тоді під час розслідування видно не набір незалежних помилок у різних системах, а один логічний ланцюжок.
Що я перевіряю при додаванні Correlation ID
Для такого механізму я перевіряю кілька речей:
- ID створюється на вході, а не лише там, де вже сталася помилка.
- Зовнішнє значення перевіряється перед використанням.
- Якщо значення немає, система створює власне.
- Той самий ID доступний під час усієї обробки запиту.
- Він повертається клієнту.
- Він присутній у потрібному діагностичному контексті.
- Він не використовується як секрет, авторизація або бізнес-ідентичність.
Маленьке поле, яке дуже спрощує діагностику
Correlation ID не вирішує помилки і не замінює журнали чи спостережуваність.
Його задача значно простіша: дати всім записам одного запиту спільний орієнтир.
Клієнт отримує ідентифікатор у відповіді. Система використовує те саме значення під час внутрішньої діагностики. Якщо запит проходить через кілька сервісів, цей зв’язок можна продовжити далі.
Для мене це один із тих невеликих технічних механізмів, які майже непомітні під час нормальної роботи, але дуже цінні в момент, коли потрібно знайти один конкретний запит серед тисяч інших.
Correlation ID не говорить, що сталося. Він точно говорить, який запит потрібно дослідити.



