Errors · Reference
Laravel SDK
mybeast/errors-laravel captures every exception your app reports, measures requests and jobs once you turn performance on, hands pages their config, marks releases, answers Errors' webhook and erases a person's data. For Laravel 12 and 13 on PHP 8.2 or later.
Install
composer require mybeast/errors-laravel
php artisan vendor:publish --tag=mybeast-errors-configThe package registers itself. The second line copies the config to config/mybeast-errors.php; it works without it, from .env alone.
.env
MYBEAST_ERRORS_URL=https://services.my-beast.com/errors
MYBEAST_ERRORS_PUBLIC_KEY=pk_…
MYBEAST_ERRORS_SECRET=sk_…
MYBEAST_ERRORS_SIGNING_KEY=sig_…
MYBEAST_ERRORS_WEBHOOK_SECRET=whsec_…
MYBEAST_ERRORS_ENVIRONMENT=productionWhile the URL or the server secret is missing or malformed, nothing is captured or sent. The SDK never throws at boot and never breaks a page. Where each value comes from: the quickstart.
Config
| Key | Default | What it does |
|---|---|---|
enabledMYBEAST_ERRORS_ENABLED | true | false switches everything off: for tests, or a checkout without the service. |
urlMYBEAST_ERRORS_URL | none | The service's address. |
public_keyMYBEAST_ERRORS_PUBLIC_KEY | none | pk_…: names the project in the browser. |
secretMYBEAST_ERRORS_SECRET | none | sk_…: sends errors and calls the API. |
signing_keyMYBEAST_ERRORS_SIGNING_KEY | none | sig_…: signs the browser's token. |
webhook_secretMYBEAST_ERRORS_WEBHOOK_SECRET | none | whsec_…: checks the service's webhook. Unset, the webhook route isn't there. |
webhook_pathMYBEAST_ERRORS_WEBHOOK_PATH | mybeast/errors/webhook | Where the webhook is answered. |
environmentMYBEAST_ERRORS_ENVIRONMENT | APP_ENV, else production | Sent with everything. Made into lower case letters, digits, - and _: EU.prod is sent as eu-prod. |
releaseMYBEAST_ERRORS_RELEASE | APP_RELEASE, else the git commit | The build this process runs. Unset, the SDK reads the commit from .git, without running git. |
request_id_headerMYBEAST_ERRORS_REQUEST_ID_HEADER | X-Request-Id | The header a proxy puts a request's id in. With mybeast/logs-laravel installed, its id is used instead. |
token_ttlMYBEAST_ERRORS_TOKEN_TTL | 1800 | How long a browser token lives, in seconds. At least 60, at most 3600. |
token_pathMYBEAST_ERRORS_TOKEN_PATH | mybeast/errors/token | Where a page left open gets a fresh token. false in the config: no such route. |
spool_pathMYBEAST_ERRORS_SPOOL_PATH | storage/app/private/mybeast-errors-spool | Where captured errors wait to be sent: files, never the database. |
breadcrumbs | 30 | Breadcrumbs kept per request or job. |
context_lines | 5 | Lines of source kept around each of your own frames. |
performance.enabledMYBEAST_ERRORS_PERFORMANCE | false | Measures requests and queued jobs for the Performance tab. |
performance.sample_rateMYBEAST_ERRORS_PERFORMANCE_SAMPLE_RATE | 0.1 | The share of ordinary requests and jobs sent: 0.1 is one in ten. |
performance.slow_msMYBEAST_ERRORS_PERFORMANCE_SLOW_MS | 1000 | Every request or job at least this slow, in milliseconds, is sent too. |
performance.n_plus_one | 5 | A query of one shape run this many times in one request or job is an N+1. |
performance.spool_pathMYBEAST_ERRORS_PERFORMANCE_SPOOL_PATH | storage/app/private/mybeast-errors-performance | Where performance samples wait, apart from errors. |
sourcemaps_pathMYBEAST_ERRORS_SOURCEMAPS_PATH | storage/app/private/sourcemaps | Where mybeast:errors-release looks for *.js.map files. |
Artisan commands
| Command | What it does |
|---|---|
mybeast:errors-test | Prints what is set, checks in with Errors at once and says whether it heard the app, or why not. Sends no error, so nobody is alerted. Exits 1 when Errors is off or didn't hear the app. |
mybeast:errors-release | Uploads the browser bundle's source maps, then tells Errors the release is live. Options: --release=, --environment=, --sourcemaps= (a folder), --files-only (upload, don't say it's live). |
mybeast:errors-flush | Sends what waits in the spool, and once an hour checks in. The SDK puts it on Laravel's scheduler every minute: you don't run it yourself. |
The scheduler must run for mybeast:errors-flush to: php artisan schedule:work, or cron's schedule:run.
What is captured
The SDK hooks the exception handler's reportable(), so whatever Laravel reports is captured, and Laravel's dontReport list still applies. Each error carries:
- the exception and the ones before it, with source lines around your own frames,
- the last 30 breadcrumbs: SQL without its values, log lines, the route or job,
- the route, the action, and the names of its parameters and fields, never their values,
- the release, the environment, and who it happened to, by id,
- the request's id as your log lines carry it, so the issue opens exactly those lines in Logs: the one
mybeast/logs-laravelgives the request when you have it, else a proxy'sX-Request-Id.
An error is written to a spool file first, never the database, which may be what failed. The spool is sent right after the response, after each queued job, and every minute by the scheduler. While Errors can't be reached, errors wait there, up to 5,000 files. Under Octane each request starts with a clean trail.
The Errors facade
MyBeast\Errors\Laravel\Facades\Errors. No alias is registered: import it.
| Method | What it does |
|---|---|
identifyUsing(Closure $resolver) | Who an error happened to: a closure returning an Identity (ids only) or null. Default: the signed-in user's id. |
scrubUsing(Closure $rule) | A rule of your own for personal data, run after the built-in ones on every text that leaves. |
clientConfig($subject, $account = null, $internal = false) | A signed-in page's browser config, with its token. null while the SDK is off. |
guestConfig() | A guest page's browser config, without a token. null while the SDK is off. |
lastReference() | The reference of the last error captured in this request (7F3A9C21), for the page that says sorry. |
capture(Throwable $e) | Captures an exception you caught yourself. Reported exceptions are captured without it. |
eraseSubject($subject) | Erases what Errors keeps of one person. Returns how many rows went, or null while the SDK is off. |
eraseAccount($account) | Erases every event of one account, whoever it happened to. |
fake() | In tests: captures into memory instead of sending. Returns the fake to assert on. |
capturing() | Whether errors are captured and sent: the URL and a server secret are set. |
measuring() | Whether requests and jobs are measured for the Performance tab. |
flush() | Sends what waits in the spool now. Returns how many events went. |
identifyUsing() is explained in Say who it happened to, clientConfig() in Catch browser errors.
Scrubbing
Before anything leaves your app, e-mail addresses, IBANs, Finnish personal identity codes, phone numbers and tokens are replaced, in messages and source lines alike, and so are long random parts of a URL's path: /reset-password/9f8e… leaves as /reset-password/[token], while UUIDs and ULIDs stay readable. A URL keeps its query's keys, never their values. Add rules of your own:
use MyBeast\Errors\Laravel\Facades\Errors;
// AppServiceProvider::boot(): your own rule, after the built-in ones.
Errors::scrubUsing(fn (string $text): string => preg_replace('/\bCUST-\d+\b/', '[customer]', $text));Errors runs the built-in rules again on arrival. More in Data and privacy.
Erasing a person or an account
use MyBeast\Errors\Laravel\Facades\Errors;
// In a queued job with retries: an erasure is never lost to a network blip.
$erased = Errors::eraseSubject($user->id); // a person
$erased = Errors::eraseAccount($user->team_id); // an account, whoever it happened toEach returns how many rows went or were emptied, or null while the SDK is off or has no server secret. Each throws MyBeast\Errors\Laravel\ErasureFailed when Errors doesn't confirm it, so call them from a queued job with retries. Errors still in the spool when it runs reach Errors after it and are kept: delay the job a few minutes, or erase again then. An erasure that finds nothing returns 0.
Testing
Errors::fake() keeps what would be sent in memory, so nothing reaches the service, and gives you assertions:
use MyBeast\Errors\Laravel\Facades\Errors;
public function test_a_failed_payment_is_reported(): void
{
$fake = Errors::fake();
$this->post('/checkout', ['plan' => 'pro'])->assertStatus(500);
$fake->assertCaptured(fn (array $event) => $event['exception_class'] === PaymentFailed::class);
}assertCaptured(?Closure $matching): at least one error was captured, matching the closure if given.assertNothingCaptured(): nothing was.last()andevents: the last captured error, and all of them, as arrays.
In a suite that doesn't fake it, switch the SDK off in phpunit.xml:
<env name="MYBEAST_ERRORS_ENABLED" value="false"/>Laravel versions