Skip to content

Commit

Permalink
Merge pull request #77 from Nevay/feature/symfony-kernel-listener
Browse files Browse the repository at this point in the history
Add basic symfony instrumentation bundle with kernel request listener
  • Loading branch information
brettmc authored Aug 28, 2022
2 parents 9bbdb16 + 572d592 commit c822915
Show file tree
Hide file tree
Showing 27 changed files with 1,158 additions and 65 deletions.
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
"require": {
"php": "^7.4 || ^8.0",
"ext-json": "*",
"open-telemetry/opentelemetry": "^0.0.13",
"open-telemetry/opentelemetry": "^0.0.15",
"php-http/discovery": "^1.14",
"php-http/message": "^1.12"
},
"replace": {
"open-telemetry/contrib-symfony-instrumentation-bundle": "self.version",
"open-telemetry/contrib-sdk-bundle": "self.version",
"open-telemetry/contrib-aws": "self.version"
},
Expand All @@ -39,6 +40,7 @@
"guzzlehttp/guzzle": "^7.3",
"guzzlehttp/psr7": "^2.0@RC",
"kriswallsmith/buzz": "^1.2",
"matthiasnoback/symfony-dependency-injection-test": "^4.3",
"mikey179/vfsstream": "^1.6",
"nyholm/psr7": "^1.4",
"open-telemetry/dev-tools": "dev-main",
Expand All @@ -48,14 +50,14 @@
"phpstan/phpstan-symfony": "^1.1",
"phpunit/phpunit": "^9.5",
"psalm/plugin-phpunit": "^0.13.0",
"qossmic/deptrac-shim": "^0.22.1",
"symfony/config": "^4.4|^5.0|^6.0",
"symfony/http-client": "^5.3",
"symfony/http-kernel": "^4.4|^5.3|^6.0",
"symfony/options-resolver": "^4.4|^5.3|^6.0",
"symfony/polyfill-php80": "^1.16",
"symfony/proxy-manager-bridge": "^4.4|^5.3|^6.0",
"symfony/yaml": "^4.4|^5.3|^6.0",
"qossmic/deptrac-shim": "^0.22.1",
"vimeo/psalm": "^4.0"
},
"suggest": {
Expand Down
24 changes: 12 additions & 12 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>

<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.5/phpunit.xsd"
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="./vendor/autoload.php"
backupGlobals="false"
backupStaticAttributes="false"
bootstrap="./tests/bootstrap.php"
cacheResult="false"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
forceCoversAnnotation="false"
processIsolation="false"
colors="false"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
forceCoversAnnotation="false"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
stopOnRisky="false"
stopOnRisky="false"
timeoutForSmallTests="1"
timeoutForMediumTests="10"
timeoutForLargeTests="60"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Symfony\OtelBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

final class SetAliasIfNotDefinedCompilerPass implements CompilerPassInterface
{
private string $service;
private string $aliasService;

public function __construct(string $service, string $aliasService)
{
$this->service = $service;
$this->aliasService = $aliasService;
}

public function process(ContainerBuilder $container): void
{
if ($container->has($this->service)) {
return;
}

$container->setAlias($this->service, $this->aliasService);
}
}
79 changes: 79 additions & 0 deletions src/Symfony/OtelBundle/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Symfony\OtelBundle\DependencyInjection;

use function class_exists;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\HttpKernel\HttpKernel;

/**
* @psalm-suppress PossiblyNullReference,PossiblyUndefinedMethod
*/
final class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$builder = new TreeBuilder('otel');

$tracing = $builder->getRootNode()
->addDefaultsIfNotSet()
->children()
->arrayNode('tracing')
->addDefaultsIfNotSet();

$tracing->children()->append($this->httpTracingNode());

if (class_exists(HttpKernel::class)) {
$tracing->children()->append($this->kernelTracingNode());
}

return $builder;
}

private function httpTracingNode(): NodeDefinition
{
return (new ArrayNodeDefinition('http'))
->addDefaultsIfNotSet()
->children()
->arrayNode('server')
->addDefaultsIfNotSet()
->fixXmlConfig('requestHeader')
->fixXmlConfig('responseHeader')
->children()
->arrayNode('requestHeaders')
->info('Request headers to capture as span attributes.')
->example(['Content-Type', 'X-Forwarded-For'])
->beforeNormalization()->castToArray()->end()
->scalarPrototype()->cannotBeEmpty()->end()
->end()
->arrayNode('responseHeaders')
->info('Response headers to capture as span attributes.')
->example(['Content-Type'])
->beforeNormalization()->castToArray()->end()
->scalarPrototype()->cannotBeEmpty()->end()
->end()
->end()
->end()
->end()
;
}

private function kernelTracingNode(): NodeDefinition
{
return (new ArrayNodeDefinition('kernel'))
->addDefaultsIfNotSet()
->canBeDisabled()
->children()
->booleanNode('extractRemoteContext')
->info('Set to `false` if the kernel runs in a runtime that extracts the remote context before passing the request to the kernel.')
->defaultTrue()
->end()
->end()
;
}
}
47 changes: 47 additions & 0 deletions src/Symfony/OtelBundle/DependencyInjection/OtelExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Symfony\OtelBundle\DependencyInjection;

use OpenTelemetry\Context\Propagation\NoopTextMapPropagator;
use OpenTelemetry\Symfony\OtelBundle\HttpKernel\RequestListener;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
use Symfony\Component\DependencyInjection\Reference;

final class OtelExtension extends Extension
{
public function load(array $configs, ContainerBuilder $container)
{
$config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs);

$loader = new PhpFileLoader($container, new FileLocator());
$loader->load(__DIR__ . '/../Resources/services.php');

$container->setParameter('otel.tracing.http.server.request_headers', $config['tracing']['http']['server']['requestHeaders']);
$container->setParameter('otel.tracing.http.server.response_headers', $config['tracing']['http']['server']['responseHeaders']);

if ($config['tracing']['kernel']['enabled'] ?? false) {
$loader->load(__DIR__ . '/../Resources/services_kernel.php');
$this->loadKernelTracing($config['tracing']['kernel'], $container);
}
}

private function loadKernelTracing(array $config, ContainerBuilder $container): void
{
if (!$config['extractRemoteContext']) {
$container->getDefinition(RequestListener::class)
->setArgument('$propagator', new Reference(NoopTextMapPropagator::class))
;
}
}

public function getConfiguration(array $config, ContainerBuilder $container): ConfigurationInterface
{
return new Configuration();
}
}
38 changes: 38 additions & 0 deletions src/Symfony/OtelBundle/HttpKernel/HeadersPropagator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace OpenTelemetry\Symfony\OtelBundle\HttpKernel;

use function count;
use function implode;
use OpenTelemetry\Context\Propagation\PropagationGetterInterface;
use Symfony\Component\HttpFoundation\Request;

/**
* @internal
*/
final class HeadersPropagator implements PropagationGetterInterface
{
/**
* @param Request $carrier
*
* @psalm-suppress LessSpecificImplementedReturnType
*/
public function keys($carrier): array
{
return $carrier->headers->keys();
}

/**
* @param Request $carrier
*/
public function get($carrier, string $key): ?string
{
/** @psalm-suppress InvalidArgument */
return count($carrier->headers->all($key)) > 1
/** @phpstan-ignore-next-line */
? implode(',', $carrier->headers->all($key))
: $carrier->headers->get($key);
}
}
Loading

0 comments on commit c822915

Please sign in to comment.