如何使用ThinkPHP6進行異步日志記錄操作?

隨著互聯網的高速發展,日志記錄服務成為了每個大型 web 應用必不可少的模塊。為了方便錯誤排查、性能監控等各種需求,本文將介紹如何使用 thinkphp6 框架進行異步日志記錄操作。

1. 什么是日志記錄

在計算機科學領域,日志記錄是指將計算機系統中發生的事件和信息記錄下來。通常,這些記錄都以文件或數據庫的形式存儲。日志記錄有助于了解系統運行狀況,及時發現和解決問題,進而提高系統的可靠性和穩定性。

在 web 應用中,日志記錄可以幫助開發者更好地了解系統的遇到的問題和錯誤。依據日志記錄,開發者可以清楚地了解應用的行為以及錯誤發生的位置和時機。

2. thinkphp6 異步日志記錄

在應用開發過程中,日志記錄是一個必不可少的模塊。而且,日志記錄經常是一個耗時的操作,如果同步執行的話會影響系統的性能。為此,ThinkPHP6 引入了異步日志記錄的功能,讓日志記錄不再影響應用的響應速度。

通常在控制器或模型中記錄日志,我們使用注入 PsrLogLoggerInterface 接口來實現。

立即學習PHP免費學習筆記(深入)”;

// Controller或Model中 use PsrLogLoggerInterface;  public function index(LoggerInterface $logger){     $logger->info('hello world'); }

簡單的使用方式。使用異步日志記錄,定義一個異步日志記錄器:

use MonologLogger; use MonologHandlerStreamHandler;  $logger=new Logger("AsyncLogger"); $logger->pushHandler(new StreamHandler('runtime/log/async.log'), Logger::INFO);

日志記錄器定義好后,使用隊列發送日志記錄信息,這里我們選擇使用 RabbitMQ 當做隊列服務。

// Message類 namespace appcommon;  class Message {     /**      * 記錄日志      * @param $level      * @param $message      * @param array $context      * @return bool      */     public static function log($level,$message,array $context=[]){         $data=[             'level'=>$level,             'message'=>$message,             'context'=>$context,             'channel'=>'AsyncLogger',             'datetime'=>date('Y-m-d H:i:s'),             'host'=>$_SERVER['SERVER_ADDR'] ?? '',             'uri'=>$_SERVER['REQUEST_URI'] ?? '',         ];          $producer=Queue::getConnection('AsyncLogger',true);         $producer->setExchangeOptions(['name'=>'async_logs','type'=>'topic','durable'=>true])->declareExchange();          try{             $producer->publish(json_encode($data),[                 'routing_key' =>'log',                 'exchange' =>'async_logs',             ]);             return true;         }catch (Exception $e){             return false;         }     } }

其中,我們使用 appcommonQueue 類來提供 rabbitmq 的連接實例;data中除了記錄日志的信息外,還包含一些環境信息,比如時間、IP地址、請求的uri地址等。

隊列處理程序:

// Consumer類 use BunnyMessage; use PsrLogLoggerInterface;  class Consumer {     /**      * @param Message $message      * @param LoggerInterface $logger      */     public function process(Message $message,LoggerInterface $logger){         $body=$message->content;         $data= json_decode($body,true);         $channel=$data['channel'] ?? 'default_logger';          $logger->notice($data['message'], $data);     } }

當然,我們還需要一個輔助處理日志的類。

// Queue類 namespace appcommon;  use BunnyAsyncClient; use BunnyChannel; use BunnyMessage; use BunnyProtocolMethodBasicConsumeOkFrame; use BunnyProtocolMethodChannelCloseFrame; use BunnyProtocolMethodChannelCloseOkFrame; use BunnyProtocolMethodConnectionCloseFrame; use BunnyProtocolMethodConnectionCloseOkFrame; use BunnyProtocolMethodConnectionStartFrame; use BunnyClientStateEnum; use BunnyMessage as BunnyMessage;  class Queue {     /**      * @param string $queueName      * @return Client|null      */     public static function getConnection(string $routingKey, bool $persistent=false):?Client     {         $config=config('rabbitmq.async_log');         $client=new Client([             'host' => $config['host'],             'port' => $config['port'],             'user' => $config['user'],             'password' => $config['password'],             'vhost' => $config['vhost'],//注意此處改為需要的 VHOST             'concurrency' => 2,         ]);          try{             $client->connect();             $client->channel()                 ->then(function (Channel $channel) use($client,$routingKey,$persistent){                     $channel->exchangeDeclare('async_logs','topic',true,true);                     $channel->queueDeclare($routingKey, $passive=false,$durable=true,$exclusive=false,$autoDelete=false,$nowait=false);                     $channel->queueBind($routingKey, 'async_logs', $routingKey);                      $channel->consume(                         function ($msg, Channel $channel, BunnyMessage $message) use($client,$routingKey){                             $className=config('rabbitmq.async_log.consumer');                             $consumer=new $className($client,$routingKey);                             $consumer->process($message,app('log.async_logger'));                             $channel->ack($msg);//處理消息                         },                         $routingKey,//隊列Name                         '',//消費Tag                         false,//no_local                         false,//no_ack                         false,//exclusive                         $persistent ? ['delivery_mode'=>2] : []                     );                 });         }catch (Exception $e){             return null;         }finally{             return $client;         }     } }

上面這段代碼中定義了隊列連接的 host、port 等,通過 $client->channel() 創建了一個 channel 對象,通過 $channel->exchangeDeclare() 和 $channel->queueDeclare() 創建了 exchange 和 queue,并將它們進行了綁定。最后,使用 $channel->consume() 異步消費隊列的消息,并將消息發送到消息處理類中。

3. 總結

本文介紹了如何使用 ThinkPHP6 框架進行異步日志記錄操作,使日志記錄不再影響應用的響應速度。總體來說,以下是操作步驟:

  1. 開發自己的異步日志記錄器
  2. 使用 RabbitMQ 進行消息隊列處理
  3. 編寫消息處理程序

在實際項目中,我們需要根據具體的需求來優化代碼和調整隊列的配置。通過異步記錄日志,可以有效提高 web 應用的運行效率,并提高系統的穩定性與可靠性。

? 版權聲明
THE END
喜歡就支持一下吧
點贊13 分享