Skip to content

Commit 8834827

Browse files
committed
feat(Instrumentation/Metrics): flush metrics on Console & Kernel TERMINATE events
1 parent 69f285d commit 8834827

40 files changed

Lines changed: 465 additions & 99 deletions

.editorconfig-checker.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"package.json",
1010
"package-lock.json",
1111
"composer.lock",
12-
"tests/Functional/DummyMeterServiceTest.php"
12+
"tests/*"
1313
],
1414
"AllowedContentTypes": [],
1515
"PassedFiles": [],

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
"doctrine/doctrine-migrations-bundle": "^3.4",
5252
"doctrine/orm": "^2.18 || ^3.3",
5353
"ergebnis/composer-normalize": "^2.45",
54-
"matthiasnoback/symfony-config-test": "^5.2",
54+
"matthiasnoback/symfony-config-test": "^6.0",
5555
"matthiasnoback/symfony-dependency-injection-test": "^6.0",
5656
"mcaskill/composer-exclude-files": "^4.0",
5757
"nyholm/symfony-bundle-test": "^3.0",
@@ -74,7 +74,7 @@
7474
"symfony/twig-bundle": "^7.2",
7575
"symfony/yaml": "^7.2",
7676
"twig/twig": "^3.18",
77-
"zalas/phpunit-globals": "^3.5"
77+
"zalas/phpunit-globals": "^4.0"
7878
},
7979
"suggest": {
8080
"doctrine/doctrine-bundle": "Needed to enable Doctrine DBAL & ORM instrumentation",

src/DependencyInjection/OpenTelemetryExtension.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ protected function loadInternal(array $mergedConfig, ContainerBuilder $container
4343
$loader->load('services_metrics.php');
4444
$loader->load('services_traces.php');
4545
$loader->load('services_tracing_instrumentation.php');
46+
$loader->load('services_metering_instrumentation.php');
4647

4748
$this->registerService($mergedConfig['service'], $container);
4849
$this->registerInstrumentation($mergedConfig['instrumentation'], $container);
@@ -91,10 +92,16 @@ private function registerService(array $config, ContainerBuilder $container): vo
9192
private function registerInstrumentation(array $config, ContainerBuilder $container): void
9293
{
9394
$this->registerCacheTracingInstrumentationConfiguration($container, $config['cache']);
95+
9496
$this->registerConsoleTracingInstrumentationConfiguration($container, $config['console']);
97+
$this->registerConsoleMeteringInstrumentationConfiguration($container, $config['console']);
98+
9599
$this->registerDoctrineTracingInstrumentationConfiguration($container, $config['doctrine']);
96100
$this->registerHttpClientTracingInstrumentationConfiguration($container, $config['http_client']);
101+
97102
$this->registerHttpKernelTracingInstrumentationConfiguration($container, $config['http_kernel']);
103+
$this->registerHttpKernelMeteringInstrumentationConfiguration($container, $config['http_kernel']);
104+
98105
$this->registerMailerTracingInstrumentationConfiguration($container, $config['mailer']);
99106
$this->registerMessengerTracingInstrumentationConfiguration($container, $config['messenger']);
100107
$this->registerTwigTracingInstrumentationConfiguration($container, $config['twig']);
@@ -137,6 +144,22 @@ private function registerConsoleTracingInstrumentationConfiguration(ContainerBui
137144
$this->setTracingInstrumentationParams($container, 'console', $config, $isConfigEnabled);
138145
}
139146

147+
/**
148+
* @param InstrumentationConfig $config
149+
*/
150+
private function registerConsoleMeteringInstrumentationConfiguration(ContainerBuilder $container, array $config): void
151+
{
152+
$isConfigEnabled = $this->isConfigEnabled($container, $config['metering']);
153+
154+
if ($isConfigEnabled && !class_exists(Command::class)) {
155+
throw new \LogicException('Console instrumentation cannot be enabled because the symfony/console package is not installed.');
156+
}
157+
158+
if (!$isConfigEnabled) {
159+
$container->removeDefinition('open_telemetry.instrumentation.console.metric.event_subscriber');
160+
}
161+
}
162+
140163
/**
141164
* @param InstrumentationConfig $config
142165
*/
@@ -188,6 +211,18 @@ private function registerHttpKernelTracingInstrumentationConfiguration(Container
188211
$this->setTracingInstrumentationParams($container, 'http_kernel', $config, $isConfigEnabled);
189212
}
190213

214+
/**
215+
* @param InstrumentationConfig $config
216+
*/
217+
private function registerHttpKernelMeteringInstrumentationConfiguration(ContainerBuilder $container, array $config): void
218+
{
219+
$isConfigEnabled = $this->isConfigEnabled($container, $config['metering']);
220+
221+
if (!$isConfigEnabled) {
222+
$container->removeDefinition('open_telemetry.instrumentation.http_kernel.metric.event_subscriber');
223+
}
224+
}
225+
191226
/**
192227
* @param InstrumentationConfig $config
193228
*/

src/DependencyInjection/OpenTelemetryMetricsExtension.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ private function loadMetricProvider(string $name, array $config): void
115115
isset($config['exporter']) ? new Reference($config['exporter']) : null,
116116
$filter,
117117
new Reference('open_telemetry.resource_info'),
118-
]);
118+
])
119+
->addTag('open_telemetry.metrics.provider')
120+
;
119121
}
120122

121123
/**
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace FriendsOfOpenTelemetry\OpenTelemetryBundle\Instrumentation\Symfony\Console;
4+
5+
use OpenTelemetry\SDK\Metrics\MeterProviderInterface;
6+
use Symfony\Component\Console\ConsoleEvents;
7+
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
8+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
9+
10+
final class ObservableConsoleEventSubscriber implements EventSubscriberInterface
11+
{
12+
public function __construct(
13+
/**
14+
* @var list<MeterProviderInterface>
15+
*/
16+
private readonly iterable $locator,
17+
) {
18+
}
19+
20+
public static function getSubscribedEvents(): array
21+
{
22+
return [
23+
ConsoleEvents::TERMINATE => [
24+
['flush', -10000],
25+
],
26+
];
27+
}
28+
29+
public function flush(ConsoleTerminateEvent $event): void
30+
{
31+
foreach ($this->locator as $provider) {
32+
$provider->shutdown();
33+
}
34+
}
35+
}

src/Instrumentation/Symfony/Console/TraceableConsoleEventSubscriber.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ public static function getSubscribedEvents(): array
5656
];
5757
}
5858

59+
public static function getSubscribedServices(): array
60+
{
61+
return [TracerInterface::class];
62+
}
63+
5964
public function startSpan(ConsoleCommandEvent $event): void
6065
{
6166
$command = $event->getCommand();
@@ -172,11 +177,6 @@ private function isAttributeTraceable(Command $command): bool
172177
&& true === $traceable instanceof Traceable;
173178
}
174179

175-
public static function getSubscribedServices(): array
176-
{
177-
return [TracerInterface::class];
178-
}
179-
180180
public function setInstrumentationType(InstrumentationTypeEnum $type): void
181181
{
182182
$this->instrumentationType = $type;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace FriendsOfOpenTelemetry\OpenTelemetryBundle\Instrumentation\Symfony\HttpKernel;
4+
5+
use OpenTelemetry\SDK\Metrics\MeterProviderInterface;
6+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
7+
use Symfony\Component\HttpKernel\Event\TerminateEvent;
8+
use Symfony\Component\HttpKernel\KernelEvents;
9+
10+
final class ObservableHttpKernelEventSubscriber implements EventSubscriberInterface
11+
{
12+
public function __construct(
13+
/**
14+
* @var list<MeterProviderInterface>
15+
*/
16+
private readonly iterable $locator,
17+
) {
18+
}
19+
20+
public static function getSubscribedEvents(): array
21+
{
22+
return [
23+
KernelEvents::TERMINATE => [
24+
['flush', 10000],
25+
],
26+
];
27+
}
28+
29+
public function flush(TerminateEvent $event): void
30+
{
31+
foreach ($this->locator as $provider) {
32+
$provider->shutdown();
33+
}
34+
}
35+
}

src/Instrumentation/Symfony/HttpKernel/TraceableHttpKernelEventSubscriber.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ public static function getSubscribedEvents(): array
104104
];
105105
}
106106

107+
public static function getSubscribedServices(): array
108+
{
109+
return [TracerInterface::class];
110+
}
111+
107112
public function startRequest(RequestEvent $event): void
108113
{
109114
$request = $event->getRequest();
@@ -393,11 +398,6 @@ private function createHeaderAttributeMapping(string $type, iterable $headers):
393398
return $headerAttributes;
394399
}
395400

396-
public static function getSubscribedServices(): array
397-
{
398-
return [TracerInterface::class];
399-
}
400-
401401
public function setInstrumentationType(InstrumentationTypeEnum $type): void
402402
{
403403
$this->instrumentationType = $type;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
use FriendsOfOpenTelemetry\OpenTelemetryBundle\Instrumentation\Symfony\Console\ObservableConsoleEventSubscriber;
4+
use FriendsOfOpenTelemetry\OpenTelemetryBundle\Instrumentation\Symfony\HttpKernel\ObservableHttpKernelEventSubscriber;
5+
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
6+
7+
use function Symfony\Component\DependencyInjection\Loader\Configurator\tagged_iterator;
8+
9+
return static function (ContainerConfigurator $container): void {
10+
$container->services()
11+
->defaults()
12+
->private()
13+
14+
// Console
15+
->set('open_telemetry.instrumentation.console.metric.event_subscriber', ObservableConsoleEventSubscriber::class)
16+
->args([tagged_iterator('open_telemetry.metrics.provider')])
17+
->tag('kernel.event_subscriber')
18+
->tag('monolog.logger', ['channel' => 'open_telemetry'])
19+
20+
// HTTP Kernel
21+
->set('open_telemetry.instrumentation.http_kernel.metric.event_subscriber', ObservableHttpKernelEventSubscriber::class)
22+
->args([tagged_iterator('open_telemetry.metrics.provider')])
23+
->tag('kernel.event_subscriber')
24+
->tag('monolog.logger', ['channel' => 'open_telemetry'])
25+
26+
;
27+
};

src/Resources/config/services_tracing_instrumentation.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
->set('open_telemetry.instrumentation.console.trace.event_subscriber', TraceableConsoleEventSubscriber::class)
3838
->arg('$tracer', service('open_telemetry.traces.default_tracer'))
3939
->tag('kernel.event_subscriber')
40+
->tag('container.service_subscriber')
4041
->tag('monolog.logger', ['channel' => 'open_telemetry'])
4142

4243
// Doctrine
@@ -56,6 +57,7 @@
5657
->arg('$propagator', service('open_telemetry.propagator_text_map.noop'))
5758
->arg('$propagationGetter', service('open_telemetry.propagation_getter.headers'))
5859
->tag('kernel.event_subscriber')
60+
->tag('container.service_subscriber')
5961
->tag('monolog.logger', ['channel' => 'open_telemetry'])
6062

6163
->set('open_telemetry.instrumentation.http_kernel.trace.route_loader', TraceableRouteLoader::class)

0 commit comments

Comments
 (0)