Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions documentation/components/bridges/symfony-telemetry-bundle.md
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,10 @@ flow_telemetry:
http_kernel:
enabled: true
context_propagation: true # Extract context from incoming headers
trace_controller: true # Controller body span (default ON)
trace_controller_resolution: false # controller.get_callable span (default OFF)
trace_controller_arguments: false # controller.get_arguments aggregate span (default OFF)
trace_controller_argument_resolvers: false # per-resolver controller.argument_value_resolver spans (default OFF)
exclude_paths:
- path: '/_profiler'
- path: '/_wdt'
Expand All @@ -1075,6 +1079,25 @@ flow_telemetry:
- path: '/^\/api\/internal\/.*/' # Regex pattern
```

In addition to the request (SERVER) span, the bundle can trace the controller lifecycle as child spans of
the request span (same instrumentation scope, kind `INTERNAL`). They are emitted only while the request span
exists, so disabling `http_kernel` or excluding the path produces none.

- `trace_controller` (default **true**) — the controller **body** execution. The span is named after the
resolved controller (e.g. `App\Controller\OrderController::import`) and carries `code.namespace`,
`code.function` and `controller` attributes. It starts after argument resolution and completes at
`kernel.view`/`kernel.response`, so it excludes resolution time and appears in both the OTLP export and the
Flow Telemetry profiler panel.
- `trace_controller_resolution` (default **false**) — controller resolution
(`ControllerResolverInterface::getController()`), emitted as a `controller.get_callable` span.
- `trace_controller_arguments` (default **false**) — argument resolution as a single aggregate
`controller.get_arguments` span.
- `trace_controller_argument_resolvers` (default **false**) — one `controller.argument_value_resolver` span
per value resolver invocation (finer-grained, higher cardinality).

The resolution and argument toggles install service decorators only when enabled, so they add zero overhead
when off.

#### Console

Traces console commands.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler;

use Flow\Bridge\Symfony\TelemetryBundle\Instrumentation\HttpKernel\TracingArgumentResolver;
use Flow\Telemetry\Telemetry;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class ArgumentResolverTelemetryPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!$container->hasParameter('flow.telemetry.http_kernel.trace_controller_arguments')) {
return;
}

if ($container->getParameter('flow.telemetry.http_kernel.trace_controller_arguments') !== true) {
return;
}

if (!$container->hasDefinition('argument_resolver')) {
return;
}

$decoratorId = 'argument_resolver.flow_telemetry';
$decoratedId = $decoratorId . '.inner';

$definition = new Definition(TracingArgumentResolver::class);
$definition->setDecoratedService('argument_resolver');
$definition->setArgument(0, new Reference($decoratedId));
$definition->setArgument(1, new Reference(Telemetry::class));

$container->setDefinition($decoratorId, $definition);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler;

use Flow\Bridge\Symfony\TelemetryBundle\Instrumentation\HttpKernel\TracingValueResolver;
use Flow\Telemetry\Telemetry;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class ArgumentValueResolverTelemetryPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!$container->hasParameter('flow.telemetry.http_kernel.trace_controller_argument_resolvers')) {
return;
}

if ($container->getParameter('flow.telemetry.http_kernel.trace_controller_argument_resolvers') !== true) {
return;
}

foreach ($container->findTaggedServiceIds('controller.argument_value_resolver') as $serviceId => $_tags) {
if ($container->getDefinition($serviceId)->isAbstract()) {
continue;
}

$decoratorId = $serviceId . '.flow_telemetry';
$decoratedId = $decoratorId . '.inner';

$definition = new Definition(TracingValueResolver::class);
$definition->setDecoratedService($serviceId);
$definition->setArgument(0, new Reference($decoratedId));
$definition->setArgument(1, new Reference(Telemetry::class));

$container->setDefinition($decoratorId, $definition);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler;

use Flow\Bridge\Symfony\TelemetryBundle\Instrumentation\HttpKernel\TracingControllerResolver;
use Flow\Telemetry\Telemetry;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;

final class ControllerResolverTelemetryPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container): void
{
if (!$container->hasParameter('flow.telemetry.http_kernel.trace_controller_resolution')) {
return;
}

if ($container->getParameter('flow.telemetry.http_kernel.trace_controller_resolution') !== true) {
return;
}

if (!$container->hasDefinition('controller_resolver')) {
return;
}

$decoratorId = 'controller_resolver.flow_telemetry';
$decoratedId = $decoratorId . '.inner';

$definition = new Definition(TracingControllerResolver::class);
$definition->setDecoratedService('controller_resolver');
$definition->setArgument(0, new Reference($decoratedId));
$definition->setArgument(1, new Reference(Telemetry::class));

$container->setDefinition($decoratorId, $definition);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
use Flow\Bridge\Psr3\Telemetry\LogRecordConverter;
use Flow\Bridge\Psr3\Telemetry\TelemetryLogger;
use Flow\Bridge\Symfony\TelemetryBundle\Attribute\WithTelemetryChannel;
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\ArgumentResolverTelemetryPass;
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\ArgumentValueResolverTelemetryPass;
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\CacheTelemetryPass;
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\ChannelLoggerPass;
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\ControllerResolverTelemetryPass;
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\DBALTelemetryPass;
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\FrameworkLoggerPass;
use Flow\Bridge\Symfony\TelemetryBundle\DependencyInjection\Compiler\HttpClientTelemetryPass;
Expand Down Expand Up @@ -158,6 +161,9 @@ public function build(ContainerBuilder $container): void

$container->addCompilerPass(new OTLPAvailabilityPass());
$container->addCompilerPass(new ProfilerSignalCapturePass());
$container->addCompilerPass(new ControllerResolverTelemetryPass());
$container->addCompilerPass(new ArgumentResolverTelemetryPass());
$container->addCompilerPass(new ArgumentValueResolverTelemetryPass());
$container->addCompilerPass(new FrameworkLoggerPass(), PassConfig::TYPE_BEFORE_OPTIMIZATION, -64);

$container->registerAttributeForAutoconfiguration(
Expand Down Expand Up @@ -498,6 +504,22 @@ public function configure(DefinitionConfigurator $definition): void
->info('Extract trace context from incoming request headers and inject it into outgoing response headers (requires flow-php/symfony-http-foundation-telemetry-bridge; silently disabled when absent)')
->defaultTrue()
->end()
->booleanNode('trace_controller')
->info('Trace controller body execution as a child of the request span (span name = resolved controller)')
->defaultTrue()
->end()
->booleanNode('trace_controller_resolution')
->info('Trace controller resolution (controller.get_callable span); opt-in, finer detail')
->defaultFalse()
->end()
->booleanNode('trace_controller_arguments')
->info('Trace argument resolution as a single aggregate span (controller.get_arguments); opt-in')
->defaultFalse()
->end()
->booleanNode('trace_controller_argument_resolvers')
->info('Trace each argument value resolver individually (controller.argument_value_resolver span); opt-in, higher cardinality')
->defaultFalse()
->end()
->end()
->end()
->arrayNode('console')
Expand Down Expand Up @@ -740,7 +762,7 @@ public function configure(DefinitionConfigurator $definition): void
}

/**
* @param array{resource: array{detectors?: array{enabled?: bool, static?: array{cache?: array{enabled?: bool, path?: null|string}, os?: array{enabled?: bool}, host?: array{enabled?: bool}, service?: array{enabled?: bool}, deployment?: array{enabled?: bool}, environment?: array{enabled?: bool}}, dynamic?: array{process?: array{enabled?: bool}}}, custom?: array<string, mixed>}, clock_service_id?: null|string, framework_logger?: null|string, capture_framework_channels?: bool, channel_attribute_target?: 'scope'|'signal'|'both', context_storage?: array{type?: string, service_id?: null|string}, propagator?: array{type?: string, service_id?: null|string}, exporters?: array<string, array<string, mixed>>, error_handlers?: array<string, array<string, mixed>>, tracer_provider?: array<string, mixed>, meter_provider?: array<string, mixed>, logger_provider?: array<string, mixed>, instrumentation?: array{http_kernel?: array{enabled?: bool, exclude_paths?: array<array{path: string, method?: null|string}>, context_propagation?: bool}, console?: array{enabled?: bool, exclude_commands?: array<string>}, messenger?: array{enabled?: bool, context_propagation?: bool, propagation_style?: 'continue'|'link', link_to_worker?: bool}, twig?: array{enabled?: bool, trace_templates?: bool, trace_blocks?: bool, trace_macros?: bool, exclude_templates?: array<string>}, http_client?: array{enabled?: bool, exclude_clients?: array<string>}, psr18_client?: array{enabled?: bool, exclude_clients?: array<string>}, dbal?: array{enabled?: bool, log_sql?: bool, max_sql_length?: int, exclude_connections?: array<string>}, cache?: array{enabled?: bool, exclude_pools?: array<string>}}, profiler?: array{enabled?: bool|null, capture_logs?: bool}, tracers?: array<string, array{version?: string, schema_url?: null|string, attributes?: array{scope?: array<string, mixed>, signal?: array<string, mixed>}}>, meters?: array<string, array{version?: string, schema_url?: null|string, attributes?: array{scope?: array<string, mixed>, signal?: array<string, mixed>}}>, loggers?: array<string, array{version?: string, schema_url?: null|string, attributes?: array{scope?: array<string, mixed>, signal?: array<string, mixed>}}>} $config
* @param array{resource: array{detectors?: array{enabled?: bool, static?: array{cache?: array{enabled?: bool, path?: null|string}, os?: array{enabled?: bool}, host?: array{enabled?: bool}, service?: array{enabled?: bool}, deployment?: array{enabled?: bool}, environment?: array{enabled?: bool}}, dynamic?: array{process?: array{enabled?: bool}}}, custom?: array<string, mixed>}, clock_service_id?: null|string, framework_logger?: null|string, capture_framework_channels?: bool, channel_attribute_target?: 'scope'|'signal'|'both', context_storage?: array{type?: string, service_id?: null|string}, propagator?: array{type?: string, service_id?: null|string}, exporters?: array<string, array<string, mixed>>, error_handlers?: array<string, array<string, mixed>>, tracer_provider?: array<string, mixed>, meter_provider?: array<string, mixed>, logger_provider?: array<string, mixed>, instrumentation?: array{http_kernel?: array{enabled?: bool, exclude_paths?: array<array{path: string, method?: null|string}>, context_propagation?: bool, trace_controller?: bool, trace_controller_resolution?: bool, trace_controller_arguments?: bool, trace_controller_argument_resolvers?: bool}, console?: array{enabled?: bool, exclude_commands?: array<string>}, messenger?: array{enabled?: bool, context_propagation?: bool, propagation_style?: 'continue'|'link', link_to_worker?: bool}, twig?: array{enabled?: bool, trace_templates?: bool, trace_blocks?: bool, trace_macros?: bool, exclude_templates?: array<string>}, http_client?: array{enabled?: bool, exclude_clients?: array<string>}, psr18_client?: array{enabled?: bool, exclude_clients?: array<string>}, dbal?: array{enabled?: bool, log_sql?: bool, max_sql_length?: int, exclude_connections?: array<string>}, cache?: array{enabled?: bool, exclude_pools?: array<string>}}, profiler?: array{enabled?: bool|null, capture_logs?: bool}, tracers?: array<string, array{version?: string, schema_url?: null|string, attributes?: array{scope?: array<string, mixed>, signal?: array<string, mixed>}}>, meters?: array<string, array{version?: string, schema_url?: null|string, attributes?: array{scope?: array<string, mixed>, signal?: array<string, mixed>}}>, loggers?: array<string, array{version?: string, schema_url?: null|string, attributes?: array{scope?: array<string, mixed>, signal?: array<string, mixed>}}>} $config
*/
#[Override]
public function loadExtension(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
Expand Down Expand Up @@ -2649,7 +2671,7 @@ private function registerGlobalServices(array $config, ContainerBuilder $builder
}

/**
* @param array{http_kernel?: array{enabled?: bool, exclude_routes?: array<string>, exclude_paths?: array<array{path: string, method?: null|string}>, context_propagation?: bool}, console?: array{enabled?: bool, exclude_commands?: array<string>}, messenger?: array{enabled?: bool, context_propagation?: bool, propagation_style?: 'continue'|'link', link_to_worker?: bool}, twig?: array{enabled?: bool, trace_templates?: bool, trace_blocks?: bool, trace_macros?: bool, exclude_templates?: array<string>}, http_client?: array{enabled?: bool, exclude_clients?: array<string>}, psr18_client?: array{enabled?: bool, exclude_clients?: array<string>}, dbal?: array{enabled?: bool, log_sql?: bool, max_sql_length?: int, exclude_connections?: array<string>}, cache?: array{enabled?: bool, exclude_pools?: array<string>}} $config
* @param array{http_kernel?: array{enabled?: bool, exclude_routes?: array<string>, exclude_paths?: array<array{path: string, method?: null|string}>, context_propagation?: bool, trace_controller?: bool, trace_controller_resolution?: bool, trace_controller_arguments?: bool, trace_controller_argument_resolvers?: bool}, console?: array{enabled?: bool, exclude_commands?: array<string>}, messenger?: array{enabled?: bool, context_propagation?: bool, propagation_style?: 'continue'|'link', link_to_worker?: bool}, twig?: array{enabled?: bool, trace_templates?: bool, trace_blocks?: bool, trace_macros?: bool, exclude_templates?: array<string>}, http_client?: array{enabled?: bool, exclude_clients?: array<string>}, psr18_client?: array{enabled?: bool, exclude_clients?: array<string>}, dbal?: array{enabled?: bool, log_sql?: bool, max_sql_length?: int, exclude_connections?: array<string>}, cache?: array{enabled?: bool, exclude_pools?: array<string>}} $config
*/
private function registerInstrumentation(array $config, ContainerConfigurator $container, ContainerBuilder $builder): void
{
Expand All @@ -2664,6 +2686,22 @@ private function registerInstrumentation(array $config, ContainerConfigurator $c
'flow.telemetry.http_kernel.context_propagation',
($httpKernelConfig['context_propagation'] ?? true) && class_exists(self::HTTP_FOUNDATION_REQUEST_CARRIER),
);
$builder->setParameter(
'flow.telemetry.http_kernel.trace_controller',
$httpKernelConfig['trace_controller'] ?? true,
);
$builder->setParameter(
'flow.telemetry.http_kernel.trace_controller_resolution',
$httpKernelConfig['trace_controller_resolution'] ?? false,
);
$builder->setParameter(
'flow.telemetry.http_kernel.trace_controller_arguments',
$httpKernelConfig['trace_controller_arguments'] ?? false,
);
$builder->setParameter(
'flow.telemetry.http_kernel.trace_controller_argument_resolvers',
$httpKernelConfig['trace_controller_argument_resolvers'] ?? false,
);
$container->import(__DIR__ . '/Resources/config/instrumentation/http_kernel.php');
}

Expand Down Expand Up @@ -3019,7 +3057,7 @@ private function registerNamedExporters(array $config, ContainerBuilder $builder
}

/**
* @param array{http_kernel?: array{enabled?: bool, exclude_routes?: array<string>, exclude_paths?: array<array{path: string, method?: null|string}>, context_propagation?: bool}, console?: array{enabled?: bool, exclude_commands?: array<string>}, messenger?: array{enabled?: bool, context_propagation?: bool, propagation_style?: 'continue'|'link', link_to_worker?: bool}, twig?: array{enabled?: bool, trace_templates?: bool, trace_blocks?: bool, trace_macros?: bool, exclude_templates?: array<string>}, http_client?: array{enabled?: bool, exclude_clients?: array<string>}, psr18_client?: array{enabled?: bool, exclude_clients?: array<string>}, dbal?: array{enabled?: bool, log_sql?: bool, max_sql_length?: int, exclude_connections?: array<string>}, cache?: array{enabled?: bool, exclude_pools?: array<string>}} $config
* @param array{http_kernel?: array{enabled?: bool, exclude_routes?: array<string>, exclude_paths?: array<array{path: string, method?: null|string}>, context_propagation?: bool, trace_controller?: bool, trace_controller_resolution?: bool, trace_controller_arguments?: bool, trace_controller_argument_resolvers?: bool}, console?: array{enabled?: bool, exclude_commands?: array<string>}, messenger?: array{enabled?: bool, context_propagation?: bool, propagation_style?: 'continue'|'link', link_to_worker?: bool}, twig?: array{enabled?: bool, trace_templates?: bool, trace_blocks?: bool, trace_macros?: bool, exclude_templates?: array<string>}, http_client?: array{enabled?: bool, exclude_clients?: array<string>}, psr18_client?: array{enabled?: bool, exclude_clients?: array<string>}, dbal?: array{enabled?: bool, log_sql?: bool, max_sql_length?: int, exclude_connections?: array<string>}, cache?: array{enabled?: bool, exclude_pools?: array<string>}} $config
*/
private function registerParameterOnlyInstrumentation(array $config, ContainerBuilder $builder): void
{
Expand Down
Loading
Loading