Skip to content

Commit 19c774f

Browse files
authored
feat(OpenTelemetry/Transport): replace Guzzle with Symfony HTTP Client and Nyholm PSR-7/17 (#209)
* feat(OpenTelemetry/Transport): replace Guzzle with Symfony HTTP Client and Nyholm PSR-7/17 * feat(DependencyInjection): add support for configuring custom PSR-18 HTTP client * refactor(DependencyInjection): rename `http_client` to `transport_http_client` for clarity in telemetry transport configuration * docs(DependencyInjection): document required PSR interfaces for transport_http_client config * feat(DependencyInjection): implement default PSR-18 client registration with support for custom configuration * tests(Unit/DI): remove useless configuration test
1 parent c221a1e commit 19c774f

25 files changed

Lines changed: 181 additions & 41 deletions

composer.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@
2222
],
2323
"require": {
2424
"php": ">=8.2",
25-
"guzzlehttp/guzzle": "^7.10",
26-
"guzzlehttp/psr7": "^2.9",
2725
"monolog/monolog": "^3.10",
26+
"nyholm/psr7": "^1.8",
2827
"open-telemetry/api": "^1",
2928
"open-telemetry/context": "^1",
3029
"open-telemetry/opentelemetry-logger-monolog": "^1",
@@ -33,6 +32,8 @@
3332
"open-telemetry/sdk": "^1",
3433
"open-telemetry/sem-conv": "^1",
3534
"open-telemetry/symfony-sdk-bundle": "^0",
35+
"psr/http-client": "^1.0",
36+
"psr/http-factory": "^1.0",
3637
"symfony/config": "^7.4",
3738
"symfony/dependency-injection": "^7.4",
3839
"symfony/event-dispatcher": "^7.4",
@@ -85,7 +86,7 @@
8586
"open-telemetry/transport-grpc": "Needed to export OpenTelemetry data via gRPC",
8687
"symfony/cache": "Needed to enable Symfony Cache instrumentation",
8788
"symfony/console": "Needed to enable Symfony Console instrumentation",
88-
"symfony/http-client": "Needed to enable Symfony HttpClient instrumentation",
89+
"symfony/http-client": "Default PSR-18 HTTP client for telemetry export transports and needed to enable Symfony HttpClient instrumentation",
8990
"symfony/http-kernel": "Needed to enable Symfony HttpKernel instrumentation",
9091
"symfony/mailer": "Needed to enable Symfony Mailer instrumentation",
9192
"symfony/messenger": "Needed to enable Symfony Messenger instrumentation",

docs/src/user-guide/getting-started.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,24 @@ To ensure a clean and optimal setup:
3131

3232
#### HTTP PSR Discovery and `php-http/discovery`
3333

34-
You may also be prompted to enable the `php-http/discovery` Composer plugin. This plugin allows libraries to discover HTTP PSR implementations dynamically. While this is required by many OpenTelemetry dependencies, our bundle relies directly on Guzzle HTTP for simplicity.
34+
You may also be prompted to enable the `php-http/discovery` Composer plugin. This plugin allows libraries to discover HTTP PSR implementations dynamically.
3535

3636
- Recommendation: Enable the plugin if your application requires it, but this is optional.
37-
- If you believe relying directly on Guzzle HTTP is not ideal, please open an issue to share your feedback. As this package is in active development, we are open to exploring better solutions.
37+
38+
#### HTTP client for telemetry export transports
39+
40+
The bundle needs a PSR-18 HTTP client to export telemetry data over HTTP (OTLP, Zipkin). By default, it uses Symfony's `Psr18Client` if `symfony/http-client` is installed:
41+
42+
```bash
43+
composer require symfony/http-client
44+
```
45+
46+
You can use any PSR-18 compatible client by configuring the `transport_http_client` option:
47+
48+
```yaml
49+
open_telemetry:
50+
transport_http_client: app.my_custom_psr18_client
51+
```
3852
3953
### Supported Versions
4054

src/DependencyInjection/Configuration.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ public function getConfigTreeBuilder(): TreeBuilder
2929

3030
$rootNode = $treeBuilder->getRootNode();
3131

32+
$rootNode
33+
->children()
34+
->scalarNode('transport_http_client')
35+
->info('Service ID used for telemetry export transports. Must implement PSR-18 ClientInterface and PSR-17 RequestFactoryInterface, StreamFactoryInterface. Defaults to Symfony Psr18Client.')
36+
->defaultNull()
37+
->end()
38+
->end()
39+
;
40+
3241
$this->addServiceSection($rootNode);
3342
$this->addInstrumentationSection($rootNode);
3443
$this->addTracesSection($rootNode);

src/DependencyInjection/OpenTelemetryExtension.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Symfony\Component\DependencyInjection\ContainerBuilder;
1212
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
1313
use Symfony\Component\HttpClient\HttpClient;
14+
use Symfony\Component\HttpClient\Psr18Client;
1415
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
1516
use Symfony\Component\Mailer\MailerInterface;
1617
use Symfony\Component\Messenger\Envelope;
@@ -52,6 +53,7 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container
5253
$loader->load('services_tracing_instrumentation.php');
5354
$loader->load('services_metering_instrumentation.php');
5455

56+
$this->registerTransportHttpClient($mergedConfig['transport_http_client'], $container);
5557
$this->registerService($mergedConfig['service'], $container);
5658
$this->registerInstrumentation($mergedConfig['instrumentation'], $container);
5759

@@ -84,6 +86,20 @@ private function registerService(array $config, ContainerBuilder $container): vo
8486
]);
8587
}
8688

89+
private function registerTransportHttpClient(?string $httpClientServiceId, ContainerBuilder $container): void
90+
{
91+
if (null !== $httpClientServiceId) {
92+
$container->setAlias('open_telemetry.transport_http_client', $httpClientServiceId);
93+
94+
return;
95+
}
96+
97+
if (class_exists(Psr18Client::class)) {
98+
$container->register('open_telemetry.transport_http_client.psr18', Psr18Client::class);
99+
$container->setAlias('open_telemetry.transport_http_client', 'open_telemetry.transport_http_client.psr18');
100+
}
101+
}
102+
87103
/**
88104
* @param array{
89105
* cache: InstrumentationConfig,

src/Instrumentation/Symfony/HttpClient/TraceableHttpClient.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
namespace FriendsOfOpenTelemetry\OpenTelemetryBundle\Instrumentation\Symfony\HttpClient;
44

5-
use GuzzleHttp\Psr7\Uri;
65
use OpenTelemetry\API\Trace\SpanKind;
76
use OpenTelemetry\API\Trace\TracerInterface;
87
use OpenTelemetry\Context\Context;
98
use OpenTelemetry\SemConv\Attributes\HttpAttributes;
109
use OpenTelemetry\SemConv\Attributes\UrlAttributes;
10+
use Psr\Http\Message\UriFactoryInterface;
1111
use Psr\Log\LoggerAwareInterface;
1212
use Psr\Log\LoggerInterface;
1313
use Symfony\Component\HttpClient\Response\ResponseStream;
@@ -21,6 +21,7 @@ final class TraceableHttpClient implements HttpClientInterface, LoggerAwareInter
2121
public function __construct(
2222
private HttpClientInterface $client,
2323
private readonly TracerInterface $tracer,
24+
private readonly UriFactoryInterface $uriFactory,
2425
private ?LoggerInterface $logger = null,
2526
) {
2627
}
@@ -37,7 +38,7 @@ public function request(string $method, string $url, array $options = []): Respo
3738
$this->logger?->debug('No active scope');
3839
}
3940

40-
$uri = new Uri($url);
41+
$uri = $this->uriFactory->createUri($url);
4142

4243
$spanBuilder = $this->tracer
4344
->spanBuilder('http.client')

src/OpenTelemetry/Exporter/OtlpExporterEndpoint.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
namespace FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Exporter;
44

55
use FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Transport\TransportEnum;
6-
use GuzzleHttp\Psr7\HttpFactory;
6+
use Nyholm\Psr7\Factory\Psr17Factory;
77
use OpenTelemetry\API\Signals;
88
use OpenTelemetry\Contrib\Otlp\HttpEndpointResolverInterface;
99
use OpenTelemetry\Contrib\Otlp\OtlpUtil;
@@ -26,7 +26,7 @@ private function __construct(
2626

2727
public static function fromDsn(ExporterDsn $dsn): self
2828
{
29-
return new self($dsn, new HttpFactory());
29+
return new self($dsn, new Psr17Factory());
3030
}
3131

3232
public function withSignal(string $signal): self

src/OpenTelemetry/Trace/ZipkinExporterEndpoint.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Exporter\ExporterEndpointInterface;
77
use FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Trace\SpanExporter\TraceExporterEnum;
88
use FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Transport\TransportEnum;
9-
use GuzzleHttp\Psr7\HttpFactory;
9+
use Nyholm\Psr7\Factory\Psr17Factory;
1010
use Psr\Http\Message\UriFactoryInterface;
1111

1212
final class ZipkinExporterEndpoint implements ExporterEndpointInterface
@@ -25,7 +25,7 @@ private function __construct(
2525

2626
public static function fromDsn(ExporterDsn $dsn): self
2727
{
28-
return new self($dsn, new HttpFactory());
28+
return new self($dsn, new Psr17Factory());
2929
}
3030

3131
public function __toString()

src/OpenTelemetry/Transport/OtlpHttpTransportFactory.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@
66
use FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Exporter\ExporterOptionsInterface;
77
use FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Exporter\OtlpExporterCompressionEnum;
88
use FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Exporter\OtlpExporterFormatEnum;
9-
use GuzzleHttp\Client;
10-
use GuzzleHttp\Psr7\HttpFactory;
119
use OpenTelemetry\SDK\Common\Export\Http\PsrTransport;
1210
use OpenTelemetry\SDK\Common\Export\Http\PsrUtils;
1311
use OpenTelemetry\SDK\Common\Export\TransportInterface;
12+
use Psr\Http\Client\ClientInterface;
13+
use Psr\Http\Message\RequestFactoryInterface;
14+
use Psr\Http\Message\StreamFactoryInterface;
1415

1516
final readonly class OtlpHttpTransportFactory implements TransportFactoryInterface
1617
{
18+
public function __construct(
19+
private ClientInterface $client,
20+
private RequestFactoryInterface $requestFactory,
21+
private StreamFactoryInterface $streamFactory,
22+
) {
23+
}
24+
1725
public function supports(#[\SensitiveParameter] ExporterEndpointInterface $endpoint, ExporterOptionsInterface $options): bool
1826
{
1927
if (null === $endpoint->getTransport()) {
@@ -34,9 +42,9 @@ public function createTransport(#[\SensitiveParameter] ExporterEndpointInterface
3442
$compression = OtlpExporterCompressionEnum::tryFrom($params->compression) ?? OtlpExporterCompressionEnum::None;
3543

3644
return new PsrTransport(
37-
new Client(),
38-
new HttpFactory(),
39-
new HttpFactory(),
45+
$this->client,
46+
$this->requestFactory,
47+
$this->streamFactory,
4048
(string) $endpoint,
4149
$format,
4250
$params->headers,

src/OpenTelemetry/Transport/PsrHttpTransportFactory.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,22 @@
66
use FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Exporter\ExporterOptionsInterface;
77
use FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Exporter\OtlpExporterCompressionEnum;
88
use FriendsOfOpenTelemetry\OpenTelemetryBundle\OpenTelemetry\Exporter\OtlpExporterFormatEnum;
9-
use GuzzleHttp\Client;
10-
use GuzzleHttp\Psr7\HttpFactory;
119
use OpenTelemetry\SDK\Common\Export\Http\PsrTransport;
1210
use OpenTelemetry\SDK\Common\Export\Http\PsrUtils;
1311
use OpenTelemetry\SDK\Common\Export\TransportInterface;
12+
use Psr\Http\Client\ClientInterface;
13+
use Psr\Http\Message\RequestFactoryInterface;
14+
use Psr\Http\Message\StreamFactoryInterface;
1415

1516
final readonly class PsrHttpTransportFactory implements TransportFactoryInterface
1617
{
18+
public function __construct(
19+
private ClientInterface $client,
20+
private RequestFactoryInterface $requestFactory,
21+
private StreamFactoryInterface $streamFactory,
22+
) {
23+
}
24+
1725
public function supports(#[\SensitiveParameter] ExporterEndpointInterface $endpoint, ExporterOptionsInterface $options): bool
1826
{
1927
return null !== $endpoint->getTransport()
@@ -27,9 +35,9 @@ public function createTransport(#[\SensitiveParameter] ExporterEndpointInterface
2735
$compression = OtlpExporterCompressionEnum::tryFrom($params->compression) ?? OtlpExporterCompressionEnum::None;
2836

2937
return new PsrTransport(
30-
new Client(),
31-
new HttpFactory(),
32-
new HttpFactory(),
38+
$this->client,
39+
$this->requestFactory,
40+
$this->streamFactory,
3341
(string) $endpoint,
3442
$format,
3543
$params->headers,

src/Resources/config/services_tracing_instrumentation.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
// HTTP Client
5050
->set('open_telemetry.instrumentation.http_client.trace.client', TraceableHttpClient::class)
5151
->arg('$tracer', service('open_telemetry.traces.default_tracer'))
52+
->arg('$uriFactory', service('open_telemetry.transport_http_client'))
5253
->tag('monolog.logger', ['channel' => 'open_telemetry'])
5354

5455
// HTTP Kernel

0 commit comments

Comments
 (0)