notonfire / php
NotOnFire for Laravel and PHP: error tracking with strict privacy defaults and privacy-friendly analytics, in one package.
Requires
- php: ^8.2
- illuminate/support: ^12.0|^13.0
- sentry/sentry-laravel: ^4.13
Requires (Dev)
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.0|^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Error tracking and privacy-friendly analytics for NotOnFire, in one package. The values below come from the application's settings page in the NotOnFire dashboard, which also shows them ready to copy.
Laravel
composer require notonfire/php
NOTONFIRE_DSN=https://… NOTONFIRE_ANALYTICS_ID=… NOTONFIRE_ANALYTICS_DOMAINS=example.com
php artisan notonfire:test
That is the whole install. The package:
- configures the Sentry SDK from
NOTONFIRE_DSN, with tracing, profiling, PII, logs, metrics, breadcrumbs, request bodies and the server name switched off; - reports unhandled exceptions on its own — no
Integration::handles()inbootstrap/app.php; - adds the analytics tag before
</head>of every HTML page in thewebmiddleware group; - sends a runtime snapshot to the dashboard's Environment view, at most once an hour, after a web response has gone out.
Laravel 12 and 13 on PHP 8.2 and later are supported.
What is not configurable
The privacy settings are enforced over config/sentry.php and every SENTRY_*
variable, on purpose. A published config/sentry.php with
traces_sample_rate => 1.0 changes nothing. Every event is also stripped of the
request, the user, extra data, tags, the transaction name and breadcrumbs before
it is sent; what remains is the exception, its stack trace and the runtime. If
you need more than that, use sentry/sentry-laravel directly instead of this
package.
An application before_send still runs, before the package's own, so nothing
it adds survives.
Environments
Nothing is sent and no analytics tag is rendered in the local and testing
environments, so a DSN copied into a local .env does not report your typos.
Everything else (production, staging, …) sends, tagged with its environment.
NOTONFIRE_ENABLED=false switches all of it off everywhere.
Analytics
The tag is added to successful, complete HTML responses only: JSON, redirects,
downloads, Livewire and other XHR requests are left alone, and so are admin,
filament, horizon, telescope, pulse and nova. To place the tag yourself,
set NOTONFIRE_ANALYTICS_INJECT=false and add this to your layout's <head>:
@notonfireAnalytics
The excluded paths live in the config file, which you can publish:
php artisan vendor:publish --tag=notonfire-config
Runtime snapshot
When a CVE comes out, the application's Environment view in the dashboard answers whether it is affected. The package keeps it current with a snapshot of the runtime:
- the PHP version, SAPI and Zend version, thread safety, and whether OPcache and the JIT are on;
- the OS family and release (from
/etc/os-release), the kernel version and the CPU architecture; - the web server's
SERVER_SOFTWARE, and the Laravel version; - the versions of OpenSSL, curl, libxml, PCRE, ICU, SQLite, GD and ImageMagick that PHP is linked against;
- every loaded extension with its version;
memory_limit,max_execution_time,upload_max_filesize,post_max_size,display_errors,expose_php,allow_url_fopen,allow_url_include,disable_functions,date.timezone,session.cookie_secure,opcache.validate_timestamps, and whetheropen_basediris set.
It never contains the hostname, a path, the include path, the document root,
an environment variable or anything from the request, and phpinfo() is never
called.
The snapshot must have no measurable impact on the website, and it is built so
that it has none. Every request costs a few microseconds: the package looks at
the modification time of one file, storage/framework/notonfire-runtime-<sapi>,
and nothing else. Once an hour one worker spends up to 2 s after the visitor
already has the page: the response is finished first, then the snapshot is
sent with a 1 s connect timeout and a 2 s total timeout. Only one worker sends,
the file is updated before it does, and an unreachable or slow dashboard is not
retried before the next hour. It never throws an error into the application.
It is sent only where the response can be finished before it: under PHP-FPM, LiteSpeed and Laravel Octane. Applications that run under Apache's mod_php, the built-in server or only on the command line send none; their error events still carry the PHP version. It also needs ext-curl.
Nothing is sent from local and testing, with NOTONFIRE_ENABLED=false or
without a DSN. NOTONFIRE_RUNTIME=false switches the snapshot off on its own,
and NOTONFIRE_URL sends it to a dashboard other than
https://app.notonfire.systems. php artisan notonfire:test sends one right
away. That one comes from the CLI, so the dashboard shows the CLI's settings
until the next web request sends its own.
Coming from a manual Sentry setup
Remove the SENTRY_* lines from .env, the Integration::handles($exceptions)
call from bootstrap/app.php and a published config/sentry.php. A leftover
SENTRY_LARAVEL_DSN is still picked up when NOTONFIRE_DSN is not set, and a
leftover Integration::handles() does not report anything twice, but nothing else
from that setup has any effect. Remove a hand-pasted analytics tag as well, or
every visit is counted twice.
Checking the install
php artisan notonfire:test shows what the package found, sends a test event
through the exception handler, the way a real error goes, and sends a runtime
snapshot. From local it sends nothing unless you pass --force.
Plain PHP
composer require notonfire/php
Right after vendor/autoload.php, as early as possible:
\NotOnFire\NotOnFire::init(['dsn' => 'https://…']);
In the <head> of your layout:
<?= \NotOnFire\NotOnFire::analyticsScript(['id' => '…', 'domains' => 'example.com']) ?>
Both read NOTONFIRE_DSN, NOTONFIRE_ANALYTICS_ID and
NOTONFIRE_ANALYTICS_DOMAINS from the environment when called without them.
NOTONFIRE_ENVIRONMENT names the environment (default production); local and
testing send nothing.
Under PHP-FPM and LiteSpeed, init() also sends the
runtime snapshot, with the same guarantees, from a shutdown
function that runs after your own. Its marker file lives in the system temp
directory. Pass 'runtime' => false or set NOTONFIRE_RUNTIME=false to switch
it off, and 'url' => … or NOTONFIRE_URL for another dashboard.
Check the install with:
NOTONFIRE_DSN='https://…' vendor/bin/notonfire-test
The package depends on sentry/sentry-laravel, so a plain PHP project also
installs illuminate/support. Nothing from Laravel is loaded unless the project
uses it.
Development
composer install vendor/bin/pest vendor/bin/pint
The runtime snapshot runs inside other people's production requests. Any
change to src/Environment/Reporter.php or what it calls has to keep every
guarantee listed in that class and under Runtime snapshot.