自定義 Command 檢視 Laravel 日誌

心智極客發表於2019-09-26

Unix 的 tail 命令可以用來檢視檔案的,例如檢視 2019-09-26 的最後 100 行日誌

$ cd storage/logs  
$ tail -100 laravel-2019-09-26.log 

tail 封裝成 artisan 指令,可實現命令列快速檢視日誌,spatie/laravel-tail 擴充套件包實現了該功能,類似功能實現如下。

建立命令

$ php artisan make:command LaravelTailCommand 

需要做兩部分的功能:

  1. 獲取最新的日誌檔案;
  2. 轉化成終端的命令執行;

獲取最新的日誌檔案

依賴注入 Filesystem

use Illuminate\Filesystem\Filesystem;
.
.
.
class LaravelTailCommand extends Command
{
    /**
     * The filesystem instance.
     *
     * @var \Illuminate\Filesystem\Filesystem
     */
    protected $files;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct(Filesystem $files)
    {
        parent::__construct();
        $this->files = $files;
    }
}

獲取全部日誌檔案

/**
 * 獲取全部日誌檔案
 * 
 * @return array
 */
public function allLogFiles() : array
{
    return $this->files->allFiles(storage_path('logs'));
}

判斷某個日誌檔案是否為 laravel 日誌檔案

use SplFileInfo;
.
.
.
/**
 * 是否為 Laravel 日誌檔案
 * 
 * @param  SplFileInfo $file 
 * @return boolean           
 */
public function isLaravelLogFile(SplFileInfo $file) : bool
{
    return strpos($file->getBaseName(),'laravel') !== false;
}

獲取最新的日誌檔案

/**
 * 獲取最新的日誌路徑
 * 
 * @return string | false
 */
public function findLatestLogFile() 
{   
    $logFile = collect($this->allLogFiles())
        ->filter(function(SplFileInfo $file){
            return $this->isLaravelLogFile($file);
        })
        ->sortByDesc(function (SplFileInfo $file) {
            return $file->getMTime();
        })
        ->first();

    return $logFile
        ? $logFile->getPathname()
        : false;
}

轉化成終端的命令執行

預設顯示 100 行,且預設只顯示日誌摘要,不顯示詳情

/**
 * The name and signature of the console command.
 *
 * @var string
 */
protected $signature = 'tail
                        {--lines=100 : 輸出行數}
                        {--stack : 是否顯示詳細資訊,預設為不顯示}';

對於是否顯示詳情的處理

/**
 * 根據引數來判斷是否顯示詳情
 * 
 * @return null | string
 */
public function getFilters()    
{   
    return $this->option('stack') 
        ? null
        : '| grep -i -E "^\[[0-9]{4}\-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\]" --color';

}

最終處理

use Symfony\Component\Process\Process;
.
.
.
/**
 * Execute the console command.
 *
 * @return mixed
 */
public function handle()
{
    $latestLogPath = $this->findLatestLogFile();

    $lines = $this->option('lines');
    $filters = $this->getFilters();
    $tail = "tail -n {$lines} -f ".escapeshellarg($latestLogPath)." {$filters}";
    $process = new Process($tail);
    $process->setTty(true)->run();
}

完整程式碼

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use SplFileInfo;
use Symfony\Component\Process\Process;

class LaravelTailCommand extends Command
{

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'tail
                            {--lines=100 : 輸出行數}
                            {--stack : 是否顯示詳細資訊,預設為不顯示}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = '快速檢視最新日誌';

    /**
     * The filesystem instance.
     *
     * @var \Illuminate\Filesystem\Filesystem
     */
    protected $files;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct(Filesystem $files)
    {
        parent::__construct();
        $this->files = $files;
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $latestLogPath = $this->findLatestLogFile();

        $lines = $this->option('lines');
        $filters = $this->getFilters();
        $tail = "tail -n {$lines} -f ".escapeshellarg($latestLogPath)." {$filters}";
        $process = new Process($tail);
        $process->setTty(true)->run();
    }

    /**
     * 根據引數來判斷是否顯示詳情
     * 
     * @return null | string
     */
    public function getFilters()    
    {   
        return $this->option('stack') 
            ? null
            : '| grep -i -E "^\[[0-9]{4}\-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\]" --color';

    }

    /**
     * 獲取全部日誌檔案
     * 
     * @return array
     */
    public function allLogFiles() : array
    {
        return $this->files->allFiles(storage_path('logs'));
    }

    /**
     * 是否為 Laravel 日誌檔案
     * 
     * @param  SplFileInfo $file 
     * @return boolean           
     */
    public function isLaravelLogFile(SplFileInfo $file) : bool
    {
        return strpos($file->getBaseName(),'laravel') !== false;
    }

    /**
     * 獲取最新的日誌路徑
     * 
     * @return string | false
     */
    public function findLatestLogFile() 
    {   
        $logFile = collect($this->allLogFiles())
            ->filter(function(SplFileInfo $file){
                return $this->isLaravelLogFile($file);
            })
            ->sortByDesc(function (SplFileInfo $file) {
                return $file->getMTime();
            })
            ->first();

        return $logFile
            ? $logFile->getPathname()
            : false;
    }

}

使用

$ php artisan tail
$ php artisan tail --stack
$ php artisan tail --lines=500
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章