Skip to content

Commit 2845df5

Browse files
authored
Merge pull request #204 from getsentry/rework-to-use-only-listeners
Rework to use only listeners
2 parents 8ab05c4 + 049e64a commit 2845df5

File tree

12 files changed

+181
-21
lines changed

12 files changed

+181
-21
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
66

77
## [Unreleased]
88

9+
## 3.0.0-beta2 - 2019-03-22
10+
- Disable Sentry's ErrorHandler, and report all errors using Symfony's events (#204)
11+
912
## 3.0.0-beta1 - 2019-03-06
1013
The 3.0 major release has multiple breaking changes. The most notable one is the upgrade to the 2.0 base SDK client.
1114
Refer to the [UPGRADE-3.0.md](https://github.com/getsentry/sentry-symfony/blob/master/UPGRADE-3.0.md) document for a

README.md

+12-13
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ Symfony integration for [Sentry](https://getsentry.com/).
1111

1212
## Notice 3.0
1313
> The current master branch contains the 3.0 version of this bundle, which is currently under development. This version
14-
> will support the newest 2.0 version of the underlying Sentry SDK version.
15-
>
16-
> A beta version will be tagged as soon as possible, in the meantime you can continue to use the previous versions.
14+
> will support the newest 2.0 version of the underlying Sentry SDK version. A beta version is already available.
1715
>
1816
> To know more about the progress of this version see [the relative milestone](https://github.com/getsentry/sentry-symfony/milestone/3)
1917
@@ -22,22 +20,20 @@ Symfony integration for [Sentry](https://getsentry.com/).
2220
Use sentry-symfony for:
2321

2422
* A fast sentry setup
25-
* Access to the `sentry.client` through the container
23+
* Easy configuration in your Symfony app
2624
* Automatic wiring in your app. Each event has the following things added automatically to it:
2725
- user
2826
- Symfony environment
2927
- app path
30-
- hostname
3128
- excluded paths (cache and vendor)
3229

33-
3430
## Installation
3531

3632
### Step 1: Download the Bundle
3733
You can install this bundle using Composer:
3834

3935
```bash
40-
composer require sentry/sentry-symfony:^3.0
36+
composer require sentry/sentry-symfony:^3.0-beta2
4137
```
4238

4339
#### Optional: use custom HTTP factory/transport
@@ -87,12 +83,13 @@ class AppKernel extends Kernel
8783
// ...
8884
}
8985
```
90-
Note that, unlike before in version 3, the bundle will be enabled in all environments.
86+
Note that, unlike before in version 3, the bundle will be enabled in all environments; event reporting, instead, is enabled
87+
only when providing a DSN (see the next step).
9188

9289
### Step 3: Configure the SDK
9390

94-
Add your [Sentry DSN](https://docs.sentry.io/quickstart/#configure-the-dsn) value of your project to ``app/config/config.yml``.
95-
Leaving this value empty will effectively disable Sentry reporting.
91+
Add your [Sentry DSN](https://docs.sentry.io/quickstart/#configure-the-dsn) value of your project to ``app/config/config_prod.yml``.
92+
Leaving this value empty (or undeclared) in other environments will effectively disable Sentry reporting.
9693

9794
```yaml
9895
sentry:
@@ -112,9 +109,11 @@ TODO
112109
## Customization
113110
114111
The Sentry 2.0 SDK uses the Unified API, hence it uses the concept of `Scope`s to hold information about the current
115-
state of the app, and attach it to any event that is reported. This bundle has two listeners (`RequestListener` and
116-
`ConsoleListener`) that adds some easy default information. Those listeners normally are executed with a priority of `1`
117-
to allow easier customization with custom listener, that by default run with a lower priority of `0`.
112+
state of the app, and attach it to any event that is reported. This bundle has three listeners (`RequestListener`,
113+
`SubRequestListener` and `ConsoleListener`) that adds some easy default information.
114+
115+
Those listeners normally are executed with a priority of `1` to allow easier customization with custom listener, that by
116+
default run with a lower priority of `0`.
118117

119118
Those listeners are `final` so not extendable, but you can look at those to know how to add more information to the
120119
current `Scope` and enrich you Sentry events.

UPGRADE-3.0.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ changed:
3131

3232
* All services are now private; declare public aliases to access them if needed; you can still use the Sentry SDK global
3333
functions if you want just to capture messages manually without injecting Sentry services in your code
34-
* All services uses the full qualified name of the interfaces to name them
35-
* The `ExceptionListener` has been splitted in two: `RequestListener` and `ConsoleListener`
34+
* All services uses the full qualified name of their interfaces to name them
35+
* The `ExceptionListener` has been splitted and renamed: we now have a simpler `ErrorListener`, and three other listeners
36+
dedicated to enriching events of data (`RequestListener`, `SubRequestListener` and `ConsoleListener`)
3637
* The listeners are now `final`; append your own listeners to override their behavior
3738
* The listeners are registered with a priority of `1`; they will run just before the default priority of `0`, to ease
3839
the registration of custom listener that will change `Scope` data
3940
* Configuration options of the bundle are now aligned with the new ones of the 2.0 SDK
40-
* Listeners are no longer used to capture exceptions, it's all demanded to the error handler
4141

4242
## New services
4343
This is a brief description of the services registered by this bundle:
@@ -47,7 +47,7 @@ This is a brief description of the services registered by this bundle:
4747
* `Sentry\ClientInterface`: this is the proper client; compared to the 1.x SDK version it's a lot more slimmed down,
4848
since a lot of the stuff has been splitted in separated components, so you probably will not interact with it as much as
4949
in the past. You also have to remind you that the client is bound to the `Hub`, and has to be changed there if you want
50-
to use it automatically in error reporting
50+
to use a different one automatically in error reporting
5151
* `Sentry\ClientBuilderInterface`: this is the factory that builds the client; you can call its methods to change all
5252
the settings and dependencies that will be injected in the latter created client. You can use this service to obtain more
5353
customized clients for your needs

phpstan.neon

+3
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ parameters:
66
ignoreErrors:
77
- "/Call to function method_exists.. with 'Symfony.+' and 'getProjectDir' will always evaluate to false./"
88
- "/Call to function method_exists.. with 'Symfony.+' and 'getRootNode' will always evaluate to false./"
9+
- '/Parameter \$.+ of method Sentry\\SentryBundle\\EventListener\\ErrorListener::onConsoleException\(\) has invalid typehint type Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent./'
10+
- '/Call to method getException\(\) on an unknown class Symfony\\Component\\Console\\Event\\ConsoleExceptionEvent./'
11+
- '/Access to undefined constant Symfony\\Component\\Console\\ConsoleEvents::EXCEPTION./'
912

1013
includes:
1114
- vendor/jangregor/phpstan-prophecy/src/extension.neon
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Sentry\SentryBundle\DependencyInjection;
4+
5+
use Sentry\ClientBuilderInterface;
6+
use Sentry\Integration\ErrorListenerIntegration;
7+
use Sentry\Integration\ExceptionListenerIntegration;
8+
use Sentry\Integration\IntegrationInterface;
9+
use Sentry\SentryBundle\SentryBundle;
10+
11+
class ClientBuilderConfigurator
12+
{
13+
public static function configure(ClientBuilderInterface $clientBuilder): void
14+
{
15+
$clientBuilder->setSdkIdentifier(SentryBundle::SDK_IDENTIFIER);
16+
$clientBuilder->setSdkVersion(SentryBundle::getSdkVersion());
17+
18+
$options = $clientBuilder->getOptions();
19+
if (! $options->hasDefaultIntegrations()) {
20+
return;
21+
}
22+
23+
$integrations = $options->getIntegrations();
24+
$options->setIntegrations(array_filter($integrations, static function (IntegrationInterface $integration): bool {
25+
if ($integration instanceof ErrorListenerIntegration) {
26+
return false;
27+
}
28+
29+
if ($integration instanceof ExceptionListenerIntegration) {
30+
return false;
31+
}
32+
33+
return true;
34+
}));
35+
}
36+
}

src/DependencyInjection/Configuration.php

+4
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ public function getConfigTreeBuilder()
120120
->defaultValue(1);
121121
$listenerPriorities->scalarNode('console')
122122
->defaultValue(1);
123+
$listenerPriorities->scalarNode('request_error')
124+
->defaultValue(128);
125+
$listenerPriorities->scalarNode('console_error')
126+
->defaultValue(128);
123127

124128
return $treeBuilder;
125129
}

src/DependencyInjection/SentryExtension.php

+29-3
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
use Sentry\ClientBuilderInterface;
66
use Sentry\Options;
77
use Sentry\SentryBundle\ErrorTypesParser;
8-
use Sentry\SentryBundle\SentryBundle;
8+
use Sentry\SentryBundle\EventListener\ErrorListener;
99
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
1010
use Symfony\Component\Config\FileLocator;
11+
use Symfony\Component\Console\ConsoleEvents;
1112
use Symfony\Component\DependencyInjection\ContainerBuilder;
1213
use Symfony\Component\DependencyInjection\Definition;
1314
use Symfony\Component\DependencyInjection\Loader;
@@ -36,12 +37,13 @@ public function load(array $configs, ContainerBuilder $container)
3637
$this->passConfigurationToOptions($container, $processedConfiguration);
3738

3839
$container->getDefinition(ClientBuilderInterface::class)
39-
->addMethodCall('setSdkIdentifier', [SentryBundle::SDK_IDENTIFIER])
40-
->addMethodCall('setSdkVersion', [SentryBundle::getSdkVersion()]);
40+
->setConfigurator([ClientBuilderConfigurator::class, 'configure']);
4141

4242
foreach ($processedConfiguration['listener_priorities'] as $key => $priority) {
4343
$container->setParameter('sentry.listener_priorities.' . $key, $priority);
4444
}
45+
46+
$this->tagConsoleErrorListener($container);
4547
}
4648

4749
private function passConfigurationToOptions(ContainerBuilder $container, array $processedConfiguration): void
@@ -121,4 +123,28 @@ private function mapCallableValue(Definition $options, string $method, $optionVa
121123

122124
$options->addMethodCall($method, [$beforeSend]);
123125
}
126+
127+
/**
128+
* BC layer for Symfony < 3.3; see https://symfony.com/blog/new-in-symfony-3-3-better-handling-of-command-exceptions
129+
*/
130+
private function tagConsoleErrorListener(ContainerBuilder $container): void
131+
{
132+
$listener = $container->getDefinition(ErrorListener::class);
133+
134+
if (class_exists('Symfony\Component\Console\Event\ConsoleErrorEvent')) {
135+
$tagAttributes = [
136+
'event' => ConsoleEvents::ERROR,
137+
'method' => 'onConsoleError',
138+
'priority' => '%sentry.listener_priorities.console_error%',
139+
];
140+
} else {
141+
$tagAttributes = [
142+
'event' => ConsoleEvents::EXCEPTION,
143+
'method' => 'onConsoleException',
144+
'priority' => '%sentry.listener_priorities.console_error%',
145+
];
146+
}
147+
148+
$listener->addTag('kernel.event_listener', $tagAttributes);
149+
}
124150
}

src/EventListener/ErrorListener.php

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Sentry\SentryBundle\EventListener;
4+
5+
use Symfony\Component\Console\Event\ConsoleErrorEvent;
6+
use Symfony\Component\Console\Event\ConsoleExceptionEvent;
7+
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
8+
9+
final class ErrorListener
10+
{
11+
public function onKernelException(GetResponseForExceptionEvent $event): void
12+
{
13+
\Sentry\captureException($event->getException());
14+
}
15+
16+
public function onConsoleError(ConsoleErrorEvent $event): void
17+
{
18+
\Sentry\captureException($event->getError());
19+
}
20+
21+
/**
22+
* BC layer for Symfony < 3.3; see https://symfony.com/blog/new-in-symfony-3-3-better-handling-of-command-exceptions
23+
*/
24+
public function onConsoleException(ConsoleExceptionEvent $event): void
25+
{
26+
\Sentry\captureException($event->getException());
27+
}
28+
}

src/Resources/config/services.xml

+6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030
<tag name="kernel.event_listener" event="console.command" method="onConsoleCommand" priority="%sentry.listener_priorities.console%" />
3131
</service>
3232

33+
<service id="Sentry\SentryBundle\EventListener\ErrorListener" class="Sentry\SentryBundle\EventListener\ErrorListener" public="false">
34+
<tag name="kernel.event_listener" event="kernel.exception" method="onKernelException" priority="%sentry.listener_priorities.request_error%" />
35+
<!-- The following tag is done manually in PHP for BC with Symfony < 3.3 -->
36+
<!-- <tag name="kernel.event_listener" event="console.error" method="onConsoleError" priority="%sentry.listener_priorities.console_error%" />-->
37+
</service>
38+
3339
<service id="Sentry\SentryBundle\EventListener\RequestListener" class="Sentry\SentryBundle\EventListener\RequestListener" public="false">
3440
<argument type="service" id="Sentry\State\HubInterface" />
3541
<argument type="service" id="security.token_storage" on-invalid="ignore" />

test/DependencyInjection/ConfigurationTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public function testConfigurationDefaults(): void
5454
'request' => 1,
5555
'sub_request' => 1,
5656
'console' => 1,
57+
'request_error' => 128,
58+
'console_error' => 128,
5759
],
5860
'options' => [
5961
'environment' => '%kernel.environment%',

test/EventListener/ConsoleListenerTest.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Sentry\SentryBundle\Test\EventListener;
44

5+
use PHPUnit\Framework\TestCase;
56
use Prophecy\Argument;
67
use Sentry\SentryBundle\EventListener\ConsoleListener;
78
use Sentry\State\Hub;
@@ -10,7 +11,7 @@
1011
use Symfony\Component\Console\Command\Command;
1112
use Symfony\Component\Console\Event\ConsoleCommandEvent;
1213

13-
class ConsoleListenerTest extends \PHPUnit\Framework\TestCase
14+
class ConsoleListenerTest extends TestCase
1415
{
1516
private $currentHub;
1617
private $currentScope;

test/SentryBundleTest.php

+52
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@
33
namespace Sentry\SentryBundle\Test;
44

55
use PHPUnit\Framework\TestCase;
6+
use Sentry\Integration\ErrorListenerIntegration;
7+
use Sentry\Integration\ExceptionListenerIntegration;
8+
use Sentry\Integration\IntegrationInterface;
9+
use Sentry\Integration\RequestIntegration;
610
use Sentry\SentryBundle\DependencyInjection\SentryExtension;
711
use Sentry\SentryBundle\EventListener\ConsoleListener;
12+
use Sentry\SentryBundle\EventListener\ErrorListener;
813
use Sentry\SentryBundle\EventListener\RequestListener;
914
use Sentry\SentryBundle\EventListener\SubRequestListener;
15+
use Sentry\State\HubInterface;
1016
use Symfony\Component\Console\ConsoleEvents;
1117
use Symfony\Component\DependencyInjection\ContainerBuilder;
1218
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -82,10 +88,56 @@ public function testContainerHasSubRequestListenerConfiguredCorrectly(): void
8288
$this->assertSame($expectedTag, $consoleListener->getTags());
8389
}
8490

91+
public function testContainerHasErrorListenerConfiguredCorrectly(): void
92+
{
93+
$container = $this->getContainer();
94+
95+
$consoleListener = $container->getDefinition(ErrorListener::class);
96+
97+
$expectedTag = [
98+
'kernel.event_listener' => [
99+
[
100+
'event' => KernelEvents::EXCEPTION,
101+
'method' => 'onKernelException',
102+
'priority' => '%sentry.listener_priorities.request_error%',
103+
],
104+
],
105+
];
106+
107+
if (class_exists('Symfony\Component\Console\Event\ConsoleErrorEvent')) {
108+
$expectedTag['kernel.event_listener'][] = [
109+
'event' => ConsoleEvents::ERROR,
110+
'method' => 'onConsoleError',
111+
'priority' => '%sentry.listener_priorities.console_error%',
112+
];
113+
} else {
114+
$expectedTag['kernel.event_listener'][] = [
115+
'event' => ConsoleEvents::EXCEPTION,
116+
'method' => 'onConsoleException',
117+
'priority' => '%sentry.listener_priorities.console_error%',
118+
];
119+
}
120+
121+
$this->assertSame($expectedTag, $consoleListener->getTags());
122+
}
123+
124+
public function testIntegrationsListenersAreDisabledByDefault(): void
125+
{
126+
$container = $this->getContainer();
127+
128+
$hub = $container->get(HubInterface::class);
129+
130+
$this->assertInstanceOf(HubInterface::class, $hub);
131+
$this->assertInstanceOf(IntegrationInterface::class, $hub->getIntegration(RequestIntegration::class));
132+
$this->assertNull($hub->getIntegration(ErrorListenerIntegration::class));
133+
$this->assertNull($hub->getIntegration(ExceptionListenerIntegration::class));
134+
}
135+
85136
private function getContainer(array $configuration = []): ContainerBuilder
86137
{
87138
$containerBuilder = new ContainerBuilder();
88139
$containerBuilder->setParameter('kernel.root_dir', 'kernel/root');
140+
$containerBuilder->setParameter('kernel.cache_dir', 'var/cache');
89141
if (method_exists(Kernel::class, 'getProjectDir')) {
90142
$containerBuilder->setParameter('kernel.project_dir', '/dir/project/root');
91143
}

0 commit comments

Comments
 (0)