You may have faced this scenario many times, where you are calling an third party API from postman. It’s working but not working when you call it from Guzzle. And wanted to Log Guzzle requests in a file or a database.
You must be aware of what Guzzle is, if you don’t : Guzzle is a PHP HTTP client that makes it easy to send HTTP requests and trivial to integrate with web services.
Source: http://docs.guzzlephp.org/en/stable/
Okay, now you know what Guzzle is, and you’re thinking you will use it in next project. Great.
TLDR;
There’s an easy way to log all Guzzle requests by passing second parameter to Client
and then it will log all the requests. But that’s ugly way if you have many methods using Guzzle Client to send request to third party server. I’ve done it using Laravel’s Service Container.
Long way via Laravel’s Service Container
So, you have patience to go for long way. Great, keep up with me don’t let me down.
When I used Guzzle client in my project and used handler to log all requests it looks good. But later on there were many methods in many different classes so I have to write logger logic every where. Then I thought why don’t to leverage Laravel’s Service Container and bind an object once and use it everywhere.
Here’s how I did it. In your AppServiceContainer.php
‘s boot
method we will add all our code. And then in Controllers we will use our Client object.
Add this use statements on top of the AppServiceContainer.php
file.
use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; use GuzzleHttp\MessageFormatter; use GuzzleHttp\Middleware; use Illuminate\Support\ServiceProvider; use Monolog\Handler\RotatingFileHandler; use Monolog\Logger;
Add below code to your AppServiceContainer.php
‘s boot
method
/** * 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])); }; }); }
Explanation
If you have noticed above code, In first line of boot method we are telling Laravel that we want to register this code as a GuzzleClient
in your Service Container.
In last return
statement we are returning a function that will accept one argument $config
. We used this function as a proxy so that we can pass an argument to it and that can be used in Client
Object.
return function ($config) use ($stack){ return new Client(array_merge($config, ['handler' => $stack])); };
Rest of the code is building Guzzle’s handler object to Log all requests to a file called guzzle-log.log
using Logger
object of Monolog library. If you have daily logs enabled, a date will be appended to file name like guzzle-log-2019-08-11.log
.
Usage
We have binded our object to Service Container, now it’s time to use this container everywhere in our code, and make it looks clean.
For demo purpose I’ve used it directly in routes/web.php
file. You can use anywhere.
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); });
As you can see I’m making an object $client
using app()
helper. Also you can pass any valid arguments array that Guzzle client supports as a second parameter. Here I’ve passed base_uri
.
When I will run my project, you can see I’m getting a response and all requests are being logged in file as well. Refer images below.
It’s more cleaner way to keep your code from repeating and making it re-usable. Hope it will be helpful.
If you have any doubts don’t hesitate to post a comment below. Thank you very much.
Enjoy Hacking !!!