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.