Skip to content

Commit

Permalink
Add helper to report lazy loading violations (#678)
Browse files Browse the repository at this point in the history
  • Loading branch information
stayallive authored May 2, 2023
1 parent 26d147b commit 04bad6b
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/Sentry/Laravel/Features/Concerns/ResolvesEventOrigin.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

namespace Sentry\Laravel\Features\Concerns;

use Illuminate\Contracts\Container\Container;
use Sentry\Laravel\Tracing\BacktraceHelper;

trait ResolvesEventOrigin
{
protected function container(): Container
{
return app();
}

protected function resolveEventOrigin(): ?string
{
$backtraceHelper = $this->makeBacktraceHelper();
Expand Down
52 changes: 52 additions & 0 deletions src/Sentry/Laravel/Integration.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

namespace Sentry\Laravel;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\LazyLoadingViolationException;
use Illuminate\Routing\Route;
use Sentry\EventHint;
use Sentry\EventId;
use Sentry\ExceptionMechanism;
use Sentry\Laravel\Features\Concerns\ResolvesEventOrigin;
use Sentry\SentrySdk;
use Sentry\Severity;
use Sentry\Tracing\TransactionSource;
use Throwable;
use function Sentry\addBreadcrumb;
Expand Down Expand Up @@ -190,6 +194,54 @@ public static function captureUnhandledException(Throwable $throwable): ?EventId
return SentrySdk::getCurrentHub()->captureException($throwable, $hint);
}

/**
* Returns a callback that can be passed to `Model::handleLazyLoadingViolationUsing` to report lazy loading violations to Sentry.
*
* @param callable|null $callback Optional callback to be called after the violation is reported to Sentry.
*
* @return callable
*/
public static function lazyLoadingViolationReporter(?callable $callback = null): callable
{
return new class($callback) {
use ResolvesEventOrigin;

/** @var callable|null $callback */
private $callback;

public function __construct(?callable $callback)
{
$this->callback = $callback;
}

public function __invoke(Model $model, string $relation): void
{
SentrySdk::getCurrentHub()->withScope(function (Scope $scope) use ($model, $relation) {
$scope->setContext('violation', [
'model' => get_class($model),
'relation' => $relation,
'origin' => $this->resolveEventOrigin(),
]);

SentrySdk::getCurrentHub()->captureEvent(
tap(Event::createEvent(), static function (Event $event) {
$event->setLevel(Severity::warning());
}),
EventHint::fromArray([
'exception' => new LazyLoadingViolationException($model, $relation),
'mechanism' => new ExceptionMechanism(ExceptionMechanism::TYPE_GENERIC, true),
])
);
});

// Forward the violation to the next handler if there is one
if ($this->callback !== null) {
call_user_func($this->callback, $model, $relation);
}
}
};
}

/**
* Try to make an educated guess if the call came from the Laravel `report` helper.
*
Expand Down

0 comments on commit 04bad6b

Please sign in to comment.