Skip to content

Commit 337c9dd

Browse files
feat: use CoroutineHandler instead CurlHandler for HTTP exporter (#12)
1 parent ecfce62 commit 337c9dd

3 files changed

Lines changed: 248 additions & 0 deletions

File tree

src/ConfigProvider.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
use OpenTelemetry\SDK\Logs\LoggerProviderInterface;
1414
use OpenTelemetry\SDK\Metrics\MeterProviderInterface;
1515
use OpenTelemetry\SDK\Resource\ResourceInfo;
16+
use Hyperf\OpenTelemetry\Factory\CachedInstrumentationFactory;
17+
use Hyperf\OpenTelemetry\Factory\OTelResourceFactory;
18+
use Hyperf\OpenTelemetry\Support\HyperfGuzzle;
19+
use OpenTelemetry\SDK\Common\Http\Psr\Client\Discovery;
20+
use OpenTelemetry\SDK\Common\Http\Psr\Client\Discovery\Guzzle;
1621
use OpenTelemetry\SDK\Trace\TracerProviderInterface;
1722

1823
class ConfigProvider
@@ -24,6 +29,11 @@ public function __invoke(): array
2429
{
2530
defined('BASE_PATH') || define('BASE_PATH', '');
2631

32+
Discovery::setDiscoverers([
33+
HyperfGuzzle::class,
34+
Guzzle::class,
35+
]);
36+
2737
return [
2838
'dependencies' => [
2939
CachedInstrumentation::class => CachedInstrumentationFactory::class,

src/Support/HyperfGuzzle.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Hyperf\OpenTelemetry\Support;
6+
7+
use GuzzleHttp\Client;
8+
use GuzzleHttp\HandlerStack;
9+
use Hyperf\Context\ApplicationContext;
10+
use Hyperf\Contract\ConfigInterface;
11+
use Hyperf\Guzzle\CoroutineHandler;
12+
use Hyperf\Guzzle\PoolHandler;
13+
use OpenTelemetry\SDK\Common\Http\Psr\Client\Discovery\DiscoveryInterface;
14+
use Psr\Http\Client\ClientInterface;
15+
16+
class HyperfGuzzle implements DiscoveryInterface
17+
{
18+
public function available(): bool
19+
{
20+
return class_exists(CoroutineHandler::class) && ApplicationContext::hasContainer();
21+
}
22+
23+
public function create(mixed $options): ClientInterface
24+
{
25+
$options = is_array($options) ? $options : [];
26+
27+
return new Client(array_merge($options, [
28+
'handler' => $this->createHandler(),
29+
]));
30+
}
31+
32+
private function createHandler(): HandlerStack
33+
{
34+
/** @var ContainerInterface $container */
35+
$container = ApplicationContext::getContainer();
36+
37+
$config = $container->get(ConfigInterface::class)->get('open-telemetry.otlp_http.pool', []);
38+
39+
if (empty($config)) {
40+
return HandlerStack::create(new CoroutineHandler());
41+
}
42+
43+
return HandlerStack::create($container->make(PoolHandler::class, ['option' => $config]));
44+
}
45+
}
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Tests\Unit\Support;
6+
7+
use GuzzleHttp\Client;
8+
use GuzzleHttp\HandlerStack;
9+
use Hyperf\Context\ApplicationContext;
10+
use Hyperf\Contract\ConfigInterface;
11+
use Hyperf\Guzzle\ClientFactory;
12+
use Hyperf\Guzzle\CoroutineHandler;
13+
use Hyperf\Guzzle\PoolHandler;
14+
use Hyperf\OpenTelemetry\Support\HyperfGuzzle;
15+
use Mockery as m;
16+
use PHPUnit\Framework\TestCase;
17+
use Psr\Container\ContainerInterface;
18+
use ReflectionAttribute;
19+
use ReflectionProperty;
20+
21+
/**
22+
* @internal
23+
*/
24+
class HyperfGuzzleTest extends TestCase
25+
{
26+
private ?ContainerInterface $container = null;
27+
28+
protected function setUp(): void
29+
{
30+
parent::setUp();
31+
32+
if (ApplicationContext::hasContainer()) {
33+
$this->container = ApplicationContext::getContainer();
34+
}
35+
}
36+
37+
protected function tearDown(): void
38+
{
39+
m::close();
40+
41+
if ($this->container !== null) {
42+
ApplicationContext::setContainer($this->container);
43+
}
44+
parent::tearDown();
45+
}
46+
47+
public function testAvailableReturnsTrueWhenCoroutineHandlerExistsAndContainerAvailable()
48+
{
49+
$container = m::mock(ContainerInterface::class);
50+
ApplicationContext::setContainer($container);
51+
52+
$hyperfGuzzle = new HyperfGuzzle();
53+
$this->assertTrue($hyperfGuzzle->available());
54+
}
55+
56+
public function testCreateReturnsClientWithCoroutineHandlerWhenPoolConfigIsEmpty()
57+
{
58+
$container = m::mock(ContainerInterface::class);
59+
$config = m::mock(ConfigInterface::class);
60+
61+
$config->shouldReceive('get')
62+
->with('open-telemetry.otlp_http.pool', [])
63+
->andReturn([]);
64+
65+
$container->shouldReceive('get')
66+
->with(ConfigInterface::class)
67+
->andReturn($config);
68+
69+
ApplicationContext::setContainer($container);
70+
71+
$hyperfGuzzle = new HyperfGuzzle();
72+
$options = ['timeout' => 30];
73+
$client = $hyperfGuzzle->create($options);
74+
75+
$this->assertInstanceOf(Client::class, $client);
76+
77+
/** @var Client $client */
78+
$clientConfig = $client->getConfig();
79+
80+
$this->assertIsArray($clientConfig);
81+
$this->assertInstanceOf(HandlerStack::class, $clientConfig['handler']);
82+
83+
$property = new ReflectionProperty(HandlerStack::class, 'handler');
84+
85+
$this->assertInstanceOf(CoroutineHandler::class, $property->getValue($clientConfig['handler']));
86+
}
87+
88+
public function testCreateReturnsClientWithPoolHandlerWhenPoolConfigIsProvided()
89+
{
90+
$container = m::mock(ContainerInterface::class);
91+
$config = m::mock(ConfigInterface::class);
92+
$poolHandler = m::mock(PoolHandler::class);
93+
94+
$poolConfig = [
95+
'min_connections' => 1,
96+
'max_connections' => 10,
97+
];
98+
99+
$config->shouldReceive('get')
100+
->with('open-telemetry.otlp_http.pool', [])
101+
->andReturn($poolConfig);
102+
103+
$container->shouldReceive('get')
104+
->with(ConfigInterface::class)
105+
->andReturn($config);
106+
107+
$container->shouldReceive('make')
108+
->with(PoolHandler::class, ['option' => $poolConfig])
109+
->andReturn($poolHandler);
110+
111+
ApplicationContext::setContainer($container);
112+
113+
$hyperfGuzzle = new HyperfGuzzle();
114+
$options = ['timeout' => 30];
115+
$client = $hyperfGuzzle->create($options);
116+
117+
$this->assertInstanceOf(Client::class, $client);
118+
119+
/** @var Client $client */
120+
$clientConfig = $client->getConfig();
121+
122+
$this->assertIsArray($clientConfig);
123+
$this->assertInstanceOf(HandlerStack::class, $clientConfig['handler']);
124+
125+
$property = new ReflectionProperty(HandlerStack::class, 'handler');
126+
127+
$this->assertInstanceOf(PoolHandler::class, $property->getValue($clientConfig['handler']));
128+
}
129+
130+
public function testCreateMergesOptionsWithHandler()
131+
{
132+
$container = m::mock(ContainerInterface::class);
133+
$config = m::mock(ConfigInterface::class);
134+
135+
$config->shouldReceive('get')
136+
->with('open-telemetry.otlp_http.pool', [])
137+
->andReturn([]);
138+
139+
$container->shouldReceive('get')
140+
->with(ConfigInterface::class)
141+
->andReturn($config);
142+
143+
ApplicationContext::setContainer($container);
144+
145+
$hyperfGuzzle = new HyperfGuzzle();
146+
$options = [
147+
'timeout' => 30,
148+
'base_uri' => 'http://localhost',
149+
];
150+
151+
/** @var Client $client */
152+
$client = $hyperfGuzzle->create($options);
153+
154+
$this->assertInstanceOf(Client::class, $client);
155+
156+
$config = $client->getConfig();
157+
$this->assertEquals(30, $config['timeout']);
158+
$this->assertEquals('http://localhost', $config['base_uri']);
159+
$this->assertInstanceOf(HandlerStack::class, $config['handler']);
160+
}
161+
162+
public function testCreateWithEmptyOptions()
163+
{
164+
$container = m::mock(ContainerInterface::class);
165+
$config = m::mock(ConfigInterface::class);
166+
167+
$config->shouldReceive('get')
168+
->with('open-telemetry.otlp_http.pool', [])
169+
->andReturn([]);
170+
171+
$container->shouldReceive('get')
172+
->with(ConfigInterface::class)
173+
->andReturn($config);
174+
175+
ApplicationContext::setContainer($container);
176+
177+
$hyperfGuzzle = new HyperfGuzzle();
178+
179+
/** @var Client $client */
180+
$client = $hyperfGuzzle->create(null);
181+
182+
$this->assertInstanceOf(Client::class, $client);
183+
184+
$clientConfig = $client->getConfig();
185+
186+
$this->assertIsArray($clientConfig);
187+
$this->assertInstanceOf(HandlerStack::class, $clientConfig['handler']);
188+
189+
$property = new ReflectionProperty(HandlerStack::class, 'handler');
190+
191+
$this->assertInstanceOf(CoroutineHandler::class, $property->getValue($clientConfig['handler']));
192+
}
193+
}

0 commit comments

Comments
 (0)