
Compartir:
Actor de formación con una disertación sobre la comedia, llegué al desarrollo de PHP a través de la escena de las reuniones. Puedes encontrarme hablando y escribiendo sobre tecnología, o tocando/comprando discos raros de mi colección de vinilos.
Monitoriza tus Webhooks con Laravel Nightwatch
Tiempo de lectura: 11 minutos
Hay un montón de maneras de supervisar su aplicación, y como desarrollador, puede ser desalentador. Para desarrolladores PHP, tienes la opción de Blackfire y Tideways dentro del espacio PHP o plataformas externas en la nube como Datadog, Sentry, Papertrail o New Relic. Los últimos cuatro ejemplos son Application Performance Monitoring, pero todos son terceros que se concentran en lo que hacen sus agentes de informes dentro de la nube. Esto no nos da mucha información sobre nuestro códigoa menos que, por supuesto, haya escrito el código de su aplicación empresarial o de escalado para registrar todo de manera eficiente.
He encontrado la configuración de agentes de monitoreo como este particularmente doloroso y tal vez un poco demasiado complejo por necesidad. Este artículo pretende mostrar cómo Laravel eliminó la mayor parte de la complejidad, por lo que vamos a obtener Laravel Nightwatch con un código de petición al servidor para probarlo.
Cuando se ejecutan webhooks en producción, es fácil que las cosas vayan mal: los mensajes fallan, las cargas útiles cambian o las respuestas se ralentizan. Como desarrolladores, recurrimos a herramientas de monitorización para anticiparnos a estos problemas, pero el panorama puede resultar desalentador. Sólo en el mundo PHP, tienes Blackfire y Tideways, y luego los pesos pesados APM como Datadog, Sentry, Papertrail, o New Relic. Estas herramientas son potentes, pero son agentes de terceros centrados en la presentación de informes del lado de la nube, que no siempre nos dan la información que queremos sobre lo que nuestro código Laravel está haciendo realmente.
Y si somos sinceros, la instalación de algunos de estos agentes es... dolorosa. Funcionan, pero a menudo parecen demasiado complejos por necesidad.
Laravel Nightwatch, lanzado este año, pretende cambiar eso. Proporciona a los desarrolladores de PHP una forma integrada y nativa de Laravel para monitorizar los eventos y el rendimiento de la aplicación sin los dolores de cabeza habituales de configuración. En este tutorial, vamos a poner en marcha un simple receptor webhook utilizando mensajes RCS de Vonage, enviaremos un poco de tráfico simulado a través de él, y veremos cómo todo cobra vida en el panel de control de Nightwatch.
Laravel ha eliminado la mayor parte del trabajo pesado de la monitorización de aplicaciones. Vamos a ver hasta dónde podemos llegar con sólo unos pocos pasos.
TLDR; Puede encontrar el código del proyecto aquí.
Requisitos previos
A Laravel Vigilancia Nocturna Account (pasos incluidos)
Configurar la aplicación Laravel
Estamos construyendo una aplicación de monitorización, ¡pero necesitamos algo que monitorizar! Para ello, vamos a construir una aplicación con mensajería RCS. La mensajería RCS es un nuevo y brillante protocolo para enviar mensajes similares a SMS, pero con mucha más capacidad. Puedes enviar y recibir mensajes RCS utilizando la Mensajes API de Vonage y una aplicación de Vonage, al igual que SMS. Donde RCS realmente difiere es en lo que puede enviar: medios de mayor calidad (audio, imágenes, video), archivos de mayor tamaño y elementos interactivos más ricos como respuestas sugeridas, acciones sugeridas, tarjetas y carruseles.
Lo que vamos a simular es el seguimiento de los datos entrantes cuando la gente responde; cuando un usuario final responde en su dispositivo, una aplicación RCS en funcionamiento se configurará para disparar Webhooks a una URL designada para consumirlo.
El atajo que podemos tomar aquí es que no necesitamos hacer toda esa configuración: lo que quiero mostrar es el Dashboard de Nightwatch configurado y recibiendo datos de la aplicación.
Usando el instalador de Laravel (que envuelve el instalador de Composer create-project), crea una nueva aplicación Laravel en tu terminal:
laravel new tutorials-rcs-webhooks_laravel_nightwatchPuedes ignorar las opciones de andamiaje, salvo que probablemente merezca la pena elegir SQLite como base de datos, ya que es fácil de poner en marcha.
Para saber cómo empezar a utilizar SQLite, consulte mi artículo anterior "El regreso de SQLite".
Una vez completado el proceso, carga tu nueva aplicación Laravel en el IDE de tu elección. Usted querrá la capacidad de ver los registros en su base de datos, así que si usted está usando algo como VSCode, tener un plugin de SQL, o si está utilizando PHPStorm, configurar la fuente de base de datos.
Esta aplicación tendrá una entidad, Webhookque representa el modelo de los datos entrantes. ¿Cuáles son los datos entrantes? Es un RCS Webhook de Vonage (o una imitación). Si echas un vistazo a la especificación OpenAPI para ello, puedes encontrar un ejemplo ficticio:
{
"canal": "rcs",
"mensaje_uuid": "aaaaaaaa-bbbb-4ccc-8ddd-0123456789ab",
"to": "Vonage",
"from": "447700900001",
"timestamp": "2025-02-03T12:14:25Z",
"context_status": "none",
"message_type": "text",
"text": "Texto de mensaje entrante".
} Hacer modelos y migraciones
Necesitaremos un modelo y una migración para crear esto, lo que puedes hacer de un solo golpe desde la línea de comandos:
php artisan make:model Webhook --migrationCon la opción --migration el comando creará tanto el modelo como la migración. El mundo de la IA ha hecho las cosas un poco menos laboriosas estos días, así que para sacar mi código de migración, lancé este JSON a un agente de IA y le dije "genera el código de migración para este JSON", lo que rápidamente hizo (¡y sorprendentemente sin errores!). Este es el aspecto de la migración:
<?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');
}
};Hasta aquí todo bien. Ese modelo va a ser hidratado por un controlador, así que para hacerlo lo más rápido posible, tendrás que editar el modelo para que todas las propiedades fillable. Dirígete a tu app\Models\Webhook.php y edita el modelo para que quede así:
<?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',
];
}Excelente, ahora el modelo puede autocompletarse con los datos entrantes (y no necesitas realizar la verificación de los datos entrantes, porque confías en Vonage, ¿verdad? Pero en serio en producción, valida siempre tus entradas).
Hacer la ruta y el controlador
Para manejar los datos entrantes, necesitamos una ruta ligada a un método Controlador. Puedes escribir esta lógica directamente en el archivo web.php como un cierre, pero en este tutorial, voy a hacerlo un poco más tradicional.
php artisan make:controller WebhookControllerHe utilizado el menor número posible de líneas de código en este controlador para manejar la carga útil:
<?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);
}
}En nuestras rutas, tendremos que especificar que la ruta está vinculada al método handle() del WebhookController. El método handle() hace una sola cosa (¡quizás la única vez que he obedecido el principio de responsabilidad única!), que es tomar el cuerpo de la petición, hidratarlo en una entidad Webhook y guardarlo en la base de datos.
Dirígete a Routes\web.phpy añade la ruta:
Route::post('/webhook', [WebhookController::class, 'handle']);Enviar una petición a este endpoint (¡puedes intentarlo si quieres!) no funcionará actualmente. La ruta está ahí, el controlador está ahí, pero sólo dará una respuesta 419. Esto se debe a que he sido un mal desarrollador de Laravel, pero te puedo decir cómo ser un buen desarrollador de Laravel. Estoy tratando de hacer esto con la menor lógica posible, así que no andamio fuera API boilerplate que está ahí si lo quieres. Como esta ruta está en web.phpLaravel está configurado para esperar que sea GET y POST peticiones que compilan HTML en lugar de tiempo real API REST JSON. Por lo tanto, da un HTTP 419 porque espera algún tipo de solicitud de formulario en virtud de un POST, y éstas tienen un token Cross-Site Request Forgery incluido.
La forma rápida y sucia de evitar esto es hackear el middleware (¡nunca hagas esto en producción!). Pensé en incluirlo para mostrar un poco lo que sucede en tiempo de ejecución con Laravel bajo el capó. Dirígete al archivo bootstrap de tu framework, ubicado en bootstrap\app.php.
Este archivo sirve como punto de configuración inicial para su aplicación, justo después del punto de entrada. Puedes configurar archivos de enrutamiento personalizados, manejadores de error personalizados a nivel global y middleware personalizado. En mis años de desarrollo de Laravel, yo diría que es probablemente una mala idea editar cualquier cosa a nivel global como este, pero también estoy ansioso por obtener nuestra solicitud completa.
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();Puede ver que withMiddleware() utiliza el método específico validateCsrfTokens() para detener cualquier falsificación de sitio cruzado. ¡Práctico si quieres saltarte las reglas!
Servidor NodeJS simulado
En el mundo real, el objetivo de usar algo como Nighwatch es monitorizar el rendimiento de las aplicaciones, lo que significa que es probable que tengas que lidiar con un montón de trabajos asíncronos en la cola, un montón de caché, un montón de registros y un montón de peticiones. Es hora de fingirlo hasta que lo hagamos, y tomé la ruta más fácil para mostrar un panel de control lleno: peticiones.
Sólo hay un tipo de petición entrante, y es el Webhook. Por lo tanto, necesitamos algún código o un servidor para generar estas solicitudes para disparar en nuestra aplicación para nosotros. Hay todo tipo de herramientas API por ahí para este tipo de cosas: Prisma, Postman, Insomnia, Wiremock, lo que sea. Citando una de mis filosofías, opté por "mantenerlo simple, estúpido". Un archivo JavaScript, actuando en bucle como servidor.
En la raíz de su proyecto, cree un directorio y nómbrelo como corresponda:
mkdir mock_server
cd mock_server
npm init -y
touch mock-webhook-server.jsEstos cuatro comandos crean el directorio, añaden la gestión de dependencias y crean el archivo que vamos a ejecutar como servidor. Hay tres cosas que necesitamos para enviar estas peticiones en un bucle:
Axiospara enviar peticiones AJAX
UUIDpara generar identificadores ficticios únicos para los webhooks ficticios
Fakeruna biblioteca para generar datos ficticios de teléfono y texto.
Para instalar todo a la vez, introduzca esto en su línea de comandos:
npm i axios uuid @faker-js/fakerAquí está el código completo del servidor:
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"); Desempaquetar el código del servidor
Tenemos dos constantes ERROR_RATE y ERROR_FIELDS. La primera es un decimal para indicar la probabilidad de que se produzca un error al enviar las peticiones, y la segunda es qué campos se eliminarán del JSON enviado que causarán el error.
generateWebhookPayload() es el método para dar forma a la carga JSON que también decidirá si la carga es válida o no. Después de todo, nunca confíes en el código de otra persona, ¿verdad? Este es el punto donde invocamos la función helper uuidv4() para generar el UUID, y faker para generar un texto ficticio de hasta 300 caracteres (esto es arbitrario, los mensajes RCS pueden tener hasta 3000 caracteres).
Nuestra pequeña e inteligente línea matemática if (Math.random() < ERROR_RATE) { calcula si va a eliminar uno de los campos obligatorios. Al fin y al cabo, Nightwatch sirve para visualizar puntos problemáticos dentro de tu aplicación.
sendWebhook() es el método llamado en un bucle de eventos, que llama a hacer los datos de carga útil y luego lo envía a nuestra aplicación. Esto se hace justo debajo de la lógica del método, en setInterval(sendWebhook, 200) // <- set this according to how quick you want to generate dummy data
Ya puedes ejecutarlo, pero recuerda que tu base de datos SQLite se va a llenar rápidamente. Es hora de la Vigilancia Nocturna.
Configuración de la vigilancia nocturna
Lo bueno de Nightwatch es que es gratis. La versión gratuita incluye hasta 300.000 eventos de aplicaciones que se registran en la plataforma en la nube, así que es fácil jugar con ellos.
Vaya al sitio web de la Guardia de la Noche y cree una Account y una solicitud. Los cuatro pasos están bastante bien documentados allí. En primer lugar, ponle un nombre a tu aplicación y elige una región de almacenamiento adecuada a tu ubicación.
Setting up your Nightwatch applicationA continuación están las partes que realmente quitan el dolor de cabeza de la configuración de un APM. Primero debe instalar Nightwatch a través de Composer:
composer require laravel/nightwatchSe le da un conjunto de claves para poner en su archivo de variables de entorno (estas son claves falsas, para los halcones de seguridad por ahí):
Everything you need to know to get your agent reportingEl tercer paso impide que su cuenta de Nightwatch alcance su cuota en la primera hora: recuerde que podría estar controlando millones de eventos, por lo que Nightwatch incluye la posibilidad de definir el tamaño de la muestra como una variable de entorno:
How much data do you want?Finalmente, arranca Nightwatch en tu consola localmente, y envía la frecuencia de muestreo a los servidores Nightwatch:
Run your agent through php artisan, same as everything else
En sólo cuatro pasos, tienes un APM. Ejecute su Mock Node Webhook Server y obtendrá los datos:
Elegant application monitoring is here!De alguna manera, incluso me las arreglé para crear una solicitud de pícaro que acaparó el tiempo de CPU, que se puede ver como un pico en el gráfico de rendimiento a la derecha. Se le presenta con un montón de características para profundizar más en los datos de su aplicación: Violaciones de SQL, excepciones de memoria, trabajos incompletos, de todo. Como estaba generando excepciones aleatoriamente, tuve que mirar a ver qué aspecto tenían:
Watch out for your exception ratesMuy bueno. También lo divide en manejado vs. no manejado, para que tengas una mejor visión de cómo de defensivo está funcionando el código de tu aplicación.
Conclusión
Cada año, los desarrolladores parecen estar dotados de otra parte de la pila de aplicaciones Laravel que aún no ha sido cubierto, y esto no es una excepción. Lo que hace que Nightwatch destaque es que los proveedores de terceros de herramientas de rendimiento y monitorización de Applications no están adaptadas a Laravel, a diferencia de algo por Laravel sí mismos. En este sentido, para los consumidores de grandes cantidades de datos (el uso de Vonage Verify, Messages o Voice API tiene sistemas de webhooks de eventos que generarán grandes cantidades de webhooks), esta es una victoria tan fácil que es difícil de ignorar.
¿Tienes alguna pregunta o algo que compartir? Únete a la conversación en Slack de la comunidad de Vonagey mantente actualizado con el Boletín para desarrolladoressíguenos en X (antes Twitter)suscríbete a nuestro canal de YouTube para ver tutoriales en video, y sigue la página de página para desarrolladores de Vonage en LinkedInun espacio para que los desarrolladores aprendan y se conecten con la comunidad. Mantente conectado, comparte tu progreso y entérate de las últimas noticias, consejos y eventos para desarrolladores.
Compartir:
Actor de formación con una disertación sobre la comedia, llegué al desarrollo de PHP a través de la escena de las reuniones. Puedes encontrarme hablando y escribiendo sobre tecnología, o tocando/comprando discos raros de mi colección de vinilos.