A practical guide to NativePHP error tracking
Compare Laravel logs, Sentry integrations, and NativeWatch across PHP, webview JavaScript, and native failures, with setup and verification steps.
By NativeWatch Updated
NativePHP error tracking starts with identifying where the failure happened. A Laravel exception inside the packaged app, an unhandled JavaScript rejection in its webview, and a crash in its native host are different events. Installing a PHP package can cover the first path without covering the others.
That distinction helps you choose a setup and test it honestly. Local logs, Sentry, and NativeWatch all have useful roles. The right choice depends on which runtimes you need to observe, how your app is packaged, and how much integration work you want to own.
Map the app before choosing the SDK
NativePHP brings Laravel and an embedded PHP runtime to the user's device. Desktop apps also have Electron processes; mobile apps have their platform host and may use native screens, webviews, or both. See NativePHP's desktop introduction and mobile introduction.
Think about three capture paths:
| Path | Example | What must observe it |
|---|---|---|
| Laravel/PHP | An exception while saving a local record | Laravel's exception handler or another PHP capture hook |
| Webview JavaScript | An unhandled promise rejection in a Vue component | Instrumentation in that JavaScript context |
| Host/native | An Electron process crash or a mobile native exception | The relevant process or platform integration |
An API your app calls is another deployment. Capturing the API's errors does not automatically capture the client's local PHP errors, and matching timestamps alone does not establish that two events belong to the same request.
Start with Laravel logs
Logs remain useful when reproducing a bug locally or investigating a device you can access. Laravel configures channels through config/logging.php, including files and stacks of channels. Its logging documentation explains how to attach structured context:
use Illuminate\Support\Facades\Log;
Log::warning('Local sync needs another attempt', [
'operation' => 'sync',
'attempt' => 2,
]);
Choose context that explains the operation without copying private document contents, access tokens, or full request bodies into the message. A log stored on a user's device is not automatically available to your team. Decide how support can retrieve it, how long it persists, and what happens when storage is unavailable.
Logs are also not an issue-management workflow by themselves. If you need repeated failures grouped across installations, release filters, and a shared investigation history, add an error service.
Sentry: configure each relevant runtime
Sentry's Laravel integration is a reasonable option for the PHP side. Its official Laravel guide documents installation:
composer require sentry/sentry-laravel
For the Laravel versions covered by that guide, add its integration to your existing exception configuration in bootstrap/app.php:
use Illuminate\Foundation\Configuration\Exceptions;
use Sentry\Laravel\Integration;
// In the existing application builder:
->withExceptions(function (Exceptions $exceptions): void {
Integration::handles($exceptions);
})
Preserve any other exception configuration in that callback. Configure the project DSN using the documented publish command, then run php artisan sentry:test. Test again inside the packaged runtime; a command executed on your development machine does not exercise the installed app.
For Electron, Sentry has a separate SDK. The Electron guide initializes the main process and every renderer. These minimal forms omit optional tracing and replay integrations:
// Electron main process entry point
import * as Sentry from '@sentry/electron/main';
Sentry.init({ dsn: 'YOUR_PUBLIC_DSN' });
// Each Electron renderer entry point
import * as Sentry from '@sentry/electron/renderer';
Sentry.init();
These are Electron integration points, not snippets to paste into a Laravel Blade template. In a NativePHP desktop app, determine how your selected version exposes or packages those entry points, check SDK compatibility, and preserve the integration when rebuilding. Sentry also documents separate utility-process and preload-context considerations.
Sentry's Laravel SDK alone does not instrument Electron's main process or mobile native crash handlers. Equally, that does not make Sentry integration impossible: it means the PHP, JavaScript, and platform setup must be designed and verified separately.
NativeWatch: a NativePHP-focused setup
NativeWatch has separate Composer packages for desktop and mobile. As of 29 October 2026, neither package is publicly published on Packagist. Obtain SDK distribution access and follow the prerequisites in mobile installation or desktop installation before running the commands below.
Once Composer can access the package for your app, install it and explicitly configure its receiving URL and project key:
composer require zinxan/native-mobile
NATIVEWATCH_URL=https://native.watch
NATIVEWATCH_KEY=nw_your_project_key
NATIVEWATCH_REQUIRE_CONSENT=true
The mobile package also needs plugin registration and a native rebuild. Follow mobile installation for those steps and the consent control. For desktop, use zinxan/native-desktop and the desktop guide.
The shared SDK hooks reportable Laravel exceptions. For webview JavaScript errors and unhandled rejections, add the supplied directive to the layout's head:
@nativewatchScripts
This opt-in script observes its window and forwards reports through the app's PHP endpoint. It does not instrument every JavaScript context or capture an Electron host crash. See JavaScript errors for its boundaries.
The mobile plugin adds native crash collection, subject to diagnostic consent and supported platform mechanisms. Native reports are collected after relaunch. For readable iOS native frames, upload the exact build's matching dSYMs using the symbolication guide.
Prove the paths you intend to rely on
After setup, use a dedicated test project and a release-like build:
- Run the service's diagnostic command and match its event in the correct project.
- Trigger a real reportable PHP exception through the app.
- Trigger an unhandled JavaScript error in an instrumented webview.
- Exercise a supported native crash outside the debugger, relaunch, and match the actual report.
- Test disconnected delivery and consent withdrawal separately.
NativeWatch's nativewatch:test captures a constructed exception and checks API acceptance. It does not crash the native app. Its bounded outbox can retry failed delivery, but finite storage, deletion, consent changes, and a device that never reconnects limit what can arrive. Read offline delivery before treating a queue as a guarantee.
Finally, inspect an actual outgoing test payload and review privacy and consent. A useful setup produces enough evidence to investigate a failure while giving you a clear account of what was captured, where it came from, and what remains untested.