Skip to content

Commit 10d41a7

Browse files
committed
Merge pull request #8 from php-http/feature/logger-plugin
Add logger plugin
2 parents f7dc4a4 + 4e77b14 commit 10d41a7

File tree

5 files changed

+257
-2
lines changed

5 files changed

+257
-2
lines changed

composer.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"henrikbjorn/phpspec-code-coverage" : "^1.0",
2121
"php-http/authentication": "^0.1@dev",
2222
"php-http/cookie": "^0.1@dev",
23-
"symfony/stopwatch": "^2.3"
23+
"symfony/stopwatch": "^2.3",
24+
"psr/log": "^1.0"
2425
},
2526
"autoload": {
2627
"psr-4": {
@@ -30,7 +31,8 @@
3031
"suggest": {
3132
"php-http/authentication": "Allow to use the AuthenticationPlugin",
3233
"php-http/cookie": "Allow to use CookiePlugin",
33-
"symfony/stopwatch": "Allow to use the StopwatchPlugin"
34+
"symfony/stopwatch": "Allow to use the StopwatchPlugin",
35+
"psr/log-implementation": "Allow to use the LoggerPlugin"
3436
},
3537
"scripts": {
3638
"test": "vendor/bin/phpspec run",

spec/LoggerPluginSpec.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
namespace spec\Http\Client\Plugin;
4+
5+
use Http\Client\Exception\HttpException;
6+
use Http\Client\Exception\NetworkException;
7+
use Http\Client\Plugin\Normalizer\Normalizer;
8+
use Http\Client\Utils\Promise\FulfilledPromise;
9+
use Http\Client\Utils\Promise\RejectedPromise;
10+
use PhpSpec\ObjectBehavior;
11+
use Prophecy\Argument;
12+
use Psr\Http\Message\RequestInterface;
13+
use Psr\Http\Message\ResponseInterface;
14+
use Psr\Log\LoggerInterface;
15+
16+
class LoggerPluginSpec extends ObjectBehavior
17+
{
18+
function let(LoggerInterface $logger)
19+
{
20+
$this->beConstructedWith($logger);
21+
}
22+
23+
function it_is_initializable()
24+
{
25+
$this->shouldHaveType('Http\Client\Plugin\LoggerPlugin');
26+
}
27+
28+
function it_is_a_plugin()
29+
{
30+
$this->shouldImplement('Http\Client\Plugin\Plugin');
31+
}
32+
33+
function it_logs_request_and_response(LoggerInterface $logger, RequestInterface $request, ResponseInterface $response)
34+
{
35+
$logger->info('Emit request: "GET / 1.1"', ['request' => $request])->shouldBeCalled();
36+
$logger->info('Receive response: "200 Ok 1.1" for request: "GET / 1.1"', ['request' => $request, 'response' => $response])->shouldBeCalled();
37+
38+
$request->getMethod()->willReturn('GET');
39+
$request->getRequestTarget()->willReturn('/');
40+
$request->getProtocolVersion()->willReturn('1.1');
41+
42+
$response->getReasonPhrase()->willReturn('Ok');
43+
$response->getProtocolVersion()->willReturn('1.1');
44+
$response->getStatusCode()->willReturn('200');
45+
46+
$next = function () use ($response) {
47+
return new FulfilledPromise($response->getWrappedObject());
48+
};
49+
50+
$this->handleRequest($request, $next, function () {});
51+
}
52+
53+
function it_logs_exception(LoggerInterface $logger, RequestInterface $request)
54+
{
55+
$exception = new NetworkException('Cannot connect', $request->getWrappedObject());
56+
57+
$logger->info('Emit request: "GET / 1.1"', ['request' => $request])->shouldBeCalled();
58+
$logger->error('Error: "Cannot connect" when emitting request: "GET / 1.1"', ['request' => $request, 'exception' => $exception])->shouldBeCalled();
59+
60+
$request->getMethod()->willReturn('GET');
61+
$request->getRequestTarget()->willReturn('/');
62+
$request->getProtocolVersion()->willReturn('1.1');
63+
64+
$next = function () use ($exception) {
65+
return new RejectedPromise($exception);
66+
};
67+
68+
$this->handleRequest($request, $next, function () {});
69+
}
70+
71+
function it_logs_response_within_exception(LoggerInterface $logger, RequestInterface $request, ResponseInterface $response)
72+
{
73+
$exception = new HttpException('Forbidden', $request->getWrappedObject(), $response->getWrappedObject());
74+
75+
$logger->info('Emit request: "GET / 1.1"', ['request' => $request])->shouldBeCalled();
76+
$logger->error('Error: "Forbidden" with response: "403 Forbidden 1.1" when emitting request: "GET / 1.1"', [
77+
'request' => $request,
78+
'response' => $response,
79+
'exception' => $exception
80+
])->shouldBeCalled();
81+
82+
$request->getMethod()->willReturn('GET');
83+
$request->getRequestTarget()->willReturn('/');
84+
$request->getProtocolVersion()->willReturn('1.1');
85+
86+
$response->getReasonPhrase()->willReturn('Forbidden');
87+
$response->getProtocolVersion()->willReturn('1.1');
88+
$response->getStatusCode()->willReturn('403');
89+
90+
$next = function () use ($exception) {
91+
return new RejectedPromise($exception);
92+
};
93+
94+
$this->handleRequest($request, $next, function () {});
95+
}
96+
}

spec/Normalizer/NormalizerSpec.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace spec\Http\Client\Plugin\Normalizer;
4+
5+
use PhpSpec\ObjectBehavior;
6+
use Prophecy\Argument;
7+
use Psr\Http\Message\RequestInterface;
8+
use Psr\Http\Message\ResponseInterface;
9+
use Psr\Http\Message\UriInterface;
10+
use Psr\Log\LoggerInterface;
11+
12+
class NormalizerSpec extends ObjectBehavior
13+
{
14+
function it_is_initializable(LoggerInterface $logger)
15+
{
16+
$this->shouldHaveType('Http\Client\Plugin\Normalizer\Normalizer');
17+
}
18+
19+
function it_normalize_request_to_string(RequestInterface $request)
20+
{
21+
$request->getMethod()->willReturn('GET');
22+
$request->getRequestTarget()->willReturn('/');
23+
$request->getProtocolVersion()->willReturn('1.1');
24+
25+
$this->normalizeRequestToString($request)->shouldReturn('GET / 1.1');
26+
}
27+
28+
function it_normalize_response_to_string(ResponseInterface $response)
29+
{
30+
$response->getReasonPhrase()->willReturn('Ok');
31+
$response->getProtocolVersion()->willReturn('1.1');
32+
$response->getStatusCode()->willReturn('200');
33+
34+
$this->normalizeResponseToString($response)->shouldReturn('200 Ok 1.1');
35+
}
36+
}

src/LoggerPlugin.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace Http\Client\Plugin;
4+
5+
use Http\Client\Exception;
6+
use Http\Client\Plugin\Normalizer\Normalizer;
7+
use Psr\Http\Message\RequestInterface;
8+
use Psr\Http\Message\ResponseInterface;
9+
use Psr\Log\LoggerInterface;
10+
11+
/**
12+
* Log request, response and exception for a HTTP Client
13+
*
14+
* @author Joel Wurtz <[email protected]>
15+
*/
16+
class LoggerPlugin implements Plugin
17+
{
18+
/**
19+
* Logger to log request / response / exception for a http call
20+
*
21+
* @var LoggerInterface
22+
*/
23+
private $logger;
24+
25+
/**
26+
* Normalize request and response to string or array
27+
*
28+
* @var Normalizer
29+
*/
30+
private $normalizer;
31+
32+
/**
33+
* @param LoggerInterface $logger
34+
*/
35+
public function __construct(LoggerInterface $logger)
36+
{
37+
$this->logger = $logger;
38+
$this->normalizer = new Normalizer();
39+
}
40+
41+
/**
42+
* {@inheritdoc}
43+
*/
44+
public function handleRequest(RequestInterface $request, callable $next, callable $first)
45+
{
46+
$this->logger->info(sprintf('Emit request: "%s"', $this->normalizer->normalizeRequestToString($request)), ['request' => $request]);
47+
48+
return $next($request)->then(function (ResponseInterface $response) use($request) {
49+
$this->logger->info(
50+
sprintf('Receive response: "%s" for request: "%s"', $this->normalizer->normalizeResponseToString($response), $this->normalizer->normalizeRequestToString($request)),
51+
[
52+
'request' => $request,
53+
'response' => $response,
54+
]
55+
);
56+
57+
return $response;
58+
}, function (Exception $exception) use($request) {
59+
if ($exception instanceof Exception\HttpException) {
60+
$this->logger->error(
61+
sprintf('Error: "%s" with response: "%s" when emitting request: "%s"', $exception->getMessage(), $this->normalizer->normalizeResponseToString($exception->getResponse()), $this->normalizer->normalizeRequestToString($request)),
62+
[
63+
'request' => $request,
64+
'response' => $exception->getResponse(),
65+
'exception' => $exception
66+
]
67+
);
68+
} else {
69+
$this->logger->error(
70+
sprintf('Error: "%s" when emitting request: "%s"', $exception->getMessage(), $this->normalizer->normalizeRequestToString($request)),
71+
[
72+
'request' => $request,
73+
'exception' => $exception
74+
]
75+
);
76+
}
77+
78+
throw $exception;
79+
});
80+
}
81+
}

src/Normalizer/Normalizer.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace Http\Client\Plugin\Normalizer;
4+
5+
use Psr\Http\Message\RequestInterface;
6+
use Psr\Http\Message\ResponseInterface;
7+
8+
/**
9+
* Normalize a request or a response into a string or an array
10+
*
11+
* @author Joel Wurtz <[email protected]>
12+
*
13+
* @internal Should not be used outside of the logger plugin
14+
*/
15+
class Normalizer
16+
{
17+
/**
18+
* Normalize a request to string
19+
*
20+
* @param RequestInterface $request
21+
*
22+
* @return string
23+
*/
24+
public function normalizeRequestToString(RequestInterface $request)
25+
{
26+
return sprintf('%s %s %s', $request->getMethod(), $request->getRequestTarget(), $request->getProtocolVersion());
27+
}
28+
29+
/**
30+
* Normalize a response to string
31+
*
32+
* @param ResponseInterface $response
33+
*
34+
* @return string
35+
*/
36+
public function normalizeResponseToString(ResponseInterface $response)
37+
{
38+
return sprintf("%s %s %s", $response->getStatusCode(), $response->getReasonPhrase(), $response->getProtocolVersion());
39+
}
40+
}

0 commit comments

Comments
 (0)