{"id":13182,"date":"2020-05-10T17:15:24","date_gmt":"2020-05-10T17:15:24","guid":{"rendered":"https:\/\/alexrusin.com\/?p=13182"},"modified":"2023-09-17T16:27:52","modified_gmt":"2023-09-17T16:27:52","slug":"retrying-and-logging-requests-with-guzzle","status":"publish","type":"post","link":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/","title":{"rendered":"Retrying and Logging Requests with Guzzle"},"content":{"rendered":"\n

When consuming 3d party API, you may want to do two things:<\/p>\n\n\n\n

    \n
  1. Retrying.  Usually you can retry your requests on 500 responses received from server.<\/li>\n\n\n\n
  2. Logging. It is considered a good practice to log requests you send and responses you get back.<\/li>\n<\/ol>\n\n\n\n

    In this article we will look at how to implement the above features using Guzzle, a popular PHP library for making API calls.<\/p>\n\n\n\n

    Let us scaffold our app<\/p>\n\n\n\n

    composer init<\/em><\/p>\n\n\n\n

    composer require guzzlehttp\/guzzle:~6.0<\/em><\/p>\n\n\n\n

    composer require monolog\/monolog<\/em><\/p>\n\n\n\n

    composer require –dev phpunit\/phpunit:^8<\/em><\/p>\n\n\n\n

    We will be using PHPUnit to run our code.  PHPUnit 9 requires PHP7.3.  Since I have PHP7.2, I will stick with PHPUnit 8.  We also need to create phpunit.xml file in the root of our project:<\/p>\n\n\n\n

    <?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit backupGlobals=\"false\"\n         backupStaticAttributes=\"false\"\n         bootstrap=\"vendor\/autoload.php\"\n         colors=\"true\"\n         convertErrorsToExceptions=\"true\"\n         convertNoticesToExceptions=\"true\"\n         convertWarningsToExceptions=\"true\"\n         processIsolation=\"false\"\n         stopOnFailure=\"false\">\n    <testsuites>\n        <testsuite name=\"Unit\">\n            <directory suffix=\"Test.php\">.\/tests<\/directory>\n        <\/testsuite>\n    <\/testsuites>\n   \n    <filter>\n        <whitelist processUncoveredFilesFromWhitelist=\"true\">\n            <directory suffix=\".php\">.\/app<\/directory>\n        <\/whitelist>\n    <\/filter>\n    <php>\n        <env name=\"APP_ENV\" value=\"testing\"\/>\n    <\/php>\n<\/phpunit><\/pre>\n\n\n\n

    Let us also define PSR-4 autoloading in composer.json file. Complete composer.json file should look something like this.<\/p>\n\n\n\n

    {\n    \"name\": \"apr\/retrying-and-logging\",\n    \"description\": \"Retrying and logging Guzzle requests\",\n    \"authors\": [\n        {\n            \"name\": \"Alex Rusin\",\n            \"email\": \"alex@email.com\"\n        }\n    ],\n    \"require\": {\n        \"guzzlehttp\/guzzle\": \"~6.0\",\n        \"monolog\/monolog\": \"^2.0\"\n    },\n    \"require-dev\": {\n        \"phpunit\/phpunit\": \"^8\"\n    },\n\n    \"autoload\": {\n        \"psr-4\": {\n            \"App\\\\\": \"src\/\"\n        }\n    },\n\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Tests\\\\\": \"tests\/\"\n        }\n    }\n}\n<\/pre>\n\n\n\n

    Don\u2019t forget to run composer dump-autoload<\/em><\/p>\n\n\n\n

    Now we are ready to start creating our code.  In src\/Api folder we are going to create Api.php file containing Api class.<\/p>\n\n\n\n

    <?php\n\nnamespace App\\Api;\n\nuse GuzzleHttp\\Client;\nuse GuzzleHttp\\HandlerStack;\nuse GuzzleHttp\\Middleware;\nuse GuzzleHttp\\MessageFormatter;\nuse GuzzleHttp\\Psr7\\Request;\nuse GuzzleHttp\\Psr7\\Response;\nuse GuzzleHttp\\Exception\\ConnectException;\nuse GuzzleHttp\\Exception\\RequestException;\nuse Psr\\Log\\LoggerInterface;\n\nabstract class Api \n{\n    protected $logger;\n    protected $client;\n    const MAX_RETRIES = 3;\n\n    public function __construct(LoggerInterface $logger)\n    {\n        $this->logger = $logger;\n\n        $this->client = new Client([\n            'base_uri' => $this->baseUri,\n            'handler' => $this->createHandlerStack(),\n            'timeout'  => 30.0,\n            'headers' => [\n                'Accept' => 'application\/json',\n            ],\n\t\t 'verify' => false\n        ]);\n    }\n\n    protected function createHandlerStack()\n    {\n        $stack = HandlerStack::create();\n        $stack->push(Middleware::retry($this->retryDecider(), $this->retryDelay()));\n        return $this->createLoggingHandlerStack($stack);\n    }\n    \n    protected function createLoggingHandlerStack(HandlerStack $stack)\n    {\n        $messageFormats = [\n            '{method} {uri} HTTP\/{version}',\n            'HEADERS: {req_headers}',\n            'BODY: {req_body}',\n            'RESPONSE: {code} - {res_body}',\n        ];\n\n        foreach ($messageFormats as $messageFormat) {\n            \/\/ We'll use unshift instead of push, to add the middleware to the bottom of the stack, not the top\n            $stack->unshift(\n                $this->createGuzzleLoggingMiddleware($messageFormat)\n            );\n        }\n    \n        return $stack;\n    }\n\n    protected function createGuzzleLoggingMiddleware(string $messageFormat)\n    {\n        return Middleware::log(\n            $this->logger,\n            new MessageFormatter($messageFormat)\n        );\n    }\n\n    protected function retryDecider()\n    {\n        return function (\n            $retries,\n            Request $request,\n            Response $response = null,\n            RequestException $exception = null\n        ) {\n            \/\/ Limit the number of retries to MAX_RETRIES\n            if ($retries >= self::MAX_RETRIES) {\n                return false;\n            }\n\n            \/\/ Retry connection exceptions\n            if ($exception instanceof ConnectException) {\n                $this->logger->info('Timeout encountered, retrying');\n                return true;\n            }\n\n            if ($response) {\n                \/\/ Retry on server errors\n                if ($response->getStatusCode() >= 500) {\n                    $this->logger->info('Server 5xx error encountered, retrying...');\n                    return true;\n                }\n            }\n\n            return false;\n        };\n    }\n\n    \/**\n     * delay 1s 2s 3s 4s 5s ...\n     *\n     * @return callable\n     *\/\n    protected function retryDelay()\n    {\n        return function ($numberOfRetries) {\n            return 1000 * $numberOfRetries;\n        };\n    }\n}\n<\/pre>\n\n\n\n

    The logging and retrying logic is defined in the class above. We use handler stack to incorporate this logic.  Since this is a demo code, I also turned off ssl verification \u2018verify\u2019 => false.  This is not a good practice to use in production. <\/p>\n\n\n\n

    Now let\u2019s create our class that will actually be used to make calls.  We will be using mocky.io.  In src\/Api create MockyApi class that will extend abstract Api.<\/p>\n\n\n\n

    <?php\n\nnamespace App\\Api;\n\nclass MockyApi extends Api\n{\n    protected $baseUri = 'https:\/\/www.mocky.io\/v2\/';\n\n    public function getTasks()\n    {\n        $response = $this->client->get('5eb81e152d00003e2b357c06');\n        $response->getBody()->rewind();\n        \n        return $response; \n    }\n\n    public function createTask()\n    {\n        $response = $this->client->post('5eb81ee82d00005800357c07', [\n            'json' => [\n                'description' => 'Write a blog post'\n            ]\n        ]);\n        $response->getBody()->rewind();\n\n        return $response;\n    }\n\n    public function failedGetTasks()\n    {\n        $response = $this->client->get('5eb829d42d00003e2b357c26');\n        $response->getBody()->rewind();\n        \n        return $response; \n    }\n}\n<\/pre>\n\n\n\n

    Since we are using the logger in Guzzle stack it will read the body of the response. Response body is a stream (PSR-7).  Unfortunately, it does not get rewound back after it is logged.  We need to make sure we rewind it before returning response.<\/p>\n\n\n\n

    Using mocky.io, we created mock responses for getting tasks, creating a task and for failing to get tasks We used generated routes in the class above for getTasks(), createTask(), and failedGetTasks() functions.<\/p>\n\n\n\n

    \"\"<\/figure>\n\n\n\n
    \"\"<\/figure>\n\n\n\n
    \"\"<\/figure>\n\n\n\n

    Now let\u2019s abstract logger creation. In app\/Factories folder create LoggerFactory.php:<\/p>\n\n\n\n

    <?php\n\nnamespace App\\Factories;\n\nuse DateTime;\nuse DateTimeZone;\nuse Monolog\\Logger;\nuse Monolog\\Handler\\StreamHandler;\nuse Monolog\\Formatter\\LineFormatter;\n\nclass LoggerFactory\n{\n    public function create(string $channel = 'testing', string $fileNameHandle = 'app_log')\n    {\n        $logger = new Logger($channel);\n        $dateFormat = \"n\/j\/Y, g:i a\";\n        $formatter = new LineFormatter(null, $dateFormat, false, true);\n        $now = (new DateTime(\"now\"))->format('m_d_Y');\n        $handler = new StreamHandler(__DIR__ . \"\/..\/logs\/{$fileNameHandle}_{$now}.log\", Logger::INFO);\n        $handler->setFormatter($formatter);\n        $logger->pushHandler($handler);\n        $logger->setTimezone(new DateTimeZone('America\/Los_Angeles'));\n        return $logger;\n    }\n}\n<\/pre>\n\n\n\n

    The logger factory creates an instance of PSR-3 complient logger, sets date formats, files to write to, and time zone.<\/p>\n\n\n\n

    It is time to try out our code.  In tests folder create MockyApiTest.php.  We create tests for each of our MockyApi methods.  Note, that here I am using PHPUnit not to test my code, but to run it because PHPUnit provides a cli environment. Normally, you would not want to hit your API endpoint when running tests.<\/p>\n\n\n\n

    <?php\nnamespace Tests;\n\nuse App\\Api\\MockyApi;\nuse App\\Factories\\LoggerFactory;\nuse GuzzleHttp\\Exception\\RequestException;\nuse PHPUnit\\Framework\\TestCase as PhpUnitTestCase;\n\nclass MockyApiTest extends PhpUnitTestCase\n{\n    private $logger;\n\n    public function setUp(): void\n    {\n        $this->logger = (new LoggerFactory)->create('retries-and-logging');\n    }\n    \/** @test *\/\n    public function it_gets_tasks()\n    {\n        $api = new MockyApi($this->logger);\n\n        $response = $api->getTasks();\n        $contents = $response->getBody()->getContents();\n\n        $this->assertCount(2, json_decode($contents));\n    }\n\n    \/** @test *\/\n    public function it_creates_a_task()\n    {\n        $api = new MockyApi($this->logger);\n\n        $response = $api->createTask();\n        $contents = $response->getBody()->getContents();\n        $description = json_decode($contents)->description;\n\n        $this->assertEquals('Write a blog post', $description);\n    }\n\n    \/** @test *\/\n    public function it_fails_to_get_tasks()\n    {\n        $api = new MockyApi($this->logger);\n\n        $this->expectException(RequestException::class);\n        $api->failedGetTasks();\n    }\n}\n<\/pre>\n\n\n\n

    composer dump-autoload<\/em><\/p>\n\n\n\n

    .vendor\/bin\/phpunit<\/em><\/p>\n\n\n\n

    \"\"<\/figure>\n\n\n\n

    And in your log file you should see something like this:<\/p>\n\n\n\n

    [5\/10\/2020, 9:30 am] retries-and-logging.INFO: GET https:\/\/www.mocky.io\/v2\/5eb81e152d00003e2b357c06 HTTP\/1.1  \n[5\/10\/2020, 9:30 am] retries-and-logging.INFO: HEADERS: GET \/v2\/5eb81e152d00003e2b357c06 HTTP\/1.1 Accept: application\/json User-Agent: GuzzleHttp\/6.5.3 curl\/7.64.0 PHP\/7.2.17 Host: www.mocky.io  \n[5\/10\/2020, 9:30 am] retries-and-logging.INFO: BODY:   \n[5\/10\/2020, 9:30 am] retries-and-logging.INFO: RESPONSE: 200 - [     {\"description\":\"Get groceries\",\"completed\":true},     {\"description\":\"Clean the room\",\"completed\":false} ]  \n[5\/10\/2020, 9:30 am] retries-and-logging.INFO: POST https:\/\/www.mocky.io\/v2\/5eb81ee82d00005800357c07 HTTP\/1.1  \n[5\/10\/2020, 9:30 am] retries-and-logging.INFO: HEADERS: POST \/v2\/5eb81ee82d00005800357c07 HTTP\/1.1 Accept: application\/json User-Agent: GuzzleHttp\/6.5.3 curl\/7.64.0 PHP\/7.2.17 Content-Type: application\/json Host: www.mocky.io  \n[5\/10\/2020, 9:30 am] retries-and-logging.INFO: BODY: {\"description\":\"Write a blog post\"}  \n[5\/10\/2020, 9:30 am] retries-and-logging.INFO: RESPONSE: 201 - {\"description\":\"Write a blog post\",\"completed\":true}   \n[5\/10\/2020, 9:30 am] retries-and-logging.INFO: Server 5xx error encountered, retrying...  \n[5\/10\/2020, 9:30 am] retries-and-logging.INFO: Server 5xx error encountered, retrying...  \n[5\/10\/2020, 9:31 am] retries-and-logging.INFO: Server 5xx error encountered, retrying...  \n[5\/10\/2020, 9:31 am] retries-and-logging.NOTICE: GET https:\/\/www.mocky.io\/v2\/5eb829d42d00003e2b357c26 HTTP\/1.1  \n[5\/10\/2020, 9:31 am] retries-and-logging.NOTICE: HEADERS: GET \/v2\/5eb829d42d00003e2b357c26 HTTP\/1.1 Accept: application\/json User-Agent: GuzzleHttp\/6.5.3 curl\/7.64.0 PHP\/7.2.17 Host: www.mocky.io  \n[5\/10\/2020, 9:31 am] retries-and-logging.NOTICE: BODY:   \n[5\/10\/2020, 9:31 am] retries-and-logging.NOTICE: RESPONSE: 500 -<\/pre>\n\n\n\n

    Resources used<\/h3>\n\n\n\n

    Guzzle Documentation<\/a><\/p>\n\n\n\n

    Monolog Documentation<\/a><\/p>\n\n\n\n

    Guzzle 6 retry middleware<\/a><\/p>\n\n\n\n

    Logging Guzzle Requests<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"

    When consuming 3d party API, you may want to do two things: In this article we will look at how to implement the above features using Guzzle, a popular PHP library for making API calls. Let us scaffold our app composer init composer require guzzlehttp\/guzzle:~6.0 composer require monolog\/monolog composer require –dev phpunit\/phpunit:^8 We will be…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"footnotes":""},"categories":[10],"tags":[],"class_list":["post-13182","post","type-post","status-publish","format-standard","hentry","category-php-mysql-development"],"yoast_head":"\nRetrying and Logging Requests with Guzzle | Alex Rusin Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Retrying and Logging Requests with Guzzle | Alex Rusin Blog\" \/>\n<meta property=\"og:description\" content=\"When consuming 3d party API, you may want to do two things: In this article we will look at how to implement the above features using Guzzle, a popular PHP library for making API calls. Let us scaffold our app composer init composer require guzzlehttp\/guzzle:~6.0 composer require monolog\/monolog composer require –dev phpunit\/phpunit:^8 We will be...\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/\" \/>\n<meta property=\"og:site_name\" content=\"Alex Rusin Blog\" \/>\n<meta property=\"article:published_time\" content=\"2020-05-10T17:15:24+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-09-17T16:27:52+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blog.alexrusin.com\/wp-content\/uploads\/2020\/05\/mocky1.jpg\" \/>\n<meta name=\"author\" content=\"alexrusin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"alexrusin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"3 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/\"},\"author\":{\"name\":\"alexrusin\",\"@id\":\"https:\/\/blog.alexrusin.com\/#\/schema\/person\/a9005ca622862109b2c514050fbaaf9a\"},\"headline\":\"Retrying and Logging Requests with Guzzle\",\"datePublished\":\"2020-05-10T17:15:24+00:00\",\"dateModified\":\"2023-09-17T16:27:52+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/\"},\"wordCount\":461,\"publisher\":{\"@id\":\"https:\/\/blog.alexrusin.com\/#\/schema\/person\/a9005ca622862109b2c514050fbaaf9a\"},\"image\":{\"@id\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/blog.alexrusin.com\/wp-content\/uploads\/2020\/05\/mocky1.jpg\",\"articleSection\":[\"PHP MySQL Development\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/\",\"url\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/\",\"name\":\"Retrying and Logging Requests with Guzzle | Alex Rusin Blog\",\"isPartOf\":{\"@id\":\"https:\/\/blog.alexrusin.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/blog.alexrusin.com\/wp-content\/uploads\/2020\/05\/mocky1.jpg\",\"datePublished\":\"2020-05-10T17:15:24+00:00\",\"dateModified\":\"2023-09-17T16:27:52+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#primaryimage\",\"url\":\"https:\/\/blog.alexrusin.com\/wp-content\/uploads\/2020\/05\/mocky1.jpg\",\"contentUrl\":\"https:\/\/blog.alexrusin.com\/wp-content\/uploads\/2020\/05\/mocky1.jpg\",\"width\":901,\"height\":525},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blog.alexrusin.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Retrying and Logging Requests with Guzzle\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blog.alexrusin.com\/#website\",\"url\":\"https:\/\/blog.alexrusin.com\/\",\"name\":\"Alex Rusin\",\"description\":\"Web Development Blog\",\"publisher\":{\"@id\":\"https:\/\/blog.alexrusin.com\/#\/schema\/person\/a9005ca622862109b2c514050fbaaf9a\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blog.alexrusin.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/blog.alexrusin.com\/#\/schema\/person\/a9005ca622862109b2c514050fbaaf9a\",\"name\":\"alexrusin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blog.alexrusin.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/c36ef231f9e0b11371891eb84360f4bc?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/c36ef231f9e0b11371891eb84360f4bc?s=96&d=mm&r=g\",\"caption\":\"alexrusin\"},\"logo\":{\"@id\":\"https:\/\/blog.alexrusin.com\/#\/schema\/person\/image\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Retrying and Logging Requests with Guzzle | Alex Rusin Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/","og_locale":"en_US","og_type":"article","og_title":"Retrying and Logging Requests with Guzzle | Alex Rusin Blog","og_description":"When consuming 3d party API, you may want to do two things: In this article we will look at how to implement the above features using Guzzle, a popular PHP library for making API calls. Let us scaffold our app composer init composer require guzzlehttp\/guzzle:~6.0 composer require monolog\/monolog composer require –dev phpunit\/phpunit:^8 We will be...","og_url":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/","og_site_name":"Alex Rusin Blog","article_published_time":"2020-05-10T17:15:24+00:00","article_modified_time":"2023-09-17T16:27:52+00:00","og_image":[{"url":"https:\/\/blog.alexrusin.com\/wp-content\/uploads\/2020\/05\/mocky1.jpg","type":"","width":"","height":""}],"author":"alexrusin","twitter_card":"summary_large_image","twitter_misc":{"Written by":"alexrusin","Est. reading time":"3 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#article","isPartOf":{"@id":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/"},"author":{"name":"alexrusin","@id":"https:\/\/blog.alexrusin.com\/#\/schema\/person\/a9005ca622862109b2c514050fbaaf9a"},"headline":"Retrying and Logging Requests with Guzzle","datePublished":"2020-05-10T17:15:24+00:00","dateModified":"2023-09-17T16:27:52+00:00","mainEntityOfPage":{"@id":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/"},"wordCount":461,"publisher":{"@id":"https:\/\/blog.alexrusin.com\/#\/schema\/person\/a9005ca622862109b2c514050fbaaf9a"},"image":{"@id":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.alexrusin.com\/wp-content\/uploads\/2020\/05\/mocky1.jpg","articleSection":["PHP MySQL Development"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/","url":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/","name":"Retrying and Logging Requests with Guzzle | Alex Rusin Blog","isPartOf":{"@id":"https:\/\/blog.alexrusin.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#primaryimage"},"image":{"@id":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#primaryimage"},"thumbnailUrl":"https:\/\/blog.alexrusin.com\/wp-content\/uploads\/2020\/05\/mocky1.jpg","datePublished":"2020-05-10T17:15:24+00:00","dateModified":"2023-09-17T16:27:52+00:00","breadcrumb":{"@id":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#primaryimage","url":"https:\/\/blog.alexrusin.com\/wp-content\/uploads\/2020\/05\/mocky1.jpg","contentUrl":"https:\/\/blog.alexrusin.com\/wp-content\/uploads\/2020\/05\/mocky1.jpg","width":901,"height":525},{"@type":"BreadcrumbList","@id":"https:\/\/blog.alexrusin.com\/retrying-and-logging-requests-with-guzzle\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.alexrusin.com\/"},{"@type":"ListItem","position":2,"name":"Retrying and Logging Requests with Guzzle"}]},{"@type":"WebSite","@id":"https:\/\/blog.alexrusin.com\/#website","url":"https:\/\/blog.alexrusin.com\/","name":"Alex Rusin","description":"Web Development Blog","publisher":{"@id":"https:\/\/blog.alexrusin.com\/#\/schema\/person\/a9005ca622862109b2c514050fbaaf9a"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.alexrusin.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/blog.alexrusin.com\/#\/schema\/person\/a9005ca622862109b2c514050fbaaf9a","name":"alexrusin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.alexrusin.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/c36ef231f9e0b11371891eb84360f4bc?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/c36ef231f9e0b11371891eb84360f4bc?s=96&d=mm&r=g","caption":"alexrusin"},"logo":{"@id":"https:\/\/blog.alexrusin.com\/#\/schema\/person\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/blog.alexrusin.com\/wp-json\/wp\/v2\/posts\/13182","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.alexrusin.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.alexrusin.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.alexrusin.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.alexrusin.com\/wp-json\/wp\/v2\/comments?post=13182"}],"version-history":[{"count":14,"href":"https:\/\/blog.alexrusin.com\/wp-json\/wp\/v2\/posts\/13182\/revisions"}],"predecessor-version":[{"id":30547,"href":"https:\/\/blog.alexrusin.com\/wp-json\/wp\/v2\/posts\/13182\/revisions\/30547"}],"wp:attachment":[{"href":"https:\/\/blog.alexrusin.com\/wp-json\/wp\/v2\/media?parent=13182"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.alexrusin.com\/wp-json\/wp\/v2\/categories?post=13182"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.alexrusin.com\/wp-json\/wp\/v2\/tags?post=13182"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}