Skip to content

Commit c822915

Browse files
authored
Merge pull request #77 from Nevay/feature/symfony-kernel-listener
Add basic symfony instrumentation bundle with kernel request listener
2 parents 9bbdb16 + 572d592 commit c822915

File tree

27 files changed

+1158
-65
lines changed

27 files changed

+1158
-65
lines changed

composer.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@
1414
"require": {
1515
"php": "^7.4 || ^8.0",
1616
"ext-json": "*",
17-
"open-telemetry/opentelemetry": "^0.0.13",
17+
"open-telemetry/opentelemetry": "^0.0.15",
1818
"php-http/discovery": "^1.14",
1919
"php-http/message": "^1.12"
2020
},
2121
"replace": {
22+
"open-telemetry/contrib-symfony-instrumentation-bundle": "self.version",
2223
"open-telemetry/contrib-sdk-bundle": "self.version",
2324
"open-telemetry/contrib-aws": "self.version"
2425
},
@@ -39,6 +40,7 @@
3940
"guzzlehttp/guzzle": "^7.3",
4041
"guzzlehttp/psr7": "^2.0@RC",
4142
"kriswallsmith/buzz": "^1.2",
43+
"matthiasnoback/symfony-dependency-injection-test": "^4.3",
4244
"mikey179/vfsstream": "^1.6",
4345
"nyholm/psr7": "^1.4",
4446
"open-telemetry/dev-tools": "dev-main",
@@ -48,14 +50,14 @@
4850
"phpstan/phpstan-symfony": "^1.1",
4951
"phpunit/phpunit": "^9.5",
5052
"psalm/plugin-phpunit": "^0.13.0",
53+
"qossmic/deptrac-shim": "^0.22.1",
5154
"symfony/config": "^4.4|^5.0|^6.0",
5255
"symfony/http-client": "^5.3",
5356
"symfony/http-kernel": "^4.4|^5.3|^6.0",
5457
"symfony/options-resolver": "^4.4|^5.3|^6.0",
5558
"symfony/polyfill-php80": "^1.16",
5659
"symfony/proxy-manager-bridge": "^4.4|^5.3|^6.0",
5760
"symfony/yaml": "^4.4|^5.3|^6.0",
58-
"qossmic/deptrac-shim": "^0.22.1",
5961
"vimeo/psalm": "^4.0"
6062
},
6163
"suggest": {

phpunit.xml.dist

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22

3-
<phpunit
4-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
<phpunit
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
55
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
6-
backupGlobals="false"
7-
backupStaticAttributes="false"
8-
bootstrap="./vendor/autoload.php"
6+
backupGlobals="false"
7+
backupStaticAttributes="false"
8+
bootstrap="./tests/bootstrap.php"
99
cacheResult="false"
10-
colors="false"
11-
convertErrorsToExceptions="true"
12-
convertNoticesToExceptions="true"
13-
convertWarningsToExceptions="true"
14-
forceCoversAnnotation="false"
15-
processIsolation="false"
10+
colors="false"
11+
convertErrorsToExceptions="true"
12+
convertNoticesToExceptions="true"
13+
convertWarningsToExceptions="true"
14+
forceCoversAnnotation="false"
15+
processIsolation="false"
1616
stopOnError="false"
1717
stopOnFailure="false"
1818
stopOnIncomplete="false"
1919
stopOnSkipped="false"
20-
stopOnRisky="false"
20+
stopOnRisky="false"
2121
timeoutForSmallTests="1"
2222
timeoutForMediumTests="10"
2323
timeoutForLargeTests="60"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Symfony\OtelBundle\DependencyInjection\Compiler;
6+
7+
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
8+
use Symfony\Component\DependencyInjection\ContainerBuilder;
9+
10+
final class SetAliasIfNotDefinedCompilerPass implements CompilerPassInterface
11+
{
12+
private string $service;
13+
private string $aliasService;
14+
15+
public function __construct(string $service, string $aliasService)
16+
{
17+
$this->service = $service;
18+
$this->aliasService = $aliasService;
19+
}
20+
21+
public function process(ContainerBuilder $container): void
22+
{
23+
if ($container->has($this->service)) {
24+
return;
25+
}
26+
27+
$container->setAlias($this->service, $this->aliasService);
28+
}
29+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Symfony\OtelBundle\DependencyInjection;
6+
7+
use function class_exists;
8+
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
9+
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
10+
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
11+
use Symfony\Component\Config\Definition\ConfigurationInterface;
12+
use Symfony\Component\HttpKernel\HttpKernel;
13+
14+
/**
15+
* @psalm-suppress PossiblyNullReference,PossiblyUndefinedMethod
16+
*/
17+
final class Configuration implements ConfigurationInterface
18+
{
19+
public function getConfigTreeBuilder(): TreeBuilder
20+
{
21+
$builder = new TreeBuilder('otel');
22+
23+
$tracing = $builder->getRootNode()
24+
->addDefaultsIfNotSet()
25+
->children()
26+
->arrayNode('tracing')
27+
->addDefaultsIfNotSet();
28+
29+
$tracing->children()->append($this->httpTracingNode());
30+
31+
if (class_exists(HttpKernel::class)) {
32+
$tracing->children()->append($this->kernelTracingNode());
33+
}
34+
35+
return $builder;
36+
}
37+
38+
private function httpTracingNode(): NodeDefinition
39+
{
40+
return (new ArrayNodeDefinition('http'))
41+
->addDefaultsIfNotSet()
42+
->children()
43+
->arrayNode('server')
44+
->addDefaultsIfNotSet()
45+
->fixXmlConfig('requestHeader')
46+
->fixXmlConfig('responseHeader')
47+
->children()
48+
->arrayNode('requestHeaders')
49+
->info('Request headers to capture as span attributes.')
50+
->example(['Content-Type', 'X-Forwarded-For'])
51+
->beforeNormalization()->castToArray()->end()
52+
->scalarPrototype()->cannotBeEmpty()->end()
53+
->end()
54+
->arrayNode('responseHeaders')
55+
->info('Response headers to capture as span attributes.')
56+
->example(['Content-Type'])
57+
->beforeNormalization()->castToArray()->end()
58+
->scalarPrototype()->cannotBeEmpty()->end()
59+
->end()
60+
->end()
61+
->end()
62+
->end()
63+
;
64+
}
65+
66+
private function kernelTracingNode(): NodeDefinition
67+
{
68+
return (new ArrayNodeDefinition('kernel'))
69+
->addDefaultsIfNotSet()
70+
->canBeDisabled()
71+
->children()
72+
->booleanNode('extractRemoteContext')
73+
->info('Set to `false` if the kernel runs in a runtime that extracts the remote context before passing the request to the kernel.')
74+
->defaultTrue()
75+
->end()
76+
->end()
77+
;
78+
}
79+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Symfony\OtelBundle\DependencyInjection;
6+
7+
use OpenTelemetry\Context\Propagation\NoopTextMapPropagator;
8+
use OpenTelemetry\Symfony\OtelBundle\HttpKernel\RequestListener;
9+
use Symfony\Component\Config\Definition\ConfigurationInterface;
10+
use Symfony\Component\Config\FileLocator;
11+
use Symfony\Component\DependencyInjection\ContainerBuilder;
12+
use Symfony\Component\DependencyInjection\Extension\Extension;
13+
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
14+
use Symfony\Component\DependencyInjection\Reference;
15+
16+
final class OtelExtension extends Extension
17+
{
18+
public function load(array $configs, ContainerBuilder $container)
19+
{
20+
$config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs);
21+
22+
$loader = new PhpFileLoader($container, new FileLocator());
23+
$loader->load(__DIR__ . '/../Resources/services.php');
24+
25+
$container->setParameter('otel.tracing.http.server.request_headers', $config['tracing']['http']['server']['requestHeaders']);
26+
$container->setParameter('otel.tracing.http.server.response_headers', $config['tracing']['http']['server']['responseHeaders']);
27+
28+
if ($config['tracing']['kernel']['enabled'] ?? false) {
29+
$loader->load(__DIR__ . '/../Resources/services_kernel.php');
30+
$this->loadKernelTracing($config['tracing']['kernel'], $container);
31+
}
32+
}
33+
34+
private function loadKernelTracing(array $config, ContainerBuilder $container): void
35+
{
36+
if (!$config['extractRemoteContext']) {
37+
$container->getDefinition(RequestListener::class)
38+
->setArgument('$propagator', new Reference(NoopTextMapPropagator::class))
39+
;
40+
}
41+
}
42+
43+
public function getConfiguration(array $config, ContainerBuilder $container): ConfigurationInterface
44+
{
45+
return new Configuration();
46+
}
47+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenTelemetry\Symfony\OtelBundle\HttpKernel;
6+
7+
use function count;
8+
use function implode;
9+
use OpenTelemetry\Context\Propagation\PropagationGetterInterface;
10+
use Symfony\Component\HttpFoundation\Request;
11+
12+
/**
13+
* @internal
14+
*/
15+
final class HeadersPropagator implements PropagationGetterInterface
16+
{
17+
/**
18+
* @param Request $carrier
19+
*
20+
* @psalm-suppress LessSpecificImplementedReturnType
21+
*/
22+
public function keys($carrier): array
23+
{
24+
return $carrier->headers->keys();
25+
}
26+
27+
/**
28+
* @param Request $carrier
29+
*/
30+
public function get($carrier, string $key): ?string
31+
{
32+
/** @psalm-suppress InvalidArgument */
33+
return count($carrier->headers->all($key)) > 1
34+
/** @phpstan-ignore-next-line */
35+
? implode(',', $carrier->headers->all($key))
36+
: $carrier->headers->get($key);
37+
}
38+
}

0 commit comments

Comments
 (0)