包含 Laravel 标签的文章

为 Laravel Http 客户端添加详细的请求日志

有时候难免要对 Http 的请求和响应包体进行记录以方便查找问题或做什么。

Laravel 的 Http 客户端是基于 Guzzle 进行封装的,在上层进行了简化,并没有直接给我们留相关的日志配置,想要对请求的 http 进行详细的记录,则需要借助于 Guzzle 的 Handler/中间件,和 withOptions 方法。

首先们要使用 composer 安装一个第三方的 Guzzle 日志中间件。

composer require rtheunissen/guzzle-log-middleware

该中间件比较简单,仅是在请求发生时将请求和响应对象传递给我们指定的闭包,然后我们在闭包中直接调用 Log 的方法进行记录即可。

简单的演示如下:

$stack = new HandlerStack();
$stack->setHandler(new CurlHandler()); //使用 HandlerStack 后必须指定一个 Handler

//日志中间件
$logger = new Logger(function ($level, $message, array $context) {
    Log::log($level, $message);
});

$stack->push($logger);

$res = Http::withOptions([
    'handler' => $stack
])->post($url, $data);

此时请求发生时我们就会在日志里看到一条这样的简单日志:

[2021-04-28 17:00:30] local.INFO: homestead GuzzleHttp/7 - [28/Apr/2021:17:00:30 +0800] "POST /your/request/url HTTP/1.1" 200 136

不过显示这个日志太简单了,我们需要更详细的信息,比如请求和响应的头信息及主体内容,通过该中间件的主页得知可使用一个闭包来进行 message 的格式化。

于是我们对 Logger 进行一番修改,从 Request 和 Response 中取出相应的信息,并且拼装成 Http 的包体结构,结果如下:

$logger = new Logger(function ($level, $message, array $context) {
    Log::log($level, $message);
}, function ($request, $response, $reason) {
    /**
     * @var Request $request
     * @var Response $response
     */
    $requestBody = $request->getBody();
    $requestBody->rewind();
    
    //请求头
    $requestHeaders = [];
    
    foreach ($request->getHeaders() as $k => $vs) {
        foreach ($vs as $v) {
            $requestHeaders[] = "$k: $v";
        }
    }
    
    //响应头
    $responseHeaders = [];
    
    foreach ($response->getHeaders() as $k => $vs) {
        foreach ($vs as $v) {
            $responseHeaders[] = "$k: $v";
        }
    }
    
    $uri = $request->getUri();
    $path = $uri->getPath();
    
    if ($query = $uri->getQuery()) {
        $path .= '?'.$query;
    }
    
    return sprintf(
        "Request %s\n%s %s HTTP/%s\r\n%s\r\n\r\n%s\r\n--------------------\r\nHTTP/%s %s %s\r\n%s\r\n\r\n%s",
        $uri,
        $request->getMethod(),
        $path,
        $request->getProtocolVersion(),
        join("\r\n", $requestHeaders),
        $requestBody->getContents(),
        $response->getProtocolVersion(),
        $response->getStatusCode(),
        $response->getReasonPhrase(),
        join("\r\n", $responseHeaders),
        $response->getBody()->getContents()
    );
});

发送请求后,日志内容如下:

[2021-04-28 17:06:11] local.NOTICE: Request https://www.baidu.com/
POST / HTTP/1.1
User-Agent: GuzzleHttp/7
Content-Type: application/json
Host: www.baidu.com

{"hello":"I am a fake POST."}
--------------------
HTTP/1.1 302 Found
Bdpagetype: 3
Connection: keep-alive
Content-Length: 154
Content-Type: text/html
Date: Wed, 28 Apr 2021 09:06:11 GMT
Location: https://www.baidu.com/search/error.html
Server: BWS/1.1
Set-Cookie: BDSVRTM=0; path=/
Traceid: 161960077103724047468432068516915727024
X-Ua-Compatible: IE=Edge,chrome=1

<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>nginx</center>
</body>
</html>

怎么样,详细否?

Pader 2021-4-28 0