nodrew/php-airbrake
A PHP client for sending application errors and exceptions to the Airbrake error-tracking service. Reports exceptions with backtraces, environment, and request context so you can monitor and debug production issues.
Install with Composer and pin a tag — the package has not seen a release since June 2018, so avoid floating constraints:
composer require nodrew/php-airbrake:"^1.0"
Before wiring anything, read these four files in the repo — they are the entire public surface:
src/Airbrake/Configuration.php — credential + option object (environmentName, projectRoot, component, action, host, timeout, curlOptions, and the flags controlling whether $_SERVER/$_POST/$_SESSION are captured).src/Airbrake/Client.php — notify() and notifyOnError().src/Airbrake/Notice.php — the payload built from an exception or a message string.src/Airbrake/MonologHandler.php — optional bridge if you prefer reporting through Laravel's logger.First decision: this client speaks Airbrake's legacy notifier API v2, authenticated with a single project API key. If your Airbrake project only shows a numeric project ID plus a key (the newer v3 scheme), stop here and use the maintained airbrake/php client instead — you will otherwise spend an afternoon debugging 401/404 responses from the notifier endpoint.
Minimal wiring in a Laravel app: put credentials in config/services.php, bind a singleton, and call it from the exception handler.
// config/services.php
'airbrake' => [
'api_key' => env('AIRBRAKE_API_KEY'),
'environment' => env('APP_ENV'),
],
// app/Providers/AppServiceProvider.php — register()
$this->app->singleton(\Airbrake\Client::class, function () {
$config = new \Airbrake\Configuration(
config('services.airbrake.api_key'),
[
'environmentName' => config('services.airbrake.environment'),
'projectRoot' => base_path(), // trims vendor frames from the backtrace
'component' => 'laravel',
'timeout' => 2, // seconds; never let reporting stall a request
]
);
return new \Airbrake\Client($config);
});
Then the first real use case — report uncaught exceptions:
// app/Exceptions/Handler.php (Laravel <=10)
public function report(Throwable $e): void
{
parent::report($e);
if ($this->shouldReport($e) && app()->environment(['production', 'staging'])) {
try {
app(\Airbrake\Client::class)->notify($e);
} catch (\Throwable $inner) {
// never let the notifier break error handling
}
}
}
In Laravel 11+, do the same inside withExceptions(fn (Exceptions $exceptions) => ...) in bootstrap/app.php.
Wrap it behind your own interface. Airbrake\Client is concrete and constructor-coupled to Configuration, which makes it awkward to fake. A one-method ErrorReporter interface bound to the client gives you a mockable seam in tests and a migration path to airbrake/php later.
Use Notice for non-exception events. When you want to push a message rather than a throwable (e.g. a failed payment reconciliation), build the notice explicitly so you control the backtrace and grouping:
$notice = new \Airbrake\Notice('Payment reconciliation failed', null, [
'component' => 'billing',
'action' => 'reconcile',
]);
// Attach structured context your Airbrake filters/grouping can use
$notice->context['invoice_id'] = $invoice->id;
$notice->context['attempt'] = $attempt;
app(\Airbrake\Client::class)->notify($notice);
Add request/user context once, in middleware. Rather than sprinkling context assignments in controllers, a small terminable middleware that stashes the authenticated user id and request id on a scoped builder avoids duplicating code and gives Airbrake something to group on.
Report, don't throw, in catch blocks you handle. For swallowed exceptions where you still want visibility (retried queue jobs, fallback branches), call notify() directly. For anything re-thrown, let the global handler do it — double reporting produces duplicate Airbrake groups.
Monolog bridge is optional and version-sensitive. If your team prefers log-based reporting, register a dedicated channel:
// config/logging.php
'airbrake' => [
'driver' => 'monolog',
'handler' => \Airbrake\MonologHandler::class,
'handler_with' => ['client' => app(\Airbrake\Client::class)],
'level' => 'error',
],
Because the package predates Monolog 2/3, expect to write a thin adapter (or a small custom handler calling notify()) if your Laravel version ships Monolog 3.
Go async for hot paths. notify() performs a blocking cURL request. In request middleware or per-job reporting, dispatch a queued job (dispatch(fn () => $client->notify($e))->onQueue('errors')) so a slow Airbrake response never adds to your p95. Keep the serialized exception payload explicit — Throwable serialization in jobs is fine on modern Laravel, but sanitize context first.
Gate by environment and exception type. Skip ValidationException, AuthenticationException, 404 ModelNotFoundException, and anything in $this->dontReport. Reporting expected control flow is the fastest way to make Airbrake useless through noise and to burn through your event quota.
Sample noisy paths. For high-volume but low-value errors (bot 404s, rate-limit hits), gate with a sample rate or a cache-backed throttle before calling notify().
Never call notifyOnError() in Laravel. It installs its own set_error_handler/set_exception_handler and will fight—or replace—Laravel's handler, silently swallowing exceptions or breaking the debug page. Use report() and leave the global handlers alone.
Set projectRoot or your grouping will be garbage. Airbrake groups by backtrace. Without projectRoot, every notice carries the full vendor stack and errors that are logically identical land in different groups (or the same group for unrelated bugs). Also set component/action deliberately; they participate in grouping.
The configuration object snapshots superglobals at construction time. In queue workers, Artisan commands, and tests, $_SERVER/$_POST/$_SESSION are empty or stale. If you rely on that context, pass serverData explicitly; if you don't want it, disable the capture flags so you are not shipping half-populated request data.
Scrub PII before it leaves the process. Post params, session data, and exception messages can contain tokens, emails, and card fragments. Client-side scrubbing is your responsibility; server-side Airbrake filters are the reliable backstop, so configure both. In particular, do not let Authorization headers or password fields ride along in a queued job payload.
Beware PHP 7+ Throwable vs Exception. The library was written against PHP 5.3 and its type hints/instance checks assume Exception. Passing Error/TypeError may produce a broken notice or a fatal in the notifier itself. Convert to a message Notice when you are unsure, and always wrap notify() in try/catch (\Throwable).
Transport failures don't always surface loudly. Depending on the code path, a failed send returns false rather than throwing. Log the return value in staging at least once so you know whether errors are actually reaching Airbrake; a silently misconfigured key is the most common "we set up Airbrake" failure.
Timeouts block the web request. The default cURL handling is synchronous with no aggressive timeout. Set timeout (and connect_timeout via curlOptions) to a couple of seconds, and prefer queueing. A hung Airbrake endpoint should not be able to take down your app.
PHP 8 compatibility is unverified. Last release 2018. Expect deprecation notices from dynamic properties and old-style constructors. Run your test suite with E_ALL on PHP 8 before committing; if it fails, either pin an older runtime, fork with a patch, or migrate to airbrake/php. Given the project's score (1.865) and single 2018 release, treat this as a stopgap, not a long-term dependency.
Monolog handler signature mismatch. Monolog 2/3 made handler constructors and handle() strictly typed. Airbrake\MonologHandler extends the 1.x API; subclassing with an overridden handle(array $record): bool is usually the quickest fix if you want the channel approach.
Config caching hides the key. php artisan config:cache is fine as long as the key is read via config()/env() in config files only. If you ever read env('AIRBRAKE_API_KEY') directly in a service provider, it returns null in production after caching — a classic silent-failure trap.
Deploy tracking is not included. This package only sends notices. If you want Airbrake to correlate errors with deploys, post to the deploy endpoint yourself in your deploy script (or use the newer official client, which supports it).
How can I help you explore Laravel packages today?