Самая большая проблема при работе со сторонними API заключается в том, что у нас очень мало информации. Мы интегрируем их в нашу кодовую базу и тестируем, но понятия не имеем, как часто мы их используем, если только API, с которым мы интегрируемся, не имеет метрик, которые мы можем использовать. Меня это очень расстраивало в течение некоторого времени, но мы можем кое-что сделать.
Laravel Telescope — это помощник по отладке вашего приложения, что означает, что он будет регистрировать и давать вам представление о том, что происходит, с высокого уровня. Мы можем воспользоваться этим и добавить настраиваемых наблюдателей, чтобы включить больше отладки и ведения журнала, и это то, что мы сделаем в этом кратком руководстве.
После того, как вы установили Laravel Telescope, убедитесь, что вы опубликовали конфигурацию и перенесли базу данных, мы можем приступить к созданию нашего наблюдателя для Guzzle — клиента под Http-фасадом. Наиболее логичным местом для хранения этих классов, по крайней мере для меня, является app/Telescope/Watchers
, поскольку код принадлежит нашему приложению, но мы расширяем сам Telescope.Но как выглядит стандартный наблюдатель? Ниже я покажу вам примерный план базовых требований:
class YourWatcher extends Watcher
{
public function register($app): void
{
// handle code for watcher here.
}
}
Это примерный план. Вы можете добавить столько методов, сколько вам нужно, чтобы добавить наблюдателя, который работает на вас. Итак, без лишних слов, давайте создадим новое приложение-наблюдатель /Telescope/Watchers/GuzzleRequestWatcher.php
и рассмотрим, что ему нужно делать.
Сначала нам нужно включить трейт FetchesStackTrace, поскольку это позволяет нам фиксировать, откуда и откуда поступают эти запросы. Если мы реорганизуем эти HTTP-вызовы в другие места, мы сможем убедиться, что вызываем их так, как собираемся. Далее нам нужно добавить метод для регистрации нашего наблюдателя:
declare(strict_types=1);
namespace App\\Telescope\\Watchers;
use GuzzleHttp\\Client;
use GuzzleHttp\\TransferStats;
use Laravel\\Telescope\\IncomingEntry;
use Laravel\\Telescope\\Telescope;
use Laravel\\Telescope\\Watchers\\FetchesStackTrace;
use Laravel\\Telescope\\Watchers\\Watcher;
final class GuzzleRequestWatcher extends Watcher
{
use FetchesStackTrace;
}
Мы перехватываем клиент Guzzle и регистрируем его в контейнере, но для этого мы хотим указать, как мы хотим, чтобы клиент был построен. Давайте посмотрим на метод buildClient:
declare(strict_types=1);
namespace App\\Telescope\\Watchers;
use GuzzleHttp\\Client;
use GuzzleHttp\\TransferStats;
use Laravel\\Telescope\\IncomingEntry;
use Laravel\\Telescope\\Telescope;
use Laravel\\Telescope\\Watchers\\FetchesStackTrace;
use Laravel\\Telescope\\Watchers\\Watcher;
final class GuzzleRequestWatcher extends Watcher
{
use FetchesStackTrace;
public function register($app)
{
$app->bind(
abstract: Client::class,
concrete: $this->buildClient(
app: $app,
),
);
}
}
Здесь мы возвращаем статическую функцию, которая создает наш клиент Guzzle. Сначала получаем любой конфиг жрать — а потом, если телескоп записывает, добавляем способ записи запроса.Наконец, мы возвращаем клиента с его конфигурацией. Так как же нам записать наш HTTP-запрос? Давайте посмотрим:
private function buildClient(Application $app): Closure
{
return static function (Application $app): Client {
$config = $app['config']['guzzle'] ?? [];
if (Telescope::isRecording()) {
// Record our Http query.
}
return new Client(
config: $config,
);
};
}
Итак, мы расширили конфигурацию, добавив параметр on_stats
, который является обратным вызовом. Этот обратный вызов получит трассировку стека и запишет новый запрос. Эта новая запись будет содержать все важные вещи, связанные с запросом, который мы можем записать. Итак, если мы сложим все вместе:
if (Telescope::isRecording()) {
$config['on_stats'] = static function (TransferStats $stats): void {
$caller = $this->getCallerFromStackTrace(); // This comes from the trait we included.
Telescope::recordQuery(
entry: IncomingEntry::make([
'connection' => 'guzzle',
'bindings' => [],
'sql' => (string) $stats->getEffectiveUri(),
'time' => number_format(
num: $stats->getTransferTime() * 1000,
decimals: 2,
thousand_separator: '',
),
'slow' => $stats->getTransferTime() > 1,
'file' => $caller['file'],
'line' => $caller['line'],
'hash' => md5((string) $stats->getEffectiveUri())
]),
);
};
}
Теперь все, что нам нужно сделать, это убедиться, что мы зарегистрировали этот новый наблюдатель внутри config/telescope.php
, и мы должны начать видеть, что наши HTTP-запросы регистрируются.
declare(strict_types=1);
namespace App\Telescope\Watchers;
use Closure;
use GuzzleHttp\Client;
use GuzzleHttp\TransferStats;
use Illuminate\Foundation\Application;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\Watchers\FetchesStackTrace;
use Laravel\Telescope\Watchers\Watcher;
final class GuzzleRequestWatcher extends Watcher
{
use FetchesStackTrace;
public function register($app): void
{
$app->bind(
abstract: Client::class,
concrete: $this->buildClient(
app: $app,
),
);
}
private function buildClient(Application $app): Closure
{
return static function (Application $app): Client {
$config = $app['config']['guzzle'] ?? [];
if (Telescope::isRecording()) {
$config['on_stats'] = function (TransferStats $stats) {
$caller = $this->getCallerFromStackTrace();
Telescope::recordQuery(
entry: IncomingEntry::make([
'connection' => 'guzzle',
'bindings' => [],
'sql' => (string) $stats->getEffectiveUri(),
'time' => number_format(
num: $stats->getTransferTime() * 1000,
decimals: 2,
thousands_separator: '',
),
'slow' => $stats->getTransferTime() > 1,
'file' => $caller['file'],
'line' => $caller['line'],
'hash' => md5((string) $stats->getEffectiveUri()),
]),
);
};
}
return new Client(
config: $config,
);
};
}
}
Чтобы проверить это, создайте тестовый маршрут:
'watchers' => [
// all other watchers
App\\Telescope\\Watchers\\GuzzleRequestWatcher::class,
]
Когда вы открываете Telescope, вы должны увидеть элемент навигации на стороне под названием HTTP-клиент, и если вы откроете его, вы увидите, что здесь появляются журналы — вы можете проверить заголовки, полезную нагрузку и статус запроса. Поэтому, если вы начнете сталкиваться со сбоями из-за интеграции API, это очень поможет вам в отладке.
Route::get('/guzzle-test', function () {
Http::post('<https://jsonplaceholder.typicode.com/posts>', ['title' => 'test']);
});
Вы нашли это полезным?Какие другие способы вы используете для мониторинга и регистрации ваших внешних запросов API? Дайте нам знать в Твиттере!