162 lines
5.7 KiB
PHP
162 lines
5.7 KiB
PHP
<?php
|
|
|
|
namespace Guzzle\Plugin\Log;
|
|
|
|
use Guzzle\Common\Event;
|
|
use Guzzle\Log\LogAdapterInterface;
|
|
use Guzzle\Log\MessageFormatter;
|
|
use Guzzle\Log\ClosureLogAdapter;
|
|
use Guzzle\Http\EntityBody;
|
|
use Guzzle\Http\Message\EntityEnclosingRequestInterface;
|
|
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
|
|
|
/**
|
|
* Plugin class that will add request and response logging to an HTTP request.
|
|
*
|
|
* The log plugin uses a message formatter that allows custom messages via template variable substitution.
|
|
*
|
|
* @see MessageLogger for a list of available log template variable substitutions
|
|
*/
|
|
class LogPlugin implements EventSubscriberInterface
|
|
{
|
|
/** @var LogAdapterInterface Adapter responsible for writing log data */
|
|
protected $logAdapter;
|
|
|
|
/** @var MessageFormatter Formatter used to format messages before logging */
|
|
protected $formatter;
|
|
|
|
/** @var bool Whether or not to wire request and response bodies */
|
|
protected $wireBodies;
|
|
|
|
/**
|
|
* @param LogAdapterInterface $logAdapter Adapter object used to log message
|
|
* @param string|MessageFormatter $formatter Formatter used to format log messages or the formatter template
|
|
* @param bool $wireBodies Set to true to track request and response bodies using a temporary
|
|
* buffer if the bodies are not repeatable.
|
|
*/
|
|
public function __construct(
|
|
LogAdapterInterface $logAdapter,
|
|
$formatter = null,
|
|
$wireBodies = false
|
|
) {
|
|
$this->logAdapter = $logAdapter;
|
|
$this->formatter = $formatter instanceof MessageFormatter ? $formatter : new MessageFormatter($formatter);
|
|
$this->wireBodies = $wireBodies;
|
|
}
|
|
|
|
/**
|
|
* Get a log plugin that outputs full request, response, and curl error information to stderr
|
|
*
|
|
* @param bool $wireBodies Set to false to disable request/response body output when they use are not repeatable
|
|
* @param resource $stream Stream to write to when logging. Defaults to STDERR when it is available
|
|
*
|
|
* @return self
|
|
*/
|
|
public static function getDebugPlugin($wireBodies = true, $stream = null)
|
|
{
|
|
if ($stream === null) {
|
|
if (defined('STDERR')) {
|
|
$stream = STDERR;
|
|
} else {
|
|
$stream = fopen('php://output', 'w');
|
|
}
|
|
}
|
|
|
|
return new self(new ClosureLogAdapter(function ($m) use ($stream) {
|
|
fwrite($stream, $m . PHP_EOL);
|
|
}), "# Request:\n{request}\n\n# Response:\n{response}\n\n# Errors: {curl_code} {curl_error}", $wireBodies);
|
|
}
|
|
|
|
public static function getSubscribedEvents()
|
|
{
|
|
return array(
|
|
'curl.callback.write' => array('onCurlWrite', 255),
|
|
'curl.callback.read' => array('onCurlRead', 255),
|
|
'request.before_send' => array('onRequestBeforeSend', 255),
|
|
'request.sent' => array('onRequestSent', 255)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Event triggered when curl data is read from a request
|
|
*
|
|
* @param Event $event
|
|
*/
|
|
public function onCurlRead(Event $event)
|
|
{
|
|
// Stream the request body to the log if the body is not repeatable
|
|
if ($wire = $event['request']->getParams()->get('request_wire')) {
|
|
$wire->write($event['read']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event triggered when curl data is written to a response
|
|
*
|
|
* @param Event $event
|
|
*/
|
|
public function onCurlWrite(Event $event)
|
|
{
|
|
// Stream the response body to the log if the body is not repeatable
|
|
if ($wire = $event['request']->getParams()->get('response_wire')) {
|
|
$wire->write($event['write']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called before a request is sent
|
|
*
|
|
* @param Event $event
|
|
*/
|
|
public function onRequestBeforeSend(Event $event)
|
|
{
|
|
if ($this->wireBodies) {
|
|
$request = $event['request'];
|
|
// Ensure that curl IO events are emitted
|
|
$request->getCurlOptions()->set('emit_io', true);
|
|
// We need to make special handling for content wiring and non-repeatable streams.
|
|
if ($request instanceof EntityEnclosingRequestInterface && $request->getBody()
|
|
&& (!$request->getBody()->isSeekable() || !$request->getBody()->isReadable())
|
|
) {
|
|
// The body of the request cannot be recalled so logging the body will require us to buffer it
|
|
$request->getParams()->set('request_wire', EntityBody::factory());
|
|
}
|
|
if (!$request->getResponseBody()->isRepeatable()) {
|
|
// The body of the response cannot be recalled so logging the body will require us to buffer it
|
|
$request->getParams()->set('response_wire', EntityBody::factory());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Triggers the actual log write when a request completes
|
|
*
|
|
* @param Event $event
|
|
*/
|
|
public function onRequestSent(Event $event)
|
|
{
|
|
$request = $event['request'];
|
|
$response = $event['response'];
|
|
$handle = $event['handle'];
|
|
|
|
if ($wire = $request->getParams()->get('request_wire')) {
|
|
$request = clone $request;
|
|
$request->setBody($wire);
|
|
}
|
|
|
|
if ($wire = $request->getParams()->get('response_wire')) {
|
|
$response = clone $response;
|
|
$response->setBody($wire);
|
|
}
|
|
|
|
// Send the log message to the adapter, adding a category and host
|
|
$priority = $response && $response->isError() ? LOG_ERR : LOG_DEBUG;
|
|
$message = $this->formatter->format($request, $response, $handle);
|
|
$this->logAdapter->log($message, $priority, array(
|
|
'request' => $request,
|
|
'response' => $response,
|
|
'handle' => $handle
|
|
));
|
|
}
|
|
}
|