Привет, Хабр! Меня зовут Александр Белышев. Хочу немного вам рассказать о библиотеке (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
Настройка
-
Добавление провайдера в
app/bootstrap/providers.php
:return [ App\Providers\AppServiceProvider::class, ZipkinTracerProvider::class, ];
-
Копирование конфигурации:
php artisan vendor:publish --tag=zipkin-tracer
-
Настройка переменных окружения в
.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
-
Настройка 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
-
Простота использования
Минимальная конфигурация
Автоматическая интеграция с Laravel
Понятный API для пользовательских спанов
-
Производительность
Асинхронная отправка данных
Минимальные накладные расходы
Эффективное хранение в файловой системе
-
Специализация
Оптимизирован для Laravel
Готовая интеграция с Zipkin
Автоматический трейсинг типичных сценариев
Недостатки Laravel Zipkin Tracer
-
Ограниченная экосистема
Только Zipkin
Меньше инструментов и интеграций
-
Функциональность
Меньше возможностей по сравнению с OpenTelemetry
Ограниченные возможности для метрик
-
Стандартизация
Не следует открытым стандартам
Может быть сложнее мигрировать в будущем
Практические примеры
Пример 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:
Откройте Zipkin UI (обычно http://localhost:9411)
Выберите сервис из выпадающего списка
Настройте временной диапазон
Просматривайте трейсы и спаны

В Zipkin вы сможете увидеть:
Время выполнения каждого спана
Зависимости между сервисами
Узкие места в производительности
Ошибки и исключения
SQL-запросы с временем выполнения
Типичные сценарии отладки
Медленные запросы: Анализ времени выполнения SQL-запросов
Ошибки внешних API: Просмотр HTTP-клиент запросов
Проблемы бизнес-логики: Анализ пользовательских спанов
Проблемы производительности: Выявление узких мест в цепочке запросов
Итоги
Laravel Zipkin Tracer вам подойдет если вам нужно:
У вас Laravel‑приложение
Используется Zipkin как система трейсинга
Простоту интеграции с минимальными изменениями в коде (не нужно устанавливать дополнительные расширения)
Автоматический трейсинг типичных сценариев
Гибкость для создания пользовательских спанов
Производительность с асинхронной отправкой данных
❌ Не рекомендуется, если:
Нужна поддержка множественных систем трейсинга
Требуется полная совместимость с OpenTelemetry
Необходимы продвинутые возможности метрик
Планируется миграция на другие системы мониторинга
Альтернативы
Для более сложных сценариев рассмотрите:
OpenTelemetry PHP — универсальный стандарт
Jaeger — альтернативная система трейсинга
DataDog APM — коммерческое решение с расширенными возможностями
Vireli
Немного оффтопный комментарий, по использованию OpenTelemetry+PHP+Laravel:
Для версии PHP 8.4 в пеклах лежит хорошая версия OpenTelemetry (
pecl install opentelemetry
или если alpine:apk add php84-opentelemetry
)Далее мы просто добавляем в проект зависимостью: (composer.json)
И можно использовать магию аннотаций в проекте:
Ну и обязательно установить env (Как пример, отправляем данные в локальный прокси коллектор по-средством http/json)
Там так же будет автоматический трейсинг запросов во-вне и к базам данных. И, что самое главное, можно оставлять всю такую же чистоту кода, за счет того, что не нужно контролировать спаны самостоятельно. Их теперь контролирует автохук в рамках пекл-расширения для пхп. Больше информации про хук тут: PHP
Дополнительно, за счет того что это OpenTelemetry, мы получаем более продвинутую схему для получения метрик из приложений, из-за push-policy: нам не нужно бояться, что данные потеряются при сетапах своих "prometheus-adapters", которые или в кеше хранить или где-нибудь еще.
Ну а еще данные из логов добавляется к спанам в качестве аттрибутов
Мы используем сетап вместе с SigNoz + Tail-Based Sample.
Из очевидных минусов: нужно добавлять памяти самому приложению (пока данные собираются, они хранятся в приложении и чего-то весят) и нужно много памяти в коллекторе, но это вопрос именно хвостового семплирования.
Ну а главным плюсом здесь я считаю то, что нет необходимости раздувать код приложения созданием спанов, что выглядит весьма ненативно для PHP.
Vireli
После того, как отправил, уже не смог отредактировать. С аттрибутами есть небольшой нюанс: нужно в рамках настроек PHP указывать
opentelemetry.attr_hooks_enabled = On
вphp.ini