Exception reporting
Exception reporting is the core of the SDK. Once configured, every exception Laravel reports is forwarded to your dashboard.
Automatic capture
During boot the SDK registers an additive reportable() callback on the application's exception handler:
Because the callback is additive, it does not replace or suppress your existing logging or any other reportable handlers, it simply forwards a copy to Quiet Guard. Any exception that reaches the handler (uncaught exceptions, or anything you pass to report()) is captured, except one raised while serving a request that matches ignore_paths: see Configuration.
Capture is fail-safe: a network outage, a misconfiguration or a server error inside the SDK is swallowed and never propagates to the host application.
Fail-safe is not the same as silent. When the server refuses a report it says why, and that sentence is written to your own log as a warning, in the form HTTP <status> from <url>: <message>: a 402 while a payment is outstanding, a 403 on a plan gate, a 422 on a malformed field. The line carries an internal marker, so it is never captured and forwarded in turn.
Manual reporting
Use the Monitor facade to report a handled exception explicitly:
Monitor::report() never throws. It silently does nothing when reporting is disabled, when the project key is missing, when the current environment is not in the allowlist, or when the request matches ignore_paths. From version 0.3.1, an empty MONITOR_URL does not stop it: it resolves to the hosted service, see Configuration.
You can resolve the same object from the container:
What gets sent
Each report is posted to the server's /api/v1/ingest endpoint and contains:
The exception
- class, message, code, file and line;
- the complete stack trace by default (
MONITOR_TRACE_LIMIT=0): every frame, so deep errors keep their origin; set a frame count to trim payloads. Each frame is reduced tofile,line,function,classandtype(arguments are never sent, they may contain secrets); - from version 0.3, the throw site at the head of the stack: the exception's own file and line travel as frame zero, since PHP's trace starts at the caller and never held the failing line.
MONITOR_TRACE_LIMITcounts that frame (1= the site alone); - a source snippet (
code) on your application's frames: the five lines on each side of the frame's line, read from the file that ran, each cut at 500 characters, on at most ten frames per report. A dependency's frames (any path containing/vendor/) never carry one. A line that names a key of thescrublist below (password,secret,token,passphrase...) and gives it a value ($secret = ...,'api_key' => ...,setApiKey('...')) is masked whole as[scrubbed], because source code is where a hardcoded secret lives; the wordskey,auth,credential,salt,private,dsn,bearer,sk_andpk_count too, and a literal that names none of them still travels. From version 0.3.1, a static call (Auth::user()) is no longer taken for a value, so its line stays readable; the whole rule is on the core's scrubbing page.MONITOR_CODE_SNIPPETS=falsesends file and line only.
Context
environment,release(fromMONITOR_RELEASE, or detected from the checkout when it is empty),php_version,laravel_version,occurred_at(ISO 8601);source:consolefor CLI/queue, orhttpfor web requests.
For HTTP requests the context additionally includes:
url: the full address of the request. From version 0.3.1 it goes through the scrubber: the value of every query-string parameter named in thescrublist (thetokenand thesignatureof a signed reset link), and every path segment made of forty letters or digits in a row (the token Laravel puts in/reset-password/{token}), become%5Bscrubbed%5D; a numeric id, a slug, a UUID or a ULID stays;method;request: headers, query string and body: with the body'spassword/password_confirmationremoved and the whole structure passed through the scrubber. From version 0.3.1, the scrubber also reaches into a value that is a JSON object written as a string, such as thesnapshota Livewire component sends back with every request, and a header spelled with hyphens is masked like its underscore name:php_auth_pwmasksphp-auth-pw, the password of an HTTP Basic request;user: the authenticated user's identifier only (id), never their attributes.
Limits applied before sending
The ingestion endpoint validates four of these fields and answers 422 for the whole report when one of them is too long. The SDK trims them itself rather than losing the report, and marks the cut with … [truncated]:
| Field | Limit |
|---|---|
exception.message | 8192 characters |
exception.class | 255 characters |
exception.file | 1024 characters |
context.release | 255 characters |
These limits are reached by the reports you most want to read: a QueryException carries the whole SQL statement and its bindings inside its message, and an exception thrown from an anonymous class has the absolute file path inside its class name, which on a release directory is most of the 255 by itself. Trimming is lossy and it is the right answer here, the first 8192 characters of a query being worth incomparably more than a report that quietly disappears. The cut counts characters, not bytes, so a multi-byte character is never split in half into a payload the server would reject as malformed UTF-8.
Scrubbing
Before any payload leaves the application, request and context data are recursively scrubbed: any key whose name contains one of the configured terms (case-insensitive) has its value replaced with [scrubbed]. From version 0.3.1, the scrubber also reads the strings that carry named values of their own: a JSON string is opened and masked by key, and a URL loses the values of its parameters named in the list along with its token-shaped path segments (see the core's scrubbing page).
The default terms are:
To add your own, publish the config (php artisan vendor:publish --tag=monitor-config) and extend the scrub array in config/monitor.php:
Keep every shipped term unless you mean to drop it. Rewriting the array by hand is how passphrase and the visitor IP headers quietly leave the list.
Matching is by substring, so api_key also masks stripe_api_key, and token masks csrf_token. From version 0.3.1, a hyphen counts as an underscore: api_key also masks the x-api-key header.
Masking by the shape of the value
The scrub list above looks at the field name. It does not see an address written in the middle of an error message, in a URL segment, or in a field somebody called reference.
The redact list looks at the value itself, on your own server, before anything is sent:
Shapes that carry a check digit are verified rather than merely matched: a sixteen digit order reference is not taken for a card number, and an IBAN has to pass mod 97. Each masked value names what was hidden, for example User [redacted:email] not found, so the message stays readable.
Remove the patterns that produce false positives on your data, or set 'redact' => [] to switch it off. For your own shapes:
Sending in the background
When MONITOR_QUEUE is set, exceptions are dispatched as a SendExceptionToMonitor queued job (2 attempts) instead of being sent inline. This keeps reporting off the request's critical path. See Configuration.
The payload is masked before it is queued, so the shapes the redactor above exists to stop never sit in clear in your own jobs and failed_jobs tables. A job whose send failed throws, carrying what the server said, so the second attempt really happens and a definitive failure is recorded in failed_jobs rather than marked as done.
Server-side grouping
The SDK only sends data; the server groups occurrences into issues by fingerprint, reopens regressions, and correlates releases with commits. See the server documentation for how issues are displayed and managed.
You are reading the Laravel SDK v1.0 documentation.