laravel maintained by stackwarden
Stackwarden for Laravel
Official Laravel SDK for Stackwarden — self-hostable error monitoring with issues, traces, logs, and metrics.
Requires PHP 8.1+ and Laravel 10 / 11 / 12 / 13.
Features
| Signal | Automatic | Manual |
|---|---|---|
| Issues / events | Unhandled exceptions, failed queue jobs | captureException, captureMessage |
| Traces | HTTP requests + SQL spans (when enabled) | startTransaction / finishTransaction |
| Logs | Laravel log lines → Explore (when enabled) | captureLog |
| Metrics | http.request.duration (when enabled) |
captureMetric |
Also: SQL / HTTP / auth / queue breadcrumbs, request + user context, deferred sending so ingest does not block the response.
Install
composer require stackwarden/laravel
Capture unhandled exceptions
Laravel 11+ — bootstrap/app.php:
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Stackwarden\Integration;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withExceptions(function (Exceptions $exceptions) {
Integration::handles($exceptions);
})->create();
Laravel 10 — app/Exceptions/Handler.php:
public function register(): void
{
$this->reportable(function (Throwable $e) {
\Stackwarden\Integration::captureUnhandledException($e);
});
}
Configure (Docker Stackwarden)
Stackwarden is normally run with the Docker install:
curl -fsSL https://stackwarden.mysticastra.org/install.sh | bash
# optional: export WEB_PORT=8001 before running
That compose stack does not publish the API on host :3000. The web container proxies /v1/* to the API, so the SDK must use the same public URL as the dashboard.
| How you installed | SDK base URL (example) |
|---|---|
WEB_PORT=8001 (common local) |
http://127.0.0.1:8001 |
Default WEB_PORT=80 |
http://127.0.0.1 or http://localhost |
| HTTPS reverse proxy | https://errors.example.com |
- Open the dashboard → your project → Settings → Keys → create/copy a project key (
sw_…). - Point the Laravel app at that host + key:
# WEB_PORT=8001 example
php artisan stackwarden:publish --dsn="http://sw_YOUR_PROJECT_KEY@127.0.0.1:8001"
Or in .env:
# Preferred — key + host in one value (http for local Docker, https in production)
STACKWARDEN_DSN=http://sw_YOUR_PROJECT_KEY@127.0.0.1:8001
# Equivalent split form:
# STACKWARDEN_BASE_URL=http://127.0.0.1:8001
# STACKWARDEN_PROJECT_KEY=sw_YOUR_PROJECT_KEY
The SDK posts to {base}/v1/ingest/events (and /traces, /logs, /metrics). Do not use http://localhost:3000 unless you intentionally published the API container port yourself.
Verify
php artisan stackwarden:test
php artisan stackwarden:test --all # event + sample trace / log / metric
Then open the same host in the browser (e.g. http://127.0.0.1:8001) and check Issues / Explore.
Throw a test exception from a route:
Route::get('/debug-stackwarden', function () {
throw new RuntimeException('My first Stackwarden error');
});
Configuration
Published file: config/stackwarden.php.
Core
| Env | Default | Description |
|---|---|---|
STACKWARDEN_DSN |
— | http(s)://sw_…@host[:port] (preferred) |
STACKWARDEN_BASE_URL |
http://localhost |
Dashboard/API origin if not using DSN (Docker public URL) |
STACKWARDEN_PROJECT_KEY |
— | Project key if not embedded in DSN |
STACKWARDEN_ENVIRONMENT |
APP_ENV |
Event / span / log environment |
STACKWARDEN_RELEASE |
— | Release label |
STACKWARDEN_SAMPLE_RATE |
1.0 |
Error event sample rate (0–1) |
STACKWARDEN_TRANSPORT |
buffered |
buffered | queue | sync |
STACKWARDEN_SEND_DEFAULT_PII |
false |
Include email, IP, cookies, body |
Explore signals (off by default)
| Env | Default | Description |
|---|---|---|
STACKWARDEN_TRACES_SAMPLE_RATE |
0 |
>0 enables HTTP + SQL tracing |
STACKWARDEN_ENABLE_LOGS |
false |
Forward Laravel logs to Explore |
STACKWARDEN_ENABLE_METRICS |
false |
Emit request duration metrics |
STACKWARDEN_TRACE_SQL |
true |
SQL child spans when tracing |
Example with local Docker on port 8001:
STACKWARDEN_DSN=http://sw_…@127.0.0.1:8001
STACKWARDEN_TRANSPORT=buffered
STACKWARDEN_TRACES_SAMPLE_RATE=1.0
STACKWARDEN_ENABLE_LOGS=true
STACKWARDEN_ENABLE_METRICS=true
Production behind HTTPS:
STACKWARDEN_DSN=https://sw_…@errors.example.com
STACKWARDEN_TRANSPORT=buffered
STACKWARDEN_TRACES_SAMPLE_RATE=0.2
STACKWARDEN_ENABLE_LOGS=true
STACKWARDEN_ENABLE_METRICS=true
STACKWARDEN_RELEASE=1.4.2
Transport
| Mode | Behavior |
|---|---|
buffered (default) |
Queue in memory; flush after the HTTP response / process shutdown. Does not block the user on the ingest HTTP call. |
queue |
Dispatch SendPayloadJob to your Laravel queue worker. Set STACKWARDEN_QUEUE / STACKWARDEN_QUEUE_CONNECTION if needed. |
sync |
POST immediately (debug only — adds latency). |
Usage
Exceptions & messages
use Stackwarden\Facade as Stackwarden;
use function Stackwarden\captureException;
try {
risky();
} catch (Throwable $e) {
Stackwarden::captureException($e);
// or: captureException($e);
}
Stackwarden::captureMessage('Something noteworthy', 'warning');
Scope (tags, user, breadcrumbs)
use Stackwarden\Breadcrumb;
use Stackwarden\Facade as Stackwarden;
Stackwarden::configureScope(function ($scope) {
$scope->setTag('tenant', 'acme');
$scope->setUser(['id' => (string) auth()->id()]);
});
Stackwarden::addBreadcrumb(Breadcrumb::make(
message: 'Checkout started',
category: 'ui',
level: 'info',
));
Traces
$tx = Stackwarden::startTransaction('POST /checkout', 'http.server');
$span = $tx?->startChild('db.query', 'load cart', 'SELECT …');
$span?->finish();
Stackwarden::finishTransaction('ok');
With STACKWARDEN_TRACES_SAMPLE_RATE > 0, each web request already starts an HTTP transaction; SQL queries become child spans when STACKWARDEN_TRACE_SQL=true.
Explore logs & metrics
Stackwarden::captureLog('Order placed', 'info', ['order_id' => '42']);
Stackwarden::captureMetric('checkout.latency', 12.5, 'ms', ['route' => 'checkout']);
Stackwarden::flush(); // push buffered logs/metrics (also runs after each request)
Log channel (issues from Monolog)
In config/logging.php:
'stackwarden' => [
'driver' => 'monolog',
'handler' => \Stackwarden\Logging\LogsHandler::class,
'level' => env('STACKWARDEN_LOGS_LEVEL', 'error'),
],
Or:
'stackwarden' => [
'driver' => 'custom',
'via' => \Stackwarden\Logging\LogChannel::class,
'level' => 'error',
],
This reports to issues (event ingest). Explore log shipping is controlled by STACKWARDEN_ENABLE_LOGS.
What is automated
Out of the box (once configured + Integration::handles):
- Unhandled exceptions reported as events
- Failed queue jobs (
Queue::failing), except the SDK’s own send job - Request URL / method / headers attached to events
- Authenticated user id (and email/IP only if
SEND_DEFAULT_PII) - Breadcrumbs: queries, logs, HTTP client, queue, auth login
- Optional: traces, Explore logs, request metrics (see env flags above)
- Flush on middleware
terminateand PHP shutdown
Development
composer dump-autoload
php tests/self_check.php
License
MIT — see LICENSE.