https://a.storyblok.com/f/270183/1368x665/5912b74ed1/laravel-nightwatch.jpg

Laravel NightwatchでWebhookを監視する

最終更新日 November 26, 2025

所要時間:1 分

アプリケーションを監視する方法は山ほどあり、開発者としては気が遠くなります。PHP開発者には、以下の選択肢がある。 BlackfireTidewaysのような外部クラウドプラットフォームがある。 Datadog, Sentry, Papertrailまたは ニューレリック.最後の4つの例はアプリケーション・パフォーマンス・モニタリングだが、いずれもサードパーティであり、そのレポート・エージェントがクラウド内で行うことに集中している。これでは コードもちろん、エンタープライズアプリやスケールアップアプリのコードを書いて、すべてを効率的にログに記録している場合は別だが。

このようなモニタリングエージェントのセットアップは特に骨が折れるもので、必然的に少し複雑になりすぎているのかもしれません。この記事では、Laravelがどのようにして複雑さの大部分を取り除いたかを紹介します。 Laravel Nightwatchをモックサーバーリクエストコードで稼働させてテストしてみましょう。

本番のWebhookを運用していると、メッセージが失敗したり、ペイロードが変更されたり、レスポンスが遅くなったりと、うまくいかないことが起こりがちです。開発者としては、これらの問題に対処するために監視ツールに手を伸ばします。PHPの世界だけでも、BlackfireやTideways、そしてDatadog、Sentry、Papertrail、New Relicのようなヘビー級のAPMがある。これらのツールは強力ですが、クラウド側のレポートに特化したサードパーティのエージェントであるため、Laravelのコードが実際に何をしているのか、私たちが望むような洞察を常に与えてくれるわけではありません。

正直に言えば、これらのエージェントをセットアップするのは...骨が折れる。それらは機能するが、しばしば必要以上に複雑に感じられる。

今年発表されたLaravel Nightwatchは、それを変えることを目的としている。これはPHP開発者に、通常の設定に頭を悩ませることなく、アプリケーションのイベントやパフォーマンスを監視するビルトインのLaravelネイティブな方法を提供します。このチュートリアルでは、Vonage RCSメッセージを使ってシンプルなWebhookレシーバーをスピンアップし、いくつかのモックトラフィックを送信して、Nightwatchダッシュボードですべてが動き出すのを見ます。

Laravelはアプリ監視の重労働をほとんど取り除いてくれました。ほんの数ステップでどこまでできるか見てみよう。

TLDR プロジェクトのコードはここにあります。

前提条件

Laravelアプリのセットアップ

モニタリング・アプリケーションを作るのだが、モニタリングするものが必要だ!そのために、RCSメッセージングを使ったアプリを作ろう。RCSメッセージングは、SMSのようなメッセージを送信するための新しいプロトコルだが、はるかに高機能だ。RCSメッセージの送受信には メッセージAPIとVonageアプリケーションを使用して、SMSのように送受信できます。RCSが本当に異なる点は、より高品質なメディア(オーディオ、イメージ、ビデオ)、より大きなファイルサイズ、そして返信提案、アクション提案、カード、カルーセルなどのリッチなインタラクティブ要素です。

エンドユーザーが自分のデバイスで返信すると、RCSアプリケーションは、指定されたURLにWebhookを発行し、そのデータを消費するように設定される。

ここでの近道は、全てのコンフィギュレーションを行う必要はないということだ。表示したいのは、コンフィギュレーションされ、アプリケーションデータを受信しているNightwatch Dashboardである。

Laravelインストーラー(Composerの create-projectを使って、ターミナルに新しいLaravelアプリケーションを作成します:

laravel new tutorials-rcs-webhooks_laravel_nightwatch

データベースとしてSQLiteを選ぶ価値はあるだろう。

SQLiteを使い始める方法については、以前の記事"SQLiteの復活".

プロセスが完了したら、新しいLaravelアプリケーションをお好みのIDEにロードします。VSCodeのようなものを使用している場合はSQLプラグインを、PHPStormを使用している場合はデータベースソースを設定してください。

このアプリケーションは1つのエンティティを持つ、 ウェブフックエンティティを持ちます。受信データとは何か?それはVonageからのRCS Webhookだ(もしくはモック)。OpenAPIの仕様を見てみると、ダミーのサンプルがある:

{

   "チャンネル":"rcs"、
   "message_uuid":"aaaaaaaa-bbbb-4ccc-8ddd-0123456789ab",
   "to":"Vonage"、
   "from":"447700900001",
   "タイムスタンプ":"2025-02-03T12:14:25Z",
   "context_status":"none"、
   "message_type":text": "テキスト"、
   "text":「受信メッセージのテキスト。
}

モデルの作成と移行

これを作成するにはモデルとマイグレーションが必要だが、これはコマンドラインから一発でできる:

php artisan make:model Webhook --migration

この --migrationスイッチを使うと、コマンドはモデルとマイグレーションの両方を作成する。AIの世界では、最近は少しは手間が省けるようになったので、マイグレーションコードを出すために、このJSONをAIエージェントに放り込んで、「このJSONのマイグレーションコードを生成して」と言うと、AIエージェントはすぐにやってくれた(しかも驚くことにミスなく!)。マイグレーションはこんな感じだ:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
   /**
    * Run the migrations.
    */
   public function up(): void
   {
       Schema::create('webhooks', function (Blueprint $table) {
           $table->id();
           $table->string('channel');
           $table->uuid('message_uuid')->unique();
           $table->string('to');
           $table->string('from');
           $table->timestamp('timestamp');
           $table->string('context_status')->nullable();
           $table->string('message_type')->nullable();
           $table->text('text')->nullable();
           $table->timestamps();
       });
   }

   /**
    * Reverse the migrations.
    */

   public function down(): void
   {
       Schema::dropIfExists('webhook_messages');
   }
};

ここまでは順調だ。このモデルはコントローラによってハイドレイトされる予定なので、できるだけ早くこれを行うには、モデルを編集してすべてのプロパティを fillable.モデルを編集して、すべてのプロパティを. app\Models\Webhook.phpにアクセスし、モデルをこのように編集します:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Webhook extends Model
{
   protected $fillable = [
       'channel',
       'message_uuid',
       'to',
       'from',
       'timestamp',
       'context_status',
       'message_type',
       'text',
   ];

   protected $casts = [
       'timestamp' => 'datetime',
   ];
}

素晴らしい、これでモデルは受信データを自動入力できるようになった(Vonageを信頼しているのだから、受信データの検証は必要ないだろう?しかし、本番環境では,常に入力を検証してください)。

ルートとコントローラーを作る

受信データを処理するには、Controller メソッドに結びついたルートが必要です。このロジックは に直接書くこともできます。に直接書くこともできますが、このチュートリアルではもう少し伝統的なやり方にします。

php artisan make:controller WebhookController

このコントローラでは、ペイロードを処理するためにできるだけ少ないコード行数を使っている:

<?php



namespace App\Http\Controllers;



use App\Models\Webhook;

use Illuminate\Http\Request;

class WebhookController extends Controller
{
   public function handle(Request $request)
   {
       $data = $request->all();

       Webhook::create($data);

       return response('Webhook Handled', 200);
   }
}

ルートでは、ルートが handle()メソッドの WebhookController.のメソッドに結びつけられることを指定する必要があります。 handle()メソッドが行うことは1つだけです(おそらく私が単一責任の原則に従ったのは今回だけです!)。 Webhookエンティティにハイドレートし、それをデータベースに永続化します。

に向かい Routes\web.phpにアクセスし、ルートを追加する:

Route::post('/webhook', [WebhookController::class, 'handle']);

このエンドポイントにリクエストを送っても(試してみてもいい!)、今のところうまくいかない。ルートはあり、コントローラーもありますが、419のレスポンスしか返ってきません。これは私がダメなLaravel開発者だったからです。私はできるだけ少ないロジックでこれを行おうとしているので、必要であればそこにあるAPI定型文を足場にはしませんでした。このルートは web.phpにあるので、Laravelはこれが GETそして POSTリクエストは、リアルタイムのAPI REST JSONではなく、HTMLをコンパイルするように設定されています。の下で何らかのフォームリクエストを期待するため、HTTP 419を返します。 POST,で、これにはクロスサイト・リクエスト・フォージェリ・トークンが含まれています。

これを回避する手っ取り早く汚い方法は、ミドルウェアをハックすることです(本番では絶対にしないでください!)。Laravelの実行時に何が起こるか、少しお見せしようと思いました。フレームワークのブートストラップ・ファイルは bootstrap\app.php.

このファイルは、エントリー・ポイントのすぐ後にある、アプリケーションのオープニング・コンフィギュレーション・ポイントとして機能します。カスタムルーティングファイル、グローバルレベルでのカスタムエラーハンドラ、カスタムミドルウェアを設定できます。私の長年のLaravel開発経験から言うと、おそらく このようにグローバルレベルでこのようにグローバルレベルで何かを編集するのは、おそらく悪い考えだと言えるでしょう。

bootstrap\app.php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
   ->withRouting(
       web: DIR.'/../routes/web.php',
       commands: DIR.'/../routes/console.php',
       health: '/up',
   )
   ->withMiddleware(function (Middleware $middleware): void {
       $middleware->validateCsrfTokens(except: [
           '/webhook',
       ]);
   })

   ->withExceptions(function (Exceptions $exceptions): void {
       //
   })->create();

このように withMiddleware()が特定のメソッド validateCsrfTokens()を使ってクロスサイト・フォージェリを阻止していることがわかる。ルールを回避したい場合に便利です!

模擬NodeJSサーバー

現実の世界では、Nighwatchのようなものを使うことの要点はアプリケーションのパフォーマンス監視であり、それは必然的に、キュー上の多くの非同期ジョブ、多くのキャッシュ、多くのロギング、そして多くのリクエストを扱う可能性が高いことを意味する。そして、ダッシュボードがリクエストでいっぱいになるのを表示する最も簡単な方法を取りました。

入ってくるリクエストの種類は1つだけで、それはWebhookだ。そのため、アプリケーションにリクエストを送信するためのコードやサーバーが必要になる。Prisma、Postman、Insomnia、Wiremockなど、この種のAPIツールはいろいろある。私の哲学を引用すると、私は「シンプルに、バカバカしく!」を選んだ。つのJavaScriptファイルで、サーバーとしてループで動作する。

プロジェクトのルートにディレクトリを作り、それに名前をつける:

mkdir mock_server
cd mock_server
npm init -y
touch mock-webhook-server.js

これらの4つのコマンドは、ディレクトリを作成し、依存関係の管理を追加し、サーバーとして実行するファイルを作成する。これらのリクエストをループで送るために必要なものが3つある:

  • アクシオスAJAXリクエスト送信用

  • UUIDダミー・ウェブフック用にユニークなダミーIDを生成する。

  • フェイカー電話やテキストのダミーデータを生成するライブラリ。

一度にインストールするには、コマンドラインに次のように入力する:

npm i axios uuid @faker-js/faker

これが完成したサーバー・コードである:

const axios = require('axios');
const { v4: uuidv4 } = require('uuid');
const { faker } = require('@faker-js/faker');
const ERROR_RATE = 0.025;
const ERROR_FIELDS = ['message_uuid', 'from', 'timestamp', 'text'];

function generateWebhookPayload() {

   const payload = {
       channel: "rcs",
       message_uuid: uuidv4(),
       to: "Vonage",
       from: faker.phone.number({ style: 'international' }).replace('+', ''),
       timestamp: new Date().toISOString(),
       context_status: "none",
       message_type: "text",
       text: faker.lorem.text().slice(0, 300)
   };

   if (Math.random() < ERROR_RATE) {
       const fieldToRemove = faker.helpers.arrayElement(ERROR_FIELDS);

       delete payload[fieldToRemove];

       console.warn[WARN] Sending malformed webhook (missing '${fieldToRemove}'));
   }
   return payload;
}

function sendWebhook() {
   const payload = generateWebhookPayload();

   axios.post('http://tutorial-voice-rcs_laravel_nightwatch.test/webhook', payload)
       .then(() => {
           console.log[${new Date().toISOString()}] Sent: ${payload.message_uuid});
       })
       .catch(error => {
           console.error[${new Date().toISOString()}] Error: ${error.message});
       });
}

setInterval(sendWebhook, 200);

console.log("Running Mock Webhook Server");

サーバーコードの解凍

つの定数がある、 ERROR_RATEERROR_FIELDS.1つ目はリクエストを送信するときにエラーが発生する確率を表す10進数で、2つ目は送信されたJSONからどのフィールドが削除されるかがエラーの原因となります。

generateWebhookPayload()は、JSONペイロードを形成するメソッドであり、ペイロードが有効かどうかも決定する。結局のところ、他人のコードを信用してはいけないということですね。ここが ここがヘルパー関数 uuidv4()を呼び出してUUIDを生成し、fakerを呼び出して300文字までのダミーテキストを生成します(これは任意です。)

私たちの賢い数学的な行 if (Math.random() < ERROR_RATE) {は、必須フィールドの1つを削除するかどうかを計算する。結局のところ、Nightwatchはアプリケーション内の問題点を視覚化するためのものなのだ。

sendWebhook()はイベントループで呼び出されるメソッドで、ペイロードデータを作成し、アプリケーションに送信します。これはメソッドのロジックのすぐ下にある setInterval(sendWebhook, 200) // <- set this according to how quick you want to generate dummy data

これで実行できるようになったが、SQLiteデータベースがすぐにいっぱいになることを覚えておいてほしい。ナイトウォッチの時間だ。

ナイトウォッチの設定

Nightwatchの魅力は、無料でいじくり回せることだ。無料版では、最大30万件のアプリケーションイベントがクラウドプラットフォームに記録される。

ナイトウォッチ・サイトにアクセスし、アカウントとアプリケーションを作成する。そこには4つのステップがかなり詳しく書かれている。まず、アプリケーションに名前を付け、お住まいの地域に適したストレージ地域を選択する。

Screenshot showing user account setup for a Nightwatch applicationSetting up your Nightwatch application次は、APMをセットアップする際に頭を悩ませる部分だ。まずComposer経由でNightwatchをインストールするように指示される:

composer require laravel/nightwatch

環境変数ファイルに入れるキーのセットが与えられる(これはダミーのキーで、セキュリティにうるさい人のためのものだ):

Screenshot showing the agent setup promptEverything you need to know to get your agent reportingステップ3は、Nightwatchアカウントが最初の1時間以内にクォータに達するのを阻止する:潜在的に何百万ものイベントを監視している可能性があることを忘れないでほしい:

Screenshot showing the agent sample size configurationHow much data do you want?最後に、ローカルにあるコンソールでNightwatchを起動すると、サンプルレートがNightwatchサーバーに送信される:

Screenshot of the panel showing you how to start the Nightwatch agent from the command lineRun your agent through php artisan, same as everything else

わずか4ステップでAPMが完成します。モック・ノード・ウェブフック・サーバーを実行し、データを取り込む:

Screenshot showing a populated Nightwatch dashboard monitoring your appElegant application monitoring is here!どういうわけか、CPU時間を独占する不正なリクエストまで作成することに成功し、右側のパフォーマンスグラフにスパイクとして表示されているのがわかる。アプリケーションのデータをさらに掘り下げるために、たくさんの機能が表示されます:SQL違反、メモリ例外、未完了のジョブなどなど。私はランダムに例外を発生させていたので、それらがどのようなものかを確認する必要がありました:

Screenshow showing exceptions rates on the Nightwatch dashboardWatch out for your exception rates素晴らしい。ハンドリングされたものとハンドリングされていないものに分けられるので、アプリケーションのコードがどの程度防御的であるかをよりよく把握することができる。

結論

毎年、開発者はまだカバーされていないLaravelアプリケーションスタックの別の部分を贈られているようですが、今回も例外ではありません。Nightwatchが際立っているのは、アプリケーションモニタリングやパフォーマンスツールのサードパーティーベンダーが、Laravelに合わせていないことです。 がLaravelに合わせていないことです、 Laravel自身によるものとは対照的です。この点で、大量のデータを使用する消費者(Verify、Messages、Voice APIを使用すると、大量のWebhookを生成するイベントWebhookシステムがある)にとって、これは無視できないほど簡単な勝利です。

ご質問がある場合、またはあなたが作っているものを共有したい場合は、こちらをクリックしてください。

最新の開発者向けニュース、ヒント、イベント情報をお届けします。

シェア:

https://a.storyblok.com/f/270183/400x385/12b3020c69/james-seconde.png
James SecondeシニアPHPデベロッパー

スタンダップ・コメディーの学位論文を持つ俳優の訓練を受け、ミートアップ・シーンを経てPHP開発に携わるようになった。技術について話したり書いたり、レコード・コレクションから変わったレコードを再生したり買ったりしています。