Home  >  Article  >  PHP Framework  >  How to use ThinkPHP6 for asynchronous logging operations?

How to use ThinkPHP6 for asynchronous logging operations?

WBOY
WBOYOriginal
2023-06-12 09:57:112176browse

With the rapid development of the Internet, logging services have become an essential module for every large-scale web application. In order to facilitate various needs such as error troubleshooting and performance monitoring, this article will introduce how to use the ThinkPHP6 framework to perform asynchronous logging operations.

1. What is logging

In the field of computer science, logging refers to recording events and information that occur in a computer system. Typically, these records are stored in files or databases. Logging helps to understand the operating status of the system, discover and solve problems in a timely manner, and thereby improve the reliability and stability of the system.

In web applications, logging can help developers better understand the problems and errors encountered by the system. Based on logging, developers can clearly understand the behavior of the application and where and when errors occur.

2. ThinkPHP6 asynchronous logging

In the application development process, logging is an essential module. Moreover, logging is often a time-consuming operation that can affect system performance if performed synchronously. To this end, ThinkPHP6 introduces the function of asynchronous logging, so that logging no longer affects the response speed of the application.

Usually logging in the controller or model, we use the injected PsrLogLoggerInterface interface to achieve this.

// Controller或Model中
use PsrLogLoggerInterface;

public function index(LoggerInterface $logger){
    $logger->info('hello world');
}

Easy to use. Use asynchronous logging to define an asynchronous logger:

use MonologLogger;
use MonologHandlerStreamHandler;

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

After the logger is defined, use a queue to send logging information. Here we choose to use RabbitMQ as the queue service.

// 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;
        }
    }
}

Among them, we use the appcommonQueue class to provide rabbitmq connection instance; data in addition to recording log information, also contains some environmental information, such as time, IP address, requested URI address, etc.

Queue handler:

// 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);
    }
}

Of course, we also need a class to assist in processing logs.

// 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;
        }
    }
}

The above code defines the host, port, etc. of the queue connection. A channel object is created through $client->channel(), and a channel object is created through $channel- >exchangeDeclare() and $channel->queueDeclare() create exchange and queue and bind them. Finally, use $channel->consume() to asynchronously consume messages from the queue and send the messages to the message processing class.

3. Summary

This article introduces how to use the ThinkPHP6 framework to perform asynchronous logging operations so that logging no longer affects the response speed of the application. In general, the following are the steps:

  1. Develop your own asynchronous logger
  2. Use RabbitMQ for message queue processing
  3. Write a message handler

In actual projects, we need to optimize the code and adjust the queue configuration according to specific needs. Through asynchronous logging, the operating efficiency of web applications can be effectively improved, and the stability and reliability of the system can be improved.

The above is the detailed content of How to use ThinkPHP6 for asynchronous logging operations?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn