Ручное внедрение OpenTelemetry в PHP-приложение
В этом пошаговом руководстве показано, как добавить возможность наблюдения в ваше PHP-приложение с помощью PHP-библиотек и инструментов OpenTelemetry.
Получение данных для доступа к Ключ-АСТРОМ
Определить базовый URL API
Подробную информацию о сборке базового URL-адреса конечной точки OTLP см. в разделе Экспорт с помощью OTLP. URL-адрес должен заканчиваться на /api/v2/otlp.
Получить токен доступа API
Токен доступа для сбора трассировок, логов и метрик можно создать в разделе Токены доступа.
Экспорт с помощью OTLP содержит более подробную информацию о формате и необходимых областях доступа.
Инструментирование своего приложения
1. Используйте Composer для установки следующих двух зависимостей.
| composer require php-http/guzzle7-adapter
composer require open-telemetry/opentelemetry |
2. Создайте новый файл otel.php и сохраните следующий код.
| <?php
declare(strict_types=1); require __DIR__ . '/vendor/autoload.php'; // ===== OpenTelemetry Imports ===== use Monolog\Handler\StreamHandler; use Monolog\Logger; use OpenTelemetry\Contrib\Otlp\OtlpHttpTransportFactory; use OpenTelemetry\Contrib\Otlp\SpanExporter; use OpenTelemetry\SDK\Sdk; use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor; use OpenTelemetry\SDK\Trace\TracerProvider; use OpenTelemetry\SDK\Resource\ResourceInfoFactory; use OpenTelemetry\SDK\Resource\ResourceInfo; use OpenTelemetry\SDK\Common\Attribute\Attributes; use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator; use OpenTelemetry\SemConv\ResourceAttributes; use OpenTelemetry\SDK\Metrics\MeterProvider; use OpenTelemetry\Contrib\Otlp\MetricExporter; use OpenTelemetry\SDK\Common\Time\ClockFactory; use OpenTelemetry\SDK\Metrics\MetricReader\ExportingReader; use OpenTelemetry\Contrib\Otlp\LogsExporter; use OpenTelemetry\SDK\Logs\LoggerProvider; use OpenTelemetry\SDK\Logs\Processor\SimpleLogRecordProcessor; use OpenTelemetry\Contrib\Logs\Monolog\Handler; use Psr\Log\LogLevel; // ===== GENERAL SETUP ===== $DT_API_URL = ''; $DT_API_TOKEN = ''; $dtMetadata = []; foreach (['/var/lib/astromkey/enrichment/dt_metadata.properties', 'dt_metadata_e617c525669e072eebe3d0f08212e8f2.properties', '/var/lib/astromkey/enrichment/dt_host_metadata.properties'] as $filePath) { try { if (file_exists($filePath)) { $props = str_starts_with($filePath, '/var/') ? parse_ini_file($filePath) : parse_ini_file(trim(file_get_contents($filePath))); $dtMetadata = array_merge($dtMetadata, $props); } } catch (Exception $e) {} } $resource = ResourceInfoFactory::defaultResource()->merge(ResourceInfo::create(Attributes::create([$dtMetadata, ResourceAttributes::SERVICE_NAME => 'php-quickstart']))); // ===== TRACING SETUP ===== $transport = (new OtlpHttpTransportFactory())->create($DT_API_URL . '/v1/traces', 'application/x-protobuf', [ 'Authorization' => 'Api-Token ' . $DT_API_TOKEN ]); $exporter = new SpanExporter($transport); $tracerProvider = new TracerProvider(new SimpleSpanProcessor($exporter), null, $resource); // ===== METRIC SETUP ===== $reader = new ExportingReader( new MetricExporter((new OtlpHttpTransportFactory())->create($DT_API_URL . '/v1/metrics', 'application/x-protobuf', [ 'Authorization' => 'Api-Token ' . $DT_API_TOKEN ])), ClockFactory::getDefault() ); $meterProvider = MeterProvider::builder()->setResource($resource)->addReader($reader)->build(); // ===== LOG SETUP ===== $transport = (new OtlpHttpTransportFactory())->create($DT_API_URL . '/v1/logs', 'application/x-protobuf', [ 'Authorization' => 'Api-Token ' . $DT_API_TOKEN ]); $exporter = new LogsExporter($transport); $loggerProvider = LoggerProvider::builder() ->addLogRecordProcessor(new SimpleLogRecordProcessor($exporter)) ->setResource($resource) ->build(); $handler = new Handler($loggerProvider, LogLevel::INFO); $monolog = new Logger('example', [$handler]); // ===== REGISTRATION ===== Sdk::builder() ->setTracerProvider($tracerProvider) ->setMeterProvider($meterProvider) ->setLoggerProvider($loggerProvider) ->setPropagator(TraceContextPropagator::getInstance()) ->setAutoShutdown(true) ->buildAndRegisterGlobal(); |
Расширение данных Ключ-АСТРОМ
Операции чтения файлов, анализирующие файлы dt_metadata в примере кода, пытаются прочитать файлы данных ЕдиногоАгента, чтобы расширить запрос OTLP и гарантировать, что вся соответствующая информация о топологии доступна в Ключ-АСТРОМ.
3. Настройте переменные $DT_API_URL и $DT_API_TOKEN с otel.php соответствующими значениями.
4. Включите otel.php во все PHP-файлы где необходимо инициализировать OpenTelemetry.
| require('otel.php'); |
Добавление телеметрических сигналов вручную
Создание интервалов
1. Для создания новых интервалов нам сначала нужен объект трассировки.
| $tracer = Globals::tracerProvider()->getTracer('my-tracer'); |
2. Теперь с помощью $tracer, мы можем использовать конструктор интервалов для создания и запуска новых интервалов.
| $span = $tracer->spanBuilder('Call to /myendpoint')->startSpan();
try { $span->setAttribute('http.method', 'GET'); $span->setAttribute('net.protocol.version', '1.1'); // TODO your code goes here } finally { $span->end(); } |
В приведенном выше коде мы:
- Создали новый диапазон и назовите его «Call to /myendpoint».
- Добавили два атрибута, следуя семантическому соглашению об именовании, специфичные для действия этого диапазона: информацию о методе HTTP и версии.
- Добавили
TODOвместо конечной бизнес-логики - Вызвали метод span
end(), чтобы закрыть span (в блокеfinally, чтобы гарантировать вызов метода)
Собор метрик
1. Как и в случае с трассировками, нам необходимо получить объект-счетчик.
| $meterProvider = Globals::meterProvider();
$meter = $meterProvider->getMeter('my-meter'); |
2. С помощью $meter мы теперь можем создавать отдельные инструменты, например, счетчик.
| $requestCounter = $meter->createCounter('request_counter'); |
3. Теперь мы можем вызвать метод add() для записи новых значений $requestCounter с помощью счетчика и сохранения дополнительных атрибутов (например, action.type).
| $requestCounter->add(1, [ 'action.type' => 'create' ]); |
4. Синхронные показатели, такие как счетчик, экспортируются при вызове forceFlush() или shutdown() счетчиков.
| $meterProvider->forceFlush(); |
Подключение логов
Поскольку переменная $monolog уже инициализирована и настроена (см. выше), мы можем напрямую подключаться к настроенной конечной точке OpenTelemetry в Ключ-АСТРОМ.
Обеспечение распространения контекста (необязательно)
Распространение контекста особенно важно, когда задействованы сетевые вызовы (например, REST).
Если вы используете автоматическое инструментирование и ваши сетевые библиотеки соответствуют PSR-15 (извлечение для входящих запросов) и PSR-18 (внедрение для исходящих запросов), распространение контекста будет обрабатываться автоматически. В противном случае ваш код должен это учитывать.
Извлечение контекста при получении запроса
В следующем примере мы предполагаем, что получили HTTP-запрос со встроенной контекстной информацией, которую мы собираемся извлечь для продолжения трассировки.
Для этого сначала создаем объект request с ServerRequestCreator::createFromGlobals().
Затем мы получаем объект TraceContextPropagator и передаём наш объект request методу extract(). Это возвращает объект контекста (на основе информации, предоставленной нам через HTTP-вызов), который мы можем впоследствии использовать для продолжения этой трассировки с нашими собственными интервалами.
| // Create a request object based on PHP's global arrays (for example, $_SERVER)
$request = ServerRequestCreator::createFromGlobals(); // Obtain propagator instance $tracePropagator = TraceContextPropagator::getInstance(); // Extract context information from headers and recreate context $context = $tracePropagator->extract($request->getHeaders()); // Start new span and set received context as parent $span = $tracer->spanBuilder("my-span") ->setParent($context) ->setSpanKind(SpanKind::KIND_SERVER) ->startSpan(); $scope = $span->activate(); try { // TODO your code here } finally { $span->end(); $scope->detach(); } |
Внедрение контекста при отправке запросов
В следующем примере мы используем PHP-библиотеку cURL для отправки HTTP-запроса другому сервису и предоставляем наш существующий контекст как часть HTTP-заголовков нашего запроса.
Для этого мы сначала получаем экземпляр TraceContextPropagator, для которого вызываем метод inject и передаем пустой массив $traceContext. Этот вызов заполняет массив соответствующими данными заголовка ассоциативным образом.
Поскольку для вызова cURL нам нужен простой строковый массив, нам нужно преобразовать его перед передачей в cURL. Для этого $traceContext на следующем шаге мы выполняем цикл и добавляем имена и значения в $contextData.
Теперь мы готовы инициализировать наш экземпляр cURL, передать $contextData и выполнить HTTP-вызов.
| $traceContext = []; $contextData = [];
$tracePropagator = TraceContextPropagator::getInstance(); $tracePropagator->inject($traceContext); // Convert associative array into plain string array foreach ($traceContext as $name => $value) $contextData[] = "$name: $value"; // Initialize cURL $ch = curl_init('[URL]'); // Set propagation headers curl_setopt($ch, CURLOPT_HTTPHEADER, $contextData); // Execute cURL call curl_exec($ch); |
Настройка сбора данных в соответствии с требованиями конфиденциальности (необязательно)
Хотя Ключ-АСТРОМ автоматически собирает все атрибуты OpenTelemetry, в веб-интерфейсе Ключ-АСТРОМ сохраняются и отображаются только значения атрибутов, указанные в списке разрешенных. Это предотвращает случайное сохранение персональных данных, позволяя вам соблюдать требования к конфиденциальности и контролировать объем хранимых данных мониторинга.
Чтобы просматривать пользовательские атрибуты, необходимо сначала разрешить их использование в веб-интерфейсе Ключ-АСТРОМ.
Проверка загрузки данных в Ключ-АСТРОМ
После завершения инструментирования вашего приложения выполните несколько тестовых действий для создания и отправки демонстрационных трассировок, метрик и логов, а также проверьте, что они были правильно загружены в Ключ-АСТРОМ.
Чтобы сделать это для трассировок, перейдите в раздел Трассировки и выберите вкладку Распределенные трассировки. Если вы используете ЕдиныйАгент, выберите PurePaths .
Для просмотра метрик и логов перейдите в раздел Метрики или Логов или Логи и события.