Собираем трейс в Laravel и отправляем в Zipkin

от автора

Привет, Хабр! Меня зовут Александр Белышев. Хочу немного вам рассказать о библиотеке (Laravel Zipkin Tracer), которую я разработал изучая трейсинг в php, возможно кому-то это будет так же интересно, как и мне. О OpenTelemetry я знал что он есть, даже его пощупал, но хотелось чуть с другой стороны изучить предмет и решить его несколько другим способом.

Laravel Zipkin Tracer — это специализированный модуль для Laravel, который обеспечивает автоматический трейсинг HTTP‑запросов, SQL‑запросов и позволяет создавать пользовательские спаны (spans) для интеграции с Zipkin.

Архитектура модуля

Модуль состоит из нескольких ключевых компонентов:

  • ZipkinTracerProvider — основной провайдер, регистрирующий все сервисы

  • ZipkinTracerMiddleware — middleware для перехвата HTTP‑запросов

  • EventSubscriber — подписчик на события Laravel для автоматического трейсинга

  • DataCollectorService — сервис для сбора и сохранения данных трейсинга

  • CustomSpanService — сервис для создания пользовательских спанов

  • SyncDataCommand — команда для синхронизации данных с Zipkin

Общая архитектура системы

Архитектура системы

Архитектура системы

Детальная схема сбора данных

Схема сбора данных

Схема сбора данных

Установка и настройка

Требования

  • PHP ^8.2

  • Laravel ^10

  • openzipkin/zipkin ^3.2

Установка

composer require xman12/laravel-zipkin-tracer

Настройка

  1. Добавление провайдера в app/bootstrap/providers.php:

    return [     App\Providers\AppServiceProvider::class,     ZipkinTracerProvider::class, ];
  2. Копирование конфигурации:

    php artisan vendor:publish --tag=zipkin-tracer
  3. Настройка переменных окружения в .env:

    ZIPKIN_TRACER_ENABLE=true ZIPKIN_TRACER_STORAGE_PATH=/path/to/storage/zipkin_tracer ZIPKIN_TRACER_SERVICE_NAME=my-service ZIPKIN_TRACER_ENDPOINT=http://127.0.0.1:9411/api/v2/spans
  4. Настройка cron для синхронизации данных:

    # Добавить в crontab * * * * * php artisan zipkin-tracer:sync_data

Функциональность

Модуль автоматически отслеживает:

HTTP-запросы

  • Метод запроса (GET, POST, PUT, DELETE)

  • URL

  • Статус код ответа

  • Размер запроса и ответа

  • Время выполнения

  • Исключения

SQL-запросы

  • Текст SQL‑запроса

  • Время выполнения

  • Файл и строка выполнения

  • Транзакции (begin, commit, rollback)

HTTP-клиент запросы

  • Исходящие HTTP‑запросы через Laravel HTTP Client

  • Заголовки запросов

  • Статус коды ответов

  • Ошибки соединения

Пользовательские спаны

Модуль позволяет создавать собственные спаны для отслеживания бизнес‑логики:

/** @var CustomSpanService $customSpanService */ $customSpanService = app(CustomSpanService::class);  // Простой спан $span = $customSpanService->createSpan('user-registration', function () {     // Бизнес-логика регистрации пользователя     $user = User::create([         'name' => 'John Doe',         'email' => 'john@example.com'     ]);          return [         'user_id' => $user->id,         'registration_method' => 'email'     ]; });  $customSpanService->addSpan($span);

Вложенные спаны

// Дочерние спаны $validationSpan = $customSpanService->createSpan('validate-user-data', function () {     // Валидация данных     return ['validation_passed' => true]; });  $emailSpan = $customSpanService->createSpan('send-welcome-email', function () {     // Отправка приветственного письма     return ['email_sent' => true]; });  // Родительский спан с дочерними $mainSpan = $customSpanService->createSpan('user-registration-process', function () {     // Основная логика     return ['process_completed' => true]; }, [$validationSpan, $emailSpan]);  $customSpanService->addSpan($mainSpan);

Процесс сбора данных

Процесс синхронизации с Zipkin

Схема синхронизации

Схема синхронизации
Жизненный цикл запроса

Жизненный цикл запроса
Схема хранения данных

Схема хранения данных

Сравнение с OpenTelemetry

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

Laravel Zipkin Tracer vs OpenTelemetry

Характеристика

Laravel Zipkin Tracer

OpenTelemetry

Специализация

Специально для Laravel + Zipkin

Универсальный стандарт

Сложность настройки

Простая

Средняя-высокая

Автоматический трейсинг

✅ HTTP, SQL, HTTP Client

✅ Более широкий спектр

Пользовательские спаны

✅ Простой API

✅ Более сложный API

Производительность

Высокая (минимальные накладные расходы)

Средняя (больше метаданных)

Интеграция с Laravel

Нативная

Требует дополнительной настройки

Поддержка стандартов

Zipkin-specific

OpenTelemetry standard

Экосистема

Ограниченная

Огромная

Сравнение архитектур

Сравнение архитектур

Преимущества Laravel Zipkin Tracer

  1. Простота использования

    • Минимальная конфигурация

    • Автоматическая интеграция с Laravel

    • Понятный API для пользовательских спанов

  2. Производительность

    • Асинхронная отправка данных

    • Минимальные накладные расходы

    • Эффективное хранение в файловой системе

  3. Специализация

    • Оптимизирован для Laravel

    • Готовая интеграция с Zipkin

    • Автоматический трейсинг типичных сценариев

Недостатки Laravel Zipkin Tracer

  1. Ограниченная экосистема

    • Только Zipkin

    • Меньше инструментов и интеграций

  2. Функциональность

    • Меньше возможностей по сравнению с OpenTelemetry

    • Ограниченные возможности для метрик

  3. Стандартизация

    • Не следует открытым стандартам

    • Может быть сложнее мигрировать в будущем

Практические примеры

Пример 1: Трейсинг API-эндпоинта

// UserController.php class UserController extends Controller {     public function show($id)     {         /** @var CustomSpanService $customSpanService */         $customSpanService = app(CustomSpanService::class);         $user = User::with(['profile', 'posts'])->findOrFail($id);          $span = $customSpanService->createSpan('get-user-profile', function () use ($user) {                          return [                 'user_id' => $user->id,                 'profile_complete' => $user->profile ? true : false,                 'posts_count' => $user->posts->count()             ];         });                  $customSpanService->addSpan($span);                  return response()->json($user);     } }

Пример 2: Трейсинг внешних API-вызовов

// ExternalApiService.php class ExternalApiService {     public function fetchUserData($userId)     {         /** @var CustomSpanService $customSpanService */         $customSpanService = app(CustomSpanService::class);                  $span = $customSpanService->createSpan('external-api-call', function () use ($userId) {             // Laravel HTTP Client автоматически трейсится             $response = Http::get("https://api.external.com/users/{$userId}");                          return [                 'external_user_id' => $userId,                 'response_status' => $response->status(),                 'response_size' => strlen($response->body())             ];         });                  $customSpanService->addSpan($span);                  return $response->json();     } }

Пример 3: Трейсинг сложной бизнес-логики

// OrderService.php class OrderService {     public function processOrder($orderData)     {         /** @var CustomSpanService $customSpanService */         $customSpanService = app(CustomSpanService::class);                  // Валидация заказа         $validationSpan = $customSpanService->createSpan('validate-order', function () use ($orderData) {             $validator = Validator::make($orderData, [                 'items' => 'required|array',                 'customer_id' => 'required|exists:customers,id'             ]);                          if ($validator->fails()) {                 throw new ValidationException($validator);             }                          return ['validation_passed' => true];         });                  // Проверка наличия товаров         $inventorySpan = $customSpanService->createSpan('check-inventory', function () use ($orderData) {             foreach ($orderData['items'] as $item) {                 $product = Product::find($item['product_id']);                 if ($product->stock < $item['quantity']) {                     throw new InsufficientStockException();                 }             }                          return ['inventory_available' => true];         });                  // Создание заказа         $orderSpan = $customSpanService->createSpan('create-order', function () use ($orderData) {             DB::transaction(function () use ($orderData) {                 $order = Order::create([                     'customer_id' => $orderData['customer_id'],                     'total_amount' => $this->calculateTotal($orderData['items'])                 ]);                                  foreach ($orderData['items'] as $item) {                     $order->items()->create($item);                 }             });                          return ['order_created' => true];         });                  // Основной спан         $mainSpan = $customSpanService->createSpan('process-order-complete', function () {             return ['order_processed' => true];         }, [$validationSpan, $inventorySpan, $orderSpan]);                  $customSpanService->addSpan($mainSpan);     } }

Мониторинг и отладка

После настройки синхронизации данные будут доступны в веб‑интерфейсе Zipkin:

  1. Откройте Zipkin UI (обычно http://localhost:9411)

  2. Выберите сервис из выпадающего списка

  3. Настройте временной диапазон

  4. Просматривайте трейсы и спаны

Интерфейс Zipkin

Интерфейс Zipkin

В Zipkin вы сможете увидеть:

  • Время выполнения каждого спана

  • Зависимости между сервисами

  • Узкие места в производительности

  • Ошибки и исключения

  • SQL-запросы с временем выполнения

Типичные сценарии отладки

  1. Медленные запросы: Анализ времени выполнения SQL-запросов

  2. Ошибки внешних API: Просмотр HTTP-клиент запросов

  3. Проблемы бизнес-логики: Анализ пользовательских спанов

  4. Проблемы производительности: Выявление узких мест в цепочке запросов

Итоги

Laravel Zipkin Tracer вам подойдет если вам нужно:

  • У вас Laravel‑приложение

  • Используется Zipkin как система трейсинга

  • Простоту интеграции с минимальными изменениями в коде (не нужно устанавливать дополнительные расширения)

  • Автоматический трейсинг типичных сценариев

  • Гибкость для создания пользовательских спанов

  • Производительность с асинхронной отправкой данных

❌ Не рекомендуется, если:

  • Нужна поддержка множественных систем трейсинга

  • Требуется полная совместимость с OpenTelemetry

  • Необходимы продвинутые возможности метрик

  • Планируется миграция на другие системы мониторинга

Альтернативы

Для более сложных сценариев рассмотрите:

  • OpenTelemetry PHP — универсальный стандарт

  • Jaeger — альтернативная система трейсинга

  • DataDog APM — коммерческое решение с расширенными возможностями

Github: https://github.com/xman12/laravel-zipkin-tracer


ссылка на оригинал статьи https://habr.com/ru/articles/920870/