1
0
mirror of https://github.com/guzzle/guzzle.git synced 2025-02-25 10:33:18 +01:00

Renaming BackoffPlugin to RetryPlugin and fixing MessageFormatter

RetryPlugin now supports logging retries by intercepting the retry delay
function.

Updated the MessageFormatter to properly log request and response strings.
This commit is contained in:
Michael Dowling 2014-02-08 17:28:01 -08:00
parent 2a2017bef1
commit 0e6a6c3d26
3 changed files with 118 additions and 94 deletions

View File

@ -1,28 +0,0 @@
{
"name": "guzzle/plugin-backoff",
"description": "Guzzle backoff retry plugins",
"homepage": "http://guzzlephp.org/",
"keywords": ["plugin", "guzzle"],
"license": "MIT",
"authors": [
{
"name": "Michael Dowling",
"email": "mtdowling@gmail.com",
"homepage": "https://github.com/mtdowling"
}
],
"require": {
"php": ">=5.3.2",
"guzzle/http": "self.version",
"guzzle/log": "self.version"
},
"autoload": {
"psr-0": { "Guzzle\\Plugin\\Backoff": "" }
},
"target-dir": "Guzzle/Plugin/Backoff",
"extra": {
"branch-alias": {
"dev-master": "3.7-dev"
}
}
}

View File

@ -6,9 +6,9 @@ use Guzzle\Http\Message\RequestInterface;
use Guzzle\Http\Message\ResponseInterface;
/**
* Message formatter used in various places in the framework
* Formats messages using variable substitutions for requests, responses, and other transactional data.
*
* Format messages using a template that can contain the the following variables:
* The following variable substitutions are supported:
*
* - {request}: Full HTTP request message
* - {response}: Full HTTP response message
@ -38,7 +38,7 @@ class MessageFormatter
const SHORT_FORMAT = '[{ts}] "{method} {resource} {protocol}/{version}" {code}';
/** @var string Template used to format log messages */
protected $template;
private $template;
/**
* @param string $template Log message template
@ -48,20 +48,6 @@ class MessageFormatter
$this->template = $template ?: self::DEFAULT_FORMAT;
}
/**
* Set the template to use for logging
*
* @param string $template Log message template
*
* @return self
*/
public function setTemplate($template)
{
$this->template = $template;
return $this;
}
/**
* Returns a formatted message
*
@ -97,10 +83,18 @@ class MessageFormatter
$result = $response;
break;
case 'req_headers':
$result = $request->getStartLine() . "\r\n" . $request->getHeaders();
$result = trim($request->getMethod() . ' '
. $request->getResource()) . ' HTTP/'
. $request->getProtocolVersion() . "\r\n"
. $request->getHeaders();
break;
case 'res_headers':
$result = $response->getStartLine() . "\r\n" . $response->getHeaders();
$result = sprintf(
'HTTP/%s %d %s',
$response->getProtocolVersion(),
$response->getStatusCode(),
$response->getReasonPhrase()
) . "\r\n" . $response->getHeaders();
break;
case 'req_body':
$result = $request->getBody();
@ -133,13 +127,13 @@ class MessageFormatter
$result = gethostname();
break;
case 'code':
$result = $response ? $response->getStatusCode() : '';
$result = $response ? $response->getStatusCode() : 'NULL';
break;
case 'phrase':
$result = $response ? $response->getReasonPhrase() : '';
$result = $response ? $response->getReasonPhrase() : 'NULL';
break;
case 'error':
$result = $error ? $error->getMessage() : null;
$result = $error ? $error->getMessage() : 'NULL';
break;
default:
if (strpos($matches[1], 'req_header_') === 0) {

View File

@ -1,16 +1,23 @@
<?php
namespace Guzzle\Plugin\Backoff;
namespace Guzzle\Plugin\Retry;
use Guzzle\Common\EventSubscriberInterface;
use Guzzle\Http\Event\AbstractTransferStatsEvent;
use Guzzle\Http\Event\RequestErrorEvent;
use Guzzle\Http\Event\RequestEvents;
use Guzzle\Plugin\Log\MessageFormatter;
use Guzzle\Plugin\Log\SimpleLogger;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
/**
* Plugin to automatically retry failed HTTP requests using a backoff strategy
* Plugin to automatically retry failed HTTP requests using filters a delay strategy.
*/
class BackoffPlugin implements EventSubscriberInterface
class RetryPlugin implements EventSubscriberInterface
{
const RETRY_FORMAT = '[{ts}] {method} {url} - {code} {phrase} - Retries: {retries}, Delay: {delay}, Time: {connect_time}, {total_time}, Error: {error}';
/** @var callable */
private $filter;
@ -20,10 +27,20 @@ class BackoffPlugin implements EventSubscriberInterface
/** @var int */
private $maxRetries;
public static function getSubscribedEvents()
{
return [
RequestEvents::AFTER_SEND => ['onRequestSent'],
RequestEvents::ERROR => ['onRequestSent']
];
}
/**
* @param callable $filter Filter used to determine whether or not to retry a request
* @param callable $delayFunc Callable that accepts the number of retries and returns the amount of
* of time in seconds to delay.
* @param callable $filter Filter used to determine whether or not to retry a request. The filter must be a
* callable that accepts the current number of retries and an AbstractTransferStatsEvent
* object. The filter must return true or false to denote if the request must be retried.
* @param callable $delayFunc Callable that accepts the number of retries and an AbstractTransferStatsEvent and
* returns the amount of of time in seconds to delay.
* @param int $maxRetries Maximum number of retries
*/
public function __construct(
@ -42,32 +59,40 @@ class BackoffPlugin implements EventSubscriberInterface
* @param int $maxRetries Maximum number of retries
* @param array $httpCodes HTTP response codes to retry
* @param array $curlCodes cURL error codes to retry
* @param LoggerInterface|bool $logger Pass a logger instance to log each retry or pass true for STDOUT
* @param string|MessageFormatter $formatter Pass a message formatter if logging to customize log messages
*
* @return self
* @throws \InvalidArgumentException if logger is not a boolean or LoggerInterface
*/
public static function getExponentialBackoff(
$maxRetries = 3,
array $httpCodes = null,
array $curlCodes = null
array $curlCodes = null,
$logger = null,
$formatter = null
) {
if (extension_loaded('curl')) {
if (!extension_loaded('curl')) {
$filter = self::createStatusFilter($httpCodes);
} else {
$filter = self::createChainFilter([
self::createStatusFilter($httpCodes),
self::createCurlFilter($curlCodes)
]);
} else {
$filter = self::createStatusFilter($httpCodes);
}
return new self($filter, array(__CLASS__, 'exponentialDelay'), $maxRetries);
$delay = [__CLASS__, 'exponentialDelay'];
if ($logger) {
if ($logger === true) {
$logger = new SimpleLogger(STDOUT);
} elseif (!($logger instanceof LoggerInterface)) {
throw new \InvalidArgumentException('$logger must be true, false, or a LoggerInterface');
}
$delay = self::createLoggingDelay($delay, $logger, $formatter);
}
public static function getSubscribedEvents()
{
return [
RequestEvents::AFTER_SEND => ['onRequestSent'],
RequestEvents::ERROR => ['onRequestSent']
];
return new self($filter, $delay, $maxRetries);
}
public function onRequestSent(AbstractTransferStatsEvent $event)
@ -79,7 +104,7 @@ class BackoffPlugin implements EventSubscriberInterface
$filterFn = $this->filter;
if ($filterFn($retries, $event)) {
$delayFn = $this->delayFunc;
sleep($delayFn($retries));
sleep($delayFn($retries, $event));
$request->getConfig()->set('retries', ++$retries);
$event->intercept($event->getClient()->send($request));
}
@ -90,14 +115,47 @@ class BackoffPlugin implements EventSubscriberInterface
* Returns an exponential delay calculation
*
* @param int $retries Number of retries so far
* @param AbstractTransferStatsEvent $event Event containing transactional information
*
* @return int
*/
public static function exponentialDelay($retries)
public static function exponentialDelay($retries, AbstractTransferStatsEvent $event)
{
return (int) pow(2, $retries - 1);
}
/**
* Creates a delay function that logs each retry before proxying to a wrapped delay function.
*
* @param callable $delayFn Delay function to proxy to
* @param LoggerInterface $logger Logger used to log messages
* @param string|MessageFormatter $formatter Format string or Message formatter used to format messages
*
* @return callable
*/
public static function createLoggingDelay(
callable $delayFn,
LoggerInterface $logger,
$formatter = null
) {
if (!$formatter) {
$formatter = new MessageFormatter(self::RETRY_FORMAT);
} elseif (!($formatter instanceof MessageFormatter)) {
$formatter = new MessageFormatter($formatter);
}
return function ($retries, AbstractTransferStatsEvent $event) use ($delayFn, $logger, $formatter) {
$delay = $delayFn($retries, $event);
$logger->log(LogLevel::NOTICE, $formatter->format(
$event->getRequest(),
$event->getResponse(),
$event instanceof RequestErrorEvent ? $event->getException() : null,
['retries' => $retries + 1, 'delay' => $delay] + $event->getTransferInfo()
));
return $delay;
};
}
/**
* Creates a retry filter based on HTTP status codes
*