Skip to content

Трассировка ​

Трассировка позволяет отслеживать цепочку вызовов внутри и между сервисами. Каждая единица работы называется спаном (span). Спаны образуют дерево, корнем которого является корневой спан (root span), а все остальные — дочерние.

Архитектура ​

ОтелПровайдерТрассировки
    └── ОтелТрассировщик
            └── ОтелСпан
  • ОтелПровайдерТрассировки — точка входа, создаёт трассировщики.
  • ОтелТрассировщик — создаёт спаны.
  • ОтелСпан — единица работы: хранит атрибуты, события, статус, временны́е метки.

Создание провайдера через построитель ​

ОтелПостроительПровайдераТрассировки предоставляет Fluent API для настройки провайдера.

bsl
#Использовать opentelemetry

Ресурс = ОтелРесурсы.ПоУмолчанию();
Ресурс.Атрибуты().Установить("service.name", "my-service");

Транспорт = Новый ОтелHttpТранспорт("http://localhost:4318");
Экспортер = Новый ОтелЭкспортерСпанов(Транспорт);

Базовый = Новый ОтелБазовыйПакетныйПроцессор(Экспортер, "traces");
Процессор = Новый ПостроительНаследника(Новый ОтелПакетныйПроцессорСпанов(), Базовый).Построить();
Процессор.ЗапуститьФоновыйЭкспорт();

Провайдер = Новый ОтелПостроительПровайдераТрассировки()
    .УстановитьРесурс(Ресурс)
    .ДобавитьПроцессор(Процессор)
    .Построить();

Трассировщик = Провайдер.ПолучитьТрассировщик("my-library", "1.0.0");

Создание спанов ​

Простой спан ​

bsl
// SpanKind задаётся при создании и неизменен (immutable)
Спан = Трассировщик.НачатьСпан("обработка-запроса", ОтелВидСпана.Сервер());
Область = Спан.СделатьТекущим();

// ... работа ...

Область.Закрыть();
Спан.Завершить();

Дочерний спан ​

НачатьСпан автоматически подхватывает родителя из текущего контекста:

bsl
// Родительский спан
Спан = Трассировщик.НачатьСпан("операция", ОтелВидСпана.Сервер());
Область = Спан.СделатьТекущим();

// Дочерний спан — НачатьСпан видит текущий спан как родителя
ДочернийСпан = Трассировщик.НачатьСпан("запрос-к-бд", ОтелВидСпана.Клиент());
ОбластьДочернего = ДочернийСпан.СделатьТекущим();
// ... работа ...
ОбластьДочернего.Закрыть();
ДочернийСпан.Завершить();

Область.Закрыть();
Спан.Завершить();

Дочерний спан от явного родителя ​

bsl
// НачатьДочернийСпан принимает контекст (Context) родителя явно
Пропагаторы = ОтелГлобальный.ПолучитьПропагаторы();
НовыйКонтекст = Пропагаторы.Извлечь(ОтелКонтекст.Текущий(), ВходящиеЗаголовки);
Спан = Трассировщик.НачатьДочернийСпан("обработка", НовыйКонтекст, ОтелВидСпана.Сервер());

// Спан-родитель передается в контексте
КонтекстРодителя = ОтелКонтекст.КонтекстСоСпаном(ОтелКонтекст.Текущий(), Спан);
ШагОбработки = Трассировщик.НачатьДочернийСпан("шаг", КонтекстРодителя);

Виды спанов (SpanKind) ​

ОтелВидСпана описывает роль спана в распределённой системе:

МетодОписание
ОтелВидСпана.Внутренний()Внутренняя операция (по умолчанию)
ОтелВидСпана.Сервер()Входящий RPC/HTTP-запрос
ОтелВидСпана.Клиент()Исходящий RPC/HTTP-запрос
ОтелВидСпана.Производитель()Отправка сообщения в очередь
ОтелВидСпана.Потребитель()Получение сообщения из очереди

Атрибуты ​

Атрибуты — пары ключ/значение, описывающие контекст операции:

bsl
Спан.УстановитьАтрибут("http.method", "GET");
Спан.УстановитьАтрибут("http.url", "/api/data");
Спан.УстановитьАтрибут("http.status_code", 200);
Спан.УстановитьАтрибут("db.rows_affected", 42);

События спана ​

События — именованные точки во времени с атрибутами:

bsl
// Простое событие
Спан.ДобавитьСобытие("cache-miss");

// Событие с атрибутами
Атрибуты = Новый ОтелАтрибуты();
Атрибуты.Установить("cache.key", "user:42");
Спан.ДобавитьСобытие("cache-miss", Атрибуты);

// Событие, которое уже произошло: момент засечен раньше
МоментПромаха = ТекущаяУниверсальнаяДатаВМиллисекундах();
// ...
Спан.ДобавитьСобытие("cache-miss", , ОтелУтилиты.МиллисекундыВНаносекунды(МоментПромаха));

Время событий и спана — строка с числом наносекунд от начала эпохи Unix, как в OTLP. Строку для своего времени дают функции ОтелУтилиты.

Исключение записывает ЗаписатьИсключение: событие exception с описанием ошибки, модулем и стеком вызовов. Тип ошибки (exception.type) — код библиотеки errors, а у нативной ошибки OneScript без кода — код native-errors, например filesystem.resource.not_found. Статус спана метод не меняет:

bsl
Попытка
    ПрочитатьНастройки();
Исключение
    Спан.ЗаписатьИсключение(ИнформацияОбОшибке());
    Спан.УстановитьСтатус(ОтелКодСтатуса.Ошибка(), ОписаниеОшибки());
КонецПопытки;

Линки связывают спан с другим контекстом (например, асинхронные операции):

bsl
КонтекстСпана = ДругойСпан.КонтекстСпана();
Линки = Новый Массив();
Линки.Добавить(Новый ОтелЛинк(КонтекстСпана));
Спан = Трассировщик.НачатьСпан("async-операция", ОтелВидСпана.Внутренний(), , Линки);

Статус спана ​

ОтелКодСтатуса описывает результат операции:

МетодОписание
ОтелКодСтатуса.НеУстановлен()Статус не задан (по умолчанию)
ОтелКодСтатуса.Ок()Операция завершилась успешно
ОтелКодСтатуса.Ошибка()Операция завершилась с ошибкой
bsl
// Успешное завершение
Спан.УстановитьСтатус(ОтелКодСтатуса.Ок());

// Ошибка с описанием
Спан.УстановитьСтатус(ОтелКодСтатуса.Ошибка(), "Таймаут подключения к БД");

Семплирование ​

Сэмплер решает, какие спаны записываются и экспортируются. Встроенные сэмплеры создаёт модуль ОтелСэмплер; сэмплер задаётся при создании провайдера или через OTEL_TRACES_SAMPLER. Без настройки используется ParentBased(root=AlwaysOn): корневые спаны сэмплируются все, дочерние наследуют решение родителя.

bsl
Провайдер = Новый ОтелПостроительПровайдераТрассировки()
    .УстановитьСэмплер(ОтелСэмплер.ВсегдаВключен())
    .Построить();
bsl
// Корневые спаны - 10% трассировок, дочерние наследуют решение родителя
Провайдер = Новый ОтелПостроительПровайдераТрассировки()
    .УстановитьСэмплер(ОтелСэмплер.НаОсновеРодителя(ОтелСэмплер.ПоДолеТрассировок(0.1)))
    .Построить();
bash
# 10% трафика
export OTEL_TRACES_SAMPLER=parentbased_traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1

# Ничего не записывать
export OTEL_TRACES_SAMPLER=always_off

Встроенные сэмплеры:

ФункцияОписание
ОтелСэмплер.ВсегдаВключен()AlwaysOn: сэмплировать все спаны
ОтелСэмплер.ВсегдаВыключен()AlwaysOff: не сэмплировать ничего
ОтелСэмплер.ПоДолеТрассировок(Доля)TraceIdRatioBased: доля трассировок (0.0 – 1.0) по TraceId
ОтелСэмплер.НаОсновеРодителя(Корневой, ...)ParentBased: корневые спаны сэмплирует Корневой, дочерние — делегаты для удалённого и локального, сэмплированного и несэмплированного родителя (по умолчанию — решение родителя)
ОтелСэмплер.ВсегдаЗаписывать(Корневой)AlwaysRecord: процессоры получают и спаны, отброшенные Корневым, экспортер — только сэмплированные

Сэмплеры комбинируются, а свой сэмплер — объект с методами ДолженСэмплировать и Описание, см. ИнтерфейсСэмплер.

Процессоры спанов ​

Простой процессор (для разработки) ​

Синхронно экспортирует каждый спан сразу после завершения. Подходит для отладки.

bsl
Процессор = Новый ОтелПростойПроцессорСпанов(Экспортер);

Пакетный процессор (для продакшена) ​

Накапливает спаны в очереди и отправляет пакетами в фоновом потоке.

Процессор собирается через ПостроительНаследника из ОтелБазовыйПакетныйПроцессор. Базовому процессору передаются экспортер и имя сигнала. Необязательные параметры: размер очереди, размер пакета, интервал экспорта и таймаут экспорта в миллисекундах. Фоновое задание экспорта запускается с первым спаном и отправляет пакет, как только он заполнен или истёк интервал; завершение спана не ждёт отправку. ЗапуститьФоновыйЭкспорт запускает задание заранее (необязательно).

bsl
Базовый = Новый ОтелБазовыйПакетныйПроцессор(Экспортер, "traces");
Процессор = Новый ПостроительНаследника(Новый ОтелПакетныйПроцессорСпанов(), Базовый).Построить();
Процессор.ЗапуститьФоновыйЭкспорт();

При автоконфигурации параметры задаются переменными окружения:

ПеременнаяПо умолчаниюОписание
OTEL_BSP_MAX_QUEUE_SIZE2048Максимальный размер очереди
OTEL_BSP_SCHEDULE_DELAY5000Интервал экспорта (мс)
OTEL_BSP_MAX_EXPORT_BATCH_SIZE512Максимальный размер пакета
OTEL_BSP_EXPORT_TIMEOUT30000Таймаут экспорта (мс)

Композитный процессор ​

Объединяет несколько процессоров для одновременной отправки в разные системы:

bsl
// ПроцессорGrafana и ПроцессорJaeger — пакетные процессоры, собранные как показано выше
Процессоры = Новый Массив();
Процессоры.Добавить(ПроцессорGrafana);
Процессоры.Добавить(ПроцессорJaeger);
Процессор = Новый ОтелКомпозитныйПроцессорСпанов(Процессоры);

Лимиты спана ​

ОтелЛимитыСпана ограничивают количество атрибутов, событий и ссылок:

bsl
Лимиты = Новый ОтелЛимитыСпана()
    .УстановитьМаксАтрибутов(64)
    .УстановитьМаксСобытий(32);

Провайдер = Новый ОтелПостроительПровайдераТрассировки()
    .УстановитьЛимитыСпана(Лимиты)
    .Построить();

При автоконфигурации лимиты задаются переменными окружения:

ПеременнаяПо умолчаниюОписание
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT128Максимальное число атрибутов спана
OTEL_SPAN_EVENT_COUNT_LIMIT128Максимальное число событий
OTEL_SPAN_LINK_COUNT_LIMIT128Максимальное число ссылок
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT128Максимальное число атрибутов события
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT128Максимальное число атрибутов ссылки

Завершение работы ​

bsl
// Обязательно закрывайте провайдер для сброса незавершённых спанов
Провайдер.Закрыть();

Смотрите также ​