
Compartilhar:
Sou ator formado, com uma dissertação sobre stand-up comedy, e comecei a me dedicar ao desenvolvimento em PHP por meio dos encontros da comunidade. Você pode me encontrar dando palestras e escrevendo sobre tecnologia, ou ouvindo e comprando discos curiosos da minha coleção de vinil.
Monitore seus webhooks com o Laravel Nightwatch
Tempo de leitura: 11 minutos
Existem inúmeras maneiras de monitorar seu aplicativo e, para um desenvolvedor, isso pode parecer um pouco assustador. Para desenvolvedores de PHP, existe a opção de Blackfire e Tideways no ecossistema PHP ou plataformas de nuvem externas, como o Datadog, Sentry, Papertrail ou New Relic. Os quatro últimos exemplos são ferramentas de monitoramento de desempenho de Applications, mas todos são de terceiros que se concentram no que seus agentes de monitoramento fazem dentro da nuvem. Isso não nos dá muita informação sobre nosso código, a menos, é claro, que você tenha escrito o código do seu aplicativo corporativo ou de scale-up de forma a registrar tudo com eficiência.
Acho que configurar agentes de monitoramento dessa forma é particularmente trabalhoso e, talvez, um pouco complexo demais por necessidade. Este artigo tem como objetivo mostrar como o Laravel eliminou grande parte dessa complexidade; então, vamos ver o Laravel Nightwatch em funcionamento com um código simulado de solicitação de servidor para testá-lo.
Quando você está executando webhooks em produção, é fácil que algo dê errado: mensagens podem falhar, cargas úteis podem mudar ou as respostas podem ficar lentas. Como desenvolvedores, recorremos a ferramentas de monitoramento para nos anteciparmos a esses problemas, mas o panorama pode parecer assustador. Só no mundo do PHP, temos o Blackfire e o Tideways, além dos APMs mais robustos, como Datadog, Sentry, Papertrail ou New Relic. Essas ferramentas são poderosas, mas são agentes de terceiros focados em relatórios no lado da nuvem, o que nem sempre nos dá a visão que queremos sobre o que nosso código Laravel está realmente fazendo.
E, para ser sincero, configurar alguns desses agentes é… complicado. Eles funcionam, mas muitas vezes parecem excessivamente complexos por uma questão de necessidade.
O Laravel Nightwatch, lançado este ano, tem como objetivo mudar isso. Ele oferece aos desenvolvedores PHP uma maneira integrada e nativa do Laravel de monitorar eventos e desempenho de aplicativos, sem as habituais dores de cabeça com a configuração. Neste tutorial, vamos criar um receptor de webhooks simples usando mensagens RCS da Vonage, enviar um pouco de tráfego simulado por meio dele e observar tudo ganhar vida no painel do Nightwatch.
O Laravel simplificou bastante o monitoramento de aplicativos. Vamos ver até onde podemos chegar com apenas algumas etapas.
Resumo: Você pode encontrar o código do projeto aqui.
Pré-requisitos
A Conta do Laravel Nightwatch (com instruções passo a passo)
Configurar o aplicativo Laravel
Estamos desenvolvendo um aplicativo de monitoramento, mas precisamos de algo para monitorar! Para isso, vamos criar um aplicativo com mensagens RCS. O RCS é um protocolo novinho em folha para enviar mensagens semelhantes às SMS, mas com muito mais recursos. Você pode enviar e receber mensagens RCS usando a Messages API da Vonage e uma aplicação da Vonage, exatamente como no SMS. Onde o RCS realmente se destaca é no que ele pode oferecer: mídia de alta qualidade (áudio, imagens, Video), arquivos maiores e elementos interativos mais ricos, como respostas sugeridas, ações sugeridas, cartões e carrosséis.
O que vamos simular é o monitoramento dos dados recebidos quando as pessoas respondem; quando um usuário final responde em seu dispositivo, um aplicativo RCS em funcionamento estará configurado para disparar Webhooks para uma URL designada, a fim de processá-los.
O atalho que podemos usar aqui é que não precisamos fazer toda essa configuração: o que quero mostrar é o Painel do Nightwatch já configurado e recebendo dados do aplicativo.
Usando o instalador do Laravel (que funciona em conjunto com o Composer create-project), crie uma nova aplicação Laravel no seu terminal:
laravel new tutorials-rcs-webhooks_laravel_nightwatchVocê pode ignorar as opções de estrutura, mas provavelmente vale a pena escolher o SQLite como banco de dados, já que é fácil começar a usá-lo.
Para saber como começar a usar o SQLite, confira meu artigo anterior “O Retorno do SQLite”.
Assim que o processo estiver concluído, carregue seu novo aplicativo Laravel no IDE de sua preferência. Você vai precisar visualizar os registros do seu banco de dados; portanto, se estiver usando algo como o VSCode, instale um plug-in de SQL, ou, se estiver usando o PHPStorm, configure a fonte do banco de dados.
Esta aplicação terá uma entidade, Webhook, que representa o modelo dos dados recebidos. O que são os dados recebidos? Trata-se de um Webhook RCS da Vonage (ou um simulado). Se você der uma olhada na especificação OpenAPI correspondente, encontrará um exemplo fictício:
{
"channel": "rcs",
"message_uuid": "aaaaaaaa-bbbb-4ccc-8ddd-0123456789ab",
"para": "Vonage",
"de": "447700900001",
"timestamp": "2025-02-03T12:14:25Z",
"context_status": "none",
"message_type": "text",
"text": "Texto da mensagem recebida."
} Criar modelos e migrações
Para criar isso, precisaremos de um modelo e de uma migração, o que você pode fazer de uma só vez pela linha de comando:
php artisan make:model Webhook --migrationCom a --migration opção, o comando criará tanto o modelo quanto a migração. O mundo da IA tornou as coisas um pouco menos trabalhosas hoje em dia, então, para gerar meu código de migração, inseri esse JSON em um agente de IA e disse “gere o código de migração para este JSON”, o que ele fez prontamente (e, surpreendentemente, sem erros!). Veja como fica essa migração:
<?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');
}
};Até agora, tudo bem. Esse modelo será hidratado por um controlador; portanto, para fazer isso o mais rápido possível, você precisará editar o modelo para tornar todas as propriedades fillable. Vá até seu app\Models\Webhook.php e edite o modelo para ficar assim:
<?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',
];
}Ótimo, agora o modelo pode ser preenchido automaticamente com os dados recebidos (e você não precisa verificar esses dados, porque confia na Vonage, certo? Mas, falando sério, em ambiente de produção, sempre valide suas entradas).
Criar a rota e o controlador
Para processar os dados recebidos, precisamos de uma rota vinculada a um método do Controlador. Você poderia escrever essa lógica diretamente no arquivo arquivo web.php como um closure, mas, neste tutorial, vou fazer isso de uma maneira um pouco mais tradicional.
php artisan make:controller WebhookControllerUsei o mínimo possível de linhas de código neste controlador para processar a 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);
}
}Em nossas rotas, precisaremos especificar que a rota está vinculada ao handle() método do WebhookController. O handle() método faz uma única coisa (talvez a única vez em que eu tenha obedecido ao princípio da responsabilidade única!), que é pegar o corpo da solicitação, preenchê-lo em uma Webhook entidade e persistí-la no banco de dados.
Acesse Routes\web.phpe adicione a rota:
Route::post('/webhook', [WebhookController::class, 'handle']);Enviar uma solicitação para esse endpoint (você pode tentar, se quiser!) não vai funcionar no momento. A rota está lá, o controlador está lá, mas só vai retornar uma resposta 419. Isso acontece porque eu não tenho sido um bom desenvolvedor Laravel, mas posso te dizer como ser um bom desenvolvedor Laravel. Estou tentando fazer isso com o mínimo de lógica possível, por isso não gerei o código padrão da API que está disponível caso você queira usá-lo. Como essa rota está em web.php, o Laravel está configurado para esperar que ela seja GET e POST solicitações que geram HTML em vez de JSON REST de API em tempo real. Portanto, ele retorna um HTTP 419 porque espera algum tipo de solicitação de formulário sob um POST, e essas incluem um token de Cross-Site Request Forgery.
A maneira mais rápida e improvisada de contornar isso é mexer no middleware (nunca faça isso em ambiente de produção!). Resolvi incluir isso para mostrar um pouco do que acontece nos bastidores do Laravel durante a execução. Acesse o arquivo de inicialização do seu framework, localizado em bootstrap\app.php.
Este arquivo funciona como o ponto de configuração inicial do seu aplicativo, logo após o ponto de entrada. Você pode configurar arquivos de roteamento personalizados, manipuladores de erros personalizados em nível global e middleware personalizado. Com base nos meus anos de experiência em desenvolvimento com Laravel, diria que provavelmente não é uma boa ideia editar qualquer coisa no nível global dessa forma, mas também estou ansioso para concluir nossa solicitação.
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();Você pode ver que withMiddleware() usa o método específico validateCsrfTokens() para impedir qualquer falsificação entre sites. É útil se você quiser contornar as regras!
Um servidor Node.js simulado
No mundo real, o objetivo principal de usar algo como o Nightwatch é o monitoramento do desempenho das aplicações, o que, por necessidade, significa que você provavelmente estará lidando com muitas tarefas assíncronas na fila, muito armazenamento em cache, muitos registros de log e muitas solicitações. É hora de “fingir até conseguir”, e eu escolhi o caminho mais fácil para mostrar um painel se enchendo: as solicitações.
Existe apenas um tipo de solicitação recebida, que é o Webhook. Portanto, precisamos de algum código ou de um servidor para gerar essas solicitações e enviá-las ao nosso aplicativo por nós. Existem diversos tipos de ferramentas de API disponíveis para esse tipo de tarefa: Prisma, Postman, Insomnia, Wiremock, entre outras. Para citar uma das minhas filosofias, optei por “manter as coisas simples, bobo!”. Um único arquivo JavaScript, atuando em um loop como um servidor.
Na raiz do seu projeto, crie um diretório e nomeie-o adequadamente:
mkdir mock_server
cd mock_server
npm init -y
touch mock-webhook-server.jsEsses quatro comandos criam o diretório, adicionam o gerenciamento de dependências e criam o arquivo que vamos executar como servidor. Há três coisas de que precisamos para enviar essas solicitações em um loop:
Axios, para enviar solicitações AJAX
UUID, para gerar IDs fictícios exclusivos para os webhooks fictícios
Faker, uma biblioteca para gerar dados fictícios de números de telefone e mensagens de texto.
Para instalar tudo de uma vez, digite o seguinte na linha de comando:
npm i axios uuid @faker-js/fakerAqui está o código completo do 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"); Descompactando o código do servidor
Temos duas constantes, ERROR_RATE e ERROR_FIELDS. A primeira é um número decimal que indica a probabilidade de ocorrer um erro ao enviar solicitações, e a segunda especifica quais campos serão removidos do JSON enviado, o que causará o erro.
generateWebhookPayload() é o método para moldar a carga JSON, que também determinará se a carga é válida ou não. Afinal, nunca se deve confiar no código de outra pessoa, certo? Esse é o ponto onde invocamos a função auxiliar uuidv4() para gerar o UUID e o faker para gerar um texto fictício com até 300 caracteres (esse número é arbitrário; mensagens RCS podem ter até 3.000 caracteres).
Nossa pequena e inteligente linha matemática if (Math.random() < ERROR_RATE) { determina se deve remover um dos campos obrigatórios. Afinal, o Nightwatch serve para visualizar pontos problemáticos em sua aplicação.
sendWebhook() é o método chamado em um loop de eventos, que é acionado para gerar os dados da carga útil e, em seguida, enviá-los ao nosso aplicativo. Isso é feito logo abaixo da lógica do método, em setInterval(sendWebhook, 200) // <- set this according to how quick you want to generate dummy data
Agora você já pode executá-lo, mas lembre-se de que seu banco de dados SQLite vai ficar cheio rapidamente. É hora de usar o Nightwatch.
Configurando o Nightwatch
O melhor do Nightwatch é que dá para experimentar de graça. O plano gratuito permite registrar até 300 mil eventos de Applications na plataforma em nuvem, o que é mais do que suficiente para você começar a explorar.
Acesse o site do Nightwatch e crie um account e um aplicativo. As quatro etapas estão bem explicadas no site. Primeiro, dê um nome ao seu aplicativo e escolha uma região de armazenamento adequada à sua localização.
Setting up your Nightwatch applicationA seguir, apresentamos as etapas que realmente facilitam a configuração de um APM. Você deve, primeiro, instalar o Nightwatch via Composer:
composer require laravel/nightwatchVocê recebe um conjunto de chaves para inserir no seu arquivo de variáveis de ambiente (trata-se de chaves fictícias, para os mais atentos à segurança):
Everything you need to know to get your agent reportingO terceiro passo evita que sua Account do Nightwatch atinja sua cota logo na primeira hora: lembre-se de que você pode estar monitorando potencialmente milhões de eventos aqui; por isso, o Nightwatch oferece a possibilidade de definir o tamanho da amostra como uma variável de ambiente:
How much data do you want?Por fim, você inicia o Nightwatch no seu console localmente, e ele envia a taxa de amostragem para os servidores do Nightwatch:
Run your agent through php artisan, same as everything else
Em apenas quatro etapas, você já tem um APM. Execute o servidor de webhooks do Mock Node e os dados começam a chegar:
Elegant application monitoring is here!De alguma forma, consegui até criar uma solicitação indesejada que consumiu muito tempo da CPU, o que você pode ver como um pico no gráfico de desempenho à direita. Você tem à disposição uma infinidade de recursos para analisar mais detalhadamente os dados do seu aplicativo: violações de SQL, exceções de memória, tarefas incompletas, entre outros. Como eu estava gerando exceções aleatoriamente, precisei verificar como elas se apresentavam:
Watch out for your exception ratesÓtimo. Ele também divide em erros tratados e não tratados, então você consegue ter uma visão melhor do desempenho do código da sua aplicação em termos de segurança.
Conclusão
A cada ano, os desenvolvedores parecem receber mais uma parte da pilha de aplicativos do Laravel que ainda não havia sido abordada, e este caso não é exceção. O que faz o Nightwatch se destacar é que os fornecedores terceirizados de ferramentas de monitoramento e desempenho de aplicativos não são adaptados ao Laravel, ao contrário do que é oferecido pela própria Laravel. Nesse sentido, para quem lida com grandes volumes de dados (o uso das APIs Vonage Verify, Messages ou Voice API, por exemplo, conta com sistemas de webhooks de eventos que geram grandes quantidades de webhooks), essa é uma vantagem tão óbvia que é difícil ignorá-la.
Tem alguma dúvida ou quer compartilhar o que está criando?
Inscreva-se no Boletim Informativo para Desenvolvedores
Siga-nos no X (antigo Twitter) para ficar por dentro das novidades
Assista aos tutoriais no nosso canal do YouTube
Conecte-se conosco na página de desenvolvedores da Vonage no LinkedIn
Fique conectado e acompanhe as últimas notícias, dicas e eventos para desenvolvedores.
Compartilhar:
Sou ator formado, com uma dissertação sobre stand-up comedy, e comecei a me dedicar ao desenvolvimento em PHP por meio dos encontros da comunidade. Você pode me encontrar dando palestras e escrevendo sobre tecnologia, ou ouvindo e comprando discos curiosos da minha coleção de vinil.