The Mission Control Laravel package sends data from your application to Grafite's Mission Control, an Application Performance Management system. It reports performance, exceptions, queue stats, traffic, emails and project documents, and notifies you within minutes when your system is overloaded or errors spike. Setup takes less than 5 minutes.
The package requires PHP 8.2+.
composer require grafite/mission-control-laravel
Add these variables to your .env file. The token is on your API Tokens page, and the key and UUID are on your project's settings page:
MISSION_CONTROL_USER_TOKEN={api_token}
MISSION_CONTROL_PROJECT_KEY={project_key}
MISSION_CONTROL_PROJECT_UUID={project_uuid} // required for JS error reporting
Then publish the configuration file:
php artisan vendor:publish --provider="Grafite\MissionControlLaravel\GrafiteMissionControlLaravelProvider"
The published config/mission-control.php file controls what the package sends:
environments - the environments Mission Control runs in, production by default.queues - the queues to monitor, keyed by queue name with the connection as the value.query_threshold - the number of seconds before a DB query is considered too long (default 5).page_load_threshold - the number of seconds before a page load is considered too long (default 2.0).log_javascript_errors - log JavaScript errors to Mission Control.log_traffic - log web traffic to Mission Control.levels - the log levels sent to Mission Control (emergency, alert, critical, error).url - the Mission Control URL, set MISSION_CONTROL_URL to use a custom domain.To use JavaScript error reporting or traffic logging, add the directive to your layout's header:
@missionControl
With log_javascript_errors on, it injects a window listener that reports errors to Mission Control. With log_traffic on, it records page visits, and it filters out bots first:
By default Mission Control logs all exceptions and logs from the environments in your config. You can turn this off by removing all environments. You can also log things manually:
use Grafite\MissionControlLaravel\Issue; try { // do some code } catch (Exception $e) { app(Issue::class)->exception($e); }
Or flag a potential issue or concern in your application:
use Grafite\MissionControlLaravel\Issue; app(Issue::class)->log('Anything you want to say goes here', 'tag');
Tags can be any terminology you want, to help sort through your issues.
Send yourself tagged notifications about your application:
use Grafite\MissionControlLaravel\Notify; app(Notify::class)->send('This is a title', 'info', 'This is a custom message');
Or from the command line:
php artisan mission-control:notify "This is a title" "This is a custom message" info
Mission Control can keep a record of every email your application sends. Turn it on in the config:
'log_emails' => true,
Once enabled, the package listens for Laravel's MessageSent event and sends the recipients, subject, bodies, headers, mailable and mailer to Mission Control. Bodies are included by default and capped at email_body_limit characters (default 65536). To leave them out:
'log_email_bodies' => false,
Mission Control encrypts the subject, bodies, headers and full recipient list at rest, so only the to and from addresses are searchable. All email logs are purged after 30 days.
Laravel only fires MessageSent for a successful delivery, so you can record a failed or queued email yourself:
use Grafite\MissionControlLaravel\Email; app(Email::class)->send([ 'to' => ['[email protected]'], 'subject' => 'Welcome aboard', 'status' => 'failed', 'error' => 'Connection refused', ]);
You or an AI agent can generate project documents, such as an incident response plan or an application rebuild process, from your codebase and push them to Mission Control. To list the available document types and the project's existing documents:
php artisan mission-control:documents
php artisan mission-control:documents --json
Push a document from a Markdown file, or pipe the content in:
php artisan mission-control:document "Incident Response Plan" --type=incident_response_plan --file=docs/mission-control/incident_response_plan.md cat rebuild.md | php artisan mission-control:document "Application Rebuild Process" --type=application_rebuild_process
Pushing a document updates any existing document with the same title and type instead of creating a duplicate. To update a specific document, pass --uuid=. Content is treated as Markdown; add --format=html to send HTML, or --json to output the response as JSON.
You can also push a document in code. Unlike the logging helpers, this throws an exception when Mission Control rejects the document:
mission_control_document('Incident Response Plan', $markdown, 'incident_response_plan');
If your application uses Laravel Boost, the package ships a guideline and a mission-control-documents skill. These teach your agent the commands and how to generate each document type from your codebase, so you can ask it to "generate our Mission Control documents".
Mission Control can schedule its own commands, so you don't need to add them to your app's Console Kernel. Make sure Laravel's scheduler is running, then turn scheduling on in your environment:
MISSION_CONTROL_SCHEDULE=true
Each command takes a cron expression, or false to skip it:
'schedule' => [ 'enabled' => env('MISSION_CONTROL_SCHEDULE', false), 'output' => storage_path('logs/scheduler.log'), 'commands' => [ 'report' => '*/5 * * * *', 'queue' => '*/15 * * * *', 'stats' => '0 9 * * *', 'dependencies' => '0 * * * *', 'virus-scan' => false, 'code' => false, ], ],
The stats and dependencies commands only run when your app has an App\Actions\AppStats or App\Actions\AppDependencies class. The virus-scan command needs ClamAV and the code command needs the dev scanners, so both are off by default.
Upgrading? When you turn on scheduling, remove the mission-control:* commands from your Console Kernel, otherwise they'll run twice.
If you'd rather not use Laravel's scheduler, add the commands to your crontab:
*/5 * * * * php /{app-path}/artisan mission-control:report
Sends performance data to Mission Control.
php artisan mission-control:report
Sends the processed, initiated and failed job counts and the current size of each configured queue.
php artisan mission-control:queue
Sends the payload from your App\Actions\AppStats action.
php artisan mission-control:stats
Sends the payload from your App\Actions\AppDependencies action.
php artisan mission-control:dependencies
Scans the app for viruses with ClamAV.
php artisan mission-control:virus-scan
Runs security and/or quality code scans and sends the results to Mission Control. Use --only or --skip with a comma-separated list of check names to limit the scan.
php artisan mission-control:code {--type=both : security|quality|both} {--only=} {--skip=}
Appends a webhook to the bashrc file so SSH logins are reported.
php artisan mission-control:ssh-logger
The virus-scan command needs ClamAV on the server:
sudo apt-get install clamav clamav-daemon mailutils -y
sudo service clamav-freshclam stop
sudo freshclam
Then turn on the virus-scan command in your schedule:
'virus-scan' => '0 1 * * *',
The package registers the Firewall middleware as firewall, so you can add it to your routes without touching your HTTP Kernel:
Route::middleware('firewall')->group(function () { // ... });
You can rename the alias, or set it to null to skip registering it:
'firewall' => [ 'alias' => 'firewall', ],
The firewall skips load balancer health checks so they aren't treated as threats. By default it skips Laravel's up health route, and any request with an AWS ELB-HealthChecker user agent that comes from a private IP. You can adjust both in the config. Paths use Request::is() patterns:
'firewall' => [ 'except' => [ 'up', 'health/*', ], 'health_check_user_agents' => [ 'ELB-HealthChecker', ], ],
Route 53 health checks come from public IPs, so add the path they check to except.
If your tests begin to fail, add these environment variables to your phpunit.xml file, or directly to your CI tool:
<env name="MISSION_CONTROL_USER_TOKEN" value="testing"/> <env name="MISSION_CONTROL_PROJECT_KEY" value="testing"/>