• Czas czytania ~4 min
  • 10.08.2022

Największym problemem związanym z pracą z zewnętrznymi interfejsami API jest to, że mamy bardzo małą widoczność. Integrujemy je z naszą bazą kodu i testujemy - ale nie mamy pojęcia, jak często ich używamy, chyba że API, z którym integrujemy, ma metryki, z których możemy korzystać. Od dłuższego czasu jestem tym sfrustrowany – ale jest coś, co możemy zrobić.

Teleskop Laravel jest asystentem debugowania dla Twojej aplikacji, co oznacza, że ​​będzie rejestrował i dawał Ci wgląd w to, co się dzieje z wysokiego poziomu. Możemy to wykorzystać i dodać niestandardowe obserwatory, aby umożliwić więcej debugowania i rejestrowania, i właśnie to zrobimy w tym krótkim samouczku.

Po zainstalowaniu Laravel Telescope, upewnij się, że opublikujesz konfigurację i zmigrujesz bazę danych, możemy zacząć tworzyć naszego obserwatora dla Guzzle - klienta pod fasadą HTTP. Najbardziej logicznym miejscem przechowywania tych klas, przynajmniej dla mnie, jest wnętrze app/Telescope/Watchers, ponieważ kod należy do naszej aplikacji - ale rozszerzamy sam Telescope.Ale jak wygląda standardowy obserwator? Poniżej przedstawię przybliżony zarys podstawowych wymagań:

class YourWatcher extends Watcher
{
  public function register($app): void
  {
    // handle code for watcher here.
  }
}

To jest przybliżony zarys. Możesz dodać tyle metod, ile potrzebujesz, aby dodać obserwatora, który działa dla Ciebie. Więc bez zbędnych ceregieli, stwórzmy nową app/Telescope/Watchers/GuzzleRequestWatcher.php obserwatora, a my przejdziemy przez to, co musi zrobić.

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;
}
br>

Najpierw musimy uwzględnić cechę FetchesStackTrace, ponieważ pozwala nam to rejestrować, skąd i skąd pochodzą te żądania. Jeśli dokonamy refaktoryzacji tych wywołań HTTP do innych lokalizacji, możemy się upewnić, że wywołamy je tak, jak zamierzamy. Następnie musimy dodać metodę rejestracji naszego obserwatora:

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,
      ),
    );
  }
}

Przechwytujemy klienta Guzzle i rejestrujemy go w kontenerze, ale aby to zrobić, chcemy określić, w jaki sposób chcemy zbudować klienta. Spójrzmy na metodę buildClient:

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,
    );
  };
}

Zwracamy tutaj funkcję statyczną, która buduje naszego klienta Guzzle. Najpierw otrzymujemy dowolną konfigurację guzzle - a następnie, jeśli teleskop nagrywa, dodajemy sposób na zarejestrowanie zapytania.Na koniec zwracamy klientowi jego konfigurację. Jak więc rejestrujemy nasze zapytanie HTTP? Spójrzmy:

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())
      ]),
    );
  };
}

Więc rozszerzamy konfigurację dodając opcję on_stats, która jest wywołaniem zwrotnym. To wywołanie zwrotne uzyska ślad stosu i zarejestruje nowe zapytanie. Ten nowy wpis będzie zawierał wszystkie istotne rzeczy związane z zapytaniem, które możemy zarejestrować. Więc jeśli zbierzemy to wszystko razem:

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,
            );
        };
    }
}

Teraz wszystko, co musimy zrobić, to upewnić się, że zarejestrowaliśmy ten nowy obserwator w config/telescope.php i powinniśmy zacząć widzieć, jak nasze zapytania HTTP są rejestrowane.

'watchers' => [
  // all other watchers
  App\\Telescope\\Watchers\\GuzzleRequestWatcher::class,
]

Aby to przetestować, utwórz trasę testową:

Route::get('/guzzle-test', function () {
    Http::post('<https://jsonplaceholder.typicode.com/posts>', ['title' => 'test']);
});

Kiedy otworzysz Telescope, powinieneś teraz zobaczyć element nawigacji po stronie o nazwie Klient HTTP, a jeśli go otworzysz, zobaczysz tutaj logi - możesz sprawdzić nagłówki, ładunek i status żądania. Jeśli więc zaczniesz dostrzegać awarie integracji z interfejsem API, pomoże ci to znacznie w debugowaniu.

Czy uważasz to za pomocne?Jakich innych sposobów używasz do monitorowania i rejestrowania zewnętrznych żądań API? Daj nam znać na Twitterze!

Comments

No comments yet
Yurij Finiv

Yurij Finiv

Full stack

O

Professional Fullstack Developer with extensive experience in website and desktop application development. Proficient in a wide range of tools and technologies, including Bootstrap, Tailwind, HTML5, CSS3, PUG, JavaScript, Alpine.js, jQuery, PHP, MODX, and Node.js. Skilled in website development using Symfony, MODX, and Laravel. Experience: Contributed to the development and translation of MODX3 i...

O autorze CrazyBoy49z
WORK EXPERIENCE
Kontakt
Ukraine, Lutsk
+380979856297