Трассировка
Трассировка позволяет отслеживать цепочку вызовов внутри и между сервисами. Каждая единица работы называется спаном (span). Спаны образуют дерево, корнем которого является корневой спан (root span), а все остальные — дочерние.
Архитектура
ОтелПровайдерТрассировки
└── ОтелТрассировщик
└── ОтелСпан- ОтелПровайдерТрассировки — точка входа, создаёт трассировщики.
- ОтелТрассировщик — создаёт спаны.
- ОтелСпан — единица работы: хранит атрибуты, события, статус, временны́е метки.
Создание провайдера через построитель
ОтелПостроительПровайдераТрассировки предоставляет Fluent API для настройки провайдера.
#Использовать opentelemetry
Ресурс = ОтелРесурсы.ПоУмолчанию();
Ресурс.Атрибуты().Установить("service.name", "my-service");
Транспорт = Новый ОтелHttpТранспорт("http://localhost:4318");
Экспортер = Новый ОтелЭкспортерСпанов(Транспорт);
Базовый = Новый ОтелБазовыйПакетныйПроцессор(Экспортер, "traces");
Процессор = Новый ПостроительНаследника(Новый ОтелПакетныйПроцессорСпанов(), Базовый).Построить();
Процессор.ЗапуститьФоновыйЭкспорт();
Провайдер = Новый ОтелПостроительПровайдераТрассировки()
.УстановитьРесурс(Ресурс)
.ДобавитьПроцессор(Процессор)
.Построить();
Трассировщик = Провайдер.ПолучитьТрассировщик("my-library", "1.0.0");Создание спанов
Простой спан
// SpanKind задаётся при создании и неизменен (immutable)
Спан = Трассировщик.НачатьСпан("обработка-запроса", ОтелВидСпана.Сервер());
Область = Спан.СделатьТекущим();
// ... работа ...
Область.Закрыть();
Спан.Завершить();Дочерний спан
НачатьСпан автоматически подхватывает родителя из текущего контекста:
// Родительский спан
Спан = Трассировщик.НачатьСпан("операция", ОтелВидСпана.Сервер());
Область = Спан.СделатьТекущим();
// Дочерний спан — НачатьСпан видит текущий спан как родителя
ДочернийСпан = Трассировщик.НачатьСпан("запрос-к-бд", ОтелВидСпана.Клиент());
ОбластьДочернего = ДочернийСпан.СделатьТекущим();
// ... работа ...
ОбластьДочернего.Закрыть();
ДочернийСпан.Завершить();
Область.Закрыть();
Спан.Завершить();Дочерний спан от явного родителя
// НачатьДочернийСпан принимает контекст (Context) родителя явно
Пропагаторы = ОтелГлобальный.ПолучитьПропагаторы();
НовыйКонтекст = Пропагаторы.Извлечь(ОтелКонтекст.Текущий(), ВходящиеЗаголовки);
Спан = Трассировщик.НачатьДочернийСпан("обработка", НовыйКонтекст, ОтелВидСпана.Сервер());
// Спан-родитель передается в контексте
КонтекстРодителя = ОтелКонтекст.КонтекстСоСпаном(ОтелКонтекст.Текущий(), Спан);
ШагОбработки = Трассировщик.НачатьДочернийСпан("шаг", КонтекстРодителя);Виды спанов (SpanKind)
ОтелВидСпана описывает роль спана в распределённой системе:
| Метод | Описание |
|---|---|
ОтелВидСпана.Внутренний() | Внутренняя операция (по умолчанию) |
ОтелВидСпана.Сервер() | Входящий RPC/HTTP-запрос |
ОтелВидСпана.Клиент() | Исходящий RPC/HTTP-запрос |
ОтелВидСпана.Производитель() | Отправка сообщения в очередь |
ОтелВидСпана.Потребитель() | Получение сообщения из очереди |
Атрибуты
Атрибуты — пары ключ/значение, описывающие контекст операции:
Спан.УстановитьАтрибут("http.method", "GET");
Спан.УстановитьАтрибут("http.url", "/api/data");
Спан.УстановитьАтрибут("http.status_code", 200);
Спан.УстановитьАтрибут("db.rows_affected", 42);События спана
События — именованные точки во времени с атрибутами:
// Простое событие
Спан.ДобавитьСобытие("cache-miss");
// Событие с атрибутами
Атрибуты = Новый ОтелАтрибуты();
Атрибуты.Установить("cache.key", "user:42");
Спан.ДобавитьСобытие("cache-miss", Атрибуты);
// Событие, которое уже произошло: момент засечен раньше
МоментПромаха = ТекущаяУниверсальнаяДатаВМиллисекундах();
// ...
Спан.ДобавитьСобытие("cache-miss", , ОтелУтилиты.МиллисекундыВНаносекунды(МоментПромаха));Время событий и спана — строка с числом наносекунд от начала эпохи Unix, как в OTLP. Строку для своего времени дают функции ОтелУтилиты.
Исключение записывает ЗаписатьИсключение: событие exception с описанием ошибки, модулем и стеком вызовов. Тип ошибки (exception.type) — код библиотеки errors, а у нативной ошибки OneScript без кода — код native-errors, например filesystem.resource.not_found. Статус спана метод не меняет:
Попытка
ПрочитатьНастройки();
Исключение
Спан.ЗаписатьИсключение(ИнформацияОбОшибке());
Спан.УстановитьСтатус(ОтелКодСтатуса.Ошибка(), ОписаниеОшибки());
КонецПопытки;Линки (Links)
Линки связывают спан с другим контекстом (например, асинхронные операции):
КонтекстСпана = ДругойСпан.КонтекстСпана();
Линки = Новый Массив();
Линки.Добавить(Новый ОтелЛинк(КонтекстСпана));
Спан = Трассировщик.НачатьСпан("async-операция", ОтелВидСпана.Внутренний(), , Линки);Статус спана
ОтелКодСтатуса описывает результат операции:
| Метод | Описание |
|---|---|
ОтелКодСтатуса.НеУстановлен() | Статус не задан (по умолчанию) |
ОтелКодСтатуса.Ок() | Операция завершилась успешно |
ОтелКодСтатуса.Ошибка() | Операция завершилась с ошибкой |
// Успешное завершение
Спан.УстановитьСтатус(ОтелКодСтатуса.Ок());
// Ошибка с описанием
Спан.УстановитьСтатус(ОтелКодСтатуса.Ошибка(), "Таймаут подключения к БД");Семплирование
Сэмплер решает, какие спаны записываются и экспортируются. Встроенные сэмплеры создаёт модуль ОтелСэмплер; сэмплер задаётся при создании провайдера или через OTEL_TRACES_SAMPLER. Без настройки используется ParentBased(root=AlwaysOn): корневые спаны сэмплируются все, дочерние наследуют решение родителя.
Провайдер = Новый ОтелПостроительПровайдераТрассировки()
.УстановитьСэмплер(ОтелСэмплер.ВсегдаВключен())
.Построить();// Корневые спаны - 10% трассировок, дочерние наследуют решение родителя
Провайдер = Новый ОтелПостроительПровайдераТрассировки()
.УстановитьСэмплер(ОтелСэмплер.НаОсновеРодителя(ОтелСэмплер.ПоДолеТрассировок(0.1)))
.Построить();# 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: процессоры получают и спаны, отброшенные Корневым, экспортер — только сэмплированные |
Сэмплеры комбинируются, а свой сэмплер — объект с методами ДолженСэмплировать и Описание, см. ИнтерфейсСэмплер.
Процессоры спанов
Простой процессор (для разработки)
Синхронно экспортирует каждый спан сразу после завершения. Подходит для отладки.
Процессор = Новый ОтелПростойПроцессорСпанов(Экспортер);Пакетный процессор (для продакшена)
Накапливает спаны в очереди и отправляет пакетами в фоновом потоке.
Процессор собирается через ПостроительНаследника из ОтелБазовыйПакетныйПроцессор. Базовому процессору передаются экспортер и имя сигнала. Необязательные параметры: размер очереди, размер пакета, интервал экспорта и таймаут экспорта в миллисекундах. Фоновое задание экспорта запускается с первым спаном и отправляет пакет, как только он заполнен или истёк интервал; завершение спана не ждёт отправку. ЗапуститьФоновыйЭкспорт запускает задание заранее (необязательно).
Базовый = Новый ОтелБазовыйПакетныйПроцессор(Экспортер, "traces");
Процессор = Новый ПостроительНаследника(Новый ОтелПакетныйПроцессорСпанов(), Базовый).Построить();
Процессор.ЗапуститьФоновыйЭкспорт();При автоконфигурации параметры задаются переменными окружения:
| Переменная | По умолчанию | Описание |
|---|---|---|
OTEL_BSP_MAX_QUEUE_SIZE | 2048 | Максимальный размер очереди |
OTEL_BSP_SCHEDULE_DELAY | 5000 | Интервал экспорта (мс) |
OTEL_BSP_MAX_EXPORT_BATCH_SIZE | 512 | Максимальный размер пакета |
OTEL_BSP_EXPORT_TIMEOUT | 30000 | Таймаут экспорта (мс) |
Композитный процессор
Объединяет несколько процессоров для одновременной отправки в разные системы:
// ПроцессорGrafana и ПроцессорJaeger — пакетные процессоры, собранные как показано выше
Процессоры = Новый Массив();
Процессоры.Добавить(ПроцессорGrafana);
Процессоры.Добавить(ПроцессорJaeger);
Процессор = Новый ОтелКомпозитныйПроцессорСпанов(Процессоры);Лимиты спана
ОтелЛимитыСпана ограничивают количество атрибутов, событий и ссылок:
Лимиты = Новый ОтелЛимитыСпана()
.УстановитьМаксАтрибутов(64)
.УстановитьМаксСобытий(32);
Провайдер = Новый ОтелПостроительПровайдераТрассировки()
.УстановитьЛимитыСпана(Лимиты)
.Построить();При автоконфигурации лимиты задаются переменными окружения:
| Переменная | По умолчанию | Описание |
|---|---|---|
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT | 128 | Максимальное число атрибутов спана |
OTEL_SPAN_EVENT_COUNT_LIMIT | 128 | Максимальное число событий |
OTEL_SPAN_LINK_COUNT_LIMIT | 128 | Максимальное число ссылок |
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT | 128 | Максимальное число атрибутов события |
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT | 128 | Максимальное число атрибутов ссылки |
Завершение работы
// Обязательно закрывайте провайдер для сброса незавершённых спанов
Провайдер.Закрыть();Смотрите также
- Автоконфигурация — настройка через env vars
- Экспорт — транспорты и экспортеры
- Пропагация — передача контекста между сервисами
- API: ОтелСпан
- API: ОтелТрассировщик
- API: ОтелПровайдерТрассировки
