winter/wn-sentry-plugin
Integrates Sentry error and performance monitoring into Winter CMS. Captures exceptions, logs, and application errors with stack traces, environment and release context, and helps track issues in production from your Sentry dashboard. Includes configurable DSN, sampling, and reporting options.
# From the Winter marketplace via CLI
php artisan plugin:install Winter.Sentry
# Or manually via Composer + plugin registration
composer require winter/wn-sentry-plugin
php artisan plugin:install winter/wn-sentry-plugin
config/sentry.php / .env. Put it in .env so it never lands in git:
SENTRY_LARAVEL_DSN=https://<key>@o0.ingest.sentry.io/<project>
SENTRY_ENVIRONMENT=production
SENTRY_TRACES_SAMPLE_RATE=0.1
Then php artisan config:clear.php artisan tinker
>>> throw new \RuntimeException('sentry smoke test');
Confirm the event shows up in the Sentry Issues stream within a few seconds.First real use case: uncaught exceptions and fatal errors from the frontend theme (CMS pages, components, partials) and from backend admin operations. This is the fastest win — replace "white screen of death" and "500 from some page" with a stack trace, URL, and user context.
Environment and release tagging. The single most valuable habit is telling Sentry which deploy broke. Ship a release tag from your deploy pipeline:
SENTRY_RELEASE=${GIT_SHA}
On Winter you can also read it from a git hash at runtime if your env is static:
// in a Plugin::boot() of your own project plugin
\Sentry\configureScope(function (\Sentry\State\Scope $scope) {
$scope->setTag('release', trim(@file_get_contents(base_path('.git/HEAD'))));
});
Attaching user context to backend errors. So a report says "Jane (id 42, admin)" rather than "anonymous":
// plugins/acme/project/Plugin.php boot() or a middleware
Event::listen('backend.user.login', function ($user) {
\Sentry\configureScope(function ($scope) use ($user) {
$scope->setUser(['id' => $user->id, 'username' => $user->login]);
});
});
Manual capture for handled failures. Don't only rely on the exception handler — catch-and-report business logic failures:
try {
$gateway->charge($order);
} catch (\Throwable $e) {
\Sentry\captureException($e);
// optionally rethrow or degrade gracefully
throw $e;
}
Breadcrumbs for context. Add domain breadcrumbs so a later crash has a story:
\Sentry\addBreadcrumb(new \Sentry\Breadcrumb(
\Sentry\Breadcrumb::LEVEL_INFO,
\Sentry\Breadcrumb::TYPE_DEFAULT,
'order',
'Order status transitioned',
['from' => 'pending', 'to' => 'paid', 'order_id' => $id]
));
Scrubbing PII and noise with before_send. Winter apps often log full request bodies and CMS passwords — filter aggressively:
// config/sentry.php
'before_send' => function (\Sentry\Event $event, ?\Sentry\EventHint $hint): ?\Sentry\Event {
$request = $event->getRequest();
unset($request['data']['password'], $request['data']['password_confirmation']);
$event->setRequest($request);
return $event;
},
Performance / tracing. If you want slow CMS page renders surfaced, set SENTRY_TRACES_SAMPLE_RATE (start at 0.05–0.1, not 1.0 in production). On a marketing site this is usually not worth the quota; on an app with authenticated dashboards it is.
Separate errors by scope. Use tags to split frontend vs backend issues so you can alert/filter differently:
\Sentry\configureScope(fn ($scope) => $scope->setTag(
'scope',
\Request::is('admin/*') ? 'backend' : 'frontend'
));
sentry/sentry-laravel. If your app pins an older Laravel/Sentry SDK and the plugin expects a newer one, you'll get a "class not found" or a silent no-op. Check composer show winter/wn-sentry-plugin sentry/sentry-laravel first, then align versions. You can also skip the plugin and configure sentry/sentry-laravel directly — the Winter plugin mostly adds the backend settings UI and boot wiring.config:cache traps the DSN. Winter is Laravel; if config is cached, env changes are ignored. Always php artisan config:clear (or recache) after touching .env. A missing DSN silently disables reporting — verify with a test exception.App\Exceptions\Handler / Exceptions::report vs. Sentry's own handler. Manual \Sentry\captureException() always works as a fallback and is the fastest way to confirm the SDK is alive independent of handler wiring.storage/logs. You'll get errors in both Sentry and the log file — that's expected. But if you also call Log::error() inside a catch and rethrow, you'll emit duplicates. Decide: log or report, not both for the same failure./favicon.ico, NotFoundHttpException storms, and HttpException 419 (CSRF/expired session) generate huge volume. Use SENTRY_IGNORE_EXCEPTIONS / before_send to drop 404s and 419s unless they spike.release — otherwise your JS errors show up with minified, useless stacks. The PHP plugin will not upload source maps for you.sentry/sentry-laravel and keep your manual-capture fallback in place. It's low-risk precisely because the surface is tiny..env and add SENTRY_* to env.example. If the DSN leaks, rotate it in Sentry (it's a write key for events, not read — but still rotate).SENTRY_ENVIRONMENT=staging) so noisy deploy errors don't pollute production issue feeds and alerting.config/sentry.php, before_send callbacks, configureScope/addBreadcrumb from your own plugins' boot(), and the Winter event bus (backend.user.login, cms.page.render) are the cleanest places to add context.How can I help you explore Laravel packages today?