Website uptime monitor with Laravel - 2

Website uptime monitor ရဲ့အပိုင်းနှစ်ဖြစ်ပါတယ်။ cron ဘယ်လို setup လုပ်မလဲနှင့် နောက်ထပ် command အသစ်တစ်ခုထည့်ထားပါတယ်။ UI လည်းနည်းနည်းစထားတယ်။

Posted by Set Kyar Wa Lar (universe) on 2 weeks ago

ကျွန်တော်တို့အရင်ရေးခဲ့တဲ့အပိုင်းမှာဆိုရင် Website တွေကို monitoring လုပ်ဖို့ထည့်လို့ရပြီ။ Frequency ပေါ်မူတည်ပြီးကြည့်လို့လည်းရပြီ။ ဒါပေမယ့် cron ဘယ်လို setup လုပ်ရမလဲဆိုတာမပြောခဲ့သေးဘူး။ အရင်အပိုင်းကိုမဖတ်ရသေးတဲ့သူတွေရှိရင် ဒီမှာ ဖတ်လို့ရပါတယ်။ ဖတ်ပြီးသားသူတွေဆိုလည်းအရင်အပိုင်းမှာ ကျွန်တော် github source ပါထည့်ပေးထားပါတယ်။

ဒီအပိုင်းမှာတော့ ကျွန်တော်တို့ရေးထားတဲ့ command နှစ်ခုအပြင် နောက်ထပ် status ကြည့်လို့ရတဲ့ command နှင့် Initial UI setup ပါပါ့မယ်။ တစ်ဖြည်းဖြည်းချင်း feature စုံအောင်အတူတူထပ်ထည့်ကြပါ့မယ်။ ဒါနှင့် website မှာ comment section ပါထည့်ထားလို့ ဒီ website uptime monitor app မှာဘာ feature တွေထပ်ထည့်သင့်လဲ comments ရေးပြီးတောင်းဆိုနိုင်ပါတယ်

ကျွန်တော်တို့အရင်အပိုင်းမှာ website ကို ဘယ်မိနစ်တစ်ခါ check ဆိုပြီး check ဖို့ command ရေးထားတယ်။ နောက်အဲ့သတ်မှတ်ထားတဲ့မိနစ်တိုင်းအတွက်စစ်ဖို့ command ရေးထားတယ်။ ဒါပေမယ့်အဲ့ command က manually run နေရတယ်။ အဲ့ command ကို manual run ဖို့ cron job setup လုပ်ရပါ့မယ်။ Cron setup လုပ်တဲ့အပိုင်းမှာ mac နှင့် linux ကိုဘဲအဓိကပြောသွားမှာဖြစ်ပါတယ်။ Window သမားတွေကတော့ ဒီမှာ ကြည့်နိုင်ပါတယ်။

Cron setup လုပ်ဖို့ terminal မှာအောက်ဖော်ပြပါ command ကို run လိုက်ပါ။

crontab -e

ပြီးသွားပြီဆိုလို့ရှိရင် crontab မှာအောက်ဖော်ပြပါ code ကိုကူးထည့်ပြီးတော့ save လိုက်ပါ

* * * * * cd /ping-project-folder && php artisan schedule:run >> /dev/null 2>&1

/ping-project-folder နေရာမှာ ကိုယ့် local application တည်ရှိတဲ့ folder path ကိုထည့်ပေးရမှာဘဲဖြစ်ပါတယ်။ လက်ရှိ application တည်ရှိတဲ့ path ကိုသိချင်တယ်ဆိုရင် terminal ကနေ project folder ထဲဝင်ပြီးတော့ pwd လို့ရိုက်ပြီးတော့လည်း path ကိုသိနိုင်ပါတယ်။

ကျွန်တော်တို့အထက်မှာ crontab ထဲထည့်ခဲ့တာက every minutes မှာ php artisan schedule:run ကို run ပါ >> /dev/null 2>&1 အပိုင်းကတော့ command ကလာတဲ့ output တွေ result တွေကို discard လုပ်ပါလို့ပြောလိုက်တာပါ။

အသုံးဝင်မှု ပြန်လည်ဝေမျှခြင်း - https://crontab.guru က Cron expression တွေကို Human readable အဖြစ်ပြောင်းပေးတယ်... တစ်ခါတစ်လေကိုယ်ရေးတဲ့ expression ကမှန်သလားမှားသလားသိချင်ရင်အဲ့မှာသွားရေးဖြစ်တယ်

အခုကျွန်တော်တို့ cron ကို every minutes run ခိုင်းလိုက်တာ ကျွန်တော်တို့ create လုပ်ထားတဲ့ command ကိုမဟုတ်ဘဲ Laravel မှာ default ပါပြီးသား schedule:run command ကို run ထားတာပါ။ အဲ့ command ကမှ App/Console/Kernel.php ထဲက schedule method ထဲမှာသတ်မှတ်ထားတဲ့ command တွေကို run တာပါ။ ဒါကအကြမ်းဖျင်းရှင်းပြတာပါ။ တကယ်လို့ Laravel Core ရဲ့ Scheduler အကြောင်းကိုအသေးစိတ်ရေးပြစေချင်တယ်ဆိုရင်ဒီ post အောက်မှာ comment ပေးပြီး request လုပ်နိုင်ပါတယ်။

အထက်မှာပြောသလိုဘဲ ကျွန်တော်တို့ cron setup လုပ်ပြီးသွားပါပြီ။ သို့ပေမယ့် ကျွန်တော်တို့ command ကို schedule ထဲမှာမထည့်ရသေးပါဘူး။ ထည့်ဖို့ဆိုရင် App/Console/Kernel.php ထဲက schedule method ထဲမှာ အောက်ဖော်ပြပါ code ကို ကူးယူထည့်သွင်းလိုက်ပါ။

$schedule->command('ping:monitor-up-time')->everyMinute();

ဘာကိုဆိုလိုတာလည်းဆိုတာ code ကိုဖတ်ကြည့်တာနှင့်သိနိုင်မယ်ထင်ပါတယ်။ မိနစ်တိုင်းမှာ ping:monitor-up-time command ကို run မယ်ဆိုပြီးပြောထားတာပါ။ ပြီးသွားပြီဆိုရင် ကျွန်တော်တို့ထည့်ထားတဲ့ website တွေကို monitor သွားလုပ်ပြီး status update လုပ်ရဲ့လားဆိုတာကို DB မှာကြည့်နိုင်ပါတယ်။ ကျွန်တော့်ဆီမှာဆိုအောက်ဖော်ပြပါအတိုင်းမြင်ရပါတယ်...

google.com ကိုမိနစ်တိုင်းစစ်မယ်ဆိုပြီးအောက်ဖော်ပြပါအတိုင်းထည့်ထားတာပါ...

အခုဆိုရင်ကျွန်တော်တို့ cron က run နေပါပီ။ ဒါပေမယ့် ကျွန်တော်တို့ထည့်ထားတဲ့ endpoint ရဲ့ latest status တွေကိုကြည့်ချင်တယ်ဆိုရင် DB ကိုသွားကြည့်နေရတယ်။ အဲ့ဒီ့အစားကျွန်တော်တို့ command တစ်ခု create လုပ်လိုက်မယ်ဆိုရင် command ကနေကြည့်လို့ရသွားမယ်။

အကြံပေးချက် - cron ကိုသွားမပြင်ချင်ဘူးဒါပေမယ့် ကျွန်တော်တို့ command ကိုလည်းမ check စေချင်ဘူးဆိုရင် App\Console\Kernel.php ထဲက schedule method မှာကျွန်တော်တို့ command ကို comment သွားပိတ်ထားလို့ရပါတယ်။

ကျွန်တော်တို့ command အသစ်တစ်ခုဆောက်ဖို့အောက်ဖော်ပြပါ command ကို run လိုက်ပါ။

php artisan make:command CheckUpTimeStatus

အထက်ဖော်ပြပါ command က App\Console\Commands folder ထဲမှာ CheckUpTimeStatus ဆိုတဲ့ command class တစ်ခုဆောက်သွားပါလိမ့်မယ်။

Signature ကိုအောက်ဖော်ပြပါအတိုင်းပြောင်းလိုက်ပါ...

protected $signature = 'ping:check-uptime-status
                {end_point_id : End point ID to check for the status}';

Description ကိုအောက်ဖော်ပြပါအတိုင်းပြောင်းလိုက်ပါ..

protected $description = 'Check uptime status by ID.'

handle method ကိုအောက်ဖော်ပြပါအတိုင်းပြောင်းလိုက်ပါ

  public function handle()
  {
    $endPointId = $this->argument('end_point_id');

    $endPoint = EndPoint::with('statuses')
            ->find($endPointId);

    if(is_null($endPoint)) {
      $this->error("There is no endpoint id {$endPointId}.");

      return;
    }

    if($endPoint->statuses->isEmpty()) {
      $this->error("There is no statuses for this endpoint id {$endPointId}.");

      return;
    }

    $headers = ['ID', 'URI', 'Frequency', 'Status Code', 'Checked Time'];

    $statuses = $this->getEndPointStatues($endPoint);

    $this->table($headers, $statuses);
  }

signature နှင့် description ကတော့ပုံမှန်အတိုင်းပါဘဲ... signature မှာ ping:check-uptime-status နှင့် end point id ထည့်ပေးရမယ်ဆိုတာထည့်တယ်။ Description မှာဆို command ကဘာကိုဆိုလိုတာလည်းဆိုတာထည့်တယ်။

Handle method အကြောင်းနည်းနည်းရှင်းပြမယ်... မရှင်းပြခင် Final result ကိုအောက်ဖော်ပြပါ image မှာကြည့်ကြည့်လိုက်ကြရအောင်...

ပထမဆုံး command run တဲ့အချိန်မှာ endpoint က status ရှိတယ်။ ဒုတိယ command မှာဆို status မရှိဘူး တတိယ command မှာဆို endpoint id ပါမရှိဘူး။

အခု handle method ကိုပြန်ရှင်းမယ်... ပထမဆုံး user က argument အနေနှင့် pass လိုက်တဲ့ end point id ကို ယူတယ်။ ဒီနေရာမှာကျွန်တော်တိုက်ရိုက် id ကို pass ထားတယ်။ သိပ်မကောင်းဘူး။ user ကသို့မဟုတ်ကိုယ့် competator က id ကိုကြည့်ပြီးဘယ်လောက် user ထည့်ထားလဲဆိုတာကိုသိနိုင်တယ်။ နောက်ပိုင်းဒီကောင်ကို refactor ပြန်လုပ်မယ်။ အခုလောလောဆယ်တော့ဒီတိုင်းထားခဲ့ဦးမယ်။

ID ရလာတဲ့အခါမှာ EndPoint ကို statuses နှင့်အတူရှာလိုက်တယ်။ ဒီနေရာမှာကျွန်တော် findOrFail မသုံးဘဲဘာလို့ find ဘဲသုံးလဲဆိုရင် FindOrFail က throw လိုက်တဲ့ Exception ကို user ကိုပြန်မပြဘဲ custom message ပြချင်လို့ find ကိုဘဲသုံးလိုက်တာဖြစ်ပါတယ်။ Endpoint ရှိလားစစ်တယ်မရှိရင် Error message ပြတယ် statuses ရှိလားစစ်တယ်မရှိရင် error ပြတယ်။

အထက်ကအချက်တွေ pass လာပြီဆိုရင်သေချာတယ်ကျွန်တော်တို့ statuses ကို table နှင့်ပြလို့ရပြီ။ Table header ကိုပြင်ဆင်လိုက်တယ်။ statuses တွေကိုတော့ getEndPointstatues ဆိုတဲ့ private method တစ်ခု create လုပ်ထားတာကိုသုံးလိုက်တယ်။ အောက်ဖော်ပြပါ getEndPointStatuses ကို class ထဲကိုကူးယူထည့်သွင်းပေးပါ။

  private function getEndPointStatues($endPoint)
  {
    $endPointData = [
      'id' => $endPoint->id,
      'URI' => $endPoint->uri,
      'Frequency' => $endPoint->frequency,
    ];

      return $endPoint->statuses
        ->map(function($status) use ($endPointData) {
          $statusData = [
            $status->status_code,
            $status->created_at->diffForHumans(),
          ];
  
          return array_merge($endPointData, $statusData);
        });
    }
  

ကျွန်တော်တို့လိုချင်တာက ID, URI, Frequency က endPoint က status code နှင့် status ဘယ်အချိန်တုန်းကဆွဲခဲ့လဲဆိုတာက statues table ထဲကသိရတာဖြစ်တဲ့အတွက် endpoint ရဲ့ statuses ကို map လုပ်ပြီး filter လုပ်လိုက်တာဖြစ်ပါတယ်။

ဒီအပိုင်းမှာဆိုလည်း statuses တွေကကျွန်တော်တို့စမ်းနေတာ နည်းနည်းလေးမလို့ အဆင်ပြေနေတယ်။ နောက်ပိုင်းတကယ့် user သုံးလာတဲ့အခါကျရင်အများကြီးဖြစ်လာနိုင်တယ်။ ဒါကိုလည်းနောက်အပိုင်းတွေမှာ refactor ပြန်လုပ်မယ်။ ကျွန်တော်စဥ်းစားနေတာက limit ထည့်ပေးပြီးတော့ statuses တွေကို limit နဲ့ပြန်ကြည့်မလားပေါ့... ဒါမှမဟုတ် date between နဲ့ပေး filter လုပ်။ ဘယ် use case ကောင်းမလဲဆိုတာသေချာစဥ်းစားပြီးမှ refactor ပြန်လုပ်တော့မယ်။

နောက်တစ်ဆင့်အနေနှင့် UI အပိုင်းနည်းနည်းစထားလိုက်ကြရအောင်... ဒီ series မှာတော့ bootstrap ဘဲသုံးမယ်။ View ကိုတော့ https://laravel-livewire.com သုံးမယ်။ ဒီအပိုင်းမှာတော့ Laravel livewire အကြောင်းသိပ်မပါသေးဘူး။ အစပိုင်းအနေနှင့် Laravel ရဲ့ default auth ဘယ်လိုထည့်မလဲ ရေးသားသွားပါ့မယ်။

Laravel Default Auth ထည့်ဖို့အတွက် အောက်ဖော်ပြပါ command ကို run နိုင်ပါတယ်။

composer require laravel/ui 
php artisan ui bootstrap --auth

ပထမဆုံး Laravel ရဲ့ default auth ကိုထည့်ဖို့အတွက် laravel/ui package အရင်ဆုံးထည့်ရပါတယ်။ ပြီးသွားရင် generate ထုတ်ပါ့မယ်။ ကျွန်တော်တို့ use case မှာဆိုရင် bootstrap သုံးမှာဖြစ်တဲ့အတွက် bootstrap option ကိုရွေးပြီးတော့ generate ထုတ်လိုက်တာဖြစ်ပါတယ်။

ပြီးသွားရင် အောက်ဖော်ပြပါ command ကို run ပြီးတော့ scaffold ကို compile လုပ်လိုက်ပါ။

npm install && npm run dev

ပြီးသွားပြီဆိုရင် php artisan migrate နှင့် php artisan serve ကို run ပြီးတော့ register/login လုပ်လို့ရပါပြီ။

ဒီနေ့အပိုင်းကတော့ဒီလောက်ပါဘဲ... နောက်အပိုင်းကျမှ UI နှင့် refactoring တွေဆက်လာပါဦးမယ်။ post နှင့်ပတ်သတ်ပြီး မေးစရာရှိရင် comment မှာမေးနိုင်ပါတယ်။ ထုံးစံအတိုင်းဘဲ ဒီအပိုင်းမှာပြီးခဲ့တဲ့ code တွေကို Github မှာတင်ထားပေးပါတယ်။

Hey, I am Set Kyar Wa Lar. Software Engineer at Carro. Maintainer of Laravel Myanmar and creator of MyanmarDevJobs.