Прочитайте, що SDK пише у ваш лог
PHPДайте SDK логер і прочитайте події, які він пише на кожен запит, — маршрут, статус і час, але ніколи ключ чи тіло.
5 хв читання
SDK веде журнал того, що робить: кожен запит і відповідь на нього, кожен повтор, кожну відмову й збій, читання очікування й вебхуки, які він відхилив. Він пише журнал у логер, який ви йому дали, а без логера — нікуди.
Дати SDK логер
use Psr\Log\AbstractLogger;
// Stands in for the PSR-3 logger of your application: it prints each event with
// the route and, for an answer, its status.
$logger = new class extends AbstractLogger {
public function log($level, string|\Stringable $message, array $context = []): void
{
$status = isset($context['status']) ? ' ' . $context['status'] : '';
echo "{$level} {$message} {$context['method']} {$context['route']}{$status}", PHP_EOL;
}
};use Slen\Sdk\SlenConfig;
use Slen\Sdk\SlenSdk;
$slen = SlenSdk::create(new SlenConfig(
apiKey: (string) getenv('SLEN_API_KEY'),
logger: $logger,
));Параметр logger приймає будь-який логер PSR-3. Кожна подія — повідомлення з контекстом її полів. У застосунку на Laravel SLEN_LOG_CHANNEL називає канал журналу, а без неї журнал іде в лог вашого застосунку — Інтеграція з Laravel.
use Slen\Sdk\Modules\Operations\Options\ExecuteOptions;
$execution = $slen->operations()->execute(
'generate_email',
['messages' => [['role' => 'user', 'content' => 'Write a short follow-up email.']]],
new ExecuteOptions(idempotencyKey: 'order-77:email'),
);Запуск пише дві події, і логер прикладу їх друкує:
debug slen.request POST /v1/operations/execute
debug slen.response POST /v1/operations/execute 200Логер, який кидає виняток, не ламає виклик, про який пише.
Логер для одного запиту
Серверу, який обслуговує багато запитів водночас, може знадобитися журнал кожного з них у власному логері — наприклад, у логері, який несе ідентифікатор запиту. Зібраний SDK не змінюється, тому йому не дають інший логер: він робить копію, яка пише в нього.
SlenSdk::withLogger() повертає новий SDK, який пише в переданий вами логер. Копія надсилає запити через той самий HTTP-клієнт і записує спани тим самим записувачем. SDK, з якого її зробили, і далі пише у свій логер. Зберіть SDK один раз і робіть копію для кожного запиту, якому вона потрібна, — Інтеграція з Laravel.
Події
| Подія | Рівень | Коли |
|---|---|---|
slen.request | debug | Перед кожною спробою запиту |
slen.response | debug | Відповідь 2xx, з її статусом, часом виклику й ідентифікатором запиту |
slen.retry | warning | Спроба, яку повторять, із паузою й причиною: no_answer, rate_limited для 429, unavailable для 503 |
slen.refused | info | Відповідь 4xx, яка відхиляє виклик: гроші, політика, перевірка запиту, конфлікт, невідомий клієнт |
slen.unauthorized | error | 401 або 403 про сам ключ — insufficient_scope, invalid_api_key, not_a_member: налаштування, яке треба виправити |
slen.failed | error | 5xx, відповідь, яку не можна прочитати, або відсутність відповіді після всіх спроб, із типом помилки й кількістю спроб |
slen.wait.poll | debug | Читання очікування, із прочитаним статусом і паузою перед наступним |
slen.wait.poll_failed | warning | Читання очікування, яке не вдалося з тимчасової причини; очікування триває |
slen.webhook.rejected | warning | Доставка вебхука, підпис чи тіло якої відхилено, із причиною — Вебхуки |
PHP SDK пише ще одну подію: slen.config.timeout_not_applied, попередження, коли він знайшов HTTP-клієнт, якому не може передати свої тайм-аути, — Конфігурація, — і коли виклик просить тайм-аут, якого його HTTP-клієнт не приймає. LogMessages зберігає імена подій як константи.
Поля кожної події — так, як їх несе контекст запису:
{
"slen.request": { "method": "POST", "route": "/v1/operations/execute", "attempt": 1 },
"slen.retry": [
{ "method": "POST", "route": "/v1/operations/execute", "attempt": 1, "delay_ms": 214, "reason": "no_answer", "status": null },
{ "method": "POST", "route": "/v1/operations/execute", "attempt": 2, "delay_ms": 2000, "reason": "rate_limited", "status": 429 },
{ "method": "POST", "route": "/v1/operations/execute", "attempt": 1, "delay_ms": 812, "reason": "unavailable", "status": 503 }
],
"slen.response": { "method": "POST", "route": "/v1/operations/execute", "status": 200, "duration_ms": 1843, "request_id": "0d3c1b9e-5a47-4f0e-9a36-2f6c8d1e7b42" },
"slen.refused": { "method": "POST", "route": "/v1/customers/{externalUserId}/charge", "status": 402, "error_code": "insufficient_credits", "request_id": "9c2d7e41-6b0a-4f3e-8d15-2a7f9e0c4b63" },
"slen.failed": { "method": "POST", "route": "/v1/operations/execute", "status": null, "error_code": null, "error_type": "Connection", "request_id": null, "attempts": 3 },
"slen.wait.poll": { "execution_id": "74438200973594800", "status": "running", "next_poll_ms": 2000 },
"slen.wait.poll_failed": { "execution_id": "74438200973594800", "failures_in_a_row": 1, "error_type": "RateLimit" },
"slen.webhook.rejected": { "reason": "no_matching_signature", "age_seconds": 2 }
}route— шаблон шляху, а не шлях із його значеннями:/v1/customers/{externalUserId}/charge, ніколи не ідентифікатор вашого клієнта.duration_msвідповіді — час усього виклику, разом із повторами й паузами.error_type— ім’я типу помилки без закінчення —ConnectionдляConnectionExceptionіConnectionError, — одне слово в обох SDK.execution_idпишеться: за ним виконання шукають у консолі.
Чого журнал ніколи не несе
API-ключа й заголовка
Authorization;секрету підпису ваших вебхуків;
ключа ідемпотентності — у ньому ідентифікатори вашої власної роботи;
тіл запитів і відповідей: входу й виходу флоу, його повідомлень і промптів;
тіла доставки вебхука;
повідомлення помилки, яким відповів API, — воно може нести текст, який написав флоу; журнал пише лише її код;
значень шляху й параметрів запиту.
Тому журнал можна надсилати в лог вашого застосунку як є.
Що читати далі
Спани — один запис про кожен запуск, із його грошима, для ваших трасувань.
Повтори й тайм-аути — про що повідомляє
slen.retry.Конфігурація — інші опції SDK.