HttpClientDataCollector.php 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpClient\DataCollector;
  11. use Symfony\Component\HttpClient\TraceableHttpClient;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Symfony\Component\HttpKernel\DataCollector\DataCollector;
  15. use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
  16. use Symfony\Component\VarDumper\Caster\ImgStub;
  17. /**
  18. * @author Jérémy Romey <jeremy@free-agent.fr>
  19. */
  20. final class HttpClientDataCollector extends DataCollector implements LateDataCollectorInterface
  21. {
  22. /**
  23. * @var TraceableHttpClient[]
  24. */
  25. private $clients = [];
  26. public function registerClient(string $name, TraceableHttpClient $client)
  27. {
  28. $this->clients[$name] = $client;
  29. }
  30. /**
  31. * {@inheritdoc}
  32. */
  33. public function collect(Request $request, Response $response, \Throwable $exception = null)
  34. {
  35. $this->lateCollect();
  36. }
  37. public function lateCollect()
  38. {
  39. $this->data['request_count'] = $this->data['request_count'] ?? 0;
  40. $this->data['error_count'] = $this->data['error_count'] ?? 0;
  41. $this->data += ['clients' => []];
  42. foreach ($this->clients as $name => $client) {
  43. [$errorCount, $traces] = $this->collectOnClient($client);
  44. $this->data['clients'] += [
  45. $name => [
  46. 'traces' => [],
  47. 'error_count' => 0,
  48. ],
  49. ];
  50. $this->data['clients'][$name]['traces'] = array_merge($this->data['clients'][$name]['traces'], $traces);
  51. $this->data['request_count'] += \count($traces);
  52. $this->data['error_count'] += $errorCount;
  53. $this->data['clients'][$name]['error_count'] += $errorCount;
  54. $client->reset();
  55. }
  56. }
  57. public function getClients(): array
  58. {
  59. return $this->data['clients'] ?? [];
  60. }
  61. public function getRequestCount(): int
  62. {
  63. return $this->data['request_count'] ?? 0;
  64. }
  65. public function getErrorCount(): int
  66. {
  67. return $this->data['error_count'] ?? 0;
  68. }
  69. /**
  70. * {@inheritdoc}
  71. */
  72. public function getName(): string
  73. {
  74. return 'http_client';
  75. }
  76. public function reset()
  77. {
  78. $this->data = [
  79. 'clients' => [],
  80. 'request_count' => 0,
  81. 'error_count' => 0,
  82. ];
  83. }
  84. private function collectOnClient(TraceableHttpClient $client): array
  85. {
  86. $traces = $client->getTracedRequests();
  87. $errorCount = 0;
  88. $baseInfo = [
  89. 'response_headers' => 1,
  90. 'retry_count' => 1,
  91. 'redirect_count' => 1,
  92. 'redirect_url' => 1,
  93. 'user_data' => 1,
  94. 'error' => 1,
  95. 'url' => 1,
  96. ];
  97. foreach ($traces as $i => $trace) {
  98. if (400 <= ($trace['info']['http_code'] ?? 0)) {
  99. ++$errorCount;
  100. }
  101. $info = $trace['info'];
  102. $traces[$i]['http_code'] = $info['http_code'] ?? 0;
  103. unset($info['filetime'], $info['http_code'], $info['ssl_verify_result'], $info['content_type']);
  104. if (($info['http_method'] ?? null) === $trace['method']) {
  105. unset($info['http_method']);
  106. }
  107. if (($info['url'] ?? null) === $trace['url']) {
  108. unset($info['url']);
  109. }
  110. foreach ($info as $k => $v) {
  111. if (!$v || (is_numeric($v) && 0 > $v)) {
  112. unset($info[$k]);
  113. }
  114. }
  115. if (\is_string($content = $trace['content'])) {
  116. $contentType = 'application/octet-stream';
  117. foreach ($info['response_headers'] ?? [] as $h) {
  118. if (0 === stripos($h, 'content-type: ')) {
  119. $contentType = substr($h, \strlen('content-type: '));
  120. break;
  121. }
  122. }
  123. if (0 === strpos($contentType, 'image/') && class_exists(ImgStub::class)) {
  124. $content = new ImgStub($content, $contentType, '');
  125. } else {
  126. $content = [$content];
  127. }
  128. $content = ['response_content' => $content];
  129. } elseif (\is_array($content)) {
  130. $content = ['response_json' => $content];
  131. } else {
  132. $content = [];
  133. }
  134. if (isset($info['retry_count'])) {
  135. $content['retries'] = $info['previous_info'];
  136. unset($info['previous_info']);
  137. }
  138. $debugInfo = array_diff_key($info, $baseInfo);
  139. $info = ['info' => $debugInfo] + array_diff_key($info, $debugInfo) + $content;
  140. unset($traces[$i]['info']); // break PHP reference used by TraceableHttpClient
  141. $traces[$i]['info'] = $this->cloneVar($info);
  142. $traces[$i]['options'] = $this->cloneVar($trace['options']);
  143. }
  144. return [$errorCount, $traces];
  145. }
  146. }