Как стать автором
Обновить

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

Уровень сложностиСредний
Время на прочтение6 мин
Количество просмотров487

Привет, Хабр! Меня зовут Александр Белышев. Хочу немного вам рассказать о библиотеке (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

Теги:
Хабы:
+1
Комментарии2

Публикации

Работа

PHP программист
66 вакансий

Ближайшие события