larave 日誌自定義配置格式記錄呼叫檔案路徑與行號

994914376發表於2018-04-11

larave Monolog日誌自定義配置

1、原有的日誌記錄檔案只記錄了時間和日誌內容沒有記錄是在哪個檔案記錄的,給新來的人員檢視日誌增加了額外的負擔。類似下面這樣

[2018-04-11 10:25:29] local.INFO: 我是來測試日誌的 {"content":1111} 
[2018-04-11 10:25:29] local.DEBUG: 我是來測試日誌的  
[2018-04-11 10:25:29] local.ERROR: 我是來測試日誌的  
[2018-04-11 10:25:29] local.WARNING: 我是來測試日誌的  

2、基於公司需求對MonoLog模組的原始碼進行了分析,釋出Laravel是對MonoLog之上做了一層封裝,達到了如下效果

[2018-04-11 10:25:29.313456] local.INFO: 我是來測試日誌的 {"content":1111} {"pid":1340,"file":"App\\Http\\Controllers\\TestController->log:20","line":20,"ip":"127.0.0.1"}
[2018-04-11 10:25:29.317710] local.DEBUG: 我是來測試日誌的  {"pid":1340,"file":"App\\Http\\Controllers\\TestController->log:21","line":21,"ip":"127.0.0.1"}
[2018-04-11 10:25:29.320648] local.ERROR: 我是來測試日誌的  {"pid":1340,"file":"App\\Http\\Controllers\\TestController->log:22","line":22,"ip":"127.0.0.1"}
[2018-04-11 10:25:29.324702] local.WARNING: 我是來測試日誌的  {"pid":1340,"file":"App\\Http\\Controllers\\TestController->log:23","line":23,"ip":"127.0.0.1"}

接下來告訴大家怎麼來實現它

1、重新建立一個Writer.php 類 暫定目錄存 app\Utils\Monolog

<?php

namespace App\Utils\Monolog;

use Illuminate\Log\Writer as BaseWriter;
use Monolog\Formatter\LineFormatter;

/**
 * 時間精確到微秒
 *
 * @package app.Utils.Monolog
 */
class Writer extends BaseWriter
{

    /**
     * Get a default Monolog formatter instance.
     *
     * @return \Monolog\Formatter\LineFormatter
     */
    protected function getDefaultFormatter()
    {
        return new LineFormatter(null, 'Y-m-d H:i:s.u', true, true);
    }

2、建立處理程式IntrospectionProcessor.php 目錄app\Utils\Monolog\Processor

<?php

namespace App\Utils\Monolog\Processor;

use Monolog\Logger;

/**
 * Monolog用處理模組
 * 標準IntrospectionProcessor的簡化版
 *
 * @package app.Utils.Monolog.Processor
 */
class IntrospectionProcessor
{
    private $level;

    private $skipClassesPartials;

    public function __construct($level = Logger::DEBUG, array $skipClassesPartials = ['Monolog\\', 'Illuminate\\'])
    {
        $this->level = Logger::toMonologLevel($level);
        $this->skipClassesPartials = $skipClassesPartials;
    }

    /**
     * @param  array $record
     * @return array
     */
    public function __invoke(array $record)
    {
        // return if the level is not high enough
        if ($record['level'] < $this->level) {
            return $record;
        }

        $trace = debug_backtrace();

        // skip first since it's always the current method
        array_shift($trace);
        // the call_user_func call is also skipped
        array_shift($trace);

        $i = 0;

        while (isset($trace[$i]['class'])) {
            foreach ($this->skipClassesPartials as $part) {
                if (strpos($trace[$i]['class'], $part) !== false) {
                    $i++;
                    continue 2;
                }
            }
            break;
        }

        $line = null;
        if (isset($trace[$i]['class'])) {
            $line = $trace[$i]['class'];
            if (isset($trace[$i]['class'])) {
                $line = $line . '->' . $trace[$i]['function'];
            }
        } else if (isset($trace[$i - 1]['file'])) {
            $line = $trace[$i - 1]['file'];
        }
        if (null !== $line && isset($trace[$i - 1]['line'])) {
            $line = $line . ':' . $trace[$i - 1]['line'];
        }

        $record['extra']['file'] = $line;
        $record['extra']['line'] = $trace[$i - 1]['line'];
        $record['extra']['ip'] = $_SERVER["REMOTE_ADDR"];
        return $record;
    }
}

3、建立 處理模組 ProcessIdProcessor.php 目錄 app\Utils\Monolog\Processor

<?php

namespace App\Utils\Monolog\Processor;

/**
 * Monolog用處理模組
 * 標準IntrospectionProcessor的簡化版, 原輸出鍵值文字長度過長
 *
 * @package app.Utils.Monolog.Processor
 */
class ProcessIdProcessor
{
    /**
     * @param  array $record
     * @return array
     */
    public function __invoke(array $record)
    {
        $record['extra']['pid'] = getmypid();

        return $record;
    }
}

4、建立 ServiceProvider 檔案 LogServiceProvider.php 目錄 app\Providers

<?php
namespace App\Providers;

use Monolog\Logger as Monolog;
use Illuminate\Support\ServiceProvider;
use App\Utils\Monolog\Writer;

class LogServiceProvider extends ServiceProvider
{
    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('log', function () {
            return $this->createLogger();
        });
    }

    /**
     * Create the logger.
     *
     * @return \Illuminate\Log\Writer
     */
    public function createLogger()
    {
        $processors = [
            new \App\Utils\Monolog\Processor\ProcessIdProcessor(),
            new \App\Utils\Monolog\Processor\IntrospectionProcessor()
        ];

        $log = new \App\Utils\Monolog\Writer(
            new Monolog($this->channel(), [], $processors), $this->app['events']
        );

        if ($this->app->hasMonologConfigurator()) {
            call_user_func($this->app->getMonologConfigurator(), $log->getMonolog());
        } else {
            $this->configureHandler($log);
        }
        return $log;
    }

    /**
     * Get the name of the log "channel".
     *
     * @return string
     */
    protected function channel()
    {
        return $this->app->bound('env') ? $this->app->environment() : 'production';
    }

    /**
     * Configure the Monolog handlers for the application.
     *
     * @param  \Illuminate\Log\Writer  $log
     * @return void
     */
    protected function configureHandler(Writer $log)
    {
        $this->{'configure'.ucfirst($this->handler()).'Handler'}($log);
    }

    /**
     * Configure the Monolog handlers for the application.
     *
     * @param  \Illuminate\Log\Writer  $log
     * @return void
     */
    protected function configureSingleHandler(Writer $log)
    {
        $log->useFiles(
            $this->app->storagePath().'/logs/laravel.log',
            $this->logLevel()
        );
    }

    /**
     * Configure the Monolog handlers for the application.
     *
     * @param  \Illuminate\Log\Writer  $log
     * @return void
     */
    protected function configureDailyHandler(Writer $log)
    {
        $log->useDailyFiles(
            $this->app->storagePath().'/logs/laravel.log', $this->maxFiles(),
            $this->logLevel()
        );
    }

    /**
     * Configure the Monolog handlers for the application.
     *
     * @param  \Illuminate\Log\Writer  $log
     * @return void
     */
    protected function configureSyslogHandler(Writer $log)
    {
        $log->useSyslog('laravel', $this->logLevel());
    }

    /**
     * Configure the Monolog handlers for the application.
     *
     * @param  \Illuminate\Log\Writer  $log
     * @return void
     */
    protected function configureErrorlogHandler(Writer $log)
    {
        $log->useErrorLog($this->logLevel());
    }

    /**
     * Get the default log handler.
     *
     * @return string
     */
    protected function handler()
    {
        if ($this->app->bound('config')) {
            return $this->app->make('config')->get('app.log', 'single');
        }

        return 'single';
    }

    /**
     * Get the log level for the application.
     *
     * @return string
     */
    protected function logLevel()
    {
        if ($this->app->bound('config')) {
            return $this->app->make('config')->get('app.log_level', 'debug');
        }

        return 'debug';
    }

    /**
     * Get the maximum number of log files for the application.
     *
     * @return int
     */
    protected function maxFiles()
    {
        if ($this->app->bound('config')) {
            return $this->app->make('config')->get('app.log_max_files', 5);
        }

        return 0;
    }
}

5、最後註冊新建的LogServiceProvider.php 服務到容器裡面覆蓋掉底層的LogServiceProvider

#開啟 config/app.php在 providers陣列裡面新增上
App\Providers\LogServiceProvider::class,

到此大功完成,可以新建一個控制器來看看實現的效果

<?php
namespace App\Http\Controllers;
/**
 * 資料
 */
class TestController extends Controller{
    public function log()
    {
        echo "我是來測試的";
        \Log::info('我是來測試日誌的', ['content' => 1111]);
        \Log::debug('我是來測試日誌的');
        \Log::error('我是來測試日誌的');
        \Log::warning('我是來測試日誌的');
    }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章