راههای زیادی برای مانیتور کردن اپلیکیشن شما وجود دارد و بهعنوان یک توسعهدهنده، این موضوع میتواند ترسناک باشد. برای توسعهدهندگان PHP، در فضای خود PHP گزینههایی مثل Blackfire و Tideways را دارید، یا پلتفرمهای ابری خارجی مثل Datadog، Sentry، Papertrail یا New Relic. چهار مورد آخر، ابزارهای پایش عملکرد اپلیکیشن (APM) هستند، اما همه آنها ابزارهای شخص ثالثیاند که تمرکزشان روی کاری است که عاملهای گزارشدهیشان در فضای ابری انجام میدهند. این موضوع معمولاً بینش زیادی درباره کد ما نمیدهد، مگر اینکه اپلیکیشن سازمانی یا مقیاسپذیرتان را طوری نوشته باشید که همهچیز را بهشکل کارآمد لاگ کند.
من راهاندازی عاملهای مانیتورینگ از این جنس را بهخصوص دردناک پیدا کردهام و شاید کمی هم بیش از حد، پیچیده باشد (البته گاهی از سر اجبار). هدف این مقاله این است که نشان دهد لاراول چطور بخش زیادی از این پیچیدگی را حذف کرده است. بنابراین میخواهیم Laravel Nightwatch را بالا بیاوریم و با مقداری کد ساختگی برای درخواستهای سرور، آن را تست کنیم.
وقتی وبهوکهای تولیدی (production) اجرا میکنید، خراب شدن چیزها خیلی راحت است: پیامها شکست میخورند، payloadها تغییر میکنند، یا پاسخها کند میشوند. ما توسعهدهندگان برای جلو افتادن از این مشکلات سراغ ابزارهای مانیتورینگ میرویم، اما این فضا میتواند ترسناک به نظر برسد. حتی فقط در دنیای PHP، Blackfire و Tideways را دارید و بعد APMهای سنگین مثل Datadog، Sentry، Papertrail یا New Relic. این ابزارها قدرتمندند، اما عاملهای شخص ثالثی هستند که بیشتر روی گزارشدهی سمت ابر تمرکز دارند و همیشه آن بینشی را که میخواهیم درباره اینکه کد Laravel ما واقعاً چه میکند، به ما نمیدهند.
و اگر صادق باشیم، راهاندازی بعضی از این عاملها… دردناک است. کار میکنند، اما اغلب از سر ضرورت، بیش از حد پیچیده به نظر میرسند.
Laravel Nightwatch که امسال عرضه شده، میخواهد این وضعیت را تغییر دهد. این ابزار به توسعهدهندگان PHP یک روش داخلی و بومیِ لاراول میدهد تا رویدادها و عملکرد اپلیکیشن را بدون دردسرهای معمولِ پیکربندی پایش کنند. در این آموزش، یک گیرنده وبهوک ساده با استفاده از پیامهای Vonage RCS راه میاندازیم، چند ترافیک ساختگی به آن میفرستیم و میبینیم همهچیز در داشبورد Nightwatch زنده میشود.
لاراول بخش زیادی از کار سنگین مانیتورینگ اپ را از دوش شما برداشته است. ببینیم با چند قدم ساده تا کجا میرسیم.
پیشنیازها
-
PHP8.1+
-
Composer
-
Laravel Installer
-
NodeJS 22+
-
یک حساب Laravel Nightwatch (مراحلش هم داخل آموزش آمده)
راهاندازی اپلیکیشن Laravel
ما داریم یک اپلیکیشن مانیتورینگ میسازیم، اما به چیزی نیاز داریم که مانیتورش کنیم! برای این کار، یک اپ با RCS Messaging میسازیم. پیامرسانی RCS یک پروتکل جدید و براق برای ارسال پیامهایی شبیه SMS است، اما با قابلیتهای خیلی بیشتر. میتوانید با استفاده از Vonage Messages API و یک Vonage Application پیامهای RCS را ارسال و دریافت کنید، درست مثل SMS. تفاوت واقعی RCS در چیزی است که میتواند منتقل کند: رسانه با کیفیت بالاتر (صدا، تصویر، ویدئو)، اندازه فایل بزرگتر، و عناصر تعاملی غنیتر مثل پاسخهای پیشنهادی، اقدامات پیشنهادی، کارتها و کاروسلها.
چیزی که قرار است شبیهسازی کنیم، مانیتور کردن دادههای ورودی هنگام پاسخ دادن کاربران است؛ وقتی کاربر نهایی روی دستگاهش پاسخ میدهد، یک اپلیکیشن RCS واقعی طوری پیکربندی میشود که وبهوکها را به یک URL مشخص شلیک کند تا داده مصرف شود.
میانبر اینجاست که لازم نیست همه آن پیکربندیها را انجام بدهیم. چیزی که میخواهم نشان بدهم این است که داشبورد Nightwatch پیکربندی شده و دادههای اپلیکیشن را دریافت میکند.
با استفاده از Laravel installer (که دور create-project کامپوزر را بستهبندی میکند)، یک اپلیکیشن جدید Laravel در ترمینال بسازید:
میتوانید گزینههای scaffolding را نادیده بگیرید، فقط احتمالاً بهتر است SQLite را بهعنوان دیتابیس انتخاب کنید چون سریع راه میافتد.
برای یاد گرفتن اینکه چطور با SQLite شروع کنید، مقاله قبلی من با عنوان “The Return of SQLite” را ببینید.
وقتی فرایند تمام شد، اپلیکیشن جدید Laravel را داخل IDE مورد علاقهتان باز کنید. بهتر است بتوانید رکوردهای دیتابیس را ببینید، پس اگر از VSCode استفاده میکنید یک افزونه SQL داشته باشید، یا اگر PHPStorm دارید، منبع دیتابیس را تنظیم کنید.
این اپلیکیشن یک موجودیت خواهد داشت: Webhook که مدلِ دادههای ورودی را نمایش میدهد. داده ورودی چیست؟ یک وبهوک RCS از Vonage است (یا یک نمونه ساختگی از آن). اگر به مشخصات OpenAPI آن نگاه کنید، میتوانید یک مثال ساختگی پیدا کنید:
ساخت مدلها و مایگریشنها
برای ساخت این ساختار به یک مدل و یک مایگریشن نیاز داریم که میتوانید با یک دستور از خط فرمان بسازید:
با سوییچ --migration، دستور هم مدل را میسازد و هم مایگریشن را. دنیای هوش مصنوعی این روزها کارها را کمی کمزحمتتر کرده، برای همین من این JSON را به یک عامل هوش مصنوعی دادم و گفتم «کد مایگریشن را برای این JSON تولید کن»، و همان لحظه تولیدش کرد (و جالب اینکه بدون اشتباه!). این هم مایگریشن:
تا اینجا همهچیز خوب است. این مدل قرار است توسط یک کنترلر با داده پر شود، بنابراین برای اینکه سریع انجامش دهیم، باید مدل را طوری ویرایش کنید که همه ویژگیها قابل پرشدن (fillable) باشند. به app\Models\Webhook.php بروید و مدل را اینطور تغییر دهید:
عالی، حالا مدل میتواند با داده ورودی بهصورت خودکار پر شود (و نیازی نیست روی داده ورودی اعتبارسنجی انجام دهید، چون به Vonage اعتماد دارید، درست است؟ ولی جدی: در تولید، همیشه ورودیها را اعتبارسنجی کنید).
ساخت Route و Controller
برای رسیدگی به داده ورودی، به یک route نیاز داریم که به یک متد در Controller وصل باشد. میتوانید این منطق را مستقیم داخل فایل route یعنی web.php بهصورت closure بنویسید، اما در این آموزش میخواهم کمی سنتیتر جلو بروم.
در این کنترلر تا حد ممکن کمکد نوشتهام تا payload را مدیریت کند:
در routeها باید مشخص کنیم که route به متد handle() در WebhookController متصل است. متد handle() فقط یک کار انجام میدهد (شاید تنها باری که اصل «تکمسئولیتی» را رعایت کردهام!) و آن این است که بدنه درخواست را میگیرد، آن را داخل یک موجودیت Webhook میریزد و در دیتابیس ذخیره میکند.
به Routes\web.phpand بروید و route را اضافه کنید:
ارسال یک درخواست به این endpoint (اگر دوست دارید امتحان کنید!) الان کار نمیکند. route هست، controller هست، اما فقط پاسخ ۴۱۹ میگیرید. دلیلش این است که من یک توسعهدهنده بدِ لاراول بودهام، اما میتوانم به شما بگویم چطور توسعهدهنده خوب لاراول باشید. من میخواهم با کمترین منطق ممکن انجامش بدهم، پس قالب آماده API را scaffold نکردم؛ همان قالبی که اگر بخواهید وجود دارد. چون این route در web.php است، لاراول انتظار دارد این درخواستهای GET و POST برای ساخت HTML باشند، نه APIهای REST JSON بلادرنگ. بنابراین یک HTTP 419 میدهد چون انتظار نوعی درخواست فرم را زیر POST دارد و این فرمها توکن Cross-Site Request Forgery دارند.
راه سریع و کثیف برای دور زدن این موضوع این است که middleware را دستکاری کنیم (هرگز این کار را در تولید انجام ندهید!). این را اضافه کردهام تا کمی نشان بدهم در زمان اجرا، زیرِ کاپوتِ Laravel چه اتفاقی میافتد. به فایل bootstrap فریمورک بروید که در bootstrap\app.php قرار دارد.
این فایل نقطه شروع پیکربندی اپلیکیشن شماست، درست بعد از entry point. میتوانید فایلهای route سفارشی، هندلرهای خطای سفارشی در سطح global و middlewareهای سفارشی را تنظیم کنید. در سالهای توسعه Laravel، فکر میکنم دستکاری چیزی در سطح global مثل این معمولاً ایده بدی است، اما من هم دوست دارم درخواستمان کامل شود.
bootstrap\app.php
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
در دنیای واقعی، کل هدف استفاده از چیزی مثل Nightwatch، پایش عملکرد اپلیکیشن است؛ که از سر ضرورت یعنی احتمالاً با تعداد زیادی job ناهمگام در صف، کشینگ زیاد، لاگ زیاد و درخواستهای زیاد سروکار دارید. وقتش است با روش «فعلاً ادای حرفهایها را دربیاوریم تا حرفهای شویم»، جلو برویم. و من سادهترین راه را برای پر شدن داشبورد انتخاب کردم: درخواستها.
فقط یک نوع درخواست ورودی داریم و آن هم Webhook است. بنابراین به یک کد یا یک سرور نیاز داریم که این درخواستها را تولید کند و به اپلیکیشن ما شلیک کند. ابزارهای زیادی برای این کار هست: Prisma، Postman، Insomnia، Wiremock و غیره. به قول یکی از فلسفههای خودم: «ساده نگهش دار، احمقانهاش نکن!» یک فایل JavaScript که در یک حلقه مثل سرور عمل کند.
در ریشه پروژه، یک پوشه بسازید و اسمش را مناسب بگذارید:
این چهار دستور پوشه را میسازند، مدیریت وابستگیها را اضافه میکنند و فایلی که قرار است بهعنوان سرور اجرا کنیم را ایجاد میکنند. سه چیز لازم داریم تا این درخواستها را در یک حلقه ارسال کنیم:
-
Axios، برای ارسال درخواستهای AJAX
-
UUID، برای تولید شناسههای یکتای ساختگی برای وبهوکهای ساختگی
-
Faker، کتابخانهای برای تولید داده ساختگیِ شماره تلفن و متن
برای نصب همه با هم، این دستور را در خط فرمان بزنید:
کد کامل سرور به این شکل است (بدون هیچ تغییری):
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(۰, ۳۰۰)
};
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, ۲۰۰);
console.log(“Running Mock Webhook Server”);
کالبدشکافی کد سرور
ما دو ثابت داریم: ERROR_RATE و ERROR_FIELDS. اولی یک عدد اعشاری است که احتمال خطا هنگام ارسال درخواستها را مشخص میکند، و دومی نشان میدهد کدام فیلدها از JSON ارسالی حذف میشوند تا خطا ایجاد شود.
generateWebhookPayload() متدی است که شکل payload JSON را میسازد و همچنین تصمیم میگیرد payload معتبر باشد یا نه. بالاخره به کد دیگران هیچوقت اعتماد نکنید، درست است؟ اینجا همان نقطهای است که از تابع کمکی uuidv4() برای ساخت UUID و از faker برای تولید متن ساختگی تا ۳۰۰ کاراکتر استفاده میکنیم (این مقدار دلخواه است، پیامهای RCS میتوانند تا ۳۰۰۰ کاراکتر هم باشند).
آن خط ریاضیِ باحالِ if (Math.random() < ERROR_RATE) { مشخص میکند آیا قرار است یکی از فیلدهای لازم را حذف کند یا نه. بالاخره Nightwatch برای دیدن نقاط مشکل در اپلیکیشن شماست.
sendWebhook() متدی است که داخل حلقه رویداد فراخوانی میشود، payload را میسازد و سپس به اپلیکیشن ما ارسالش میکند. این دقیقاً با این خط اجرا میشود:setInterval(sendWebhook, 200) // <- set this according to how quick you want to generate dummy data
حالا میتوانید آن را اجرا کنید، اما یادتان باشد دیتابیس SQLite خیلی سریع پر میشود. وقت Nightwatch است.
راهاندازی Nightwatch
زیبایی Nightwatch این است که رایگان میتوانید با آن ور بروید. پلن رایگان تا ۳۰۰ هزار رویداد اپلیکیشن را روی پلتفرم ابری ثبت میکند، که برای بازی و تست کاملاً کافی است.
به سایت Nightwatch بروید و یک حساب و یک اپلیکیشن بسازید. چهار مرحله آنجا خوب مستند شده است. اول اسم اپلیکیشن را بگذارید و یک منطقه ذخیرهسازی متناسب با موقعیتتان انتخاب کنید.

بعد میرسیم به بخشهایی که واقعاً سردرد راهاندازی یک APM را کم میکند. به شما میگوید ابتدا Nightwatch را با Composer نصب کنید:
یک سری کلید به شما میدهد تا داخل فایل متغیرهای محیطیتان قرار دهید (اینها کلیدهای ساختگی هستند، برای دوستانی که چشم عقاب امنیتی دارند):

مرحله سوم جلوی این را میگیرد که حساب Nightwatch شما در همان ساعت اول به سقف سهمیه برسد: یادتان باشد ممکن است میلیونها رویداد را مانیتور کنید، پس Nightwatch امکان تعریف sample size را بهصورت متغیر محیطی میدهد:

در نهایت، Nightwatch را در کنسول محلی اجرا میکنید و sample rate را به سرورهای Nightwatch میفرستد:

در فقط چهار مرحله، شما یک APM دارید. سرور ساختگی Webhook با Node را اجرا کنید و دادهها سرازیر میشود:

بهنحوی حتی موفق شدم یک درخواست خرابکار هم بسازم که زمان CPU را میبلعد، که میتوانید آن را بهصورت یک اسپایک در نمودار عملکرد سمت راست ببینید. کلی قابلیت دارید تا بیشتر در دادههای اپلیکیشنتان ریز شوید: خطاهای SQL، استثناهای حافظه، jobهای ناقص، هرچی فکرش را بکنید. چون من داشتم بهصورت تصادفی استثنا تولید میکردم، رفتم ببینم ظاهرشان چطور است:

خوب است. حتی آن را به handled و unhandled هم تقسیم میکند، تا دید بهتری از میزان دفاعی بودن کد اپلیکیشنتان داشته باشید.
نتیجهگیری
هر سال انگار به توسعهدهندگان یک بخش جدید از پشته اپلیکیشن Laravel هدیه داده میشود که قبلاً پوشش داده نشده بود، و این هم از همانهاست. چیزی که Nightwatch را متمایز میکند این است که فروشندگان شخص ثالث ابزارهای مانیتورینگ و عملکرد اپلیکیشن، برای Laravel شخصیسازی نشدهاند؛ بر خلاف چیزی که خود Laravel ساخته باشد. از این نظر، برای کسانی که با حجم عظیمی از داده سروکار دارند (استفاده از Vonage Verify، Messages یا Voice API سیستمهای وبهوکی ایجاد میکند که حجم زیادی وبهوک تولید میکند)، این یک بردِ خیلی راحت است که واقعاً سخت میشود نادیدهاش گرفت.
