systemsx / lantern
Laravel-first custom events and metrics for Lantern.
Requires
- php: ^8.2
- illuminate/bus: ^11.0|^12.0|^13.0
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/contracts: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/queue: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0|^11.0
- pestphp/pest: ^3.8|^4.0|^5.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0|^5.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Send type-safe Custom Events and counter, gauge, and distribution points from Laravel to Lantern. Delivery uses shared Redis buffering or your Laravel queue and Lantern's native HTTP API; Laravel Nightwatch is optional and is used only for best-effort trace correlation.
composer require systemsx/lantern
LANTERN_ENABLED=true LANTERN_INGEST_URL=https://ingress.lantern.example LANTERN_TOKEN= LANTERN_QUEUE_CONNECTION= LANTERN_QUEUE=lantern LANTERN_TIMEOUT=10 LANTERN_CONNECT_TIMEOUT=3
Ensure a worker processes the dedicated queue:
php artisan queue:work --queue=lantern,default
use SystemsX\Lantern\Facades\Lantern; Lantern::event('ai.completion', [ 'model' => 'gpt-5', 'input_tokens' => 1250, 'output_tokens' => 420, ]); Lantern::counter('ai.cost_usd', 0.0142, ['model' => 'gpt-5']); Lantern::gauge('users.active', 428); Lantern::distribution('checkout.duration_ms', 182.4, ['region' => 'eu-west-2']);
By default, records are validated synchronously, buffered for the current request, command, or queued job, and then delivered in retried queue jobs capped at 500 records by default. Network and receiver failures do not fail the instrumented execution. Long-running custom workers should call Lantern::flush() at their own execution boundary.
Shared Redis batching
Enable this mode to combine records from separate requests and workers before making HTTP requests:
LANTERN_BUFFER_DRIVER=redis LANTERN_REDIS_CONNECTION=default LANTERN_FLUSH_INTERVAL=10 LANTERN_BATCH_SIZE=500 LANTERN_BATCH_BYTES=7340032 LANTERN_FLUSH_MAX_BATCHES=20
LANTERN_REDIS_CONNECTION names a Redis connection in the application's config/database.php. All application servers that should combine telemetry must use the same Redis instance, database, connection prefix and Lantern token. Buffers are isolated by destination URL and token; never put the token in the Redis key prefix. LANTERN_BUFFER_PREFIX optionally changes the default lantern:buffer prefix.
In this mode Lantern::flush() appends records to Redis at each request/job boundary. It does not send HTTP requests or dispatch a delivery job. The original event timestamp and trace context are captured before buffering. Once Redis accepts a transfer, repeating that transfer within 24 hours cannot append duplicate records.
The package automatically registers lantern:flush with Laravel's scheduler. Enable the application's usual scheduler cron (once per minute):
* * * * * cd /path/to/application && php artisan schedule:run >> /dev/null 2>&1
Laravel keeps schedule:run running through the minute to invoke the background command every 10 seconds. php artisan schedule:work is an alternative for local development. There is no separate OS cron entry every 10 seconds and no queue worker is required for new Redis-buffered records. Keep existing queue workers running to deliver jobs created before switching modes.
Each flush splits events and metrics into separate HTTP batches, capped by both the record count and the encoded JSON body size, including its envelope. The interval is periodic: reaching the record cap does not send from an application request. A flush sends at most LANTERN_FLUSH_MAX_BATCHES batches; remaining records wait for another invocation. Normal delivery latency is up to the interval plus sender/receiver time, and can be longer during a backlog or outage.
Supported intervals are 1, 2, 5, 10, 15, 20, 30 and 60 seconds. Record limits are bounded to 1–10,000, and body limits to 1 KiB–8 MiB to match the receiver's envelope limit. Defaults are 500 records and 7 MiB. An individual record that cannot fit the configured body limit is rejected before enqueueing. Increasing a cap does not bypass the receiver's per-record validation limits.
Only one sender holds each endpoint's current Redis lease. A process crash leaves the batch recoverable after its lease expires. Timeouts, 429s and server errors retain the batch and its original request ID; retries use backoff and respect Retry-After across both endpoints. Transient errors remain retryable without an automatic discard deadline. Deterministic HTTP rejections are retained in the buffer's failed list and logged. After correcting the cause, retry up to 100 retained batches per endpoint with:
php artisan lantern:flush --retry-failed
Pending and in-flight records have no automatic expiry. Redis persistence, capacity and an eviction policy that preserves these keys are necessary for durability. Redis write failures are reported at automatic lifecycle flushes; explicit Lantern::flush() calls throw, and records not accepted by Redis cannot survive an application process exit. Drain buffers before rotating the token, URL, Redis connection or prefix, since those settings identify the existing buffer.
Existing installations remain in LANTERN_BUFFER_DRIVER=queue mode until Redis mode is enabled. If you previously published config/lantern.php, merge the new ENV-backed entries from the package config. Rebuild cached configuration, restart long-running application workers, and run php artisan schedule:interrupt after deployment so the next scheduler run loads the updated settings. php artisan schedule:list should show lantern:flush when Redis mode is enabled.
For application tests:
$lantern = Lantern::fake(); Lantern::event('order.completed', ['order_id' => '123']); $lantern->assertEventRecorded('order.completed');
Development
The Redis integration tests require the PHP Redis extension and a local Redis server on 127.0.0.1:6379. They use database 15 with unique test prefixes and delete only their own keys.
composer install
composer test
The package supports PHP 8.2+ and Laravel 11, 12, and 13.