首頁  >  文章  >  php框架  >  如何使用ThinkPHP6進行非同步日誌記錄操作?

如何使用ThinkPHP6進行非同步日誌記錄操作?

WBOY
WBOY原創
2023-06-12 09:57:112107瀏覽

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

1. 什麼是日誌記錄

在電腦科學領域,日誌記錄是指將電腦系統中發生的事件和資訊記錄下來。通常,這些記錄都以文件或資料庫的形式儲存。日誌記錄有助於了解系統運作狀況,及時發現和解決問題,進而提高系統的可靠性和穩定性。

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

2. ThinkPHP6 非同步日誌記錄

在應用程式開發過程中,日誌記錄是一個不可或缺的模組。而且,日誌記錄經常是一個耗時的操作,如果同步執行的話會影響系統的效能。為此,ThinkPHP6 引入了非同步日誌記錄的功能,讓日誌記錄不再影響應用的反應速度。

通常在控制器或模型中記錄日誌,我們使用注入 PsrLogLoggerInterface 介面來實作。

// 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 應用的運作效率,並提高系統的穩定性與可靠性。

以上是如何使用ThinkPHP6進行非同步日誌記錄操作?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn