Skip to main content

Quick Start

composer require kirschbaum-development/monitor
php artisan vendor:publish --tag=monitor-config

Add the trace middleware so every request, and every job it dispatches, shares one trace id:

// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->append(\Kirschbaum\Monitor\Http\Middleware\StartTrace::class);
})

Declare a critical operation as a class:

php artisan make:control-point Payments/ChargeCard --profile=external
#[Point('payments.charge_card', profile: 'external')]
final class ChargeCard extends ControlPoint
{
public function __construct(private readonly Invoice $invoice) {}

protected function control(Control $control): void
{
$control
->recover(CardDeclined::class, fn (CardDeclined $e) => ChargeResult::declined($e->code))
->ensure(fn (ChargeResult $r): bool => $r->settled, 'charge must be settled')
->escalate(PagePayments::class);
}

public function context(): array
{
return ['invoice' => $this->invoice->id];
}

public function handle(StripeClient $stripe): ChargeResult
{
return $stripe->charge($this->invoice);
}
}

ChargeCard::run($invoice); // the value, or throws what escaped
ChargeCard::attempt($invoice); // an Outcome: ->status, ->value, ->exception, ->attempts, ->durationMs

Test it by name:

Monitor::fake()->failing('payments.charge_card', new CardDeclined('insufficient_funds'));

ChargeCard::run($invoice);

Monitor::assertRecovered('payments.charge_card', from: CardDeclined::class);
Monitor::assertNothingEscalated();

And prove in CI that every critical operation is declared and complete:

php artisan monitor:points --check