How do I log requests and responses in Guzzle?

Logging HTTP requests and responses in Guzzle is essential for debugging, monitoring API interactions, and meeting compliance requirements. Guzzle provides built-in middleware that makes it easy to capture and log all HTTP traffic.

Installation

First, install Guzzle and Monolog via Composer:

composer require guzzlehttp/guzzle monolog/monolog

Basic Request/Response Logging

Here's a complete example that logs both requests and responses:

<?php

require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\MessageFormatter;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;

// Create a logger
$logger = new Logger('guzzle');
$logger->pushHandler(new StreamHandler('logs/guzzle.log', Logger::DEBUG));

// Create handler stack with logging middleware
$stack = HandlerStack::create();
$stack->push(Middleware::log(
    $logger,
    new MessageFormatter('{method} {uri} - {req_body} - {code} {phrase} - {res_body}')
));

// Create client with custom handler
$client = new Client(['handler' => $stack]);

// Make requests - they will be automatically logged
$response = $client->request('GET', 'https://httpbin.org/get');
$postResponse = $client->request('POST', 'https://httpbin.org/post', [
    'json' => ['key' => 'value']
]);

Custom Log Formats

The MessageFormatter supports various template variables for detailed logging:

// Minimal logging - just method, URL, and status
$formatter = new MessageFormatter('{method} {uri} - {code}');

// Detailed logging with headers
$formatter = new MessageFormatter(
    "REQUEST: {method} {uri}\nHeaders: {req_headers}\nBody: {req_body}\n" .
    "RESPONSE: {code} {phrase}\nHeaders: {res_headers}\nBody: {res_body}\n"
);

// Custom format with timing
$formatter = new MessageFormatter(
    '[{ts}] {method} {uri} - {code} {phrase} - {total_time}ms'
);

Available Template Variables

| Variable | Description | |----------|-------------| | {method} | HTTP method (GET, POST, etc.) | | {uri} | Request URI | | {version} | HTTP version | | {req_headers} | Request headers | | {req_body} | Request body | | {code} | Response status code | | {phrase} | Response reason phrase | | {res_headers} | Response headers | | {res_body} | Response body | | {total_time} | Total request time in milliseconds | | {ts} | Timestamp |

Conditional Logging

Log only failed requests (4xx/5xx status codes):

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

$stack->push(Middleware::log(
    $logger,
    new MessageFormatter('{method} {uri} - {code} {phrase} - {res_body}'),
    'error' // Log level for failed requests
));

Different Log Handlers

File Logging with Rotation

use Monolog\Handler\RotatingFileHandler;

$logger = new Logger('guzzle');
$logger->pushHandler(new RotatingFileHandler('logs/guzzle.log', 7, Logger::DEBUG));

Database Logging

use Monolog\Handler\PDOHandler;

$pdo = new PDO('mysql:host=localhost;dbname=logs', $user, $pass);
$logger->pushHandler(new PDOHandler($pdo, 'http_logs', Logger::DEBUG));

Syslog Integration

use Monolog\Handler\SyslogHandler;

$logger->pushHandler(new SyslogHandler('guzzle', LOG_USER, Logger::DEBUG));

Custom Logging Middleware

For advanced use cases, create custom middleware:

use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;

$customLogger = function (callable $handler) use ($logger) {
    return function (RequestInterface $request, array $options) use ($handler, $logger) {
        $start = microtime(true);

        return $handler($request, $options)->then(
            function (ResponseInterface $response) use ($request, $logger, $start) {
                $duration = round((microtime(true) - $start) * 1000, 2);

                $logger->info('HTTP Request', [
                    'method' => $request->getMethod(),
                    'uri' => (string) $request->getUri(),
                    'status' => $response->getStatusCode(),
                    'duration_ms' => $duration,
                    'request_size' => $request->getBody()->getSize(),
                    'response_size' => $response->getBody()->getSize()
                ]);

                return $response;
            }
        );
    };
};

$stack->push($customLogger);

Production Considerations

Sensitive Data Protection

// Avoid logging sensitive data in production
$formatter = new MessageFormatter('{method} {uri} - {code} {phrase}');

// Or use a custom formatter that filters sensitive headers
$sensitiveHeaders = ['authorization', 'x-api-key', 'cookie'];
// Implementation to filter these headers...

Performance Impact

// Log only errors in production
if ($environment === 'production') {
    $stack->push(Middleware::log($logger, $formatter, 'error'));
} else {
    $stack->push(Middleware::log($logger, $formatter, 'debug'));
}

Log Levels

// Use appropriate log levels
$logger->debug('Development debugging info');
$logger->info('General request information');
$logger->warning('Unexpected but handled situations');
$logger->error('Error conditions');

This comprehensive logging setup provides full visibility into your HTTP requests and responses, making debugging and monitoring much easier while maintaining flexibility for different environments and requirements.

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon