Laravel 使用 Elasticsearch 作為日誌儲存

Jinrenjie發表於2019-11-25

Laravel 使用 Elasticsearch 作為日誌儲存

在實際開發中,我們發現在 Debug 的時候經常需要查詢日誌。而傳統的方式是需要 SSH 到生產環境,然後使用 cattailgrep 等命令查詢日誌,且無法進行日誌的統計和分析,深度挖掘這些日誌的價值。

本片文章的側重點在於優雅的讓 Laravel 直接將日誌寫入 Elasticsearch,當然你也可以選擇使用 Logstash 採集 Laravel 的本地日誌。

環境搭建可以參考 《Elastic Stack 之 Elasticsearch》《Elastic Stack 之 Kibana》 這兩篇博文。

  • elasticsearch/elasticsearch
  • betterde/logger(如果需要)

Laravel 使用 monolog/monolog 作為預設日誌處理模組。monolog 自帶了很多 Handler 其中就有 ElasticsearchHandler。

生命週期

  • Illuminate\Foundation\Application 的建構函式中註冊基本的服務提供者,其中就有 Illuminate\Log\LogServiceProvider

  • Illuminate\Log\LogServiceProvider 中將 Illuminate\Log\LogManager 註冊到容器,此時還沒有 Logger。

  • 當我們使用 Illuminate\Support\Facades\Log::info() 的時候,LogManager 會呼叫內部一系列方法根據配置檔案建立 Logger 和其所需的 Handlers

  • 最終呼叫 Logger 的 info()、error()、debug() 等方法,實現記錄日誌的功能。

lifecycle

簡單適配

只需要通過修改 config/logging.php.env 檔案就可以實現,將日誌直接寫入 Elasticcsearch:

'elastic' => [
    'driver' => 'monolog',
    'level' => 'debug',
    'name' => 'Develop',
    'tap' => [],
    'handler' => ElasticsearchHandler::class,
    'formatter' => \Monolog\Formatter\ElasticsearchFormatter::class,
    'formatter_with' => [
        'index' => 'monolog',
        'type' => '_doc'
    ],

    'handler_with' => [
        'client' => \Elasticsearch\ClientBuilder::create()->setHosts(['http://localhost:9200'])->build(),
    ],
],
LOG_CHANNEL=elastic

簡單適配後會發現當我們在寫入日誌的時候,Laravel 是單條同步進行的,對於我們使用原創儲存來說這將影響一定的效能。所以我們需要在整個生命週期中臨時儲存所有的日誌資訊,在請求結束前觸發同步或非同步將日誌批量寫入儲存。

為此我週末寫了一個擴充套件包betterde/logger,如果你在使用的過程中有任何問題可以在Github上提 IssuePR

目前只支援 Laravel ^6.0.0 因為從 6.0 開始 monolog 的依賴是 2.,後面如果需要的話,會考慮開個1.分支相容5.* 的版本。

安裝

$ composer require betterde/logger
$ php artisan vendor:publish --tag=betterde.logger

配置

將如下配置追加到 config/logging.phpchannels 中:

use Betterde\Logger\ElasticsearchLogger;
'channels' => [
     'elastic' => [
         'driver' => 'custom',
         'via' => ElasticsearchLogger::class,
     ],
 ],

\Betterde\Logger\Http\Middleware\BulkCollectionLog 中介軟體新增到 App\Http\Kernel.php 檔案中

/**
 * The application's global HTTP middleware stack.
 *
 * These middleware are run during every request to your application.
 *
 * @var array
 */
 protected $middleware = [
     \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
     \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
     \App\Http\Middleware\TrimStrings::class,
     \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
     \App\Http\Middleware\TrustProxies::class,
     \Betterde\Logger\Http\Middleware\BulkCollectionLog::class
 ];

.env 檔案中新增如下配置項:

LOG_CHANNEL=elastic
ELASTICSEARCH_HOST=localhost
ELASTICSEARCH_PORT=9200
ELASTICSEARCH_SCHEME=http
ELASTICSEARCH_USER=
ELASTICSEARCH_PASS=

修改 config/logger.php 配置檔案:

<?php
return [
    /*
    * 是否開啟批量寫入,需要設定中介軟體
    */
    'batch' => false,

    /*
    * 是否使用佇列
    */
    'queue' => [
        'enable' => false,
        'name' => env('LOG_QUEUE_NAME', 'logging')
    ],
    /*
    * 日誌級別,值可以參考 Monolog\Logger.php 中的定義
    */
    'level' => 200,
    /*
    * 是否在多個 Handler 中流轉日誌資料
    */
    'bubble' => false,
    /*
    * Elasticsearch DB
    */
    'elasticsearch' => [
        'hosts' => [
            [
                /*
                * host 是必填項
                */
                'host' => env('ELASTICSEARCH_HOST', 'localhost'),
                'port' => env('ELASTICSEARCH_PORT', 9200),
                'scheme' => env('ELASTICSEARCH_SCHEME', 'http'),
                'user' => env('ELASTICSEARCH_USER', null),
                'pass' => env('ELASTICSEARCH_PASS', null)
            ],
        ],
        'retries' => 2,
        /*
        * 證照路徑
        */
        'cert' => ''
    ],
    /*
    * Handler 的設定
    */
    'options' => [
        'index' => 'monolog', // Elastic index name
        'type' => '_doc', // Elastic document type
        'ignore_error' => false, // Suppress Elasticsearch exceptions
    ],

    /*
    * 對於異常日誌是否記錄追蹤詳情
    */
    'exception' => [
        'trace' => false,
    ],
    /*
    * 擴充套件屬性,你可以用於 Elasticsearch Index,extra 陣列裡的 Key 都是可以自定義的,我這裡只是舉例
    */
    'extra' => [
        'host' => 'example.com',
        'php' => '7.3.5',
        'laravel' => '6.5.2'
    ]
];

Kibana

好了現在可以盡情的享受寫 CURD 的樂趣了!

如果你對我的文章感興趣可以關注我的訂閱號

相關文章