From d2d7fe48d0e5cf5c1ba951b0fce7714cca70bd64 Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Tue, 2 Dec 2025 17:12:50 +0100 Subject: [PATCH 1/4] ref(metrics): add metric options --- composer.json | 2 +- src/DependencyInjection/Configuration.php | 2 + src/DependencyInjection/SentryExtension.php | 10 ++-- .../DependencyInjection/ConfigurationTest.php | 1 + .../SentryExtensionTest.php | 3 +- .../End2End/App/KernelWithMetricsCallback.php | 18 ++++++++ .../End2End/App/KernelWithMetricsDisabled.php | 18 ++++++++ tests/End2End/App/metrics_disabled.yml | 3 ++ tests/End2End/App/metrics_with_callback.yml | 12 +++++ tests/End2End/Fixtures/SentryCallbacks.php | 20 ++++++++ tests/End2End/MetricsDisabledEnd2EndTest.php | 34 ++++++++++++++ .../MetricsWithCallbackEnd2EndTest.php | 46 +++++++++++++++++++ 12 files changed, 164 insertions(+), 5 deletions(-) create mode 100644 tests/End2End/App/KernelWithMetricsCallback.php create mode 100644 tests/End2End/App/KernelWithMetricsDisabled.php create mode 100644 tests/End2End/App/metrics_disabled.yml create mode 100644 tests/End2End/App/metrics_with_callback.yml create mode 100644 tests/End2End/Fixtures/SentryCallbacks.php create mode 100644 tests/End2End/MetricsDisabledEnd2EndTest.php create mode 100644 tests/End2End/MetricsWithCallbackEnd2EndTest.php diff --git a/composer.json b/composer.json index 50dc4781..b1a52c8f 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "php": "^7.2||^8.0", "guzzlehttp/psr7": "^2.1.1", "jean85/pretty-package-versions": "^1.5||^2.0", - "sentry/sentry": "^4.19", + "sentry/sentry": "^4.19.1", "symfony/cache-contracts": "^1.1||^2.4||^3.0", "symfony/config": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0", "symfony/console": "^4.4.20||^5.0.11||^6.0||^7.0||^8.0", diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 3719262e..80c22ce1 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -89,6 +89,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->info('The sampling factor to apply to profiles. A value of 0 will deny sending any profiles, and a value of 1 will send all profiles. Profiles are sampled in relation to traces_sample_rate') ->end() ->booleanNode('enable_logs')->end() + ->booleanNode('enable_metrics')->defaultTrue()->end() ->booleanNode('attach_stacktrace')->end() ->booleanNode('attach_metric_code_locations')->end() ->integerNode('context_lines')->min(0)->end() @@ -117,6 +118,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->scalarNode('before_send_check_in')->end() ->scalarNode('before_send_metrics')->end() ->scalarNode('before_send_log')->end() + ->scalarNode('before_send_metric')->end() ->variableNode('trace_propagation_targets')->end() ->arrayNode('tags') ->useAttributeAsKey('name') diff --git a/src/DependencyInjection/SentryExtension.php b/src/DependencyInjection/SentryExtension.php index f64f2991..8c7ee7ed 100644 --- a/src/DependencyInjection/SentryExtension.php +++ b/src/DependencyInjection/SentryExtension.php @@ -137,6 +137,10 @@ private function registerConfiguration(ContainerBuilder $container, array $confi $options['before_breadcrumb'] = new Reference($options['before_breadcrumb']); } + if (isset($options['before_send_metric'])) { + $options['before_send_metric'] = new Reference($options['before_send_metric']); + } + if (isset($options['class_serializers'])) { $options['class_serializers'] = array_map(static function (string $value): Reference { return new Reference($value); @@ -316,7 +320,7 @@ private function registerHttpClientTracingConfiguration(ContainerBuilder $contai } /** - * @param string[] $integrations + * @param string[] $integrations * @param array $config * * @return array @@ -348,12 +352,12 @@ private function configureRequestIntegration(array $integrations, bool $useDefau /** * @param class-string $integrationClass - * @param array $integrations + * @param array $integrations */ private function isIntegrationEnabled(string $integrationClass, array $integrations): bool { foreach ($integrations as $integration) { - if ($integration instanceof Reference && $integrationClass === (string) $integration) { + if ($integration instanceof Reference && $integrationClass === (string)$integration) { return true; } diff --git a/tests/DependencyInjection/ConfigurationTest.php b/tests/DependencyInjection/ConfigurationTest.php index 6bc34499..d6cbf3b9 100644 --- a/tests/DependencyInjection/ConfigurationTest.php +++ b/tests/DependencyInjection/ConfigurationTest.php @@ -27,6 +27,7 @@ public function testProcessConfigurationWithDefaultConfiguration(): void 'options' => [ 'integrations' => [], 'prefixes' => array_merge(['%kernel.project_dir%'], array_filter(explode(\PATH_SEPARATOR, get_include_path() ?: ''))), + 'enable_metrics' => true, 'environment' => '%kernel.environment%', 'release' => '%env(default::SENTRY_RELEASE)%', 'ignore_exceptions' => [], diff --git a/tests/DependencyInjection/SentryExtensionTest.php b/tests/DependencyInjection/SentryExtensionTest.php index 0d64d77b..5d7b0a5f 100644 --- a/tests/DependencyInjection/SentryExtensionTest.php +++ b/tests/DependencyInjection/SentryExtensionTest.php @@ -217,6 +217,7 @@ public function testClientIsCreatedFromOptions(): void 'traces_sampler' => new Reference('App\\Sentry\\Tracing\\TracesSampler'), 'profiles_sample_rate' => 1, 'enable_logs' => true, + 'enable_metrics' => true, 'attach_stacktrace' => true, 'attach_metric_code_locations' => true, 'context_lines' => 0, @@ -498,7 +499,7 @@ private function createContainerFromFixture(string $fixtureFile): ContainerBuild /** * @param array $methodCall - * @param mixed[] $arguments + * @param mixed[] $arguments */ private function assertDefinitionMethodCallAt(array $methodCall, string $method, array $arguments): void { diff --git a/tests/End2End/App/KernelWithMetricsCallback.php b/tests/End2End/App/KernelWithMetricsCallback.php new file mode 100644 index 00000000..515aa11a --- /dev/null +++ b/tests/End2End/App/KernelWithMetricsCallback.php @@ -0,0 +1,18 @@ +load(__DIR__ . '/metrics.yml'); + $loader->load(__DIR__ . '/metrics_with_callback.yml'); + } +} diff --git a/tests/End2End/App/KernelWithMetricsDisabled.php b/tests/End2End/App/KernelWithMetricsDisabled.php new file mode 100644 index 00000000..cf332e13 --- /dev/null +++ b/tests/End2End/App/KernelWithMetricsDisabled.php @@ -0,0 +1,18 @@ +load(__DIR__ . '/metrics.yml'); + $loader->load(__DIR__ . '/metrics_disabled.yml'); + } +} diff --git a/tests/End2End/App/metrics_disabled.yml b/tests/End2End/App/metrics_disabled.yml new file mode 100644 index 00000000..d0217c6f --- /dev/null +++ b/tests/End2End/App/metrics_disabled.yml @@ -0,0 +1,3 @@ +sentry: + options: + enable_metrics: false diff --git a/tests/End2End/App/metrics_with_callback.yml b/tests/End2End/App/metrics_with_callback.yml new file mode 100644 index 00000000..e2085ea1 --- /dev/null +++ b/tests/End2End/App/metrics_with_callback.yml @@ -0,0 +1,12 @@ +sentry: + options: + before_send_metric: 'sentry.callback.before_send_metric' + +services: + Sentry\SentryBundle\Tests\End2End\Fixtures\SentryCallbacks: + class: 'Sentry\SentryBundle\Tests\End2End\Fixtures\SentryCallbacks' + + sentry.callback.before_send_metric: + class: 'Sentry\SentryBundle\Tests\End2End\Fixtures\SentryCallbacks' + factory: ['@Sentry\SentryBundle\Tests\End2End\Fixtures\SentryCallbacks', 'getBeforeSendMetric'] + diff --git a/tests/End2End/Fixtures/SentryCallbacks.php b/tests/End2End/Fixtures/SentryCallbacks.php new file mode 100644 index 00000000..ae15b920 --- /dev/null +++ b/tests/End2End/Fixtures/SentryCallbacks.php @@ -0,0 +1,20 @@ +getName() === 'test-counter') { + return null; + } + return $metric; + }; + } + +} diff --git a/tests/End2End/MetricsDisabledEnd2EndTest.php b/tests/End2End/MetricsDisabledEnd2EndTest.php new file mode 100644 index 00000000..34b15350 --- /dev/null +++ b/tests/End2End/MetricsDisabledEnd2EndTest.php @@ -0,0 +1,34 @@ +request('GET', '/metrics'); + $this->assertSame(200, $client->getResponse()->getStatusCode()); + + $this->assertEmpty(StubTransport::$events); + } +} diff --git a/tests/End2End/MetricsWithCallbackEnd2EndTest.php b/tests/End2End/MetricsWithCallbackEnd2EndTest.php new file mode 100644 index 00000000..3642c9e2 --- /dev/null +++ b/tests/End2End/MetricsWithCallbackEnd2EndTest.php @@ -0,0 +1,46 @@ +request('GET', '/metrics'); + $this->assertSame(200, $client->getResponse()->getStatusCode()); + + $this->assertCount(1, StubTransport::$events); + + $metrics = StubTransport::$events[0]->getMetrics(); + $this->assertCount(2, $metrics); + + // Counter metric removed by `before_send_metric` + $gauge = $metrics[0]; + $this->assertSame('test-gauge', $gauge->getName()); + $this->assertSame(20.51, $gauge->getValue()); + + $distribution = $metrics[1]; + $this->assertSame('test-distribution', $distribution->getName()); + $this->assertSame(100.81, $distribution->getValue()); + } +} From 7f3dc174fb8d17ecc8bce24b94cba8fb0ef6be07 Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Tue, 2 Dec 2025 17:28:04 +0100 Subject: [PATCH 2/4] test fixes --- tests/End2End/MetricsCommandTest.php | 2 +- tests/End2End/MetricsEnd2EndTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/End2End/MetricsCommandTest.php b/tests/End2End/MetricsCommandTest.php index a86a0f13..895fcbcc 100644 --- a/tests/End2End/MetricsCommandTest.php +++ b/tests/End2End/MetricsCommandTest.php @@ -42,7 +42,7 @@ public function testMetricsInCommand(): void $count = $metrics[0]; $this->assertSame('test-counter', $count->getName()); - $this->assertSame(10.0, $count->getValue()); + $this->assertSame(10, $count->getValue()); $gauge = $metrics[1]; $this->assertSame('test-gauge', $gauge->getName()); diff --git a/tests/End2End/MetricsEnd2EndTest.php b/tests/End2End/MetricsEnd2EndTest.php index d206bf1c..092ee66a 100644 --- a/tests/End2End/MetricsEnd2EndTest.php +++ b/tests/End2End/MetricsEnd2EndTest.php @@ -35,7 +35,7 @@ public function testMetricsAreFlushedAfterRequest(): void $count = $metrics[0]; $this->assertSame('test-counter', $count->getName()); - $this->assertSame(10.0, $count->getValue()); + $this->assertSame(10, $count->getValue()); $gauge = $metrics[1]; $this->assertSame('test-gauge', $gauge->getName()); From bc2c172fed8897c6297490932ef78d4ef3c0a9b4 Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Tue, 2 Dec 2025 21:01:38 +0100 Subject: [PATCH 3/4] PHPStan + CS --- phpstan-baseline.neon | 22 +++++-------------- src/DependencyInjection/SentryExtension.php | 6 ++--- .../SentryExtensionTest.php | 2 +- tests/End2End/Fixtures/SentryCallbacks.php | 7 +++--- 4 files changed, 14 insertions(+), 23 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6770bc6c..d8b1b153 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -90,6 +90,11 @@ parameters: count: 1 path: src/DependencyInjection/SentryExtension.php + - + message: "#^Cannot access offset 'before_send_metric' on mixed\\.$#" + count: 1 + path: src/DependencyInjection/SentryExtension.php + - message: "#^Parameter \\#1 \\$array of function array_filter expects array, mixed given\\.$#" count: 1 @@ -102,7 +107,7 @@ parameters: - message: "#^Parameter \\#1 \\$id of class Symfony\\\\Component\\\\DependencyInjection\\\\Reference constructor expects string, mixed given\\.$#" - count: 11 + count: 12 path: src/DependencyInjection/SentryExtension.php - @@ -170,21 +175,6 @@ parameters: count: 1 path: src/EventListener/LoginListener.php - - - message: "#^Instanceof between Throwable and Symfony\\\\Component\\\\Messenger\\\\Exception\\\\DelayedMessageHandlingException will always evaluate to false\\.$#" - count: 1 - path: src/EventListener/MessengerListener.php - - - - message: "#^Instanceof between Throwable and Symfony\\\\Component\\\\Messenger\\\\Exception\\\\HandlerFailedException will always evaluate to false\\.$#" - count: 1 - path: src/EventListener/MessengerListener.php - - - - message: "#^Result of && is always false\\.$#" - count: 2 - path: src/EventListener/MessengerListener.php - - message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\KernelEvent\\:\\:isMasterRequest\\(\\)\\.$#" count: 1 diff --git a/src/DependencyInjection/SentryExtension.php b/src/DependencyInjection/SentryExtension.php index 8c7ee7ed..cd5258ab 100644 --- a/src/DependencyInjection/SentryExtension.php +++ b/src/DependencyInjection/SentryExtension.php @@ -320,7 +320,7 @@ private function registerHttpClientTracingConfiguration(ContainerBuilder $contai } /** - * @param string[] $integrations + * @param string[] $integrations * @param array $config * * @return array @@ -352,12 +352,12 @@ private function configureRequestIntegration(array $integrations, bool $useDefau /** * @param class-string $integrationClass - * @param array $integrations + * @param array $integrations */ private function isIntegrationEnabled(string $integrationClass, array $integrations): bool { foreach ($integrations as $integration) { - if ($integration instanceof Reference && $integrationClass === (string)$integration) { + if ($integration instanceof Reference && $integrationClass === (string) $integration) { return true; } diff --git a/tests/DependencyInjection/SentryExtensionTest.php b/tests/DependencyInjection/SentryExtensionTest.php index 5d7b0a5f..fc252f22 100644 --- a/tests/DependencyInjection/SentryExtensionTest.php +++ b/tests/DependencyInjection/SentryExtensionTest.php @@ -499,7 +499,7 @@ private function createContainerFromFixture(string $fixtureFile): ContainerBuild /** * @param array $methodCall - * @param mixed[] $arguments + * @param mixed[] $arguments */ private function assertDefinitionMethodCallAt(array $methodCall, string $method, array $arguments): void { diff --git a/tests/End2End/Fixtures/SentryCallbacks.php b/tests/End2End/Fixtures/SentryCallbacks.php index ae15b920..cc70e802 100644 --- a/tests/End2End/Fixtures/SentryCallbacks.php +++ b/tests/End2End/Fixtures/SentryCallbacks.php @@ -1,20 +1,21 @@ getName() === 'test-counter') { + if ('test-counter' === $metric->getName()) { return null; } + return $metric; }; } - } From 23d742d338fd4e9e866c79f9ce71fe000da7d238 Mon Sep 17 00:00:00 2001 From: Martin Linzmayer Date: Tue, 2 Dec 2025 21:10:08 +0100 Subject: [PATCH 4/4] phpstan --- phpstan-baseline.neon | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index d8b1b153..8f9578c7 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -175,6 +175,21 @@ parameters: count: 1 path: src/EventListener/LoginListener.php + - + message: "#^Instanceof between Throwable and Symfony\\\\Component\\\\Messenger\\\\Exception\\\\DelayedMessageHandlingException will always evaluate to false\\.$#" + count: 1 + path: src/EventListener/MessengerListener.php + + - + message: "#^Instanceof between Throwable and Symfony\\\\Component\\\\Messenger\\\\Exception\\\\HandlerFailedException will always evaluate to false\\.$#" + count: 1 + path: src/EventListener/MessengerListener.php + + - + message: "#^Result of && is always false\\.$#" + count: 2 + path: src/EventListener/MessengerListener.php + - message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\KernelEvent\\:\\:isMasterRequest\\(\\)\\.$#" count: 1