25

I'm trying to use guzzle 6 which works fine but I'm lost when it comes to how to log all the api calls. I would like to simply log timing, logged in user from session, url and any other usual pertinent info that has to do with the API call. I can't seem to find any documentation for Guzzle 6 that refers to this, only guzzle 3 (Where they've changed the logging addSubscriber call). This is how my current API calls are:

$client = new GuzzleHttp\Client(['defaults' => ['verify' => false]]);
$res = $client->get($this->url . '/api/details', ['form_params' => ['file' => $file_id]]);
KingKongFrog
  • 13,946
  • 21
  • 75
  • 124

4 Answers4

70

You can use any logger which implements PSR-3 interface with Guzzle 6

I used Monolog as logger and builtin middleware of Guzzle with MessageFormatter in below example.

use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\MessageFormatter;
use Monolog\Logger;

$stack = HandlerStack::create();
$stack->push(
    Middleware::log(
        new Logger('Logger'),
        new MessageFormatter('{req_body} - {res_body}')
    )
);
$client = new \GuzzleHttp\Client(
    [
        'base_uri' => 'http://httpbin.org',
        'handler' => $stack,
    ]
);

echo (string) $client->get('ip')->getBody();

The details about the log middleware and message formatter has not well documented yet. But you can check the list which variables you can use in MessageFormatter

Also there is a guzzle-logmiddleware which allows you to customize formatter etc.

velioglu
  • 816
  • 7
  • 4
  • 1
    Where do you specify the name of the log file in this code? Thanks for posting this! – KingKongFrog Sep 21 '15 at 20:47
  • 1
    I didn't set the handler in example. You can chose which kind of handler you will use in logger. If you want to log your messages to file, you should pick StreamHandler. [check the monolog documentation](https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md#handlers) for other options. You can pass the handler while creating Logger or set it after via `pushHandler` method. check this [example](https://github.com/Seldaek/monolog/blob/master/doc/01-usage.md#configuring-a-logger) @KingKongFrog – velioglu Sep 22 '15 at 17:52
  • This worked as mentioned! but @velioglu Can you please help to do the same in laravel where I use single line API call like $response = Http::withBody( json_encode($reqArray), 'application/json')->withToken($token)->post($SearchByNameApi); I also ask question regarding this https://stackoverflow.com/questions/67920040/how-to-log-guzzle-http-request-response-in-file-using-laravel-7 i feel happy if you help me to achieve this – Vinay Kaithwas Jun 11 '21 at 10:10
  • If you log the response then you need to rewind the response body, otherwise you will get empty data. `$response->getBody()->rewind();` and then `$response->getBody()->getContents();`. Or you can also rewind adding another handler: `$mapResponse = Middleware::mapResponse( function(ResponseInterface $response) { $response->getBody()->rewind(); return $response; } ); $stack->push($mapResponse);` – Nuryagdy Mustapayev Aug 22 '22 at 17:24
  • 1
    @NuryagdyMustapayev `$responseBody = (string)$response->getBody();` does the same: rewind and deliver content – Oliver Mar 09 '23 at 18:30
8

@KingKongFrog This is the way to specify the name of the log file

$logger = new Logger('MyLog');
$logger->pushHandler(new StreamHandler(__DIR__ . '/test.log'), Logger::DEBUG);

$stack->push(Middleware::log(
$logger,
new MessageFormatter('{req_body} - {res_body}')
));
anhduc.bkhn
  • 695
  • 3
  • 10
  • 21
1

For Guzzle 7 I did this::

require './guzzle_7.2.0.0/vendor/autoload.php';
require './monolog/vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;

use GuzzleHttp\MessageFormatter;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

use GuzzleHttp\TransferStats;

//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$logger = null;
$messageFormat = 
            //['REQUEST: ', 'METHOD: {method}', 'URL: {uri}', 'HTTP/{version}', 'HEADERS: {req_headers}', 'Payload: {req_body}', 'RESPONSE: ', 'STATUS: {code}', 'BODY: {res_body}'];
            'REQUEST: urldecode(req_body)';
$handlerStack = \GuzzleHttp\HandlerStack::create(); 
$handlerStack->push(createGuzzleLoggingMiddleware($messageFormat));
function getLogger() {
    global $logger;
    if ($logger==null) {
        $logger = new Logger('api-consumer');
        $logger->pushHandler(new \Monolog\Handler\RotatingFileHandler('./TataAigHealthErrorMiddlewarelog.txt'));
    }
    var_dump($logger);
    return $logger;
    }
function createGuzzleLoggingMiddleware(string $messageFormat){
    return \GuzzleHttp\Middleware::log(getLogger(), new \GuzzleHttp\MessageFormatter($messageFormat));
}

function createLoggingHandlerStack(array $messageFormats){  
    global $logger;
    $stack = \GuzzleHttp\HandlerStack::create();
    var_dump($logger);
    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(createGuzzleLoggingMiddleware($messageFormat) );
    });
    return $stack;
}
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

$client = new Client(['verify' => false, 'handler' => $tapMiddleware($handlerStack)]);

WOW !!

Saurabh
  • 1,545
  • 1
  • 9
  • 9
1

unshift() is indeed better than push() in reverse order ...

$handlers = HandlerStack::create();
$logger = new Logger('Logger');
$templates = [
    '{code} >> {req_headers}',
    '{code} >> {req_body}',
    '{code} << {res_headers}',
    '{code} << {res_body}'
];
foreach ($templates as $template) {
    $handlers->unshift($this->getMiddleware($logger, $template));
}
$client = new Client([
    RequestOptions::DEBUG => false,
    'handler' => $handlers
]);

Using this function to obtain the Middleware:

private function getMiddleware(Logger $logger, string $template): callable {
    return Middleware::log($logger, new MessageFormatter($template));
}

Logger comes from "monolog/monolog": "^1.27.1".

And these are all supported variable substitutions.

Martin Zeitler
  • 1
  • 19
  • 155
  • 216