課題
- 各バッチ処理が成否を素早く確認したい
- またサーバが落ちてしまった場合に何が処理できていなかったのかの確認も素早くしたい
- ログ出力だと確認に時間がかかる。
対処方法
- バッチ処理結果を保存するDBを作成。今回は
JobsLog
とする
- 以下のようなクラスの作成
<?php
class JobsLog extends Eloquent
{
protected $table = 'jobs_log';
protected $casts = [
'status' => 'int'
];
const STATUS_PROCESS = 1;
const STATUS_SUCCESS = 2;
const STATUS_FAILED = 0;
const STATUS_NOT_RUN = 3;
const UNSENT_MAIL = 0;
const SENT_MAIL = 1;
const WEEK_MAP = [
0 => 'SU',
1 => 'MO',
2 => 'TU',
3 => 'WE',
4 => 'TH',
5 => 'FR',
6 => 'SA',
];
const TYPE_DAILY = 1;
const TYPE_WEEKLY = 2;
const TYPE_MONTHLY = 3;
const TYPE_MINUTELY = 4;
protected $dates = [
'start_time',
'end_time'
];
protected $fillable = [
'job_name',
'status',
'start_time',
'end_time',
'message',
'send_mail',
'created_at',
];
public static function createJobLog(String $jobName)
{
JobsLog::firstOrCreate(
[
'job_name' => $jobName,
'created_at' => Carbon::now()->format('Y-m-d'),
],
[
'status' => JobsLog::STATUS_PROCESS,
'start_time' => Carbon::now(),
]
);
}
public static function updateJobLog(String $jobName)
{
JobsLog::where([
'job_name' => $jobName,
'created_at' => Carbon::now()->format('Y-m-d'),
'status' => JobsLog::STATUS_PROCESS,
])->update([
'status' => JobsLog::STATUS_SUCCESS,
'end_time' => Carbon::now(),
]);
}
public static function jobFail(String $jobName, $message)
{
JobsLog::where([
'job_name' => $jobName,
'created_at' => Carbon::now()->format('Y-m-d'),
'status' => JobsLog::STATUS_PROCESS,
])->update([
'status' => JobsLog::STATUS_FAILED,
'end_time' => Carbon::now(),
'message' => $message,
]);
}
public static function createLogJobNotRun(String $jobName)
{
JobsLog::firstOrCreate(
[
'job_name' => $jobName,
'created_at' => Carbon::now()->format('Y-m-d'),
],
[
'status' => JobsLog::STATUS_NOT_RUN,
'send_mail' => JobsLog::SENT_MAIL,
]
);
}
public static function listJobRun()
{
$listJob = [];
$media = (env('GET_MEDIAS')) ? true : false;
$getLocalPost = (env('GET_LOCAL_POSTS')) ? true : false;
if ($media) {
$key = (new MediasPreserveScheduler())->getCmd();
$listJob[$key] = array(
'status' => $media,
'type' => JobsLog::TYPE_DAILY,
'time' => config('schedule.get_medias'),
'dayOfWeek' => '',
'dayOfMonth' => ''
);
}
if ($getLocalPost) {
$key = (new LocalPostsPreserveScheduler())->getCmd();
$listJob[$key] = array(
'status' => $getLocalPost,
'type' => JobsLog::TYPE_DAILY,
'time' => config('schedule.local_posts_get'),
'dayOfWeek' => '',
'dayOfMonth' => ''
);
}
return $listJob;
}
}
- バッチ処理がサーバダウンで処理されていなかった場合にslackに通知するクラス
<?php
class ScanJobSchedule extends Command
{
@var
protected $signature = 'jobs:check_log';
@var
protected $description = 'Check the to-do list and send a failure message';
@return
public function __construct()
{
parent::__construct();
}
@return
public function handle()
{
try {
$runJobs = JobsLog::listJobRun();
$jobs = JobsLog::whereDate('created_at', '=', date('Y-m-d'));
$this->failJobs($jobs);
if (!empty($runJobs)) {
$this->ranJobs($runJobs, $jobs);
}
Log::info('jobs:check_log: success');
} catch (\Throwable $e) {
echo $e->getMessage();
Log::error($e->getMessage());
}
}
public function failJobs($jobs)
{
$jobs = (clone $jobs)->where('status', JobsLog::STATUS_FAILED)->get();
foreach ($jobs as $job) {
if ($job->send_mail == JobsLog::UNSENT_MAIL) {
$msg = 'Command: '. $job->job_name .' ('. env('APP_URL') . ') 》not run ' . "\n" . $job->message;
Notification::route('slack', env('SLACK_WEBHOOK_URL'))
->notify(new UpdateStoreByUsingCsvFileNotification($msg));
Log::error($msg);
$job->update([
'send_mail' => JobsLog::SENT_MAIL,
]);
}
}
}
public function ranJobs($runJobs, $jobs)
{
foreach ($runJobs as $key => $runJob) {
if ($runJob['type'] == JobsLog::TYPE_DAILY || $runJob['type'] == JobsLog::TYPE_MINUTELY) {
$timeRunSub2 = Carbon::parse($runJob['time'])->addMinutes(2);
$timeCurrent = Carbon::now();
if ($timeRunSub2->greaterThan($timeCurrent)) {
continue;
}
if ((clone $jobs)->where('job_name', $key)->exists()) {
continue;
}
$msg = 'Command: '. $key .' ('. env('APP_URL') . ') 》not run';
Notification::route('slack', env('SLACK_WEBHOOK_URL'))
->notify(new UpdateStoreByUsingCsvFileNotification($msg));
Log::error($msg);
JobsLog::createLogJobNotRun($key);
}
if ($runJob['type'] == JobsLog::TYPE_WEEKLY) {
$dayOfTheWeek = Carbon::now()->dayOfWeek;
if (JobsLog::WEEK_MAP[$dayOfTheWeek] != $runJob['dayOfWeek']) {
continue;
}
$timeRunSub2 = Carbon::parse($runJob['time'])->addMinutes(2);
$timeCurrent = Carbon::now();
if ($timeRunSub2->greaterThan($timeCurrent)) {
continue;
}
if ((clone $jobs)->where('job_name', $key)->exists()) {
continue;
}
$msg = 'Command: '. $key .' ('. env('APP_URL') . ') 》not run';
Notification::route('slack', env('SLACK_WEBHOOK_URL'))
->notify(new UpdateStoreByUsingCsvFileNotification($msg));
Log::error($msg);
JobsLog::createLogJobNotRun($key);
}
}
}
}
<?php
if (env('GET_MEDIAS')) {
$cmd = (new MediasPreserveScheduler())->getCmd();
$schedule->command('medias:get')->dailyAt(config('schedule.get_medias'))
->sendOutputTo(storage_path('logs/' . strval(date('Y-m-d')) . '_medias' . '.log'))
->before(function () use ($cmd) {
JobsLog::createJobLog($cmd);
})
->after(function () use ($cmd) {
JobsLog::updateJobLog($cmd);
})->runInBackground();
}
if (env('IKKATSU_JOB_CHECK')) {
$schedule->command('jobs:check_log')
->everyFiveMinutes();
}
MediasPreserveScheduler.php
の処理
<?php
class MediasPreserveScheduler extends Command
{
@var
protected $signature = 'medias:get';
@var
protected $description = 'get medias information per month';
@return
public function __construct()
{
parent::__construct();
}
@return
@throws
public function handle()
{
try {
} catch (Throwable $e) {
report($e);
JobsLog::jobFail($this->getCmd(), $e->getMessage());
}
}
public function getCmd()
{
return $this->signature;
}
}