Записуйте кожен запуск як спан
PHPСпан, який SDK пише на кожен запуск і кожне очікування, — його поля, куди він іде і як потрапляє у ваші логи чи трасування.
6 хв читання
Спан — запис про один запуск так, як його бачить інструмент трасування: виклик, чим він закінчився, час, ідентифікатори й гроші — але ніколи промпт чи відповідь моделі. SDK пише по одному спану на кожен запуск і кожне очікування й передає його записувачеві, якого ви йому дали. Без записувача спани нікуди не пишуться.
Дати SDK записувач
Записувач нижче пише кожен спан у лог. Логер тут стоїть замість логера вашого застосунку.
use Psr\Log\AbstractLogger;
use Slen\Sdk\Observability\Psr3SpanRecorder;
// Stands in for the PSR-3 logger of your application: it prints the fields this page talks about.
$logger = new class extends AbstractLogger {
public function log($level, string|\Stringable $message, array $context = []): void
{
echo "{$message}: {$context['operation']} {$context['outcome']} {$context['status']} {$context['operation_code']} {$context['execution_id']} {$context['estimated']}", PHP_EOL;
}
};
$recorder = new Psr3SpanRecorder($logger);use Slen\Sdk\SlenConfig;
use Slen\Sdk\SlenSdk;
$slen = SlenSdk::create(new SlenConfig(
apiKey: (string) getenv('SLEN_API_KEY'),
spanRecorder: $recorder,
));use Slen\Sdk\Modules\Operations\Options\ExecuteOptions;
$execution = $slen->operations()->execute(
'generate_email',
['messages' => [['role' => 'user', 'content' => 'Write a short follow-up email to Ada.']]],
new ExecuteOptions(idempotencyKey: 'order-61:email'),
);Запуск пише один спан, і логер друкує:
slen.span: operations.execute ok completed generate_email 74438200973594710 0.00061590SDK має такі записувачі:
Psr3SpanRecorderпише спан повідомленнямslen.spanз його полями в контексті через логер PSR-3, на рівніinfo, якщо ви не назвали інший. Невідомий рівень відхиляється зInvalidArgumentExceptionпід час збирання записувача.RecordingSpanRecorderтримає спани в пам’яті, для тесту.
Щоб надсилати спани деінде, реалізуйте SpanRecorderInterface: він має один метод, record(). У застосунку на Laravel записувач вибирає файл конфігурації SDK — Інтеграція з Laravel.
Записувача викликають, поки ваш виклик ще повертається до вас. Те, що він кидає, ковтається: зламаний записувач ніколи не ламає запуск. Тримайте record() швидким і надсилайте спани далі у фоні.
Поля спану
Спан має десять полів, однакових в обох SDK і в цьому порядку. Рядок логу й спан OpenTelemetry несуть їх як атрибути, під іменами другої колонки, і пропускають поле, що дорівнює null.
| Поле | Атрибут | Що в ньому |
|---|---|---|
operation | operation | Ім’я спану — нижче |
outcome | outcome | ok, refused або error — нижче |
status | status | Статус виконання або null, коли виклик кинув помилку |
durationMs | duration_ms | Час виклику в мілісекундах, разом із повторами SDK; для очікування — усе очікування |
executionId | execution_id | Виконання |
operationCode | operation_code | Код операції, який назвав запуск; для очікування — той, який називає його відповідь |
estimated | estimated | Скільки, за оцінкою, коштували виклики моделей у запуску, десятковим рядком |
actual | actual | Скільки вони коштували, десятковим рядком, коли це відомо |
errorCode | error_code | Код помилки або виконання, яке не завершилося |
cacheHit | cache_hit | Для виконання, яке має відповідь, — чи взято її з кешу. false зберігається як значення |
[
{ "operation": "operations.execute", "outcome": "ok", "status": "completed", "duration_ms": 1843, "execution_id": "74438200973594710", "operation_code": "generate_email", "estimated": "0.00061590", "cache_hit": false },
{ "operation": "operations.execute", "outcome": "refused", "duration_ms": 96, "operation_code": "generate_email", "error_code": "budget_exceeded" },
{ "operation": "executions.wait", "outcome": "error", "status": "failed", "duration_ms": 30512, "execution_id": "74438200973594800", "operation_code": "summarize_report", "error_code": "flow_runtime_error", "cache_hit": false }
]Жодне поле не може зберігати те, що сказали моделі. Це правило типу: спан не має поля для повідомлення, промпту чи відповіді.
Чим закінчився запуск
ok— запуск завершився або ще не закінчився. Очікування, якому забракло часу на незакінченому виконанні, тежok.refused— Slen сказав «ні»: будь-який402, політика чи заблокований клієнт, або флоу, який закінчився зі статусомrejected. Це рішення, а не поломка.error— усе інше: мережа, провайдер, неправильний запит, відхилений ключ, ліміт запитів ключа — і виконання, яке закінчилося зі статусомfailedабоuncertain.
Імена спанів
| Ім’я | Хто пише |
|---|---|
operations.execute | operations.execute(): запуск, який чекає на свою відповідь |
operations.execute_async | operations.executeAsync(): запуск, надісланий у фон. Його перша відповідь каже queued, і нічого ще не списано |
executions.wait | executions.wait(): один спан, коли очікування закінчується, скільки б разів воно не читало статус |
Жоден інший виклик спан не пише: ні читання, ні виклик клієнтів, ні вебхук.
Порахувати вартість виконання
Одне виконання може лишити кілька спанів: запуск, кожне очікування на ньому, кожен повтор його ключа ідемпотентності. Кожен із них повторює estimated і actual, які мало виконання, коли спан писався. Якщо їх скласти, вартість вийде завищеною. Беріть вартість один раз на кожен execution_id, з його останнього спану.
Надіслати спани через OTLP
PHP SDK не має експортера OTLP. Реалізуйте SpanRecorderInterface поверх клієнта вашого інструмента трасування або пишіть спани в лог через Psr3SpanRecorder, і нехай їх несе ваш конвеєр логів.
Перед завершенням процесу
Записувачі PHP SDK пишуть кожен спан одразу, тому перед завершенням процесу надсилати нічого.
Що читати далі
Журнал — журнал кожного запиту поруч зі спанами.
Інтеграція з Laravel — спани, записані в лог застосунку на Laravel.
Тестування інтеграції — тестуйте свій код без мережі.