どのコマンドがいつ実行されて、どのくらい実行時間がかかったのか。
実行時の処理件数や最大使用メモリを知りたかったので、試しにやってみました。
環境
- PHP: 7.4.x
- Laravel: 7.x
作成するファイルについて
- app/Console/Command.php
- app/Console/CommandResult.php
- app/Console/CommandTimer.php
- app/Console/Commands/HelloWorldCommand.php
上記の4つのファイルを作成します。
app/Console/Command.php
Illuminate\Console\Command クラスを継承して App\Console\Command
クラスを作成します。
やってることはコマンド実行前にコマンドの計測を開始して、コマンド実行後に計測結果を出力するようにしてます。
app/Console/Command.php
<?php declare(strict_types=1);
namespace App\Console;
use Exception;
use Illuminate\Console\Command as BaseCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
abstract class Command extends BaseCommand
{
protected CommandTimer $timer;
protected int $statusCode = 0;
public function __construct()
{
parent::__construct();
$this->timer = new CommandTimer();
}
/**
* Execute the console command.
*
* @param InputInterface $input
* @param OutputInterface $output
* @return mixed
* @throws
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$this->beforeHandle();
try {
$result = $this->laravel->call([$this, 'handle']);
if ($result instanceof CommandResult) {
$addMessage = $result->getMessage();
$this->statusCode = $result->getStatusCode();
}
$this->afterHandle($addMessage ?? '');
} catch (Exception $e) {
$this->errorHandle();
throw $e;
}
return $this->statusCode;
}
/**
* コマンド実行前の処理
* @throws Exception
*/
protected function beforeHandle(): void
{
$this->timer->start();
$message = sprintf('[Start] Execute command %s is started', $this->name);
$this->comment($message);
info($message);
}
/**
* コマンド実行後の処理
* @param string $addMessage
* @throws Exception
*/
protected function afterHandle(string $addMessage): void
{
$this->timer->stop();
$message = sprintf('[Successful] Execute command %s is finished, %s seconds, max memory: %s MB.', $this->name, $this->timer->getTotalSeconds(), $this->getMaxMemory());
$this->comment($message . ' ' . $addMessage);
info($message . ' ' . $addMessage);
}
/**
* コマンド実行エラーの処理
* @throws Exception
*/
protected function errorHandle(): void
{
$this->timer->stop();
$message = sprintf('[Failed] Execute command %s is finished, %s seconds, max memory: %s MB.', $this->name, $this->timer->getTotalSeconds(), $this->getMaxMemory());
$this->error($message);
logger()->critical($message);
}
/**
* コマンド実行時に割り当てられたメモリの最大値をMB単位で返す
* @return float
*/
private function getMaxMemory(): float
{
return memory_get_peak_usage(true) / (1024 * 1024);
}
}
app/Console/CommandTimer.php
コマンドの実行時間を計測するクラスを作成します。
app/Console/CommandTimer.php
<?php declare(strict_types=1);
namespace App\Console;
use Carbon\Carbon;
use DateTime;
use Exception;
/**
* コマンドの実行時間を計測
*/
class CommandTimer
{
private const DATE_FORMAT = 'Y-m-d H:i:s.u';
private Carbon $startedTime;
private Carbon $stoppedTime;
/**
* @return DateTime
* @throws Exception
*/
public function start(): DateTime
{
return $this->startedTime = new Carbon();
}
/**
* @return DateTime
* @throws Exception
*/
public function stop(): DateTime
{
return $this->stoppedTime = new Carbon();
}
/**
* @return float
* @throws Exception
*/
public function getTotalSeconds(): float
{
if (empty($this->startedTime)) {
throw new Exception('The timer has not started.');
}
if (empty($this->stoppedTime)) {
throw new Exception('The timer has not stopped');
}
return $this->startedTime->floatDiffInSeconds($this->stoppedTime);
}
/**
* @return string
*/
public function getStartedTime(): string
{
return $this->startedTime->format(self::DATE_FORMAT);
}
/**
* @return string
*/
public function getStoppedTime(): string
{
return $this->stoppedTime->format(self::DATE_FORMAT);
}
}
app/Console/CommandResult.php
app/Console/CommandResult.php
<?php declare(strict_types=1);
namespace App\Console;
/**
* コマンドの実行結果
*/
class CommandResult
{
private int $statusCode = 0;
private int $successCount = 0;
private int $failedCount = 0;
/**
* @param int $statusCode
*/
public function setStatusCode(int $statusCode): void
{
$this->statusCode = $statusCode;
}
/**
* @return int
*/
public function getStatusCode(): int
{
return $this->statusCode;
}
public function addSuccess(): void
{
$this->successCount++;
}
public function addFail(): void
{
$this->failedCount++;
}
/**
* @param int $count
*/
public function setSuccess(int $count): void
{
$this->successCount = $count;
}
/**
* @param int $count
*/
public function setFail(int $count): void
{
$this->failedCount = $count;
}
/**
* @return int
*/
public function getSuccessCount(): int
{
return $this->successCount;
}
/**
* @return int
*/
public function getFailedCount(): int
{
return $this->failedCount;
}
/**
* @return int
*/
public function getTotalCount(): int
{
return $this->successCount + $this->failedCount;
}
/**
* @return string
*/
public function getMessage(): string
{
return sprintf('Total: %d Success: %d Failed: %d', $this->getTotalCount(), $this->getSuccessCount(), $this->getFailedCount());
}
}
試しにコマンドを作成する
app/Console/Commands/HelloWorldCommand.php
app/Console/Commands/HelloWorldCommand.php
<?php declare(strict_types=1);
namespace App\Console\Commands;
use App\Console\Command;
use App\Console\CommandResult;
class HelloWorldCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'hello:world';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Hello World を出力するコマンド';
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$commandResult = new CommandResult();
$this->comment('Hello World');
$commandResult->addSuccess();
return $commandResult;
}
}
コマンドを実行
$ php artisan hello:world
[Start] Execute command hello:world is started
Hello World
[Successful] Execute command hello:world is finished, 0.05698 seconds, max memory: 20 MB. Total: 1 Success: 1 Failed: 0
このようにコマンドの実行結果が出力されます。
- 実行時間: 0.05698 秒
- 最大使用メモリ: 20 MB
- 実行件数: 1件
- 成功件数: 1件
- 失敗件数: 0件
ログファイルも確認してみます。
storage/logs/laravel.log
[2020-06-16 09:30:00] local.INFO: [Start] Execute command hello:world is started
[2020-06-16 09:30:00] local.INFO: [Successful] Execute command hello:world is finished, 0.05698 seconds, max memory: 20 MB. Total: 1 Success: 1 Failed: 0
ログにも同様の内容が出力されます。
追記: スタブの差し替え
stubs/console.stub
ファイルを差し替えておくと、
php artisan make:command
した時に変更したテンプレートを使用できます。
stubs/console.stub
<?php declare(strict_types=1);
namespace {{ namespace }};
use App\Console\Command;
class {{ class }} extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = '{{ command }}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
//
}
}