8000 feature #33015 [HttpClient] Added TraceableHttpClient and WebProfiler… · symfony/symfony@123418e · GitHub
[go: up one dir, main page]

Skip to content

Commit 123418e

Browse files
feature #33015 [HttpClient] Added TraceableHttpClient and WebProfiler panel (jeremyFreeAgent)
This PR was merged into the 4.4 branch. Discussion ---------- [HttpClient] Added TraceableHttpClient and WebProfiler panel | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | BC breaks? | no <!-- see https://symfony.com/bc --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tests pass? | yes | Fixed tickets | | License | MIT | Doc PR | Replace #30494 I added : - tests - move debug services declaration in dedicated `http_client_debug.xml` file - rename stuff to follow messenger data collector stuff - add CompilerPass to allow bundle to trace their own http client I didn't add all @nicolas-grekas requests on UI profiler. I will continue to make more PR after this one. IMO everything looks fine to make a first merge except one strange behavior that I am not sure to get : When making a sub request : - we still have the http_client parent data. (like messenger, but currently I did not see anything in code that could avoid that, so different topic I guess). - The data collected are already "converted" to VarDumper data, so I have errors when trying to do all the unset stuff in the TraceableHttpClient. Is it for this reason, some collector use `lateCollect` ? Should we also move to lateCollect in the `HttpClientDataCollector` ? But I'm still new on this subject but glad to help, so feel free to request more changes ! Commits ------- 5164001 [HttpClient] Added TraceableHttpClient and WebProfiler panel
2 parents 0fa1246 + 5164001 commit 123418e

File tree

17 files changed

+727
-10
lines changed

17 files changed

+727
-10
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/UnusedTagsPass.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class UnusedTagsPass implements CompilerPassInterface
3535
'form.type',
3636
'form.type_extension',
3737
'form.type_guesser',
38+
'http_client.client',
3839
'kernel.cache_clearer',
3940
'kernel.cache_warmer',
4041
'kernel.event_listener',

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ class FrameworkExtension extends Extension
150150
private $validatorConfigEnabled = false;
151151
private $messengerConfigEnabled = false;
152152
private $mailerConfigEnabled = false;
153+
private $httpClientConfigEnabled = false;
153154

154155
/**
155156
* Responds to the app.config configuration parameter.
@@ -311,6 +312,10 @@ public function load(array $configs, ContainerBuilder $container)
311312
$container->removeDefinition('console.command.messenger_failed_messages_remove');
312313
}
313314

315+
if ($this->httpClientConfigEnabled = $this->isConfigEnabled($container, $config['http_client'])) {
316+
$this->registerHttpClientConfiguration($config['http_client'], $container, $loader, $config['profiler']);
317+
}
318+
314319
$propertyInfoEnabled = $this->isConfigEnabled($container, $config['property_info']);
315320
$this->registerValidationConfiguration($config['validation'], $container, $loader, $propertyInfoEnabled);
316321
$this->registerEsiConfiguration($config['esi'], $container, $loader);
@@ -341,10 +346,6 @@ public function load(array $configs, ContainerBuilder $container)
341346
$this->registerLockConfiguration($config['lock'], $container, $loader);
342347
}
343348

344-
if ($this->isConfigEnabled($container, $config['http_client'])) {
345-
$this->registerHttpClientConfiguration($config['http_client'], $container, $loader);
346-
}
347-
348349
if ($this->mailerConfigEnabled = $this->isConfigEnabled($container, $config['mailer'])) {
349350
$this->registerMailerConfiguration($config['mailer'], $container, $loader);
350351
}
@@ -562,6 +563,10 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $
562563
$loader->load('mailer_debug.xml');
563564
}
564565

566+
if ($this->httpClientConfigEnabled) {
567+
$loader->load('http_client_debug.xml');
568+
}
569+
565570
$container->setParameter('profiler_listener.only_exceptions', $config['only_exceptions']);
566571
$container->setParameter('profiler_listener.only_master_requests', $config['only_master_requests']);
567572

@@ -1915,7 +1920,7 @@ private function registerCacheConfiguration(array $config, ContainerBuilder $con
19151920
}
19161921
}
19171922

1918-
private function registerHttpClientConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader)
1923+
private function registerHttpClientConfiguration(array $config, ContainerBuilder $container, XmlFileLoader $loader, array $profilerConfig)
19191924
{
19201925
$loader->load('http_client.xml');
19211926

@@ -1930,6 +1935,8 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder
19301935
$container->removeDefinition(HttpClient::class);
19311936
}
19321937

1938+
$httpClientId = $this->isConfigEnabled($container, $profilerConfig) ? '.debug.http_client.inner' : 'http_client';
1939+
19331940
foreach ($config['scoped_clients'] as $name => $scopeConfig) {
19341941
if ('http_client' === $name) {
19351942
throw new InvalidArgumentException(sprintf('Invalid scope name: "%s" is reserved.', $name));
@@ -1941,10 +1948,14 @@ private function registerHttpClientConfiguration(array $config, ContainerBuilder
19411948
if (null === $scope) {
19421949
$container->register($name, ScopingHttpClient::class)
19431950
->setFactory([ScopingHttpClient::class, 'forBaseUri'])
1944-
->setArguments([new Reference('http_client'), $scopeConfig['base_uri'], $scopeConfig]);
1951+
->setArguments([new Reference($httpClientId), $scopeConfig['base_uri'], $scopeConfig])
1952+
->addTag('http_client.client')
1953+
;
19451954
} else {
19461955
$container->register($name, ScopingHttpClient::class)
1947-
->setArguments([new Reference('http_client'), [$scope => $scopeConfig], $scope]);
1956+
->setArguments([new Reference($httpClientId), [$scope => $scopeConfig], $scope])
1957+
->addTag('http_client.client')
1958+
;
19481959
}
19491960

19501961
$container->registerAliasForArgument($name, HttpClientInterface::class);

src/Symfony/Bundle/FrameworkBundle/FrameworkBundle.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use Symfony\Component\ErrorRenderer\DependencyInjection\ErrorRendererPass;
3737
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
3838
use Symfony\Component\Form\DependencyInjection\FormPass;
39+
use Symfony\Component\HttpClient\DependencyInjection\HttpClientPass;
3940
use Symfony\Component\HttpFoundation\Request;
4041
use Symfony\Component\HttpKernel\Bundle\Bundle;
4142
use Symfony\Component\HttpKernel\DependencyInjection\ControllerArgumentValueResolverPass;
@@ -129,6 +130,7 @@ public function build(ContainerBuilder $container)
129130
$container->addCompilerPass(new TestServiceContainerRealRefPass(), PassConfig::TYPE_AFTER_REMOVING);
130131
$this->addCompilerPassIfExists($container, AddMimeTypeGuesserPass::class);
131132
$this->addCompilerPassIfExists($container, MessengerPass::class);
133+
$this->addCompilerPassIfExists($container, HttpClientPass::class);
132134
$this->addCompilerPassIfExists($container, AddAutoMappingConfigurationPass::class);
133135
$container->addCompilerPass(new RegisterReverseContainerPass(true));
134136
$container->addCompilerPass(new RegisterReverseContainerPass(false), PassConfig::TYPE_AFTER_REMOVING);

src/Symfony/Bundle/FrameworkBundle/Resources/config/http_client.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<services>
88
<service id="http_client" class="Symfony\Contracts\HttpClient\HttpClientInterface">
99
<tag name="monolog.logger" channel="http_client" />
10+
<tag name="http_client.client" />
1011
<factory class="Symfony\Component\HttpClient\HttpClient" method="create" />
1112
<argument type="collection" /> <!-- default options -->
1213
<argument /> <!-- max host connections -->
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<service id="data_collector.http_client" class="Symfony\Component\HttpClient\DataCollector\HttpClientDataCollector">
9+
<tag name="data_collector" template="@WebProfiler/Collector/http_client.html.twig" id="http_client" priority="250" />
10+
</service>
11+
</services>
12+
</container>

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"symfony/polyfill-intl-icu": "~1.0",
4141
"symfony/form": "^4.3.4|^5.0",
4242
"symfony/expression-language": "^3.4|^4.0|^5.0",
43-
"symfony/http-client": "^4.3|^5.0",
43+
"symfony/http-client": "^4.4|^5.0",
4444
"symfony/lock": "^4.4|^5.0",
4545
"symfony/mailer": "^4.4|^5.0",
4646
"symfony/messenger": "^4.3|^5.0",
@@ -71,6 +71,7 @@
7171
"symfony/console": "<4.3",
7272
"symfony/dotenv": "<4.2",
7373
"symfony/dom-crawler": "<4.3",
74+
"symfony/http-client": "<4.4",
7475
"symfony/form": "<4.3",
7576
"symfony/lock": "<4.4",
7677
"symfony/mailer": "<4.4",

src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
-----
66

77
* added support for the Mailer component
8+
* added support for the HttpClient component
89
* added button to clear the ajax request tab
910
* deprecated the `ExceptionController::templateExists()` method
1011
* deprecated the `TemplateManager::templateExists()` method
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{% extends '@WebProfiler/Profiler/layout.html.twig' %}
2+
3+
{% block toolbar %}
4+
{% if collector.requestCount %}
5+
{% set icon %}
6+
{{ include('@WebProfiler/Icon/http-client.svg') }}
7+
{% set status_color = '' %}
8+
<span class="sf-toolbar-value">{{ collector.requestCount }}</span>
9+
{% endset %}
10+
11+
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_color }) }}
12+
{% endif %}
13+
{% endblock %}
14+
15+
{% block menu %}
16+
<span class="label {{ collector.requestCount == 0 ? 'disabled' }}">
17+
<span class="icon">{{ include('@WebProfiler/Icon/http-client.svg') }}</span>
18+
<strong>HTTP Client</strong>
19+
{% if collector.requestCount %}
20+
<span class="count">
21+
{{ collector.requestCount }}
22+
</span>
23+
{% endif %}
24+
</span>
25+
{% endblock %}
26+
27+
{% block panel %}
28+
<h2>HTTP Client</h2>
29+
{% if collector.requestCount == 0 %}
30+
<div class="empty">
31+
<p>No HTTP requests were made.</p>
32+
</div>
33+
{% else %}
34+
<div class="metrics">
35+
<div class="metric">
36+
<span class="value">{{ collector.requestCount }}</span>
37+
<span class="label">Total requests</span>
38+
</div>
39+
<div class="metric">
40+
<span class="value">{{ collector.errorCount }}</span>
41+
<span class="label">HTTP errors</span>
42+
</div>
43+
</div>
44+
<h2>Clients</h2>
45+
<div class="sf-tabs">
46+
{% for name, client in collector.clients %}
47+
<div class="tab {{ client.traces|length == 0 ? 'disabled' }}">
48+
<h3 class="tab-title">{{ name }} <span class="badge">{{ client.traces|length }}</span></h3>
49+
<div class="tab-content">
50+
{% if client.traces|length == 0 %}
51+
<div class="empty">
52+
<p>No requests were made with the "{{ name }}" service.</p>
53+
</div>
54+
{% else %}
55+
<h4>Requests</h4>
56+
{% for trace in client.traces %}
57+
<table>
58+
<thead>
59+
<tr>
60+
<th>
61+
<span class="label">{{ trace.method }}</span>
62+
</th>
63+
<th class="full-width">
64+
{{ trace.url }}
65+
{% if trace.options is not empty %}
66+
{{ profiler_dump(trace.options, maxDepth=1) }}
67+
{% endif %}
68+
</th>
69+
</tr>
70+
</thead>
71+
<tbody>
72+
<tr>
73+
<th>
74+
{% if trace.http_code >= 500 %}
75+
{% set responseStatus = 'error' %}
76+
{% elseif trace.http_code >= 400 %}
77+
{% set responseStatus = 'warning' %}
78+
{% else %}
79+
{% set responseStatus = 'success' %}
80+
{% endif %}
81+
<span class="label status-{{ responseStatus }}">
82+
{{ trace.http_code }}
83+
</span>
84+
</th>
85+
<td>
86+
{{ profiler_dump(trace.info, maxDepth=1) }}
87+
</td>
88+
</tr>
89+
</tbody>
90+
</table>
91+
{% endfor %}
92+
{% endif %}
93+
</div>
EE9F
94+
</div>
95+
{% endfor %}
96+
{% endif %}
97+
</div>
98+
{% endblock %}
Lines changed: 1 addition & 0 deletions
Loading

src/Symfony/Component/HttpClient/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ CHANGELOG
66

77
* added `StreamWrapper`
88
* added `HttplugClient`
9+
* added `max_duration` option
910
* added support for NTLM authentication
1011
* added `$response->toStream()` to cast responses to regular PHP streams
1112
* made `Psr18Client` implement relevant PSR-17 factories and have streaming responses
12-
* added `max_duration` option
13+
* added `TraceableHttpClient`, `HttpClientDataCollector` and `HttpClientPass` to integrate with the web profiler
1314

1415
4.3.0
1516
-----

0 commit comments

Comments
 (0)
0