Skip to content

Commit 5cbc122

Browse files
authored
Merge pull request #696 from getsentry/develop
Prepare 4.6.0
2 parents 6c852e3 + 5ad2ad2 commit 5cbc122

26 files changed

+558
-52
lines changed

CHANGELOG.md

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,85 @@
11
# Changelog
22

3-
## Unreleased
3+
## 4.6.0
4+
5+
The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v4.6.0.
6+
This release contains a colorful bouquet of new features.
7+
8+
### Features
9+
10+
- Report exceptions to Sentry as unhandled by default [(#674)](https://github.com/getsentry/sentry-symfony/pull/674)
11+
12+
All unhandled exceptions will now be marked as `handled: false`. You can query for such events on the issues list page,
13+
by searching for `error.handled:false`.
14+
15+
- Exceptions from messages which will be retried are sent to Sentry as handled [(#674)](https://github.com/getsentry/sentry-symfony/pull/674)
16+
17+
All unhandled exceptions happening on the message bus will now be unpacked and reported individually.
18+
The `WorkerMessageFailedEvent::willRetry` property is used to determine the `handled` value of the event sent to Sentry.
19+
20+
- Add `register_error_handler` config option [(#687)](https://github.com/getsentry/sentry-symfony/pull/687)
21+
22+
With this option, you can disable the global error and exception handlers of the base PHP SDK.
23+
If disabled, only events logged by Monolog will be sent to Sentry.
24+
25+
```yaml
26+
sentry:
27+
dsn: '%env(SENTRY_DSN)%'
28+
register_error_listener: false
29+
register_error_handler: false
30+
31+
monolog:
32+
handlers:
33+
sentry:
34+
type: sentry
35+
level: !php/const Monolog\Logger::ERROR
36+
hub_id: Sentry\State\HubInterface
37+
```
38+
39+
- Add `before_send_transaction` [(#691)](https://github.com/getsentry/sentry-symfony/pull/691)
40+
41+
Similar to `before_send`, you can now apply additional logic for `transaction` events.
42+
You can mutate the `transaction` event before it is sent to Sentry. If your callback returns `null`,
43+
the event is dropped.
44+
45+
```yaml
46+
sentry:
47+
options:
48+
before_send_transaction: 'sentry.callback.before_send_transaction'
49+
50+
services:
51+
sentry.callback.before_send_transaction:
52+
class: 'App\Service\Sentry'
53+
factory: [ '@App\Service\Sentry', 'getBeforeSendTransaction' ]
54+
```
55+
56+
```php
57+
<?php
58+
59+
namespace App\Service;
60+
61+
class Sentry
62+
{
63+
public function getBeforeSendTransaction(): callable
64+
{
65+
return function (\Sentry\Event $event): ?\Sentry\Event {
66+
return $event;
67+
};
68+
}
69+
}
70+
```
71+
72+
- Use the `_route` attribute as the transaction name [(#692)](https://github.com/getsentry/sentry-symfony/pull/692)
73+
74+
If you're using named routes, the SDK will default to use this attribute as the transaction name.
75+
With this change, you should be able to see a full list of your transactions on the performance page,
76+
instead of `<< unparameterized >>`.
77+
78+
You may need to update your starred transactions as well as your dashboards due to this change.
79+
80+
### Bug Fixes
81+
82+
- Sanatize HTTP client spans [(#690)](https://github.com/getsentry/sentry-symfony/pull/690)
483

584
## 4.5.0 (2022-11-28)
685

CONTRIBUTING.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ If you feel that you can fix or implement it yourself, please read on to learn h
1919
- Add tests for your changes to `tests/`.
2020
- Run tests and make sure all of them pass.
2121
- Submit a pull request, referencing any issues it addresses.
22-
- Make sure to update the `CHANGELOG.md` file below the `Unreleased` heading.
2322

2423
We will review your pull request as soon as possible.
2524
Thank you for contributing!

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"php": "^7.2||^8.0",
2727
"jean85/pretty-package-versions": "^1.5 || ^2.0",
2828
"sentry/sdk": "^3.3",
29+
"sentry/sentry": "^3.12",
2930
"symfony/cache-contracts": "^1.1||^2.4||^3.0",
3031
"symfony/config": "^4.4.20||^5.0.11||^6.0",
3132
"symfony/console": "^4.4.20||^5.0.11||^6.0",
@@ -97,7 +98,7 @@
9798
},
9899
"extra": {
99100
"branch-alias": {
100-
"dev-master": "4.4.x-dev",
101+
"dev-master": "4.6.x-dev",
101102
"releases/3.2.x": "3.2.x-dev",
102103
"releases/2.x": "2.x-dev",
103104
"releases/1.x": "1.x-dev"

phpstan-baseline.neon

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ parameters:
2020
count: 3
2121
path: src/DependencyInjection/SentryExtension.php
2222

23+
-
24+
message: "#^Cannot access offset 'before_send…' on mixed\\.$#"
25+
count: 3
26+
path: src/DependencyInjection/SentryExtension.php
27+
2328
-
2429
message: "#^Cannot access offset 'class_serializers' on mixed\\.$#"
2530
count: 3
@@ -52,7 +57,7 @@ parameters:
5257

5358
-
5459
message: "#^Cannot access offset 'integrations' on mixed\\.$#"
55-
count: 3
60+
count: 2
5661
path: src/DependencyInjection/SentryExtension.php
5762

5863
-
@@ -77,7 +82,7 @@ parameters:
7782

7883
-
7984
message: "#^Parameter \\#1 \\$id of class Symfony\\\\Component\\\\DependencyInjection\\\\Reference constructor expects string, mixed given\\.$#"
80-
count: 5
85+
count: 6
8186
path: src/DependencyInjection/SentryExtension.php
8287

8388
-
@@ -280,11 +285,6 @@ parameters:
280285
count: 1
281286
path: tests/DependencyInjection/SentryExtensionTest.php
282287

283-
-
284-
message: "#^Cannot access offset 'integrations' on mixed\\.$#"
285-
count: 1
286-
path: tests/DependencyInjection/SentryExtensionTest.php
287-
288288
-
289289
message: "#^Class Symfony\\\\Component\\\\Debug\\\\Exception\\\\FatalErrorException not found\\.$#"
290290
count: 1

src/DependencyInjection/Configuration.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public function getConfigTreeBuilder(): TreeBuilder
3737
->info('If this value is not provided, the SDK will try to read it from the SENTRY_DSN environment variable. If that variable also does not exist, the SDK will just not send any events.')
3838
->end()
3939
->booleanNode('register_error_listener')->defaultTrue()->end()
40+
->booleanNode('register_error_handler')->defaultTrue()->end()
4041
->scalarNode('logger')
4142
->info('The service ID of the PSR-3 logger used to log messages coming from the SDK client. Be aware that setting the same logger of the application may create a circular loop when an event fails to be sent.')
4243
->defaultNull()
@@ -91,6 +92,7 @@ public function getConfigTreeBuilder(): TreeBuilder
9192
->end()
9293
->scalarNode('server_name')->end()
9394
->scalarNode('before_send')->end()
95+
->scalarNode('before_send_transaction')->end()
9496
->arrayNode('tags')
9597
->useAttributeAsKey('name')
9698
->normalizeKeys(false)

src/DependencyInjection/SentryExtension.php

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Sentry\SentryBundle\EventListener\TracingConsoleListener;
2020
use Sentry\SentryBundle\EventListener\TracingRequestListener;
2121
use Sentry\SentryBundle\EventListener\TracingSubRequestListener;
22+
use Sentry\SentryBundle\Integration\IntegrationConfigurator;
2223
use Sentry\SentryBundle\SentryBundle;
2324
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\ConnectionConfigurator;
2425
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverMiddleware;
@@ -101,6 +102,10 @@ private function registerConfiguration(ContainerBuilder $container, array $confi
101102
$options['before_send'] = new Reference($options['before_send']);
102103
}
103104

105+
if (isset($options['before_send_transaction'])) {
106+
$options['before_send_transaction'] = new Reference($options['before_send_transaction']);
107+
}
108+
104109
if (isset($options['before_breadcrumb'])) {
105110
$options['before_breadcrumb'] = new Reference($options['before_breadcrumb']);
106111
}
@@ -111,9 +116,10 @@ private function registerConfiguration(ContainerBuilder $container, array $confi
111116
}, $options['class_serializers']);
112117
}
113118

114-
if (isset($options['integrations'])) {
115-
$options['integrations'] = $this->configureIntegrationsOption($options['integrations'], $config);
116-
}
119+
$container->getDefinition(IntegrationConfigurator::class)
120+
->setArgument(0, $this->configureIntegrationsOption($options['integrations'], $config))
121+
->setArgument(1, $config['register_error_handler']);
122+
$options['integrations'] = new Reference(IntegrationConfigurator::class);
117123

118124
$container
119125
->register('sentry.client.options', Options::class)

src/EventListener/ConsoleListener.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
namespace Sentry\SentryBundle\EventListener;
66

7+
use Sentry\Event;
8+
use Sentry\EventHint;
9+
use Sentry\ExceptionMechanism;
710
use Sentry\State\HubInterface;
811
use Sentry\State\Scope;
912
use Symfony\Component\Console\Event\ConsoleCommandEvent;
@@ -82,7 +85,12 @@ public function handleConsoleErrorEvent(ConsoleErrorEvent $event): void
8285
$scope->setTag('console.command.exit_code', (string) $event->getExitCode());
8386

8487
if ($this->captureErrors) {
85-
$this->hub->captureException($event->getError());
88+
$hint = EventHint::fromArray([
89+
'exception' => $event->getError(),
90+
'mechanism' => new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, false),
91+
]);
92+
93+
$this->hub->captureEvent(Event::createEvent(), $hint);
8694
}
8795
});
8896
}

src/EventListener/ErrorListener.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
namespace Sentry\SentryBundle\EventListener;
66

7+
use Sentry\Event;
8+
use Sentry\EventHint;
9+
use Sentry\ExceptionMechanism;
710
use Sentry\State\HubInterface;
811
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
912

@@ -34,6 +37,11 @@ public function __construct(HubInterface $hub)
3437
*/
3538
public function handleExceptionEvent(ExceptionEvent $event): void
3639
{
37-
$this->hub->captureException($event->getThrowable());
40+
$hint = EventHint::fromArray([
41+
'exception' => $event->getThrowable(),
42+
'mechanism' => new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, false),
43+
]);
44+
45+
$this->hub->captureEvent(Event::createEvent(), $hint);
3846
}
3947
}

src/EventListener/MessengerListener.php

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
namespace Sentry\SentryBundle\EventListener;
66

77
use Sentry\Event;
8+
use Sentry\EventHint;
9+
use Sentry\ExceptionMechanism;
810
use Sentry\State\HubInterface;
911
use Sentry\State\Scope;
1012
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
@@ -62,13 +64,7 @@ public function handleWorkerMessageFailedEvent(WorkerMessageFailedEvent $event):
6264
$scope->setTag('messenger.message_bus', $messageBusStamp->getBusName());
6365
}
6466

65-
if ($exception instanceof HandlerFailedException) {
66-
foreach ($exception->getNestedExceptions() as $nestedException) {
67-
$this->hub->captureException($nestedException);
68-
}
69-
} else {
70-
$this->hub->captureException($exception);
71-
}
67+
$this->captureException($exception, $event->willRetry());
7268
});
7369

7470
$this->flushClient();
@@ -86,6 +82,33 @@ public function handleWorkerMessageHandledEvent(WorkerMessageHandledEvent $event
8682
$this->flushClient();
8783
}
8884

85+
/**
86+
* Creates Sentry events from the given exception.
87+
*
88+
* Unpacks multiple exceptions wrapped in a HandlerFailedException and notifies
89+
* Sentry of each individual exception.
90+
*
91+
* If the message will be retried the exceptions will be marked as handled
92+
* in Sentry.
93+
*/
94+
private function captureException(\Throwable $exception, bool $willRetry): void
95+
{
96+
if ($exception instanceof HandlerFailedException) {
97+
foreach ($exception->getNestedExceptions() as $nestedException) {
98+
$this->captureException($nestedException, $willRetry);
99+
}
100+
101+
return;
102+
}
103+
104+
$hint = EventHint::fromArray([
105+
'exception' => $exception,
106+
'mechanism' => new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, $willRetry),
107+
]);
108+
109+
$this->hub->captureEvent(Event::createEvent(), $hint);
110+
}
111+
89112
private function flushClient(): void
90113
{
91114
$client = $this->hub->getClient();
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sentry\SentryBundle\Integration;
6+
7+
use Sentry\Integration\ErrorListenerIntegration;
8+
use Sentry\Integration\ExceptionListenerIntegration;
9+
use Sentry\Integration\FatalErrorListenerIntegration;
10+
use Sentry\Integration\IntegrationInterface;
11+
12+
/**
13+
* @internal
14+
*/
15+
final class IntegrationConfigurator
16+
{
17+
private const ERROR_HANDLER_INTEGRATIONS = [
18+
ErrorListenerIntegration::class => true,
19+
ExceptionListenerIntegration::class => true,
20+
FatalErrorListenerIntegration::class => true,
21+
];
22+
23+
/**
24+
* @var IntegrationInterface[]
25+
*/
26+
private $userIntegrations;
27+
28+
/**
29+
* @var bool
30+
*/
31+
private $registerErrorHandler;
32+
33+
/**
34+
* @param IntegrationInterface[] $userIntegrations
35+
*/
36+
public function __construct(array $userIntegrations, bool $registerErrorHandler)
37+
{
38+
$this->userIntegrations = $userIntegrations;
39+
$this->registerErrorHandler = $registerErrorHandler;
40+
}
41+
42+
/**
43+
* @see IntegrationRegistry::getIntegrationsToSetup()
44+
*
45+
* @param IntegrationInterface[] $defaultIntegrations
46+
*
47+
* @return IntegrationInterface[]
48+
*/
49+
public function __invoke(array $defaultIntegrations): array
50+
{
51+
$integrations = [];
52+
53+
$userIntegrationsClasses = array_map('get_class', $this->userIntegrations);
54+
$pickedIntegrationsClasses = [];
55+
56+
foreach ($defaultIntegrations as $defaultIntegration) {
57+
$integrationClassName = \get_class($defaultIntegration);
58+
59+
if (!$this->registerErrorHandler && isset(self::ERROR_HANDLER_INTEGRATIONS[$integrationClassName])) {
60+
continue;
61+
}
62+
63+
if (!\in_array($integrationClassName, $userIntegrationsClasses, true) && !isset($pickedIntegrationsClasses[$integrationClassName])) {
64+
$integrations[] = $defaultIntegration;
65+
$pickedIntegrationsClasses[$integrationClassName] = true;
66+
}
67+
}
68+
69+
foreach ($this->userIntegrations as $userIntegration) {
70+
$integrationClassName = \get_class($userIntegration);
71+
72+
if (!isset($pickedIntegrationsClasses[$integrationClassName])) {
73+
$integrations[] = $userIntegration;
74+
$pickedIntegrationsClasses[$integrationClassName] = true;
75+
}
76+
}
77+
78+
return $integrations;
79+
}
80+
}

0 commit comments

Comments
 (0)