-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[HttpClient] logger integration #30537
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
antonch1989
commented
Mar 12, 2019
Q | A |
---|---|
Branch? | master |
Bug fix? | no |
New feature? | yes |
BC breaks? | no |
Deprecations? | no |
Tests pass? | yes |
Fixed tickets | #30502 |
License | MIT |
Doc PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for starting this, let's do something useful with the logger now :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this PR!
|
||
/** | ||
* @param array $defaultOptions Default requests' options | ||
* @param int $maxHostConnections The maximum number of connections to a single host | ||
* | ||
* @see HttpClientInterface::OPTIONS_DEFAULTS for available options | ||
*/ | ||
public function __construct(array $defaultOptions = [], int $maxHostConnections = 6) | ||
public function __construct(LoggerInterface $logger = null, array $defaultOptions = [], int $maxHostConnections = 6) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be the last argument IMHO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That does make it harder to pass the default values properly (in case they change) on line 165: $this->multi->handle = (new self($this->logger))->multi->handle;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, what about something like $this->multi->handle = (clone $this)->multi->handle;
, with any proper cloning handling if needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would then have to empty the clone
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about putting the argument 2nd?
Looks best ordering to me, from most often passed to most often left to defaults.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively: withLogger()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having it last in the constructor and/or using a mutator looks more legit, since from a strict object point of view, if we create it the most needed is to configure it. The logger is not a requirement for this client, this is obviously why we add it in this PR. I don't think we should break the previous implementation. Plus using a logger also surely means in most cases that DI is used and we don't care about having it third.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or it could implement Psr\Log\LoggerAwareInterface
(adds a setLogger()
method). This interface triggers the autoconfiguration in Symfony IIRC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like @dunglas suggestion.
Not really a fan of having a logger inside the client implementation when it could use decoration, but really depends if you want to log what the implementation does. If it's only logging request / response transmission it should use a decoration system (or at least a trait but not a fan of this either). Using decorations would allow:
|
@joelwurtz same as #30539: logging in a cross-cutting concern, it's a bad fit for a middleware. I'd like implementation to log what they do internally also, e.g. dealing with redirections, or with server pushes for the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with the adding as 2nd argument personally. We should now log some things :)
0d3acb4
to
63c48db
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I pushed a 2nd commit with logs at critical points + fixes for HTTP/2 PUSH found meanwhile)
441caef
to
4314e82
Compare
4314e82
to
26d15c8
Compare
Thank you @antonch1989. |
…grekas) This PR was merged into the 4.3-dev branch. Discussion ---------- [HttpClient] logger integration | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no <!-- see https://symfony.com/bc --> | Deprecations? | no | Tests pass? | yes <!-- please add some, will be required by reviewers --> | Fixed tickets | #30502 <!-- #-prefixed issue number(s), if any --> | License | MIT | Doc PR | Commits ------- 26d15c8 [HttpClient] log requests, responses and pushes when they happen fc6ba7e [HttpClient] logger integration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I came late But I think this could increase the DX. I could do the PR if you want
@@ -74,8 +79,10 @@ public function __construct(array $defaultOptions = [], int $maxHostConnections | |||
return; | |||
} | |||
|
|||
curl_multi_setopt($mh, CURLMOPT_PUSHFUNCTION, static function ($parent, $pushed, array $requestHeaders) use ($multi) { | |||
return self::handlePush($parent, $pushed, $requestHeaders, $multi); | |||
$logger = &$this->logger; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Why do you use a reference here ?
- There is no need to create a logger variable here since we can use $this in a close since PHP 5.5 IIRC)
@@ -103,13 +110,19 @@ public function request(string $method, string $url, array $options = []): Respo | |||
]; | |||
|
|||
if ('GET' === $method && !$options['body'] && $expectedHeaders === $pushedHeaders) { | |||
$this->logger && $this->logger->info(sprintf('Connecting request to pushed response: %s %s', $method, $url)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I would use quote around
%s
(to detect empty method or empty URL for exemple) - I would move this to debug log level
|
||
return $pushedResponse; | ||
} | ||
|
||
$this->logger && $this->logger->info(sprintf('Rejecting pushed response for "%s": authorization headers don\'t match the request', $url)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move that to warning log level
} | ||
|
||
$this->logger && $this->logger->info(sprintf('Request: %s %s', $method, $url)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use quote around %s (to detect empty method or empty URL for exemple)
|
||
foreach ($requestHeaders as $h) { | ||
if (false !== $i = strpos($h, ':', 1)) { | ||
$headers[substr($h, 0, $i)] = substr($h, 1 + $i); | ||
} | ||
} | ||
|
||
if ('GET' !== $headers[':method'] || isset($headers['range'])) { | ||
if (!isset($headers[':method']) || !isset($headers[':scheme']) || !isset($headers[':authority']) || !isset($headers[':path']) || 'GET' !== $headers[':method'] || isset($headers['range'])) { | ||
$logger && $logger->info(sprintf('Rejecting pushed response from "%s": pushed headers are invalid', $origin)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move that to warning
@@ -205,7 +208,10 @@ public function request(string $method, string $url, array $options = []): Respo | |||
$context = stream_context_create($context, ['notification' => $notification]); | |||
self::configureHeadersAndProxy($context, $host, $options['request_headers'], $proxy, $noProxy); | |||
|
|||
return new NativeResponse($this->multi, $context, implode('', $url), $options, $gzipEnabled, $info, $resolveRedirect, $onProgress); | |||
$url = implode('', $url); | |||
$this->logger && $this->logger->info(sprintf('Request: %s %s', $method, $url)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add quote
if (!$this->multi->openHandles) { | ||
if ($this->logger) { | ||
foreach ($this->multi->pushedResponses as $url => $response) { | ||
$this->logger->info(sprintf('Unused pushed response: %s', $url)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Warning and quote
@@ -306,6 +318,8 @@ private static function parseHeaderLine($ch, string $data, array &$info, array & | |||
} | |||
|
|||
curl_setopt($ch, CURLOPT_PRIVATE, 'content'); | |||
} elseif (null !== $info['redirect_url'] && $logger) { | |||
$logger->info(sprintf('Redirecting: %s %s', $info['http_code'], $info['redirect_url'])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quote
break; | ||
} | ||
|
||
$this->logger && $this->logger->info(sprintf('Redirecting: %s %s', $this->info['http_code'], $url ?? $this->url)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quote
@@ -299,6 +300,9 @@ public static function stream(iterable $responses, float $timeout = null): \Gene | |||
} elseif ($chunk instanceof ErrorChunk) { | |||
unset($responses[$j]); | |||
$isTimeout = true; | |||
} elseif ($chunk instanceof FirstChunk && $response->logger) { | |||
$info = $response->getInfo(); | |||
$response->logger->info(sprintf('Response: %s %s', $info['http_code'], $info['url'])); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quote
See #30873 |
Who broke framework bundle?
in vendor/symfony/framework-bundle/Resources/config/http_client.xml:13: <call method="setLogger">
<argument type="service" id="logger" on-invalid="ignore" />
</call> |
How did you produce this error? I doesn't look legit to me. |
I'm using 4.3.x-dev version, updated via composer update and got this error when loading my page. This is what was upgraded: - Removing symfony/cache (dev-master e5e9a6d)
- Installing symfony/cache (dev-master 7963e63): Loading from cache
- Removing symfony/contracts (dev-master 99c0737)
- Installing symfony/contracts (dev-master a4b5e69): Loading from cache
- Removing symfony/dependency-injection (dev-master 0d69ef5)
- Installing symfony/dependency-injection (dev-master c5fd454): Loading from cache
- Removing symfony/event-dispatcher (dev-master d3fe4ea)
- Installing symfony/event-dispatcher (dev-master 96212e1): Loading from cache
- Removing symfony/framework-bundle (dev-master cb79b7e)
- Installing symfony/framework-bundle (dev-master 8c262ff): Loading from cache
- Removing symfony/http-client (dev-master e6ea6d6)
- Installing symfony/http-client (dev-master 39fedf1): Loading from cache
- Removing symfony/http-foundation (dev-master bd824ed)
- Installing symfony/http-foundation (dev-master 62b6e8c): Loading from cache
- Removing symfony/http-kernel (dev-master cb89321)
- Installing symfony/http-kernel (dev-master 8d51d21): Loading from cache
- Removing symfony/security-bundle (dev-master 4f84238)
- Installing symfony/security-bundle (dev-master b36fe26): Loading from cache
- Removing symfony/twig-bundle (dev-master 3d669e5)
- Installing symfony/twig-bundle (dev-master 181d351): Loading from cache |
Thanks. What's the stack trace of the exception? |
|
Thanks, would you mind opening a separate issue with that content? |