Інструментування

Ручне інструментування для OpenTelemetry PHP

Інструментування — це процес додавання коду спостереження до вашого застосунку.

Якщо ви інструментуєте застосунок, вам потрібно використовувати OpenTelemetry SDK для вашої мови програмування. Потім ви будете використовувати SDK для ініціалізації OpenTelemetry та API для інструментування вашого коду. Це буде генерувати телеметрію з вашого застосунку та будь-якої бібліотеки, яку ви встановили, що також містить інструментування.

Якщо ви інструментуєте бібліотеку, встановіть лише пакет OpenTelemetry API для вашої мови програмування. Ваша бібліотека не буде генерувати телеметрію самостійно. Вона буде генерувати телеметрію лише тоді, коли буде частиною застосунку, що використовує OpenTelemetry SDK. Для отримання додаткової інформації про інструментування бібліотек дивіться Бібліотеки.

Для отримання додаткової інформації про OpenTelemetry API та SDK дивіться специфікацію.

Підготовка демонстраційного застосунку

Ці інструкції використовують модифіковану версію демонстраційного застосунку з Початку роботи, щоб допомогти вам навчитися інструментувати ваш PHP код.

Якщо ви хочете інструментувати свій власний застосунок або бібліотеку, дотримуйтесь інструкцій, щоб адаптувати процес до свого коду.

Залежності

У порожній теці ініціалізуйте мінімальний файл composer.json з наступним вмістом:

composer init \
  --no-interaction \
  --require slim/slim:"^4" \
  --require slim/psr7:"^1" \
  --require monolog/monolog:"^3"
composer update

Створення та запуск HTTP сервера

Щоб підкреслити різницю між інструментуванням бібліотеки та самостійним застосунком, розділіть кидання кубиків на файл бібліотеки, який потім буде імпортований як залежність файлу застосунку.

Створіть файл бібліотеки з назвою dice.php і додайте до нього наступний код:

<?php
class Dice {

    private $tracer;

    function __construct() {
    }

    public function roll($rolls) {
        $result = [];
        for ($i = 0; $i < $rolls; $i++) {
            $result[] = $this->rollOnce();
        }
        return $result;
    }

    private function rollOnce() {
      $result = random_int(1, 6);
      return $result;
    }
}

Створіть файл застосунку з назвою index.php і додайте до нього наступний код:

<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LogLevel;
use Slim\Factory\AppFactory;
use Monolog\Logger;
use Monolog\Level;
use Monolog\Handler\StreamHandler;

require __DIR__ . '/vendor/autoload.php';

require('dice.php');

$logger = new Logger('dice-server');
$logger->pushHandler(new StreamHandler('php://stdout', Level::INFO));

$app = AppFactory::create();

$dice = new Dice();

$app->get('/rolldice', function (Request $request, Response $response) use ($logger, $dice) {
    $params = $request->getQueryParams();
    if(isset($params['rolls'])) {
        $result = $dice->roll($params['rolls']);
        if (isset($params['player'])) {
          $logger->info("Гравець кидає кубики.", ['player' => $params['player'], 'result' => $result]);
        } else {
          $logger->info("Анонімний гравець кидає кубики.", ['result' => $result]);
        }
        $response->getBody()->write(json_encode($result));
    } else {
        $response->withStatus(400)->getBody()->write("Будь ласка, введіть кількість кидків");
    }
    return $response;
});

$app->run();

Щоб переконатися, що все працює, запустіть застосунок за допомогою наступної команди та відкрийте http://localhost:8080/rolldice?rolls=12 у вашому вебоглядачі.

php -S localhost:8080

Налаштування інструментування

Залежності

Встановіть пакунки OpenTelemetry API:

composer require open-telemetry/api open-telemetry/sem-conv

Ініціалізація SDK

Щоб використовувати OpenTelemetry SDK для PHP, вам потрібні пакунки, які задовольняють залежності для psr/http-client-implementation та psr/http-factory-implementation. Тут ми будемо використовувати Guzzle, який забезпечує обидва:

composer require guzzlehttp/guzzle

Тепер ви можете встановити OpenTelemetry SDK та OTLP експортер:

composer require \
  open-telemetry/sdk \
  open-telemetry/exporter-otlp

Якщо ви розробник застосунків, вам потрібно налаштувати екземпляр OpenTelemetry SDK якомога раніше у вашому застосунку. Тут ми будемо використовувати метод Sdk::builder(), і ми глобально зареєструємо провайдерів.

Ви можете створити провайдерів, використовуючи методи TracerProvider::builder(), LoggerProvider::builder(), та MeterProvider::builder().

У випадку демонстраційного застосунку, створіть файл з назвою instrumentation.php з наступним вмістом:

<?php

use OpenTelemetry\API\Globals;
use OpenTelemetry\API\Logs\EventLogger;
use OpenTelemetry\API\Logs\LogRecord;
use OpenTelemetry\API\Trace\Propagation\TraceContextPropagator;
use OpenTelemetry\Contrib\Otlp\LogsExporter;
use OpenTelemetry\Contrib\Otlp\MetricExporter;
use OpenTelemetry\Contrib\Otlp\SpanExporter;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory;
use OpenTelemetry\SDK\Logs\LoggerProvider;
use OpenTelemetry\SDK\Logs\Processor\SimpleLogRecordProcessor;
use OpenTelemetry\SDK\Metrics\MeterProvider;
use OpenTelemetry\SDK\Metrics\MetricReader\ExportingReader;
use OpenTelemetry\SDK\Resource\ResourceInfo;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
use OpenTelemetry\SDK\Sdk;
use OpenTelemetry\SDK\Trace\Sampler\AlwaysOnSampler;
use OpenTelemetry\SDK\Trace\Sampler\ParentBased;
use OpenTelemetry\SDK\Trace\SpanProcessor\SimpleSpanProcessor;
use OpenTelemetry\SDK\Trace\TracerProvider;
use OpenTelemetry\SemConv\Attributes\ServiceAttributes;
use OpenTelemetry\SemConv\Incubating\Attributes\DeploymentIncubatingAttributes;
use OpenTelemetry\SemConv\Incubating\Attributes\ServiceIncubatingAttributes;

require 'vendor/autoload.php';

$resource = ResourceInfoFactory::emptyResource()->merge(ResourceInfo::create(Attributes::create([
    ServiceIncubatingAttributes::SERVICE_NAMESPACE => 'demo',
    ServiceAttributes::SERVICE_NAME => 'test-application',
    ServiceAttributes::SERVICE_VERSION => '0.1',
    DeploymentIncubatingAttributes::DEPLOYMENT_ENVIRONMENT_NAME => 'development',
])));
$spanExporter = new SpanExporter(
    (new StreamTransportFactory())->create('php://stdout', 'application/json')
);

$logExporter = new LogsExporter(
    (new StreamTransportFactory())->create('php://stdout', 'application/json')
);

$reader = new ExportingReader(
    new MetricExporter(
        (new StreamTransportFactory())->create('php://stdout', 'application/json')
    )
);

$meterProvider = MeterProvider::builder()
    ->setResource($resource)
    ->addReader($reader)
    ->build();

$tracerProvider = TracerProvider::builder()
    ->addSpanProcessor(
        new SimpleSpanProcessor($spanExporter)
    )
    ->setResource($resource)
    ->setSampler(new ParentBased(new AlwaysOnSampler()))
    ->build();

$loggerProvider = LoggerProvider::builder()
    ->setResource($resource)
    ->addLogRecordProcessor(
        new SimpleLogRecordProcessor($logExporter)
    )
    ->build();

Sdk::builder()
    ->setTracerProvider($tracerProvider)
    ->setMeterProvider($meterProvider)
    ->setLoggerProvider($loggerProvider)
    ->setPropagator(TraceContextPropagator::getInstance())
    ->setAutoShutdown(true)
    ->buildAndRegisterGlobal();

Включіть цей код на початку вашого файлу застосунку index.php:

<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LogLevel;
use Slim\Factory\AppFactory;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

require __DIR__ . '/vendor/autoload.php';

require('dice.php');
require('instrumentation.php');

// ...

Для налагодження та локальної розробки, приклад експортує телеметрію в консоль. Після завершення налаштування ручного інструментування, вам потрібно налаштувати відповідний експортер для експорту телеметричних даних застосунку до одного або більше бекендів телеметрії.

Приклад також налаштовує обовʼязковий стандартний атрибут SDK service.name, який містить логічну назву сервісу, та необовʼязковий, але дуже рекомендований атрибут service.version, який містить версію API або реалізації сервісу.

Існують альтернативні методи налаштування атрибутів ресурсу. Для більш детальної інформації дивіться Ресурси.

Глобальні провайдери

Протягом наступних прикладів ми зазвичай будемо отримувати глобально зареєстрованих провайдерів через OpenTelemetry\API\Globals:

$tracerProvider = \OpenTelemetry\API\Globals::tracerProvider();
$meterProvider = \OpenTelemetry\API\Globals::meterProvider();
$loggerProvider = \OpenTelemetry\API\Globals::loggerProvider();

Завершення роботи

Важливо, щоб метод shutdown() кожного провайдера виконувався при завершенні процесу PHP, щоб забезпечити скидання будь-якої черги телеметрії. У наведеному вище прикладі це було враховано за допомогою setAutoShutdown(true).

Ви також можете використовувати ShutdownHandler, щоб зареєструвати функцію завершення роботи кожного провайдера як частину процесу завершення роботи PHP:

\OpenTelemetry\SDK\Common\Util\ShutdownHandler::register([$tracerProvider, 'shutdown']);
\OpenTelemetry\SDK\Common\Util\ShutdownHandler::register([$meterProvider, 'shutdown']);
\OpenTelemetry\SDK\Common\Util\ShutdownHandler::register([$loggerProvider, 'shutdown']);

Трейси

Ініціалізація трасування

Щоб увімкнути трасування у вашому застосунку, вам потрібно мати ініціалізований TracerProvider, який дозволить вам створювати Tracer.

Якщо TracerProvider не створено, API OpenTelemetry для трасування будуть використовувати реалізацію no-op і не генеруватимуть дані.

Якщо ви дотримувалися інструкцій щодо ініціалізації SDK вище, у вас вже налаштований TracerProvider. Ви можете продовжити з отриманням трейсера.

Отримання трейсера

Будь-де у вашому застосунку, де ви пишете код ручного трасування, слід викликати getTracer, щоб отримати трейсер. Наприклад:

$tracerProvider = Globals::tracerProvider();
$tracer = $tracerProvider->getTracer(
  'instrumentation-scope-name', //назва (обовʼязково)
  'instrumentation-scope-version', //версія
  'http://example.com/my-schema', //URL схеми
  ['foo' => 'bar'] //атрибути
);

Значення instrumentation-scope-name та instrumentation-scope-version повинні унікально ідентифікувати Область інструментування, таку як пакунок, модуль або назва класу. Хоча назва є обовʼязковою, версія все ще рекомендована, попри те, що є необовʼязковою.

Зазвичай рекомендується викликати getTracer у вашому застосунку, коли це потрібно, а не експортувати екземпляр tracer до решти вашого застосунку. Це допомагає уникнути складніших проблем із завантаженням застосунку, коли залучені інші необхідні залежності.

У випадку демонстраційного застосунку, є два місця, де можна отримати трейсер з відповідною Областю Інструментування:

По-перше, у файлі застосунку index.php:

<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Log\LogLevel;
use Slim\Factory\AppFactory;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use OpenTelemetry\API\Globals;

require __DIR__ . '/vendor/autoload.php';

require('dice.php');
require('instrumentation.php');

$tracerProvider = Globals::tracerProvider();
$tracer = $tracerProvider->getTracer(
  'dice-server',
  '0.1.0',
  'https://opentelemetry.io/schemas/1.24.0'
);

$logger = new Logger('dice-server');
$logger->pushHandler(new StreamHandler('php://stdout', Logger::INFO));

$app = AppFactory::create();

$dice = new Dice();

$app->get('/rolldice', function (Request $request, Response $response) use ($logger, $dice, $tracer) {
// ...
}

І по-друге, у файлі бібліотеки dice.php:

<?php
use OpenTelemetry\API\Globals;
use OpenTelemetry\SemConv\TraceAttributes;

class Dice {

    private $tracer;

    function __construct() {
        $tracerProvider = Globals::tracerProvider();
        $this->tracer = $tracerProvider->getTracer(
          'dice-lib',
          '0.1.0',
          'https://opentelemetry.io/schemas/1.24.0'
        );
    }

    public function roll($rolls) {
        $result = [];
        for ($i = 0; $i < $rolls; $i++) {
            $result[] = $this->rollOnce();
        }
        return $result;
    }

    private function rollOnce() {
      $result = random_int(1, 6);
      return $result;
    }
}

Створення відрізків

Тепер, коли у вас є ініціалізовані трейсери, ви можете створювати відрізки.

Код нижче ілюструє, як створити відрізок.

<?php
public function roll($rolls) {
    $span = $this->tracer->spanBuilder("rollTheDice")->startSpan();
    $result = [];
    for ($i = 0; $i < $rolls; $i++) {
        $result[] = $this->rollOnce();
    }
    $span->end();
    return $result;
}

Зверніть увагу, що потрібно викликати end() для відрізка, інакше він не буде експортований.

Якщо ви дотримувалися інструкцій, використовуючи прикладний застосунок до цього моменту, ви можете скопіювати код вище у ваш файл бібліотеки dice.php. Ви тепер повинні бачити відрізки, що генеруються вашим застосунком.

Запустіть ваш застосунок наступним чином, а потім надсилайте йому запити, відвідуючи http://localhost:8080/rolldice?rolls=12 за допомогою вашого оглядача або curl.

php -S 8080 localhost

Через деякий час ви повинні побачити відрізки, надруковані в консолі SpanExporter, щось на кшталт цього:

{
  "resourceSpans": [
    {
      "resource": {
        "attributes": [
          {
            "key": "service.namespace",
            "value": {
              "stringValue": "demo"
            }
          },
          {
            "key": "service.name",
            "value": {
              "stringValue": "test-application"
            }
          },
          {
            "key": "service.version",
            "value": {
              "stringValue": "0.1"
            }
          },
          {
            "key": "deployment.environment",
            "value": {
              "stringValue": "development"
            }
          }
        ]
      },
      "scopeSpans": [
        {
          "scope": {
            "name": "dice-lib",
            "version": "0.1.0"
          },
          "spans": [
            {
              "traceId": "007a1e7a89f21f98b600d288b7d65390",
              "spanId": "c32797fc72c252d2",
              "flags": 1,
              "name": "rollTheDice",
              "kind": 1,
              "startTimeUnixNano": "1706111239077485365",
              "endTimeUnixNano": "1706111239077735657",
              "status": {}
            }
          ],
          "schemaUrl": "https://opentelemetry.io/schemas/1.24.0"
        }
      ]
    }
  ]
}

Створення вкладених відрізків

Вкладені відрізки дозволяють відстежувати роботу, яка має вкладену природу. Наприклад, функція rollOnce() нижче представляє вкладену операцію. Наступний приклад створює вкладений відрізок, який відстежує rollOnce():

private function rollOnce() {
    $parent = OpenTelemetry\API\Trace\Span::getCurrent();
    $scope = $parent->activate();
    try {
        $span = $this->tracer->spanBuilder("rollTheDice")->startSpan();
        $result = random_int(1, 6);
        $span->end();
    } finally {
        $scope->detach();
    }
    return $result;
}

Ви повинні зробити detach для активної області, якщо ви її активували.

Отримання поточного відрізка

У наведеному вище прикладі ми отримали поточний відрізок, використовуючи наступний метод:

$span = OpenTelemetry\API\Trace\Span::getCurrent();

Отримання відрізка з контексту

Також може бути корисно отримати відрізок з даного контексту, який не обовʼязково є активним відрізком.

$span = OpenTelemetry\API\Trace\Span::fromContext($context);

Атрибути відрізка

Атрибути дозволяють вам прикріплювати пари ключ/значення до Span, щоб він містив більше інформації про поточну операцію, яку він відстежує.

private function rollOnce() {
    $parent = OpenTelemetry\API\Trace\Span::getCurrent();
    $scope = $parent->activate();
    try {
        $span = $this->tracer->spanBuilder("rollTheDice")->startSpan();
        $result = random_int(1, 6);
        $span->setAttribute('dicelib.rolled', $result);
        $span->end();
    } finally {
        $scope->detach();
    }
    return $result;
}

Семантичні атрибути

Існують семантичні домовленості для відрізків, що представляють операції у відомих протоколах, таких як HTTP або виклики бази даних. Семантичні домовленості для цих відрізків визначені у специфікації на Семантичні домовленості трасування. У простому прикладі цього посібника можна використовувати атрибути вихідного коду.

Спочатку додайте семантичні домовленості як залежність до вашого застосунку:

composer require open-telemetry/sem-conv

Додайте наступне на початку вашого файлу:

use OpenTelemetry\SemConv\Attributes\CodeAttributes;

Нарешті, ви можете оновити ваш файл, щоб включити семантичні атрибути:

$span->setAttribute(CodeAttributes::CODE_FUNCTION_NAME, 'rollOnce');
$span->setAttribute(CodeAttributes::CODE_FILE_PATH, __FILE__);

Створення відрізків з подіями

Відрізки можуть бути анотовані з іменованими подіями (називаються Події відрізка), які можуть містити нуль або більше Атрибути відрізка, кожен з яких сам по собі є парою ключ:значення, автоматично поєднаною з часовою міткою.

$span->addEvent("Init");
...
$span->addEvent("End");
$eventAttributes = Attributes::create([
    "operation" => "calculate-pi",
    "result" => 3.14159,
]);
$span->addEvent("End Computation", $eventAttributes);

Відрізок може бути повʼязаний з нуль або більше іншими відрізками, які є повʼязаними через Посилання відрізка. Посилання можуть бути використані для представлення пакетних операцій, де Відрізок був ініційований кількома ініціюючими Відрізками, кожен з яких представляє один вхідний елемент, що обробляється в пакеті.

$span = $tracer->spanBuilder("span-with-links")
    ->addLink($parentSpan1->getContext())
    ->addLink($parentSpan2->getContext())
    ->addLink($parentSpan3->getContext())
    ->addLink($remoteSpanContext)
    ->startSpan();

Для більш детальної інформації про те, як читати контекст з віддалених процесів, дивіться Поширення контексту.

Встановлення статусу відрізка та запис помилок

Статус можна встановити на Відрізок, зазвичай використовується для вказівки, що Відрізок не завершився успішно — Error. Стандартно, всі відрізки мають статус Unset, що означає, що відрізок завершився без помилок. Статус Ok зарезервований для випадків, коли потрібно явно позначити відрізок як успішний, а не залишати його зі статусом Unset (тобто “без помилок”).

Статус можна встановити в будь-який час до завершення відрізка.

Може бути гарною ідеєю записувати помилки, коли вони трапляються. Рекомендується робити це разом з встановленням статусу відрізка.

Статус можна встановити в будь-який час до завершення відрізка:

$span = $tracer->spanBuilder("my-span")->startSpan();
try {
  // зробити щось, що може зазнати невдачі
  throw new \Exception('uh-oh');
} catch (\Throwable $t) {
  $span->setStatus(\OpenTelemetry\API\Trace\StatusCode::STATUS_ERROR, "Щось пішло не так!");
  // Це захопить такі речі, як поточний стек викликів у спані.
  $span->recordException($t, ['exception.escaped' => true]);
  throw $t;
} finally {
  $span->end();
}

Процесор відрізків

OpenTelemetry пропонує різні процесори відрізків. SimpleSpanProcessor негайно пересилає завершені відрізки до експортера, тоді як BatchSpanProcessor групує їх і надсилає періодично.

$tracerProvider = TracerProvider::builder()
  ->addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporterFactory()->create()))
  ->build();

Транспорти

Усі експортери вимагають Transport, який відповідає за надсилання телеметричних даних:

  • PsrTransport — використовує клієнт PSR-18 для надсилання даних через HTTP
  • StreamTransport — використовує потік для надсилання даних (наприклад, до файлу або stdout)
  • GrpcTransport — використовує gRPC для надсилання даних, закодованих у protobuf

Експортер

Дивіться Експортери

Метрики

OpenTelemetry можна використовувати для вимірювання та запису різних типів метрик з застосунку, які потім можуть бути відправлені до сервісу метрик, такого як OpenTelemetry Collector:

  • counter
  • async counter
  • histogram
  • async gauge
  • up/down counter
  • async up/down counter

Типи вимірювачів та їх використання пояснюються у концепції метрик документації.

Налаштування

Спочатку створіть MeterProvider:

<?php

use OpenTelemetry\SDK\Metrics\MetricExporter\ConsoleMetricExporterFactory;
use OpenTelemetry\SDK\Metrics\MeterProvider;
use OpenTelemetry\SDK\Metrics\MetricReader\ExportingReader;

require 'vendor/autoload.php';

$reader = new ExportingReader((new ConsoleMetricExporterFactory())->create());

$meterProvider = MeterProvider::builder()
    ->addReader($reader)
    ->build();

Синхронні вимірювачі

Синхронний вимірювач повинен бути вручну налаштований при зміні даних:

$up_down = $meterProvider
    ->getMeter('demo_meter')
    ->createUpDownCounter('queued', 'jobs', 'Кількість завдань у черзі');
// завдання надходять
$up_down->add(5);
// завдання завершено
$up_down->add(-1);
// надходять ще завдання
$up_down->add(2);

$meterProvider->forceFlush();

Синхронні метрики експортуються, коли викликаються forceFlush() або shutdown() на провайдері вимірювачів.

Переглянути вивід
{
  "resourceMetrics": [
    {
      "resource": {},
      "scopeMetrics": [
        {
          "scope": { "name": "demo_meter" },
          "metrics": [
            {
              "name": "queued",
              "description": "Кількість завдань у черзі",
              "unit": "jobs",
              "sum": {
                "dataPoints": [
                  {
                    "startTimeUnixNano": "1687332126443709851",
                    "timeUnixNano": "1687332126445544432",
                    "asInt": "6"
                  }
                ],
                "aggregationTemporality": "AGGREGATION_TEMPORALITY_DELTA"
              }
            }
          ]
        }
      ]
    }
  ]
}

Асинхронні вимірювачі

Асинхронні вимірювачі є observable, наприклад, ObservableGauge. При реєстрації асинхронного вимірювача, ви надаєте одну або більше функцій зворотного виклику. Функції зворотного виклику будуть викликатися читачем метрик коли його метод collect() викликається, наприклад, на основі таймера циклу подій. Функції зворотного виклику відповідають за повернення поточних даних для вимірювача.

У цьому прикладі функції зворотного виклику виконуються кожного разу, коли виконується $reader->collect():

$queue = [
    'job1',
    'job2',
    'job3',
];
$reader = $meterProvider
    ->getMeter('demo_meter')
    ->createObservableGauge('queued', 'jobs', 'Кількість завдань у черзі')
    ->observe(static function (ObserverInterface $observer) use (&$queue): void {
        $observer->observe(count($queue));
    });
$reader->collect();
array_pop($queue);
$reader->collect();
Переглянути вивід
{"resourceMetrics":[{"resource":{},"scopeMetrics":[{"scope":{"name":"demo_meter"},"metrics":[{"name":"queued","description":"Кількість завдань у черзі","unit":"jobs","gauge":{"dataPoints":[{"startTimeUnixNano":"1687331630161510994","timeUnixNano":"1687331630162989144","asInt":"3"}]}}]}]}]}
{"resourceMetrics":[{"resource":{},"scopeMetrics":[{"scope":{"name":"demo_meter"},"metrics":[{"name":"queued","description":"Кількість завдань у черзі","unit":"jobs","gauge":{"dataPoints":[{"startTimeUnixNano":"1687331630161510994","timeUnixNano":"1687331631164759171","asInt":"2"}]}}]}]}]}

Логи

Оскільки логування є зрілою та добре встановленою функцією, підхід OpenTelemetry трохи відрізняється для цього сигналу.

Логер OpenTelemetry не призначений для використання безпосередньо, а скоріше для інтеграції в наявні бібліотеки логування. Таким чином, ви можете вибрати, щоб деякі або всі ваші логи застосунку надсилалися до сервісу, сумісного з OpenTelemetry, такого як колектор.

Налаштування

Спочатку ми створюємо LoggerProvider:

<?php

use OpenTelemetry\API\Logs\EventLogger;
use OpenTelemetry\API\Logs\LogRecord;
use OpenTelemetry\Contrib\Otlp\LogsExporter;
use OpenTelemetry\SDK\Common\Export\Stream\StreamTransportFactory;
use OpenTelemetry\SDK\Logs\LoggerProvider;
use OpenTelemetry\SDK\Logs\Processor\SimpleLogRecordProcessor;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;

require 'vendor/autoload.php';

$exporter = new LogsExporter(
    (new StreamTransportFactory())->create('php://stdout', 'application/json')
);

$loggerProvider = LoggerProvider::builder()
    ->addLogRecordProcessor(new SimpleLogRecordProcessor($exporter))
    ->setResource(ResourceInfoFactory::emptyResource())
    ->build();

Логування подій

EventLogger може використовувати Logger для генерування лог подій:

$logger = $loggerProvider->getLogger('demo', '1.0', 'http://schema.url', [/*attributes*/]);
$eventLogger = new EventLogger($logger, 'my-domain');
$record = (new LogRecord('hello world'))
    ->setSeverityText('INFO')
    ->setAttributes([/*attributes*/]);

$eventLogger->logEvent('foo', $record);
Переглянути приклад виводу
{
  "resourceLogs": [
    {
      "resource": {},
      "scopeLogs": [
        {
          "scope": {
            "name": "demo",
            "version": "1.0"
          },
          "logRecords": [
            {
              "observedTimeUnixNano": "1687496730010009088",
              "severityText": "INFO",
              "body": {
                "stringValue": "hello world"
              },
              "attributes": [
                {
                  "key": "event.name",
                  "value": {
                    "stringValue": "foo"
                  }
                },
                {
                  "key": "event.domain",
                  "value": {
                    "stringValue": "my-domain"
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Інтеграції для сторонніх бібліотек логування

Monolog

Ви можете використовувати обробник monolog для надсилання логів monolog до приймача, сумісного з OpenTelemetry. Спочатку встановіть бібліотеку monolog та обробник:

composer require \
  monolog/monolog \
  open-telemetry/opentelemetry-logger-monolog

Продовжуючи приклад логування вище:

$handler = new \OpenTelemetry\Contrib\Logs\Monolog\Handler(
    $loggerProvider,
    \Psr\Log\LogLevel::ERROR,
);
$monolog = new \Monolog\Logger('example', [$handler]);

$monolog->info('hello, world');
$monolog->error('oh no', [
    'foo' => 'bar',
    'exception' => new \Exception('something went wrong'),
]);
Переглянути приклад виводу
{
  "resourceLogs": [
    {
      "resource": {},
      "scopeLogs": [
        {
          "scope": {
            "name": "monolog"
          },
          "logRecords": [
            {
              "timeUnixNano": "1687496945597429000",
              "observedTimeUnixNano": "1687496945598242048",
              "severityNumber": "SEVERITY_NUMBER_ERROR",
              "severityText": "ERROR",
              "body": {
                "stringValue": "oh no"
              },
              "attributes": [
                {
                  "key": "channel",
                  "value": {
                    "stringValue": "example"
                  }
                },
                {
                  "key": "context",
                  "value": {
                    "arrayValue": {
                      "values": [
                        {
                          "stringValue": "bar"
                        },
                        {
                          "arrayValue": {
                            "values": [
                              {
                                "stringValue": "Exception"
                              },
                              {
                                "stringValue": "something went wrong"
                              },
                              {
                                "intValue": "0"
                              },
                              {
                                "stringValue": "/usr/src/myapp/logging.php:31"
                              }
                            ]
                          }
                        }
                      ]
                    }
                  }
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Обробка помилок

Стандартно, OpenTelemetry буде логувати помилки та попередження через функцію PHP error_log. Докладність можна контролювати або вимикати за допомогою налаштування OTEL_LOG_LEVEL.

Змінна OTEL_PHP_LOG_DESTINATION може бути використана для контролю місця призначення логів або повного вимкнення логування помилок. Дійсні значення: default, error_log, stderr, stdout, psr3, або none. default (або якщо змінна не встановлена), буде використовувати error_log, якщо не налаштований логер PSR-3:

$logger = new \Example\Psr3Logger(LogLevel::INFO);
\OpenTelemetry\API\LoggerHolder::set($logger);

Для більш тонкого контролю та спеціального оброблення, можна застосовувати власні обробники та фільтри до логера PSR-3 (якщо логер пропонує таку можливість).

Наступні кроки

Вам також потрібно налаштувати відповідний експортер для експорту ваших телеметричних даних до одного або більше бекендів телеметрії.


Востаннє змінено December 26, 2024: [uk] Ukrainian documentation for OpenTelemetry (2a3c5648)