Skip to content

Prepare 4.6.0 #696

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Feb 14, 2023
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
81 changes: 80 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,85 @@
# Changelog

## Unreleased
## 4.6.0

The Sentry SDK team is happy to announce the immediate availability of Sentry Symfony SDK v4.6.0.
This release contains a colorful bouquet of new features.

### Features

- Report exceptions to Sentry as unhandled by default [(#674)](https://github.com/getsentry/sentry-symfony/pull/674)

All unhandled exceptions will now be marked as `handled: false`. You can query for such events on the issues list page,
by searching for `error.handled:false`.

- Exceptions from messages which will be retried are sent to Sentry as handled [(#674)](https://github.com/getsentry/sentry-symfony/pull/674)

All unhandled exceptions happening on the message bus will now be unpacked and reported individually.
The `WorkerMessageFailedEvent::willRetry` property is used to determine the `handled` value of the event sent to Sentry.

- Add `register_error_handler` config option [(#687)](https://github.com/getsentry/sentry-symfony/pull/687)

With this option, you can disable the global error and exception handlers of the base PHP SDK.
If disabled, only events logged by Monolog will be sent to Sentry.

```yaml
sentry:
dsn: '%env(SENTRY_DSN)%'
register_error_listener: false
register_error_handler: false

monolog:
handlers:
sentry:
type: sentry
level: !php/const Monolog\Logger::ERROR
hub_id: Sentry\State\HubInterface
```

- Add `before_send_transaction` [(#691)](https://github.com/getsentry/sentry-symfony/pull/691)

Similar to `before_send`, you can now apply additional logic for `transaction` events.
You can mutate the `transaction` event before it is sent to Sentry. If your callback returns `null`,
the event is dropped.

```yaml
sentry:
options:
before_send_transaction: 'sentry.callback.before_send_transaction'

services:
sentry.callback.before_send_transaction:
class: 'App\Service\Sentry'
factory: [ '@App\Service\Sentry', 'getBeforeSendTransaction' ]
```

```php
<?php

namespace App\Service;

class Sentry
{
public function getBeforeSendTransaction(): callable
{
return function (\Sentry\Event $event): ?\Sentry\Event {
return $event;
};
}
}
```

- Use the `_route` attribute as the transaction name [(#692)](https://github.com/getsentry/sentry-symfony/pull/692)

If you're using named routes, the SDK will default to use this attribute as the transaction name.
With this change, you should be able to see a full list of your transactions on the performance page,
instead of `<< unparameterized >>`.

You may need to update your starred transactions as well as your dashboards due to this change.

### Bug Fixes

- Sanatize HTTP client spans [(#690)](https://github.com/getsentry/sentry-symfony/pull/690)

## 4.5.0 (2022-11-28)

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

We will review your pull request as soon as possible.
Thank you for contributing!
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"php": "^7.2||^8.0",
"jean85/pretty-package-versions": "^1.5 || ^2.0",
"sentry/sdk": "^3.3",
"sentry/sentry": "^3.12",
"symfony/cache-contracts": "^1.1||^2.4||^3.0",
"symfony/config": "^4.4.20||^5.0.11||^6.0",
"symfony/console": "^4.4.20||^5.0.11||^6.0",
Expand Down Expand Up @@ -97,7 +98,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "4.4.x-dev",
"dev-master": "4.6.x-dev",
"releases/3.2.x": "3.2.x-dev",
"releases/2.x": "2.x-dev",
"releases/1.x": "1.x-dev"
Expand Down
14 changes: 7 additions & 7 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ parameters:
count: 3
path: src/DependencyInjection/SentryExtension.php

-
message: "#^Cannot access offset 'before_send…' on mixed\\.$#"
count: 3
path: src/DependencyInjection/SentryExtension.php

-
message: "#^Cannot access offset 'class_serializers' on mixed\\.$#"
count: 3
Expand Down Expand Up @@ -52,7 +57,7 @@ parameters:

-
message: "#^Cannot access offset 'integrations' on mixed\\.$#"
count: 3
count: 2
path: src/DependencyInjection/SentryExtension.php

-
Expand All @@ -77,7 +82,7 @@ parameters:

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

-
Expand Down Expand Up @@ -280,11 +285,6 @@ parameters:
count: 1
path: tests/DependencyInjection/SentryExtensionTest.php

-
message: "#^Cannot access offset 'integrations' on mixed\\.$#"
count: 1
path: tests/DependencyInjection/SentryExtensionTest.php

-
message: "#^Class Symfony\\\\Component\\\\Debug\\\\Exception\\\\FatalErrorException not found\\.$#"
count: 1
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->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.')
->end()
->booleanNode('register_error_listener')->defaultTrue()->end()
->booleanNode('register_error_handler')->defaultTrue()->end()
->scalarNode('logger')
->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.')
->defaultNull()
Expand Down Expand Up @@ -91,6 +92,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->scalarNode('server_name')->end()
->scalarNode('before_send')->end()
->scalarNode('before_send_transaction')->end()
->arrayNode('tags')
->useAttributeAsKey('name')
->normalizeKeys(false)
Expand Down
12 changes: 9 additions & 3 deletions src/DependencyInjection/SentryExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Sentry\SentryBundle\EventListener\TracingConsoleListener;
use Sentry\SentryBundle\EventListener\TracingRequestListener;
use Sentry\SentryBundle\EventListener\TracingSubRequestListener;
use Sentry\SentryBundle\Integration\IntegrationConfigurator;
use Sentry\SentryBundle\SentryBundle;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\ConnectionConfigurator;
use Sentry\SentryBundle\Tracing\Doctrine\DBAL\TracingDriverMiddleware;
Expand Down Expand Up @@ -101,6 +102,10 @@ private function registerConfiguration(ContainerBuilder $container, array $confi
$options['before_send'] = new Reference($options['before_send']);
}

if (isset($options['before_send_transaction'])) {
$options['before_send_transaction'] = new Reference($options['before_send_transaction']);
}

if (isset($options['before_breadcrumb'])) {
$options['before_breadcrumb'] = new Reference($options['before_breadcrumb']);
}
Expand All @@ -111,9 +116,10 @@ private function registerConfiguration(ContainerBuilder $container, array $confi
}, $options['class_serializers']);
}

if (isset($options['integrations'])) {
$options['integrations'] = $this->configureIntegrationsOption($options['integrations'], $config);
}
$container->getDefinition(IntegrationConfigurator::class)
->setArgument(0, $this->configureIntegrationsOption($options['integrations'], $config))
->setArgument(1, $config['register_error_handler']);
$options['integrations'] = new Reference(IntegrationConfigurator::class);

$container
->register('sentry.client.options', Options::class)
Expand Down
10 changes: 9 additions & 1 deletion src/EventListener/ConsoleListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace Sentry\SentryBundle\EventListener;

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

if ($this->captureErrors) {
$this->hub->captureException($event->getError());
$hint = EventHint::fromArray([
'exception' => $event->getError(),
'mechanism' => new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, false),
]);

$this->hub->captureEvent(Event::createEvent(), $hint);
}
});
}
Expand Down
10 changes: 9 additions & 1 deletion src/EventListener/ErrorListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace Sentry\SentryBundle\EventListener;

use Sentry\Event;
use Sentry\EventHint;
use Sentry\ExceptionMechanism;
use Sentry\State\HubInterface;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;

Expand Down Expand Up @@ -34,6 +37,11 @@ public function __construct(HubInterface $hub)
*/
public function handleExceptionEvent(ExceptionEvent $event): void
{
$this->hub->captureException($event->getThrowable());
$hint = EventHint::fromArray([
'exception' => $event->getThrowable(),
'mechanism' => new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, false),
]);

$this->hub->captureEvent(Event::createEvent(), $hint);
}
}
37 changes: 30 additions & 7 deletions src/EventListener/MessengerListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
namespace Sentry\SentryBundle\EventListener;

use Sentry\Event;
use Sentry\EventHint;
use Sentry\ExceptionMechanism;
use Sentry\State\HubInterface;
use Sentry\State\Scope;
use Symfony\Component\Messenger\Event\WorkerMessageFailedEvent;
Expand Down Expand Up @@ -62,13 +64,7 @@ public function handleWorkerMessageFailedEvent(WorkerMessageFailedEvent $event):
$scope->setTag('messenger.message_bus', $messageBusStamp->getBusName());
}

if ($exception instanceof HandlerFailedException) {
foreach ($exception->getNestedExceptions() as $nestedException) {
$this->hub->captureException($nestedException);
}
} else {
$this->hub->captureException($exception);
}
$this->captureException($exception, $event->willRetry());
});

$this->flushClient();
Expand All @@ -86,6 +82,33 @@ public function handleWorkerMessageHandledEvent(WorkerMessageHandledEvent $event
$this->flushClient();
}

/**
* Creates Sentry events from the given exception.
*
* Unpacks multiple exceptions wrapped in a HandlerFailedException and notifies
* Sentry of each individual exception.
*
* If the message will be retried the exceptions will be marked as handled
* in Sentry.
*/
private function captureException(\Throwable $exception, bool $willRetry): void
{
if ($exception instanceof HandlerFailedException) {
foreach ($exception->getNestedExceptions() as $nestedException) {
$this->captureException($nestedException, $willRetry);
}

return;
}

$hint = EventHint::fromArray([
'exception' => $exception,
'mechanism' => new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, $willRetry),
]);

$this->hub->captureEvent(Event::createEvent(), $hint);
}

private function flushClient(): void
{
$client = $this->hub->getClient();
Expand Down
80 changes: 80 additions & 0 deletions src/Integration/IntegrationConfigurator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

declare(strict_types=1);

namespace Sentry\SentryBundle\Integration;

use Sentry\Integration\ErrorListenerIntegration;
use Sentry\Integration\ExceptionListenerIntegration;
use Sentry\Integration\FatalErrorListenerIntegration;
use Sentry\Integration\IntegrationInterface;

/**
* @internal
*/
final class IntegrationConfigurator
{
private const ERROR_HANDLER_INTEGRATIONS = [
ErrorListenerIntegration::class => true,
ExceptionListenerIntegration::class => true,
FatalErrorListenerIntegration::class => true,
];

/**
* @var IntegrationInterface[]
*/
private $userIntegrations;

/**
* @var bool
*/
private $registerErrorHandler;

/**
* @param IntegrationInterface[] $userIntegrations
*/
public function __construct(array $userIntegrations, bool $registerErrorHandler)
{
$this->userIntegrations = $userIntegrations;
$this->registerErrorHandler = $registerErrorHandler;
}

/**
* @see IntegrationRegistry::getIntegrationsToSetup()
*
* @param IntegrationInterface[] $defaultIntegrations
*
* @return IntegrationInterface[]
*/
public function __invoke(array $defaultIntegrations): array
{
$integrations = [];

$userIntegrationsClasses = array_map('get_class', $this->userIntegrations);
$pickedIntegrationsClasses = [];

foreach ($defaultIntegrations as $defaultIntegration) {
$integrationClassName = \get_class($defaultIntegration);

if (!$this->registerErrorHandler && isset(self::ERROR_HANDLER_INTEGRATIONS[$integrationClassName])) {
continue;
}

if (!\in_array($integrationClassName, $userIntegrationsClasses, true) && !isset($pickedIntegrationsClasses[$integrationClassName])) {
$integrations[] = $defaultIntegration;
$pickedIntegrationsClasses[$integrationClassName] = true;
}
}

foreach ($this->userIntegrations as $userIntegration) {
$integrationClassName = \get_class($userIntegration);

if (!isset($pickedIntegrationsClasses[$integrationClassName])) {
$integrations[] = $userIntegration;
$pickedIntegrationsClasses[$integrationClassName] = true;
}
}

return $integrations;
}
}
Loading