隨著互聯(lián)網(wǎng)的高速發(fā)展,日志記錄服務(wù)成為了每個(gè)大型 web 應(yīng)用必不可少的模塊。為了方便錯(cuò)誤排查、性能監(jiān)控等各種需求,本文將介紹如何使用 ThinkPHP6 框架進(jìn)行異步日志記錄操作。
1. 什么是日志記錄
在計(jì)算機(jī)科學(xué)領(lǐng)域,日志記錄是指將計(jì)算機(jī)系統(tǒng)中發(fā)生的事件和信息記錄下來。通常,這些記錄都以文件或數(shù)據(jù)庫(kù)的形式存儲(chǔ)。日志記錄有助于了解系統(tǒng)運(yùn)行狀況,及時(shí)發(fā)現(xiàn)和解決問題,進(jìn)而提高系統(tǒng)的可靠性和穩(wěn)定性。
在 web 應(yīng)用中,日志記錄可以幫助開發(fā)者更好地了解系統(tǒng)的遇到的問題和錯(cuò)誤。依據(jù)日志記錄,開發(fā)者可以清楚地了解應(yīng)用的行為以及錯(cuò)誤發(fā)生的位置和時(shí)機(jī)。
2. ThinkPHP6 異步日志記錄
在應(yīng)用開發(fā)過程中,日志記錄是一個(gè)必不可少的模塊。而且,日志記錄經(jīng)常是一個(gè)耗時(shí)的操作,如果同步執(zhí)行的話會(huì)影響系統(tǒng)的性能。為此,ThinkPHP6 引入了異步日志記錄的功能,讓日志記錄不再影響應(yīng)用的響應(yīng)速度。
通常在控制器或模型中記錄日志,我們使用注入 PsrLogLoggerInterface
接口來實(shí)現(xiàn)。
// Controller或Model中 use PsrLogLoggerInterface; public function index(LoggerInterface $logger){ $logger->info('hello world'); }
登錄后復(fù)制
簡(jiǎn)單的使用方式。使用異步日志記錄,定義一個(gè)異步日志記錄器:
use MonologLogger; use MonologHandlerStreamHandler; $logger=new Logger("AsyncLogger"); $logger->pushHandler(new StreamHandler('runtime/log/async.log'), Logger::INFO);
登錄后復(fù)制
日志記錄器定義好后,使用隊(duì)列發(fā)送日志記錄信息,這里我們選擇使用 RabbitMQ 當(dāng)做隊(duì)列服務(wù)。
// 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; } } }
登錄后復(fù)制
其中,我們使用 appcommonQueue
類來提供 rabbitmq 的連接實(shí)例;data
中除了記錄日志的信息外,還包含一些環(huán)境信息,比如時(shí)間、IP地址、請(qǐng)求的uri地址等。
隊(duì)列處理程序:
// 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); } }
登錄后復(fù)制
當(dāng)然,我們還需要一個(gè)輔助處理日志的類。
// 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,//隊(duì)列Name '',//消費(fèi)Tag false,//no_local false,//no_ack false,//exclusive $persistent ? ['delivery_mode'=>2] : [] ); }); }catch (Exception $e){ return null; }finally{ return $client; } } }
登錄后復(fù)制
上面這段代碼中定義了隊(duì)列連接的 host、port 等,通過 $client->channel()
創(chuàng)建了一個(gè) channel 對(duì)象,通過 $channel->exchangeDeclare()
和 $channel->queueDeclare()
創(chuàng)建了 exchange 和 queue,并將它們進(jìn)行了綁定。最后,使用 $channel->consume()
異步消費(fèi)隊(duì)列的消息,并將消息發(fā)送到消息處理類中。
3. 總結(jié)
本文介紹了如何使用 ThinkPHP6 框架進(jìn)行異步日志記錄操作,使日志記錄不再影響應(yīng)用的響應(yīng)速度??傮w來說,以下是操作步驟:
- 開發(fā)自己的異步日志記錄器使用 RabbitMQ 進(jìn)行消息隊(duì)列處理編寫消息處理程序
在實(shí)際項(xiàng)目中,我們需要根據(jù)具體的需求來優(yōu)化代碼和調(diào)整隊(duì)列的配置。通過異步記錄日志,可以有效提高 web 應(yīng)用的運(yùn)行效率,并提高系統(tǒng)的穩(wěn)定性與可靠性。
以上就是如何使用ThinkPHP6進(jìn)行異步日志記錄操作?的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注www.xfxf.net其它相關(guān)文章!