Stories

Detail Return Return

laravel 小技巧:為日誌組件的非默認通道註冊全局上下文 context - Stories Detail

在使用 laravel 的日誌組件(Facade門面模式)時,我們可以通過 withContext 方法為請求週期註冊全局的上下文信息,用來做 RequestID/TraceId 類的請求鏈路追蹤,非常的方便。但在 10- 以下的版本中,withContext 只能為默認日誌通道注入全局上下文,在非默認通道的場景,比如 cli 下, 就無法優雅的實現了。

但如果你瞭解 ServiceServiceProviderFacade 三者之間的關係,那可以參考如下兩類方法來優雅的實現非默認日誌通道下注冊全局 context 的方法:

門面 \Illuminate\Support\Facades\Log 代理的日誌服務
實為 \Illuminate\Log\LogServiceProviderapp 容器中註冊的
標識名為 log\Illuminate\Log\LogManager 日誌服務的實例。

只要理解這三者的關係,我們就可以為Log門面重新註冊。

方法一、非默認通道的日誌Facade

\App\Supports\Facades\LogCli 代理一個非默認通道日誌服務的實例即可,比如 log-cli

<?php

namespace App\Supports\Facades;

use Illuminate\Support\Facades\Facade;

/**
 * @method static \Psr\Log\LoggerInterface channel(string $channel = null)
 * @method static \Psr\Log\LoggerInterface stack(array $channels, string $channel = null)
 * @method static \Psr\Log\LoggerInterface build(array $config)
 * @method static \Illuminate\Log\Logger withContext(array $context = [])
 * @method static \Illuminate\Log\Logger withoutContext()
 * @method static void alert(string $message, array $context = [])
 * @method static void critical(string $message, array $context = [])
 * @method static void debug(string $message, array $context = [])
 * @method static void emergency(string $message, array $context = [])
 * @method static void error(string $message, array $context = [])
 * @method static void info(string $message, array $context = [])
 * @method static void log($level, string $message, array $context = [])
 * @method static void notice(string $message, array $context = [])
 * @method static void warning(string $message, array $context = [])
 * @method static void write(string $level, string $message, array $context = [])
 * @method static void listen(\Closure $callback)
 *
 * @see \Illuminate\Log\Logger
 */
class LogCli extends Facade
{
    public static function getFacadeAccessor(): string
    {
        return 'log-cli';
    }
}

\App\Providers\AppServiceProvider 中註冊 log-cli 的實例到 app 容器中

<?php

namespace App\Providers;

use Illuminate\Log\LogManager;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        /**
         * @see \App\Supports\Facades\LogCli
         */
        $this->app->singleton('log-cli', function () {
            return (new LogManager($this->app))->channel('cli');
        });
    }
}

如此後,我們就可以在使用 LogCli::withContext 註冊運行週期內的全局上下文參數了。

LogCli::withContext(['traceId' => "traceId will record in all log"]);
// 以下日誌都會攜帶上 traceId 參數
LogCli::info("this is info");
LogCli::warning("this is warning");
LogCli::error("this is error");

方法二、自適應日誌Facade

聽起來是不是很高大上很優雅?其實就是仍使用 laravelLog FacadeFacade 只是個代理,將 實例 的諸多 方法靜態 的風格 暴露 出來 方便調用。何謂自適應呢?讓 Log Facadehttp 下使用 default 通道,在 cli 下使用 cli 通道,使用者無需刻意的去指定。
實現起來也很簡單,根據運行時環境來判斷,決定 log 代理哪個通道的日誌服務。

<?php

namespace App\Providers;

use Illuminate\Log\LogManager;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        /**
         * @see \Illuminate\Log\LogManager
         * @see \Illuminate\Log\LogServiceProvider
         * @see \Illuminate\Support\Facades\Log
         * @see \Illuminate\Support\Facades\Log::withContext()
         * cli 環境下,將 log 服務重新註冊為 cli 通道的日誌對象
         * 主要為了解決 10- 版本下無法為非默認通道注入全局context的問題
         */
        if ('cli' == \PHP_SAPI) {
            $this->app->singleton('log', function () {
                return (new LogManager($this->app))->channel('cli');
            });
            // 替換 log 門面
            Log::swap(app("log"));
            // 全局 with 一個 trace_id
            Log::withContext(['trace_id' => Str::uuid()->toString()]);
        }
    }
}

這樣在 http 模式下自動使用 default 通道,console 模式下自動使用 cli 通道。

Log::withContext(['traceId' => "traceId will record in all log"]);
// 以下日誌都會攜帶上 traceId 參數
Log::info("this is info");
Log::warning("this is warning");
Log::error("this is error");
user avatar 0xboo Avatar phpercode Avatar biliangxianting Avatar baiyu_5e8165d8c9fd8 Avatar
Favorites 4 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.