richness / laravel-monitoring-agent
Safely report Laravel application exceptions to a central monitoring server. Monitoring failure must never break the host application.
Package info
github.com/richnessagency/laravel-monitoring-agent
pkg:composer/richness/laravel-monitoring-agent
Requires
- php: ^8.2
- laravel/framework: ^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^11.0
- phpunit/phpunit: ^11.5|^12.0
README
A production-grade, security-hardened, and lightweight monitoring client package for Laravel. It captures unhandled exceptions and logs them securely to an external Central Monitoring Server without affecting the host application's performance, stability, or database.
📖 Table of Contents
- Core Safety Philosophy
- Architecture Overview
- Installation
- Configuration Reference
- In-Depth Feature Explanations
- Developer API (Facade & Breadcrumbs)
- Console & Scheduling Commands
- Central Server API Ingestion Contracts
- Troubleshooting & FAQs
🛡️ Core Safety Philosophy
Important
THE MONITORING PACKAGE MUST NEVER BREAK THE HOST APPLICATION Error reporting is a secondary concern. The user-facing application lifecycle must remain fast, stable, and completely unimpeded.
- Defensive Error Boundaries: Every remote call is wrapped in try-catch blocks with short timeouts (default: 2s connection timeout, 5s request timeout).
- Circuit Breaker state-machine: Prevents cascade failures when the Central Server goes offline or experiences network congestion.
- Zero-DB Footprint: Installs without any migrations, database tables, or queries, preserving database connection pools.
- Re-Entry Protection: A thread-safe
RecursionGuardprevents infinite loops if the package itself encounters an internal error during report delivery.
🏗️ Architecture Overview
The agent hooks directly into the Laravel Exception Handler container lifecycle, intercepting exceptions during boot:
Host App Exception
│
▼
[RecursionGuard] (Checks nested call depth)
│
▼
[Sanitization & Enrichment] (Strip secrets, read resources, check source context)
│
▼
[Circuit Breaker]
├──► CLOSED: Attempt HTTP Transport
│ ├──► Success (201): Event delivered.
│ └──► Failure (5xx/429/Timeout): Writes to [Local Buffer]
│
└──► OPEN: Bypasses HTTP entirely, goes directly to [Local Buffer]
🚀 Installation
Install the package via Composer:
composer require richness/laravel-monitoring-agent
Publish the config file and initialize setup:
php artisan monitoring:install
This command publishes config/monitoring.php and runs test diagnostics.
⚙️ Configuration Reference
Below is the complete set of .env configurations supported:
# Enable/disable monitoring entirely MONITORING_ENABLED=true # Central Server Connection Settings MONITORING_URL=https://monitor.example.com MONITORING_TOKEN=mon_live_your_project_token_here # Release & Server Identifiers MONITORING_RELEASE=v1.0.4 MONITORING_SERVER_NAME=web-prod-01 # Transport Timeouts (in seconds) MONITORING_CONNECT_TIMEOUT=2 MONITORING_TIMEOUT=5 # Circuit Breaker Options MONITORING_CIRCUIT_BREAKER_ENABLED=true MONITORING_CIRCUIT_FAILURE_THRESHOLD=5 MONITORING_CIRCUIT_COOLDOWN_SECONDS=60 # Capture Settings MONITORING_CAPTURE_EXCEPTIONS=true MONITORING_CAPTURE_LOGS=false MONITORING_MIN_LEVEL=error # Source Window & Breadcrumbs MONITORING_SOURCE_CONTEXT=true MONITORING_MAX_SOURCE_LINES=5 MONITORING_BREADCRUMBS=true MONITORING_MAX_BREADCRUMBS=25 # Privacy Toggles MONITORING_SEND_USER=false MONITORING_SEND_IP=false MONITORING_SEND_HOSTNAME=false
🔍 In-Depth Feature Explanations
Circuit Breaker State Machine
The Circuit Breaker prevents the application from hanging when the monitoring server is offline:
[Closed] ◄───────────────────────────┐
│ │
(5 consecutive │ (Success Probe)
failures) │
│ │
▼ │
[Open] ──► (60s Cooldown) ──► [Half-Open]
- Closed (Normal Operation): All events are sent immediately over HTTP. If a request fails, it records a failure.
- Open (Tripped State): Occurs after 5 consecutive failures. Outbound HTTP requests are blocked. Events go straight to the buffer.
- Half-Open (Testing Probe): Occurs after the 60-second cooldown has passed. The next event is sent via HTTP as a test probe. If successful, state returns to Closed. If it fails, state returns to Open.
Recursive Privacy Sanitizer
Our sanitizer scrubs payloads recursively to prevent credential leakage:
- Redacted Keys: Matches keys containing
password,secret,token,authorization,card,cvv,cookie, orsession. - Trace Sanitization: Function argument lists in backtraces are completely removed to prevent DB password exposure.
- Query Params: URL query strings (e.g.
?token=123) are parsed, redacted, and reconstructed. - Path Normalization: Replaces absolute host paths (e.g.,
/home/username/public_html/app) with relative roots (/app) to conceal folder structures.
Offline Local File Buffer
When the server is down, retryable events are written to storage/app/monitoring/:
- Atomic Writes: Written to a temporary file first and renamed (
rename()) to avoid partial write corruptions. - File Locks: Retry tasks acquire an exclusive lock (
flock) on individual files to avoid concurrent retry tasks sending the same payload twice. - Pruning Policy: Enforces limits (default 500 files, 50MB) and evicts oldest items first (FIFO) to prevent server disk bloat.
Server Resource Collector
Every reported error automatically appends a resource snapshot:
- PHP Memory: Current process memory usage, peak memory, and configured limit.
- System Load: 1-minute, 5-minute, and 15-minute load averages via
sys_getloadavg()(returns null on unsupported operating systems). - Disk Usage: Total, free, and used bytes of the main partition.
- Writable Directories: Verifies permissions for
storage/,storage/logs/, andbootstrap/cache/.
OOM Safety (Minimal Mode)
If the agent catches a fatal Out-Of-Memory (OOM) error or a Maximum Execution Time Exceeded error:
- It bypasses expensive CPU and memory-intensive processes (like reading files for source code, scanning DB status, or traversing heavy stack frames).
- It compiles a minimal payload containing the error class, OOM message, memory usage details, and basic environment details.
- This ensures the report is delivered successfully before the PHP engine terminates.
🛠️ Developer API
Manual Capture
use Richness\LaravelMonitoring\MonitoringFacade as Monitoring; try { // ... } catch (\Throwable $e) { Monitoring::captureException($e); }
Attaching Breadcrumbs
Breadcrumbs act as a timeline of events leading up to the error:
Monitoring::breadcrumb( message: 'User added item to cart', metadata: ['item_id' => 456, 'quantity' => 1], category: 'cart' );
Custom Scope Context & Tags
Context attached to Monitoring is scoped to the current request and is automatically cleared in long-running processes (like Laravel Octane or Horizon):
Monitoring::context([ 'tenant_id' => 99, 'user_tier' => 'premium', ])->tags([ 'feature' => 'billing', ]);
💻 Console & Scheduling Commands
The package includes Artisan commands to manage local buffering and diagnostics:
php artisan monitoring:test
Sends a mock connection test exception to the central server.
php artisan monitoring:health
Prints configuration status, local buffer metrics, circuit breaker state, and server resources.
php artisan monitoring:health-report
Gathers database/cache latency status and resource load, and sends a system health check payload to the server.
php artisan monitoring:retry
Attempts to deliver buffered events to the central server using exponential backoff.
php artisan monitoring:prune
Cleans up expired or over-budget buffered event files.
Automation via Laravel Scheduler:
Add the following tasks to routes/console.php:
use Illuminate\Support\Facades\Schedule; Schedule::command('monitoring:health-report')->everyFiveMinutes(); Schedule::command('monitoring:retry')->everyFiveMinutes(); Schedule::command('monitoring:prune')->daily();
📡 Central Server API Ingestion Contracts
For your central server to process events, it must expose the following JSON API contracts:
1. Exception Report (POST /api/monitor/v1/errors)
{
"schema_version": 1,
"event_type": "error",
"event_id": "01M6HV2NVJ...",
"timestamp": "2026-08-17T03:00:00+00:00",
"correlation_id": "01M6HV2NT...",
"environment": "production",
"release": "v1.0.4",
"server_name": "web-prod-01",
"level": "error",
"exception_class": "RuntimeException",
"message": "Division by zero",
"code": 0,
"file": "/app/Http/Controllers/MathController.php",
"line": 42,
"category": "application",
"previous_exceptions": [],
"trace": [],
"resources": {}
}
2. Health Check (POST /api/monitor/v1/health)
{
"schema_version": 1,
"event_type": "health",
"event_id": "01M6HV2NV...",
"timestamp": "2026-08-17T03:00:00+00:00",
"environment": "production",
"release": "v1.0.4",
"server_name": "web-prod-01",
"resources": {
"memory": {},
"load_average": {},
"disk": {},
"storage": {},
"runtime": {}
},
"services": {
"database": {
"reachable": true,
"latency_ms": 15
},
"cache": {
"working": true
}
}
}
❓ Troubleshooting & FAQs
Q: Does the package buffer errors in database tables?
No. It has a zero-DB footprint. It buffers failed events strictly as flat .json files inside storage/app/monitoring/ to ensure no load is placed on MySQL/PostgreSQL during outages.
Q: Can I use this in Octane / Swoole daemons?
Yes. All context memory storage is managed by MonitoringContextStore utilizing Laravel's request-lifecycle container bindings, preventing memory leaks between separate HTTP worker requests.
Q: How do I know if the Circuit Breaker is tripped?
Run php artisan monitoring:health. It will report the current status: Closed, Open, or Half-Open.
📄 License
The MIT License (MIT). See LICENSE.md for details.