Laravel - 将 Guzzle 请求记录到文件
Laravel - Log Guzzle requests to file
在处理一个项目时,我发现第三方 API 正在使用 Postman,但不适用于 Guzzle Client。
调试 Guzzle 请求可能很困难,那么有没有什么方法可以记录 Guzzle 客户端发出的所有请求?
TLDR;
有一种简单的方法可以通过将第二个参数传递给客户端来记录所有 Guzzle 请求,然后它将记录所有请求。但如果你有很多方法使用 Guzzle 客户端向第三方服务器发送请求,那将是一种丑陋的方式。我已经使用 Laravel 的服务容器完成了它。
通过 Laravel 的服务容器
很长一段路要走
当我在我的项目中使用 Guzzle 客户端并使用处理程序记录所有请求时,它看起来不错。但后来在许多不同的 类 中有许多方法,所以我必须在每个地方编写记录器逻辑。然后我想为什么不利用 Laravel 的 Service Container 并绑定一个对象并在所有地方使用它。
我是这样做的。在您的 AppServiceContainer.php
的启动方法中,我们将添加所有代码。然后在控制器中我们将使用我们的客户端对象。
在 AppServiceContainer.php
文件顶部添加此 use 语句。
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
use Illuminate\Support\ServiceProvider;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
将以下代码添加到您的 AppServiceContainer.php 的 boot
方法中
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->app->bind('GuzzleClient', function () {
$messageFormats = [
'REQUEST: {method} - {uri} - HTTP/{version} - {req_headers} - {req_body}',
'RESPONSE: {code} - {res_body}',
];
$stack = HandlerStack::create();
collect($messageFormats)->each(function ($messageFormat) use ($stack) {
// We'll use unshift instead of push, to add the middleware to the bottom of the stack, not the top
$stack->unshift(
Middleware::log(
with(new Logger('guzzle-log'))->pushHandler(
new RotatingFileHandler(storage_path('logs/guzzle-log.log'))
),
new MessageFormatter($messageFormat)
)
);
});
return function ($config) use ($stack){
return new Client(array_merge($config, ['handler' => $stack]));
};
});
}
说明
如果您注意到上面的代码,在启动方法的第一行我们告诉 Laravel 我们想在您的服务容器中将此代码注册为 GuzzleClient。
在最后一个 return 语句中,我们 return 将接受一个参数 $config
的函数。我们将此函数用作代理,以便我们可以将参数传递给它并且可以在客户端对象中使用。
return function ($config) use ($stack){
return new Client(array_merge($config, ['handler' => $stack]));
};
其余代码正在构建 Guzzle 的处理程序对象,以使用 Monolog 库的 Logger 对象将所有请求记录到名为 guzzle-log.log
的文件中。如果启用每日日志,日期将附加到文件名,如 guzzle-log-2019-08-11.log
。
用法
我们已经将我们的对象绑定到服务容器,现在是时候在我们的代码中的任何地方使用这个容器,并让它看起来干净。
出于演示目的,我直接在 routes/web.php
文件中使用了它。你可以在任何地方使用。
Route::get('/', function () {
$client = app('GuzzleClient')(['base_uri' => 'http://httpbin.org/']);
$request = $client->get('get',[
'query' => ['foo'=>'bar', 'baz' => 'baz2'] ,
'headers' => [ 'accept' => 'application/json']
]);
$response = json_decode((string) $request->getBody());
return response()->json($response);
});
如您所见,我正在使用 app()
助手创建一个对象 $client
。您还可以将 Guzzle 客户端支持的任何有效参数数组作为第二个参数传递。这里我已经过了base_uri
。
来源:http://shyammakwana.me/laravel/laravel-log-guzzle-requests-to-file-using-service-container.html
接受的答案通过使用 Service Providers. Another option would be to attach GuzzleHttp\Middleware
's log
to wherever you use Illuminate\Support\Facades\Http
效果很好。记录请求、响应和发现的任何错误的示例是使用 Middleware::log
:
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
/**
* Class TestService
* @package App
*/
class TestService
{
private function getAccessToken()
{
try {
$response = Http::asForm()->withMiddleware(Middleware::log(with(new Logger('guzzle-log'))->pushHandler(
new RotatingFileHandler(storage_path('logs/guzzle-log.log'))
), new MessageFormatter(MessageFormatter::DEBUG)))->post("https://test.com/oauth/v2/token", [
'grant_type' => 'client_credentials',
]);
$response->throw();
} catch (\Throwable $th) {
$accessToken = false;
}
return $accessToken;
}
}
这会将日志记录写入 logs/guzzle-log-{currentDate}.log
文件。我在这个例子中使用的日志记录格式是 MessageFormatter::DEBUG
即 ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"
可以很好地输出请求、响应和任何错误。日志文件的示例是:
[2020-08-07T07:13:23.712124+00:00] guzzle-log.INFO: >>>>>>>> POST /oauth/v2/token HTTP/1.1 Content-Length: 29 User-Agent: GuzzleHttp/7 Host: xxxx:4000 Content-Type: application/x-www-form-urlencoded grant_type=client_credentials <<<<<<<< HTTP/1.1 200 OK X-DNS-Prefetch-Control: off X-Frame-Options: SAMEORIGIN Strict-Transport-Security: max-age=15552000; includeSubDomains X-Download-Options: noopen X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Access-Control-Allow-Origin: * Content-Type: application/json; charset=utf-8 Content-Length: 113 ETag: W/"71-DyA+KEnetTKfUlb0lznokGTt0qk" Date: Fri, 07 Aug 2020 07:13:23 GMT Connection: keep-alive {"data":{"token_type":"Bearer","access_token":"XYZ","expires_in":"7776000"}} -------- NULL [] []
注意:此选项的缺点是您必须将 withMiddleware(Middleware::log(...))
附加到您使用 Illuminate\Support\Facades\Http
的任何地方。
在处理一个项目时,我发现第三方 API 正在使用 Postman,但不适用于 Guzzle Client。
调试 Guzzle 请求可能很困难,那么有没有什么方法可以记录 Guzzle 客户端发出的所有请求?
TLDR;
有一种简单的方法可以通过将第二个参数传递给客户端来记录所有 Guzzle 请求,然后它将记录所有请求。但如果你有很多方法使用 Guzzle 客户端向第三方服务器发送请求,那将是一种丑陋的方式。我已经使用 Laravel 的服务容器完成了它。
通过 Laravel 的服务容器
很长一段路要走当我在我的项目中使用 Guzzle 客户端并使用处理程序记录所有请求时,它看起来不错。但后来在许多不同的 类 中有许多方法,所以我必须在每个地方编写记录器逻辑。然后我想为什么不利用 Laravel 的 Service Container 并绑定一个对象并在所有地方使用它。
我是这样做的。在您的 AppServiceContainer.php
的启动方法中,我们将添加所有代码。然后在控制器中我们将使用我们的客户端对象。
在 AppServiceContainer.php
文件顶部添加此 use 语句。
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
use Illuminate\Support\ServiceProvider;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
将以下代码添加到您的 AppServiceContainer.php 的 boot
方法中
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->app->bind('GuzzleClient', function () {
$messageFormats = [
'REQUEST: {method} - {uri} - HTTP/{version} - {req_headers} - {req_body}',
'RESPONSE: {code} - {res_body}',
];
$stack = HandlerStack::create();
collect($messageFormats)->each(function ($messageFormat) use ($stack) {
// We'll use unshift instead of push, to add the middleware to the bottom of the stack, not the top
$stack->unshift(
Middleware::log(
with(new Logger('guzzle-log'))->pushHandler(
new RotatingFileHandler(storage_path('logs/guzzle-log.log'))
),
new MessageFormatter($messageFormat)
)
);
});
return function ($config) use ($stack){
return new Client(array_merge($config, ['handler' => $stack]));
};
});
}
说明
如果您注意到上面的代码,在启动方法的第一行我们告诉 Laravel 我们想在您的服务容器中将此代码注册为 GuzzleClient。
在最后一个 return 语句中,我们 return 将接受一个参数 $config
的函数。我们将此函数用作代理,以便我们可以将参数传递给它并且可以在客户端对象中使用。
return function ($config) use ($stack){
return new Client(array_merge($config, ['handler' => $stack]));
};
其余代码正在构建 Guzzle 的处理程序对象,以使用 Monolog 库的 Logger 对象将所有请求记录到名为 guzzle-log.log
的文件中。如果启用每日日志,日期将附加到文件名,如 guzzle-log-2019-08-11.log
。
用法
我们已经将我们的对象绑定到服务容器,现在是时候在我们的代码中的任何地方使用这个容器,并让它看起来干净。
出于演示目的,我直接在 routes/web.php
文件中使用了它。你可以在任何地方使用。
Route::get('/', function () {
$client = app('GuzzleClient')(['base_uri' => 'http://httpbin.org/']);
$request = $client->get('get',[
'query' => ['foo'=>'bar', 'baz' => 'baz2'] ,
'headers' => [ 'accept' => 'application/json']
]);
$response = json_decode((string) $request->getBody());
return response()->json($response);
});
如您所见,我正在使用 app()
助手创建一个对象 $client
。您还可以将 Guzzle 客户端支持的任何有效参数数组作为第二个参数传递。这里我已经过了base_uri
。
来源:http://shyammakwana.me/laravel/laravel-log-guzzle-requests-to-file-using-service-container.html
接受的答案通过使用 Service Providers. Another option would be to attach GuzzleHttp\Middleware
's log
to wherever you use Illuminate\Support\Facades\Http
效果很好。记录请求、响应和发现的任何错误的示例是使用 Middleware::log
:
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Logger;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
/**
* Class TestService
* @package App
*/
class TestService
{
private function getAccessToken()
{
try {
$response = Http::asForm()->withMiddleware(Middleware::log(with(new Logger('guzzle-log'))->pushHandler(
new RotatingFileHandler(storage_path('logs/guzzle-log.log'))
), new MessageFormatter(MessageFormatter::DEBUG)))->post("https://test.com/oauth/v2/token", [
'grant_type' => 'client_credentials',
]);
$response->throw();
} catch (\Throwable $th) {
$accessToken = false;
}
return $accessToken;
}
}
这会将日志记录写入 logs/guzzle-log-{currentDate}.log
文件。我在这个例子中使用的日志记录格式是 MessageFormatter::DEBUG
即 ">>>>>>>>\n{request}\n<<<<<<<<\n{response}\n--------\n{error}"
可以很好地输出请求、响应和任何错误。日志文件的示例是:
[2020-08-07T07:13:23.712124+00:00] guzzle-log.INFO: >>>>>>>> POST /oauth/v2/token HTTP/1.1 Content-Length: 29 User-Agent: GuzzleHttp/7 Host: xxxx:4000 Content-Type: application/x-www-form-urlencoded grant_type=client_credentials <<<<<<<< HTTP/1.1 200 OK X-DNS-Prefetch-Control: off X-Frame-Options: SAMEORIGIN Strict-Transport-Security: max-age=15552000; includeSubDomains X-Download-Options: noopen X-Content-Type-Options: nosniff X-XSS-Protection: 1; mode=block Access-Control-Allow-Origin: * Content-Type: application/json; charset=utf-8 Content-Length: 113 ETag: W/"71-DyA+KEnetTKfUlb0lznokGTt0qk" Date: Fri, 07 Aug 2020 07:13:23 GMT Connection: keep-alive {"data":{"token_type":"Bearer","access_token":"XYZ","expires_in":"7776000"}} -------- NULL [] []
注意:此选项的缺点是您必须将 withMiddleware(Middleware::log(...))
附加到您使用 Illuminate\Support\Facades\Http
的任何地方。